Getting started
The controller runs on one machine — usually not one it protects, though it may share a machine with an agent — and the agents connect to it. This page takes an empty machine to a Web UI with its first enrolled server.
Install
On Debian and its derivatives, the APT repository (the same one as the agent's; add it on this machine too, it is not there by itself):
curl -fsSL https://repo.lrob.net/apt/shieldlist/shieldlist.gpg -o /usr/share/keyrings/shieldlist.gpg
echo "deb [signed-by=/usr/share/keyrings/shieldlist.gpg] https://repo.lrob.net/apt/shieldlist stable main" > /etc/apt/sources.list.d/shieldlist.list
apt update && apt install shieldlist-controller
The repository is signed with the Shieldlist key, fingerprint
B528 0C88 7016 8A94 40A9 05C4 3BB5 8113 CFAB 6DC5
(gpg --show-keys shieldlist.gpg); dev in place of stable gets
prereleases and development builds. The same .deb sits on the release
page, one per architecture, for apt install ./file.deb.
The package creates the shieldlist system user, writes a starter tree
under /etc/shieldlist/controller when there is none (local listeners,
no rules yet), installs the unit and starts the service. Elsewhere, or
without packages, see Installing — the binary, the user,
the tree, the unit, by hand.
Requirements
- Linux with systemd; one static binary, no database daemon (the
store is SQLite in
/var/lib/shieldlist/controller). - A name and a certificate for the Web UI, if anyone opens it from
elsewhere: the API and Web UI listen on
127.0.0.1:17454and expect a reverse proxy with TLS in front (below). - Port 17453 reachable by the agents — the controller mints its own certificate for it, agents pin its fingerprint; nothing else to set up.
Where it listens
/etc/shieldlist/controller/controller.yaml:
listen:
agents: ":17453" # the agents; TLS the controller signs itself
api: 127.0.0.1:17454 # the API and the Web UI; put nginx in front
nginx and certbot's nginx plugin first, if they are not there —
apt install nginx certbot python3-certbot-nginx on Debian and Ubuntu,
dnf install nginx certbot python3-certbot-nginx on Red Hat and its
family. Without the plugin, --nginx is not a flag certbot knows.
Then, on the same machine — in
/etc/nginx/sites-available/shield.example.net.conf on Debian and
Ubuntu, /etc/nginx/conf.d/shield.example.net.conf on Red Hat and its
family:
server {
server_name shield.example.net;
listen 80;
listen [::]:80;
location / {
proxy_pass http://127.0.0.1:17454;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
}
}
# Debian and Ubuntu: the symlink is what enables the site
ln -s /etc/nginx/sites-available/shield.example.net.conf /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
certbot --nginx -d shield.example.net --redirect
certbot adds the certificate, the listen 443 ssl lines and the
redirection from 80 in place, and renews on its own; the name must
already resolve here, since it proves you own it over port 80.
The controller believes X-Forwarded-* from a loopback peer only.
The first person
shieldlist-controller account -name you # asks for a password twice
Twelve characters at least, and there is a reason it is strict: an account is never locked out, however many times its password is refused. Locking accounts would hand an attacker a way to lock you out; the refusals are logged instead, and a rule of your own can ban whoever collects them. A short password with no lockout behind it is the one combination that does not work.
Sign in at the Web UI with that name and password. Later, under
Settings → Access, add people; a one-time code can be enrolled per
account. For scripts and for shield from your desk:
shieldlist-controller api-token -name deploy
Run them as root: they open the controller's store directly, and the binary drops to the service user itself, so what it creates belongs to the service and not to root.
Give it rules
The tree is an agent's tree — parsers/, rules/, policies/,
reports/, exemptions.yaml at the root (what every machine gets), the
same four directories under packs/<name>/ for each pack (a set
given to machines by name) — plus controller.yaml and one
agents/<hostname>.yaml per machine: its packs, what makes it differ.
The starter tree has no rules: create packs from the Web UI (Rules →
New pack…, then rules and patterns in it), copy a working machine's
/etc/shieldlist language into packs/<something>/ (minus agent.yaml
and secrets.yaml, which stay on the machine), or start from the
agent's example pack (/usr/share/shieldlist-agent/examples/ssh/ where
the agent is installed). Then:
shieldlist-controller -t # validates the tree AND every machine's assembly
systemctl reload shieldlist-controller # SIGHUP: loaded whole or refused whole, pushed to the agents
-t says, per declared machine, what it would receive and where it
differs from the defaults. A machine runs the root and the packs its
declaration names — give it packs in the Web UI (Servers → the server →
Rules) or packs: [...] in its file. See Configuration
for packs, levels, overrides and per-machine switches.
Enrol the first machine
On the controller:
shieldlist-controller token # a join token: one use, valid a day
On the machine, running the agent (0.0.4 or newer):
# /etc/shieldlist/agent.yaml
mode: enrolled
controller: https://shield.example.net:17453 # or the address, port 17453 either way
shieldlist-agent -t && systemctl restart shieldlist-agent
shield enrol <join-token>
Within seconds the machine appears under Servers with the revision it
holds, its bans and detections start arriving, and shield status on
the machine shows controller_sync. The machine keeps its own
agent.yaml, secrets and local exemptions; the rule language now comes
from the controller. Fallback at any time: mode: local, restart — the
local files were never touched.
Then
- The Web UI: the feed, the map, the panel, bans by hand.
- Configuration: what each machine runs (levels, overrides, offer/enforce, who may lift), published exemption sources, geolocation, retention, updates.
- The API, and
shield -controller URL -token Tfor the fleet from any machine.
Report offenders to AbuseIPDB
Reporting is one switch and one key: Settings → General → AbuseIPDB in the Web UI
(or abuseipdb: in the controller's secrets.yaml — key,
daily_limit, enabled). The controller reports for every server whose
declaration says reporting: controller (the default); a server saying
reporting: agent reports with the key in its own secrets.yaml.
- One report per ban, composed from the rule's preset — categories and named fields only, never the raw log line. Recidivism is reported once per episode. Test bans are never reported.
- The daily limit is your plan's: leave it on
autoand the first answer of the day teaches it, or set a number of your own — deliberately below the plan when several machines share one account. - Two counts on the page, kept apart on purpose: what this controller sent today, and what AbuseIPDB says is left on the whole account. On a shared account the second moves faster than the first — every machine and tool reporting with the key draws on it.
- Off keeps the key and sends nothing until it is turned back on. A
server reporting with its own key follows its own
secrets.yaml(enabled: falsethere).