Shieldlist

Getting started

Cette page n'existe qu'en anglais pour le moment.

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:17454 and 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 T for 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 auto and 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: false there).