1
Systemd Setup
anti edited this page 2026-04-18 06:04:56 -04:00

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, DB drivers, 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):
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.

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.

sudo -u decnet cp /opt/DECNET/env.config.example /opt/DECNET/.env
sudo -u decnet editor /opt/DECNET/.env

See Environment variables for the full list. The services expect at least:

  • DECNET_DB_URL (or the SQLite default) - see DB 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:

sudo /opt/DECNET/.venv/bin/decnet deploy \
    --mode unihost \
    --deckies 5 \
    --interface eth0 \
    --randomize-services

Confirm with:

/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

[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

[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:

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.

[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.

[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.

[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.

/etc/systemd/system/decnet-mutate.service

Stateless helper that rewrites decky personas; Wants the API.

[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

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:

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:
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.