diff --git a/hw/dixie/analysis.nix b/hw/dixie/analysis.nix index 40bcbf6..e166cef 100644 --- a/hw/dixie/analysis.nix +++ b/hw/dixie/analysis.nix @@ -13,10 +13,21 @@ # The HDD wipe is the only destructive step and is authorized for sda only. It is # NOT declarative — it runs once as an imperative step during deploy (see below). # -# Password: kept out of git. dixie writes the plaintext dropsonde password to -# /root/dropsonde-pg (mode 600, root-owned) — a file that is never committed. -# The dropsonde-pg-setup service reads it via a systemd LoadCredential and runs -# ALTER ROLE + CREATE EXTENSION IF NOT EXISTS. See the note at the bottom. +# HDD wipe (authorized for sda = WDC WD6003FZBX-00K5WB0, serial V7G47PKH only): +# doas smartctl -i -d sat /dev/sda # confirm model+serial first +# doas wipefs -a /dev/sda # drop BitLocker sig + GPT +# doas sgdisk --zap-all /dev/sda # remove any leftover partition table +# doas sgdisk -n 0:0:+0 -t 0:8300 -c 0:dropsonde-cold /dev/sda +# doas mkfs.xfs -L dropsonde-cold /dev/sda1 +# One GPT partition, XFS, label dropsonde-cold. sdb (stick) and nvme0n1 (root) untouched. +# +# Password: generated on dixie as root, never committed: +# umask 077; openssl rand -base64 32 > /root/dropsonde-pg +# The dropsonde-pg-setup service reads it via a systemd LoadCredential. Local +# analysis uses the unix socket as the dropsonde user (peer auth, no password). +# The only remote client is the future ADS-B collector on hyperborea; when that +# is built, copy /root/dropsonde-pg over ssh to +# /srv/dropsonde2/.env.pg (mode 600). Nobody needs to know the value. { inputs, pkgs, lib, config, ... }: @@ -91,10 +102,12 @@ in enable = true; package = pkgs.postgresql_17; - # Listen on the tailnet address only (not 0.0.0.0). enableTCPIP is required - # for any TCP bind; listen_addresses below scopes it to tailscale0. + # Listen on all interfaces; the firewall opens 5432 on tailscale0 only, so + # nothing else is exposed. Binding to the tailnet IP directly would race + # tailscale0 coming up after postgres at boot and leave the service down + # until a manual restart. enableTCPIP = true; - listen_addresses = lib.mkForce tailnet; + listen_addresses = "*"; settings = { shared_preload_libraries = "timescaledb"; @@ -130,7 +143,9 @@ in # Create the extensions in the dropsonde db once postgres is up and the db + # role exist. Idempotent (IF NOT EXISTS) so redeploys are safe. The password - # is read out of git via a systemd LoadCredential (see note). + # is generated on dixie (see note) and read out of git via a systemd + # LoadCredential; psql interpolates it as a bound variable so it never shows + # in the process list or journal. systemd.services.dropsonde-pg-setup = { description = "dropsonde2: set dropsonde role password + create extensions"; wants = [ "postgresql-setup.service" ]; @@ -140,9 +155,22 @@ in RemainAfterExit = true; User = "postgres"; Group = "postgres"; + Restart = "on-failure"; + RestartSec = "10s"; LoadCredential = [ "dropsonde-pg-password:/root/dropsonde-pg" ]; - ExecStart = ''${pkgs.postgresql}/bin/psql -v ON_ERROR_STOP=1 -d postgres -c "ALTER ROLE dropsonde WITH LOGIN PASSWORD '${''${CREDENTIALS_DIRECTORY}/dropsonde-pg-password}';"'' - + ''${pkgs.postgresql}/bin/psql -v ON_ERROR_STOP=1 -d dropsonde -c "CREATE EXTENSION IF NOT EXISTS timescaledb;" -c "CREATE EXTENSION IF NOT EXISTS postgis;" -c "CREATE EXTENSION IF NOT EXISTS h3;" -c "CREATE EXTENSION IF NOT EXISTS h3_postgis CASCADE;''; + ExecStart = ''${pkgs.coreutils}/bin/bash <<'BASH' +set -euo pipefail +pw=$(cat "$CREDENTIALS_DIRECTORY/dropsonde-pg-password") +psql -v ON_ERROR_STOP=1 -v pw="$pw" -d postgres <<'SQL' +ALTER ROLE dropsonde WITH LOGIN PASSWORD :'pw'; +SQL +psql -v ON_ERROR_STOP=1 -d dropsonde <<'SQL' +CREATE EXTENSION IF NOT EXISTS timescaledb; +CREATE EXTENSION IF NOT EXISTS postgis; +CREATE EXTENSION IF NOT EXISTS h3; +CREATE EXTENSION IF NOT EXISTS h3_postgis CASCADE; +SQL +BASH }; wantedBy = [ "multi-user.target" ]; }; diff --git a/hw/dixie/default.nix b/hw/dixie/default.nix index 2d0c43e..8216f6a 100644 --- a/hw/dixie/default.nix +++ b/hw/dixie/default.nix @@ -126,7 +126,7 @@ let SupplementaryGroups = [ "video" ]; # Own slice, above default CPUWeight (set in analysis.nix) so the # analysis.slice workloads never starve the helper-tier router. - Slice = "llama.service"; + Slice = "llama.slice"; Restart = "on-failure"; RestartSec = "10s"; TimeoutStartSec = "600";