Add Systemd Setup page

2026-04-18 06:04:56 -04:00
parent 9de6ea3dff
commit d6c5351747

373
Systemd-Setup.md Normal file

@@ -0,0 +1,373 @@
# Systemd Setup
End-to-end walkthrough: take DECNET from a fresh clone to a set of
systemd-managed services running as a non-root user with the minimum
Linux capabilities needed to manage MACVLAN interfaces.
See also: [Environment variables](Environment-Variables),
[DB drivers](Database-Drivers), [CLI reference](CLI-Reference).
## 1. Prerequisites
- Linux host (bare metal or VM). WSL is not supported for the network
path.
- Docker Engine and the Docker Compose plugin.
- Python 3.11, 3.12 or 3.13.
- `sudo` / root access (needed once for `decnet deploy` to create
MACVLAN links, and to install unit files).
- A NIC placed in promiscuous mode if you plan to run the sniffer on
the host interface.
- A dedicated UNIX user and group named `decnet` (the unit files run
as that user):
```bash
sudo useradd --system --create-home --shell /usr/sbin/nologin decnet
sudo usermod -aG docker decnet
```
## 2. Clone and install
Install into a virtualenv owned by the `decnet` user so the service
files can point at `.venv/bin/decnet`.
```bash
sudo -u decnet -H bash <<'EOF'
cd /opt
git clone https://git.resacachile.cl/anti/DECNET.git
cd DECNET
python3 -m venv .venv
.venv/bin/pip install --upgrade pip
.venv/bin/pip install -e .
EOF
```
After this, the CLI is available at `/opt/DECNET/.venv/bin/decnet`.
## 3. Configure the environment
DECNET loads configuration from a `.env` file in the working
directory. Create `.env.local` with real values and symlink (or
rename) it to `.env`, or point `EnvironmentFile=` at whichever file
you use.
```bash
sudo -u decnet cp /opt/DECNET/env.config.example /opt/DECNET/.env
sudo -u decnet editor /opt/DECNET/.env
```
See [Environment variables](Environment-Variables) for the full list.
The services expect at least:
- `DECNET_DB_URL` (or the SQLite default) - see [DB drivers](Database-Drivers)
- `DECNET_SYSTEM_LOGS` - path to the rotating system log file
- API / Web bind hosts and ports if you are overriding the defaults
## 4. Deploy honeypots once
`decnet deploy` provisions the MACVLAN network and the decky
containers. Run it once, as root, before enabling the long-lived
services:
```bash
sudo /opt/DECNET/.venv/bin/decnet deploy \
--mode unihost \
--deckies 5 \
--interface eth0 \
--randomize-services
```
Confirm with:
```bash
/opt/DECNET/.venv/bin/decnet status
```
Deploy only needs root for the MACVLAN setup; the long-running API /
web / microservice units run unprivileged with the capabilities
listed below.
## 5. Install the bundled unit files
Two unit files ship in `deploy/`:
### `/etc/systemd/system/decnet-api.service`
```ini
[Unit]
Description=DECNET API Service
After=network.target docker.service
Requires=docker.service
[Service]
Type=simple
User=decnet
Group=decnet
WorkingDirectory=/opt/DECNET
EnvironmentFile=/opt/DECNET/.env
ExecStart=/opt/DECNET/.venv/bin/decnet api
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=read-only
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
```
### `/etc/systemd/system/decnet-web.service`
```ini
[Unit]
Description=DECNET Web Dashboard Service
After=network.target decnet-api.service
[Service]
Type=simple
User=decnet
Group=decnet
WorkingDirectory=/opt/DECNET
EnvironmentFile=/opt/DECNET/.env
ExecStart=/opt/DECNET/.venv/bin/decnet web
# Uncomment to bind to a privileged port (80/443) as non-root.
# CapabilityBoundingSet=CAP_NET_BIND_SERVICE
# AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=read-only
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
```
### Directive reference
- `User`, `Group` - run as the unprivileged `decnet` account.
- `WorkingDirectory` - sets `$PWD`; DECNET resolves relative paths
(log files, sqlite URLs) from here.
- `EnvironmentFile` - loads `.env` the same way the CLI does when run
interactively.
- `ExecStart` - the venv's `decnet` entrypoint; no daemon flag is
needed because systemd already runs the process in the background
and handles restarts.
- `Restart=on-failure`, `RestartSec=5` - systemd restarts crashes
after five seconds; successful exits are not restarted.
- `CapabilityBoundingSet` / `AmbientCapabilities` - hand the process
`CAP_NET_ADMIN` (netlink, MACVLAN/IPVLAN) and `CAP_NET_RAW` (packet
capture, ARP) without making it root. `CAP_NET_BIND_SERVICE` is
only needed if a service binds a port below 1024.
- `NoNewPrivileges=yes` - blocks setuid escalation inside the
process tree.
- `ProtectSystem=full` - `/usr`, `/boot`, `/etc` become read-only for
the unit.
- `ProtectHome=read-only` - `/home` and `/root` become read-only.
- `After=` / `Requires=` - API needs Docker; Web starts after API.
Copy them into place and fix the paths:
```bash
sudo install -m 644 /opt/DECNET/deploy/decnet-api.service /etc/systemd/system/
sudo install -m 644 /opt/DECNET/deploy/decnet-web.service /etc/systemd/system/
sudo sed -i 's|/path/to/DECNET|/opt/DECNET|g' \
/etc/systemd/system/decnet-api.service \
/etc/systemd/system/decnet-web.service
```
## 6. Optional per-microservice units
The CLI already supports detached mode for several subcommands
(`decnet profiler --daemon`, `decnet sniffer --daemon`,
`decnet collect --daemon`, `decnet mutate --daemon`, `decnet probe
--daemon`, `decnet correlate --daemon`). Under systemd you should
**omit** `--daemon`: systemd is the supervisor, and double-forking
breaks its child tracking. Set `Type=simple` and let the foreground
process run.
All microservice units should order after the API. Use `Wants=`
unless the microservice cannot function at all without the API
(persistence-heavy units such as `collect` and `correlate`), in which
case use `Requires=`.
### `/etc/systemd/system/decnet-profiler.service`
Wants the API - it enriches sessions but can start independently.
```ini
[Unit]
Description=DECNET Profiler
After=network.target decnet-api.service
Wants=decnet-api.service
[Service]
Type=simple
User=decnet
Group=decnet
WorkingDirectory=/opt/DECNET
EnvironmentFile=/opt/DECNET/.env
ExecStart=/opt/DECNET/.venv/bin/decnet profiler
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=read-only
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
```
Required env: `DECNET_DB_URL`, `DECNET_SYSTEM_LOGS`.
### `/etc/systemd/system/decnet-sniffer.service`
Needs `CAP_NET_RAW` plus `CAP_NET_ADMIN` to attach to the decoy
interface. Wants the API.
```ini
[Unit]
Description=DECNET Sniffer
After=network.target decnet-api.service
Wants=decnet-api.service
[Service]
Type=simple
User=decnet
Group=decnet
WorkingDirectory=/opt/DECNET
EnvironmentFile=/opt/DECNET/.env
ExecStart=/opt/DECNET/.venv/bin/decnet sniffer
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=read-only
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
```
Required env: `DECNET_SNIFFER_IFACE` (or the auto-probed interface),
`DECNET_SYSTEM_LOGS`.
### `/etc/systemd/system/decnet-collect.service`
Writes into the shared database - `Requires=` the API so schema and
migrations are in place first.
```ini
[Unit]
Description=DECNET Collector
After=network.target decnet-api.service
Requires=decnet-api.service
[Service]
Type=simple
User=decnet
Group=decnet
WorkingDirectory=/opt/DECNET
EnvironmentFile=/opt/DECNET/.env
ExecStart=/opt/DECNET/.venv/bin/decnet collect
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=read-only
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
```
Required env: `DECNET_DB_URL`, `DECNET_SYSTEM_LOGS`, and any input
source variables documented in [Environment variables](Environment-Variables).
### `/etc/systemd/system/decnet-mutate.service`
Stateless helper that rewrites decky personas; Wants the API.
```ini
[Unit]
Description=DECNET Mutator
After=network.target decnet-api.service
Wants=decnet-api.service
[Service]
Type=simple
User=decnet
Group=decnet
WorkingDirectory=/opt/DECNET
EnvironmentFile=/opt/DECNET/.env
ExecStart=/opt/DECNET/.venv/bin/decnet mutate --watch
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=read-only
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
```
Required env: `DECNET_SYSTEM_LOGS` plus any per-service override
files referenced by the mutator.
## 7. Enable and start
```bash
sudo systemctl daemon-reload
sudo systemctl enable --now \
decnet-api.service \
decnet-web.service \
decnet-profiler.service \
decnet-sniffer.service \
decnet-collect.service \
decnet-mutate.service
```
Check status and tail logs:
```bash
systemctl status decnet-api
journalctl -u decnet-api -f
journalctl -u decnet-web -f
```
## 8. Log locations
DECNET writes logs in two places:
- **journald**, via stdout/stderr from each unit. Query with
`journalctl -u decnet-<name>`.
- **`$DECNET_SYSTEM_LOGS`**, a rotating file handler
(`InodeAwareRotatingFileHandler`) that survives external log
rotation. The default is `decnet.system.log` in the unit's
`WorkingDirectory` (so `/opt/DECNET/decnet.system.log` with the
layout above). Point it at `/var/log/decnet/system.log` for
multi-service deployments and make sure the `decnet` user owns the
target directory:
```bash
sudo install -d -o decnet -g decnet /var/log/decnet
```
Then set `DECNET_SYSTEM_LOGS=/var/log/decnet/system.log` in `.env`
and restart the units.