Configuration

Behavior is controlled through environment variables and optional PROFILE presets. Explicit env vars always win over presets and auto-detection.

Minimal invocations

# Standard (keys on root)
sudo bash hardener.sh

# Tailscale only — one secret
sudo TAILSCALE_AUTHKEY=tskey-auth-xxx bash hardener.sh

# Presets
sudo PROFILE=web bash hardener.sh
sudo PROFILE=minimal bash hardener.sh
sudo PROFILE=lockdown bash hardener.sh   # over SSH: locks to your IP
sudo PROFILE=tailscale TAILSCALE_AUTHKEY=tskey-auth-xxx bash hardener.sh

PROFILE presets

PROFILEEffect
(unset) / defaultStandard defaults + auto-detect
minimalALLOW_HTTP=false, ALLOW_HTTPS=false
webForce UFW allow 80/443
tailscaleUSE_TAILSCALE=true, KEEP_PUBLIC_SSH=false (after join)
lockdownKEEP_PUBLIC_SSH=false, ALLOW_SSH_FROM from SSH client IP

Auto-detection rules

TriggerResult
TAILSCALE_AUTHKEY or file at TAILSCALE_AUTHKEY_FILEUSE_TAILSCALE=true
Tailscale join succeedsKEEP_PUBLIC_SSH=false unless you set KEEP_PUBLIC_SSH explicitly
PROFILE=lockdown or KEEP_PUBLIC_SSH=false without TailscaleALLOW_SSH_FROM=<SSH_CLIENT>/32 if unset
Port 80/443 not listeningALLOW_HTTP / ALLOW_HTTPSfalse unless explicit or PROFILE=web
sshd -T port ≠ 22SSH_PORT updated if not set explicitly
System timezone ≠ UTCTIMEZONE updated if not set explicitly
Hetzner metadata (optional)TAILSCALE_UP_EXTRA_ARGS=--hostname=...
DEPLOY_USER=autoUse $SUDO_USER when not root

Core variables

VariableDefaultDescription
DEPLOY_USERdeploySudo user (NOPASSWD); auto uses $SUDO_USER
USE_TAILSCALEfalseInstall Tailscale
KEEP_PUBLIC_SSHtrueAllow SSH on public interface
TAILSCALE_AUTHKEY(unset)Auth key for tailscale up
TAILSCALE_AUTHKEY_FILE/etc/vps-hardener/tailscale.authkeyFallback key file (mode 600)
SSH_PORT22SSH listen port
SSH_PUBLIC_KEY(unset)One public key line
SSH_AUTHORIZED_KEYS(unset)Keys or path to a file
ALLOW_SSH_FROM(unset)Comma-separated CIDRs for SSH

KEEP_PUBLIC_SSH behavior

USE_TAILSCALEKEEP_PUBLIC_SSHResult
falsetruePublic SSH on SSH_PORT
falsefalseSSH only from ALLOW_SSH_FROM (required or auto from SSH)
truetruePublic + Tailscale SSH
truefalseTailscale SSH only after node has Tailscale IPv4

If Tailscale is enabled but the node never joins, public SSH stays open with a warning.

Optional variables

VariableDefaultPurpose
ALLOW_HTTPtrueUFW TCP 80
ALLOW_HTTPStrueUFW TCP 443
INSTALL_MICROfalseInstall micro via getmic.ro
INSTALL_SNAP_MICROfalseInstall micro via snap
TIMEZONEUTCSystem timezone
TAILSCALE_UP_EXTRA_ARGS(auto on Hetzner)Extra tailscale up flags

SSH key preflight

Stops before changes unless keys exist from:

  1. /root/.ssh/authorized_keys
  2. /home/$SUDO_USER/.ssh/authorized_keys (when using sudo)
  3. SSH_PUBLIC_KEY / SSH_AUTHORIZED_KEYS
  4. Existing DEPLOY_USER keys (re-runs)

Tailscale auth key file

On the server (optional):

sudo mkdir -p /etc/vps-hardener
echo 'tskey-auth-xxxxx' | sudo tee /etc/vps-hardener/tailscale.authkey
sudo chmod 600 /etc/vps-hardener/tailscale.authkey
sudo bash hardener.sh   # no TAILSCALE_AUTHKEY in command line

Local overrides

set -a && source ./.env.local && set +a
sudo -E bash hardener.sh