refactor(1b): RL5 — consolidate Nix code under nix/ (modules->nix/modules, hosts->nix/hosts)
flake.nix/flake.lock STAY at root so the build ref #cc-ci is unchanged; only flake's internal configuration.nix path updated. Root-relative refs inside moved modules re-based ../X -> ../../X (secrets/bridge/dashboard); configuration.nix's ../../modules imports unchanged (both dirs under nix/). Living docs (README, architecture/install/secrets/enroll) + .drone.yml comment updated to nix/...; append-only history logs left as-is. DECISIONS.md records RL5 + the deferred-coordinated RL6. Verified on cc-ci: nixos-rebuild build 'path:#cc-ci' -> toplevel 8i3jcad9 (BYTE-IDENTICAL to the pre-move build — store derivations are content-addressed on file contents, module .nix not in the runtime closure); scripts/lint.sh -> lint: PASS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
67
nix/modules/proxy.nix
Normal file
67
nix/modules/proxy.nix
Normal file
@ -0,0 +1,67 @@
|
||||
# Reverse proxy = the canonical Co-op Cloud `traefik` recipe, deployed via abra in
|
||||
# wildcard / file-provider mode (wildcard cert as ssl_cert/ssl_key swarm secrets,
|
||||
# LETS_ENCRYPT_ENV empty => NO ACME, no DNS token). See DECISIONS.md "Proxy: real coop-cloud/traefik".
|
||||
# Phase-1c: the cert at CERT_DIR is sops-decrypted from git (cc-ci-secrets) at activation
|
||||
# (modules/secrets.nix wildcard_cert/wildcard_key), NOT an out-of-band operator file drop.
|
||||
#
|
||||
# Declared as an idempotent-RECONCILE systemd oneshot (like swarm-init): it inspects current
|
||||
# state and converges every activation/boot, self-healing drift (redeploys if the stack is gone,
|
||||
# re-inserts secrets if missing). No run-once sentinel. So a from-scratch install is just
|
||||
# `nixos-rebuild switch` + operator preconditions (D8) — no manual post-steps.
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
reconcile = pkgs.writeShellApplication {
|
||||
name = "cc-ci-reconcile-proxy";
|
||||
runtimeInputs = with pkgs; [ abra docker jq gnused gnugrep coreutils git ];
|
||||
text = ''
|
||||
PROXY_DOMAIN="traefik.ci.commoninternet.net"
|
||||
CERT_DIR="/var/lib/ci-certs/live"
|
||||
ENV_FILE="$HOME/.abra/servers/default/$PROXY_DOMAIN.env"
|
||||
|
||||
# Fail visibly (failed unit) if the cert is missing — do NOT silently skip. It is
|
||||
# sops-decrypted from git (cc-ci-secrets) at activation; a miss here means the sops decrypt
|
||||
# path is broken (e.g. age identity not present), which must surface, not be papered over.
|
||||
if [ ! -r "$CERT_DIR/fullchain.pem" ] || [ ! -r "$CERT_DIR/privkey.pem" ]; then
|
||||
echo "FATAL: wildcard cert missing at $CERT_DIR (sops decrypt from cc-ci-secrets failed?)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
abra server ls -m -n >/dev/null 2>&1 || abra server add --local -n || true
|
||||
abra recipe fetch traefik -n >/dev/null
|
||||
|
||||
[ -f "$ENV_FILE" ] || abra app new traefik -s default -D "$PROXY_DOMAIN" -n
|
||||
|
||||
set_env() {
|
||||
sed -i -E "/^[[:space:]]*#?[[:space:]]*$1=/d" "$ENV_FILE"
|
||||
printf '%s=%s\n' "$1" "$2" >> "$ENV_FILE"
|
||||
}
|
||||
set_env LETS_ENCRYPT_ENV ""
|
||||
set_env WILDCARDS_ENABLED "1"
|
||||
set_env SECRET_WILDCARD_CERT_VERSION "v1"
|
||||
set_env SECRET_WILDCARD_KEY_VERSION "v1"
|
||||
set_env COMPOSE_FILE '"compose.yml:compose.wildcard.yml"'
|
||||
|
||||
have_secret() { docker secret ls --format '{{.Name}}' | grep -q "_$1_v1$"; }
|
||||
have_secret ssl_cert || abra app secret insert "$PROXY_DOMAIN" ssl_cert v1 "$CERT_DIR/fullchain.pem" -f -n
|
||||
have_secret ssl_key || abra app secret insert "$PROXY_DOMAIN" ssl_key v1 "$CERT_DIR/privkey.pem" -f -n
|
||||
|
||||
# Converge the stack (idempotent: no-op if already at desired state).
|
||||
abra app deploy "$PROXY_DOMAIN" -n -C
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
systemd.services.deploy-proxy = {
|
||||
description = "Reconcile the Co-op Cloud traefik proxy (wildcard/no-ACME) via abra";
|
||||
after = [ "swarm-init.service" "docker.service" "network-online.target" ];
|
||||
requires = [ "swarm-init.service" "docker.service" ];
|
||||
wants = [ "network-online.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment.HOME = "/root";
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = "${reconcile}/bin/cc-ci-reconcile-proxy";
|
||||
};
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user