Open app

Security checklist

Each server’s Security tab opens with a checklist: the handful of things every server exposed to the internet should have. Each item is answered from what the agent reported, never assumed, and every item still to fix comes with the command that fixes it.

ItemPasses when
SSH logins by key onlysshd refuses passwords (PasswordAuthentication and KbdInteractiveAuthentication both no)
No password login as rootPermitRootLogin is no or prohibit-password
SSH guessers get blockedSSH is not reachable from the internet, the warden enforces ssh-bruteforce, or a fail2ban SSH jail is on
A firewall refuses what is not alloweda firewall is on, and does not accept by default
No database or admin service open to the internetno sensitive port is reachable from outside
Security updates installedno security update is waiting
Security updates install themselvesunattended-upgrades (or dnf-automatic) is installed and turned on
Running the kernel it has installedno reboot is pending for a kernel update
Operating system still supportedthe system and its runtimes are not past end of life
Demerzel reads the web logsweb traffic was reported in the last 24 hours
No secret file servedno site answered a probe for .env, .git… with real content
Every site on valid HTTPSevery domain has a valid, non-expiring certificate
Every phpMyAdmin behind a basic autheach phpMyAdmin the web server serves asks for a basic auth password before its own login page
Apache gives up on silent clients quicklyApache’s Timeout is 60 seconds or less and mod_reqtimeout is on (n/a without Apache)
SSH limits what a connection may tryMaxAuthTries 4 or less, LoginGraceTime 60 s or less, X11Forwarding no, no empty passwords (from v0.29)
SSH offers only strong algorithmssshd offers no SHA-1, MD5 or 64-bit MAC, no CBC cipher, no DSA or SHA-1 RSA host key — read from what sshd really offers a client, root-only drop-ins included (from v0.29)
The kernel keeps its internals from userskptr_restrict ≥ 1, dmesg_restrict 1, yama.ptrace_scope ≥ 1, unprivileged_bpf_disabled ≥ 1, randomize_va_space 2 — now and at the next boot (ANSSI-BP-028 R9, R11)
The network stack ignores redirects and spoofed sourcesredirects and source routing refused, reverse-path filtering on (loose is fine on a host that routes containers), SYN cookies on (ANSSI-BP-028 R12)
Shared directories resist link tricksfs.protected_* on and fs.suid_dumpable 0 (ANSSI-BP-028 R14)
/tmp and /dev/shm cannot run programsboth are their own mounts with nosuid, nodev, noexec (ANSSI-BP-028 R28)
No rarely used network protocol is loadednone of dccp, sctp, rds, tipc is loaded
Network services run confinedno web, database or mail service scores UNSAFE (9.0 or more) in systemd-analyze security
A hardening audit runs regularlythe audit tool you run (Lynis, OpenSCAP) reported a run in the last 8 days (from v0.29)
Active Defense blocks attackersthe warden is installed and enforcing
Agent up to datethe agent runs the latest release

A kernel setting that is right now but that the sysctl.d files will set back at the next boot fails too: the agent resolves what the boot will apply the way systemd-sysctl does, so a value set by hand with sysctl -w cannot pass for a lasting one. The references (ANSSI-BP-028 v2.0) say which recommendation an item answers.

Four answers, not two

  • OK: the evidence says it is in place.
  • To fix: the evidence says it is not. The item explains why it matters and gives the command.
  • Unknown: Demerzel has no evidence either way, and says so rather than guess. For example, sshd reads cloud-init’s /etc/ssh/sshd_config.d/50-cloud-init.conf before the main file, and that file is readable by root only: whatever the main file says, the agent cannot tell what sshd really applies. Check it yourself with sudo sshd -T.
  • N/A: the item does not apply to this host (no web server, SSH not reachable from outside) and is not counted.

The score at the top counts what is in place out of what applies. The checklist is computed from the latest facts every time you open the page, so an item you fix turns green on the agent’s next report. SSH and automatic updates need agent v0.21 or later. The phpMyAdmin item needs agent v0.23 or later, the Apache timeout item agent v0.27 or later.

phpMyAdmin

Scanners look for phpMyAdmin on every site they reach; its own login page is then all that stands between them and the database. The agent finds each phpMyAdmin Apache or nginx serves — a vhost’s DocumentRoot, an Alias such as Debian’s /phpmyadmin, an nginx root or alias — by its directory name or the files every release ships. It reads the enabled configuration files, and the .htaccess only where AllowOverride lets Apache read it, to tell whether AuthType Basic with Require valid-user covers it. A Require all granted beside it lets anyone in, and is judged so. A vhost that only redirects to HTTPS does not count. An open phpMyAdmin also raises a critical Action on the dashboard.

Apache timeouts

Apache keeps a worker busy for every connection it is waiting on. Its Timeout says how long it waits on a client that went silent, and Debian ships Timeout 300: a few hundred connections that stop answering — a slow-read attack, or an address banned while it held them, since a ban stops new packets but not open connections — then hold every worker for five minutes, and every site stops answering although the server is fine. mod_reqtimeout is the other half: without it, a client may send its request as slowly as it likes (Slowloris).

The agent reads the Timeout of apache2.conf and every enabled file, a virtual host’s included, and keeps the longest. Above 60 seconds, or with mod_reqtimeout off, the item fails and a warning Action gives the fix:

# /etc/apache2/apache2.conf
Timeout 30
ProxyTimeout 300   # PHP behind proxy_fcgi keeps its long budget

then sudo a2enmod reqtimeout if it is off, and sudo apache2ctl configtest && sudo systemctl reload apache2. nginx does not hold a worker per connection, so the item does not apply to it.