diff options
Diffstat (limited to 'CLAUDE.md')
| -rw-r--r-- | CLAUDE.md | 33 |
1 files changed, 16 insertions, 17 deletions
@@ -18,20 +18,14 @@ Check the flake evaluates without deploying: nix flake check ``` -Deploy a change (from an x86_64-linux machine): +Deploy a change: ```bash -nixos-rebuild switch --flake .#eurovm \ - --target-host admin@<server-ipv4> \ - --elevate=sudo -``` - -Deploy from a non-x86_64-linux machine (e.g. Apple Silicon) — the server builds its own closure: -```bash -nixos-rebuild switch --flake .#eurovm \ - --target-host admin@<server-ipv4> \ - --build-host admin@<server-ipv4> \ - --elevate=sudo +./deploy.sh ``` +`deploy.sh` reads `SERVER_IP` from a git-ignored `.env` file (copy `.env.example` to start) — +the server's Tailscale IP — and runs `nixos-rebuild switch --flake .#eurovm` against it with +both `--target-host` and `--build-host` set to that address, so the server always builds its +own closure regardless of the local machine's architecture. First-time provisioning of a fresh VM (partitions the disk via disko, installs NixOS): ```bash @@ -71,8 +65,9 @@ service module: **Service modules (`modules/`)** are one file per concern, each self-contained and imported by `hosts/eurovm/default.nix`: -- `common.nix` — SSH hardening (key-only, no root login), firewall (22/80/443/53 only), timezone, - nix settings (flakes, auto-gc, auto-optimise-store), base packages, disabled autoUpgrade. +- `common.nix` — SSH hardening (key-only, no root login), firewall (22/80/443/853/8443 only — + plain DNS on 53 is deliberately closed, see `adguard.nix`), timezone, nix settings (flakes, + auto-gc, auto-optimise-store), base packages, disabled autoUpgrade. - `sops.nix` — sops-nix wiring. The host's own age key is derived at boot from `/etc/ssh/ssh_host_ed25519_key`, so no separate host key material needs to be provisioned. Declares which secrets exist, their owning user/group, and file mode. @@ -88,9 +83,13 @@ service module: 8080, 8086) are bound to `127.0.0.1` and never exposed directly — Caddy is the public HTTP(S) entry point for everything except AdGuard's encrypted-DNS ports, and handles Let's Encrypt automatically for its own certs. -- `adguard.nix` — AdGuard Home; plain DNS on `0.0.0.0:53`, DNS-over-TLS on `0.0.0.0:853`, - DNS-over-HTTPS on `0.0.0.0:8443` (path `/dns-query`), web UI on `0.0.0.0:3000` (port 3000 is - never opened in the firewall, so it's still effectively internal-only, reached via Caddy). +- `adguard.nix` — AdGuard Home; plain DNS (`dns.port`) is set to `0` and deliberately not opened + in the firewall — CERT-Bund/BSI flagged this host as an open DNS resolver abusable for UDP + reflection/amplification DDoS (spoofed source IP, no handshake needed), so plain DNS is + disabled entirely and DNS-over-TLS (`0.0.0.0:853`) / DNS-over-HTTPS (`0.0.0.0:8443`, path + `/dns-query`) — both TLS-handshake-based and therefore not spoofable the same way — are the + only supported client paths. Web UI on `0.0.0.0:3000` (port 3000 is never opened in the + firewall, so it's still effectively internal-only, reached via Caddy). `http.address` binds `0.0.0.0` rather than `127.0.0.1` specifically because AdGuard's HTTPS/DoH listener reuses that same bind host internally (confirmed from AdGuard's source) — narrowing it back to loopback would silently make DoH unreachable. TLS for DoH/DoT uses the |
