Interstellio Lunar Install
==========================

Installation instructions for Lunar.

Minimum dedicated server requirements
-------------------------------------

    * Ubuntu 24.04 LTS
    * 2 GB RAM
    * 4 thread processor
    * 100 GB disk space

.. note::

    The need for dedicated processor resources is critical
    for ensuring latency does not impact the processing of
    RADIUS packets.

Prepare Server
--------------

Hostname
~~~~~~~~

Edit */etc/hosts* and ensure it is updated as follows. Remember to change the lunar-1.domain.com.

.. code:: text

    127.0.0.1 localhost
    127.0.1.1 lunar-1 lunar-1.domain.com

    # The following lines are desirable for IPv6 capable hosts
    ::1     ip6-localhost ip6-loopback
    fe00::0 ip6-localnet
    ff00::0 ip6-mcastprefix
    ff02::1 ip6-allnodes
    ff02::2 ip6-allrouters

**Update hostname with the following command:**

.. code:: bash

    hostnamectl set-hostname lunar-1.domain.com

Performance configuration
~~~~~~~~~~~~~~~~~~~~~~~~~

Ensure following set in */etc/security/limits.conf* for the ``lunar`` user.
These per-user entries take precedence over the wildcard (``*``) defaults, so
nothing in *limits.conf* caps the ``lunar`` process:

.. code:: text

    lunar  soft  nofile      2000000
    lunar  hard  nofile      2000000
    lunar  soft  memlock     unlimited
    lunar  hard  memlock     unlimited

For virtual/kvm or physical server excluding LXC set following in */etc/sysctl.conf*:

.. code:: text

    fs.aio-max-nr=1048576
    fs.nr_open=2097152
    fs.inotify.max_queued_events=1048576
    fs.inotify.max_user_instances=8192
    fs.inotify.max_user_watches=1048576
    kernel.keys.maxkeys=2000
    kernel.threads-max=262144
    kernel.keys.maxbytes=2000000
    net.ipv4.tcp_tw_reuse=1
    net.ipv4.tcp_fin_timeout=3
    net.ipv4.tcp_max_syn_backlog=32768
    net.ipv4.ip_local_port_range=11000 65535
    net.ipv4.neigh.default.gc_thresh3=8192
    net.ipv6.neigh.default.gc_thresh3=8192
    net.core.netdev_max_backlog=250000
    net.core.rmem_max=33554432
    net.core.somaxconn=65535
    vm.max_map_count=655300
    vm.swappiness=0

Upgrade the operating system and reboot
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code:: bash

    sudo apt update
    sudo apt upgrade -y
    reboot

Install Dependencies
--------------------

Lunar ships as prebuilt binaries, so only runtime libraries are needed - no
compiler, headers or build tools.

.. code:: bash

    sudo apt install nginx -y
    sudo apt install libjemalloc2 -y
    sudo apt install libmsgpackc2 -y
    sudo apt install libnghttp2-14 -y
    sudo apt install librdkafka1 -y
    sudo apt install libyaml-cpp0.8 -y
    sudo apt install libssl3t64 -y
    sudo apt install zlib1g -y

Installation
------------

Create lunar user
~~~~~~~~~~~~~~~~~

.. code:: bash

    sudo useradd lunar -s /usr/sbin/nologin -d /opt/lunar

Copy Lunar
~~~~~~~~~~

The content of lunar-*.*.*-ubuntu-*.*-*.tgz package should be placed in /opt/lunar.

We strongly recommend installing Lunar under ``/opt/lunar``. This is our
standard location, and all of our documentation, the systemd service, the
default configuration paths and support procedures assume it. Installing
elsewhere is possible but unsupported and will require you to adjust these
references yourself.


Systemd service
~~~~~~~~~~~~~~~

.. code:: bash

    sudo ln -s /opt/lunar/etc/systemd/system/lunar.service /etc/systemd/system/lunar.service
    sudo systemctl enable lunar


Configuration
-------------

**The NebularStack team would have provided you the following:**

    * Endpoint URL to your nearest Nebularstack region subscriber platform.
    * Server ID  identifies your server uniquely.
    * Secret is used for authentication between Lunar and our Subscriber platform.
    * License token authorising this server and the services it may run.

**Requesting your license:**

The license is bound to this specific machine. Get the machine's machine-id
and give it to the NebularStack team when requesting your license:

.. code:: bash

    /opt/lunar/bin/lunar -m

You will receive a license token in return. Place it in the ``license`` option
as shown below.

**Update /opt/lunar/etc/lunar.yaml with the above as per example:**

.. code:: yaml

    lunar:
      license: <your_license_token>
      log_level: DEBUG
      log_prefix: ""
      debug_mode: True
      server_id: <your_lunar_server_id>
      secret: <your_lunar_secret_key>
      api_threads: 16
      api_port: 1337
      api_host: 127.0.0.1

    services:
      subscriber: false

    subscriber:
      endpoint: https://<region nebulastack url endpoint>
      ssl_verify: True

    protocol:
      radius:
        dictionary: /opt/lunar/share/lunar/radius/dictionary.yaml
        virtual_rate_limit: 2500
        queue_size: 2500
        request_timeout: 2
        udp_listen_threads: 2
        udp_reply_threads: 2
        inbound_request_process_threads: 2
        api_client_threads: 2
        api_client_thread_inflight: 128
        outbound_request_process_threads: 2

To go live, set ``server_id`` and ``secret`` to the values NebularStack gave
you, paste your ``license`` token (request one with ``/opt/lunar/bin/lunar -m``, see above),
point ``subscriber.endpoint`` at your nearest region, and set
``services.subscriber`` to ``true`` so the RADIUS service starts. For
production also set ``log_level`` to ``INFO`` and ``debug_mode`` to ``False``.

Configuration reference
~~~~~~~~~~~~~~~~~~~~~~~

Every setting below shows the value shipped in the default ``lunar.yaml``.

**lunar** - core service and the local management API.

``license`` (default: ``""``)
    License token authorising this server and unlocking the services it may
    run. Verified offline and bound to this machine, so a license issued for
    one host will not validate on another. Request one from NebularStack,
    providing this machine's machine-id from ``lunar -m``.

``log_level`` (default: ``DEBUG``)
    Logging verbosity. One of ``DEBUG``, ``INFO``, ``WARNING`` or ``ERROR``.
    Use ``INFO`` in production.

``log_prefix`` (default: ``""``)
    Text prepended to every syslog line (e.g. the hostname or region). Handy
    when several instances ship to the same syslog. Empty disables it.

``debug_mode`` (default: ``True``)
    When ``True``, also writes log lines to stdout/stderr. Set ``False`` in
    production.

``server_id`` (default: ``<your_lunar_server_id>``)
    Unique server identifier supplied by NebularStack.

``secret`` (default: ``<your_lunar_secret_key>``)
    Shared secret used to authenticate Lunar to the Subscriber platform.

``api_threads`` (default: ``16``)
    Worker threads for the local management API server.

``api_port`` (default: ``1337``)
    Port for the local management API.

``api_host`` (default: ``127.0.0.1``)
    Address the local management API binds to (kept behind nginx).

**services** - which protocol services to start.

``subscriber`` (default: ``false``)
    Enable the subscriber (RADIUS) service. Must be ``true`` to serve RADIUS.

**subscriber** - connection to the NebularStack Subscriber platform.

``endpoint`` (default: region URL)
    URL of your nearest NebularStack region subscriber platform, or your own
    backend that implements the same API.

``ssl_verify`` (default: ``True``)
    Verify the endpoint's TLS certificate. Only disable for local testing.

**protocol.radius** - the RADIUS server and its pipeline tuning.

``dictionary`` (default: ``/opt/lunar/share/lunar/radius/dictionary.yaml``)
    Path to the RADIUS dictionary loaded at start-up.

``virtual_rate_limit`` (default: ``2500``)
    Per-virtual-server request rate limit (packets/second).

``queue_size`` (default: ``2500``)
    Maximum in-flight packets queued before new requests are dropped.

``request_timeout`` (default: ``2``)
    Seconds to wait for a Subscriber API response before timing out.

``udp_listen_threads`` (default: ``2``)
    Threads reading inbound packets off the UDP sockets.

``udp_reply_threads`` (default: ``2``)
    Threads writing reply packets back to the UDP sockets.

``inbound_request_process_threads`` (default: ``2``)
    Threads parsing/validating inbound packets (capped at 512).

``api_client_threads`` (default: ``2``)
    Worker threads of the async API client that calls the Subscriber platform.

``api_client_thread_inflight`` (default: ``128``)
    Maximum concurrent API requests per ``api_client_threads`` worker.

``outbound_request_process_threads`` (default: ``2``)
    Threads building replies from API responses (capped at 512).

.. note::

    The thread and queue settings are performance tuning - the defaults suit most
    deployments. Raise the ``*_threads`` and ``queue_size`` values only on
    high-throughput servers with dedicated cores.

.. warning::

    **Ensure this config file is only readable by the lunar user!**

.. code:: bash

    sudo chmod 600 /opt/lunar/etc/lunar.yaml
    sudo chown lunar:lunar /opt/lunar/etc/lunar.yaml

**Generate SSL Key and Certs:**

To securely make API calls from the Subscriber Management platform, NGINX should provide us
with an SSL proxy to Lunar, and we will need to create a self-signed certificate. Alternatively,
you can set up your own SSL certificates that a certificate authority has signed. However, (CA)
authority-signed certificates will only enhance security if you point a fully qualified domain
name (FQDN) to your server.

Self-signed certificate generation process:

.. code:: bash

    sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/lunar.key -out /etc/ssl/certs/lunar.crt

.. warning::

    **Ensure certificate key is only readable by the root and ssl-cert group!**

.. code:: bash

    sudo chmod 640 /etc/ssl/private/lunar.key
    sudo chown root:ssl-cert /etc/ssl/private/lunar.key

**Update NGINX Configuration:**

.. note::

    The following assumes you're using a dedicated server for your Lunar server.

Ensure the default */etc/nginx/sites-enabled/default* contains only the following:

.. code:: nginx

    upstream LUNAR {
        keepalive 128;
        keepalive_requests 128;
        keepalive_timeout 30;
        server 127.0.0.1:1337;
    }

    server {
        listen 80 default_server;

        root /var/www;

        location / {
            proxy_redirect off;
            proxy_http_version 1.1;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header Host $host;
            proxy_set_header Connection "";
            proxy_buffering on;
            proxy_pass http://LUNAR;
        }
    }

    server {
        listen 443 default_server ssl;

        ssl_certificate /etc/ssl/certs/lunar.crt;
        ssl_certificate_key /etc/ssl/private/lunar.key;
        ssl_protocols TLSv1.2 TLSv1.3;

        root /var/www;

        location / {
            proxy_redirect off;
            proxy_http_version 1.1;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header Host $host;
            proxy_set_header Connection "";
            proxy_buffering on;
            proxy_pass http://LUNAR;
        }
    }

Start Server
------------

.. code:: bash

    sudo systemctl enable nginx
    sudo systemctl restart nginx
    sudo systemctl start lunar

Next steps
==========

You can test your instance locally or remotely with ``radtest``, the RADIUS
test client that ships with lunar (installed as ``/opt/lunar/bin/radtest``).
It is built on lunar's own RADIUS codec, so a request it accepts is a request
the server accepts, and it supports PAP, CHAP, MS-CHAP and MS-CHAPv2.

The RADIUS virtual server and its clients are NOT configured on lunar directly -
lunar fetches them from your backend. So to test against a live setup, first
create a virtual on your backend (the NebularStack Subscriber Management
platform, or your own backend) with a client for THIS server: type NAS, the
server's primary IP as the client address, and a shared secret (for example
``testing123``). Ensure the client name is the hostname of the server, and
create a test subscriber account to authenticate with.

Alternatively, for a quick self-contained check with NO backend, enable testing
mode: set ``protocol.radius.testing: true`` (and ``services.subscriber: true``)
in ``lunar.yaml`` and restart lunar. Lunar then starts a built-in ``TEST``
virtual on ``127.0.0.1`` with a ``client-test`` client and secret
``testing123``, mirroring authentication back so ``radtest`` succeeds without any
backend - point ``radtest`` at ``127.0.0.1`` in that case. Never use testing mode
in production.

**Also, ensure a test account is created and tested as the following example:**

.. code:: bash

    $ /opt/lunar/bin/radtest --username test@example.com --password testing123 172.16.0.1 testing123

     Interstellio Lunar 2.0.2
         (C)Copyright Interstellio IO (Pty) Ltd 2024-2026.

     Sent Access-Request Id 23 from 172.16.0.1:40297 to 172.16.0.1:1812 length 90
         User-Name = "test@example.com"
         User-Password = "testing123"
         NAS-IP-Address = 172.16.0.1
         Message-Authenticator = 0x00000000000000000000000000000000
     Received Access-Accept Id 23 from 172.16.0.1:1812 to 172.16.0.1:40297 length 93
         Message-Authenticator = 0x93d72b55740823997bbd197e740bc898
         Class = 0x6e733a3a38613638333466352d656364312d343632352d626530312d3565376165613539353964363a3a313639303838393938393a3a61637469766174652d6c6f67696e3a3a31

    radtest: Success

*As per above we should receive an Access-Accept packet, ending in* ``radtest: Success``.

Run ``radtest`` with no arguments to see all options (``--username`` /
``--password`` for the credential, ``-t`` to pick the authentication type,
``--nas-port``, ``--nas-ip-address``, ``--source-ip``, and more). ``--username``
and ``--password`` are both optional: omit ``--password`` and no password
credential is sent (``-t`` is ignored), which is the basis for future
passwordless methods such as MAC authentication by ``Calling-Station-Id``.