feat(deploy): manifest-driven config, teardown + db-perms, hardening

deploy.sh:
- never rsync into /; stage to /tmp on the remote and install at final
  paths via sudo bash heredoc, closing the parent-dir attribute leak
  that broke three hosts in the earlier rsync incident
- shell-quote heredoc args via ${var@Q}
- drop -A -X on the remaining (web) rsyncs
- generic worker.secrets loop reads (env-var → pass path) from manifest;
  GITEA_TOKEN now flows through automatically
- in-memory bash substitution for templates (secrets never on argv)
- simplify semanage port labelling: --add 2>/dev/null || --modify (the
  old grep pre-check matched only the first listed port)
- restorecon back to short flags (Fedora policycoreutils has no long
  forms; --recursive errored at deploy time)
- quieter health probe loop: curl diagnostics only on final failure

manifest as source of truth:
- api.config.bind drives BIND_ADDR, firewalld port, semanage label,
  health-probe URL
- web.config.{server_name,root,api_upstream} drives nginx render,
  rsync targets, restorecon scope
- nginx config renamed to site.conf.tmpl; firewalld svc to
  moments-api.xml.tmpl; both rendered at deploy time
- topology flip: api → nikola, worker → frootmig (anjie freed)

new scripts:
- script/teardown.sh: idempotent component teardown, never rsyncs,
  shared-state cleanup gated on absence of remaining env files,
  --remove-docroot guard against shallow / system paths
- script/db-perms.sh: rewritten — fixes grep/append role mismatch that
  appended duplicates on re-run, adds postgres reload, hits primary +
  standby in a single invocation

readme: genericized; deployment topology no longer carries real host
or site names.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-04 16:39:10 +03:00
parent f30f949895
commit 8867ff5df3
9 changed files with 643 additions and 181 deletions

View File

@@ -1,6 +1,6 @@
JOURNAL_STREAM=1
RUST_LOG=info,sqlx=warn,tower_http=info
BIND_ADDR=0.0.0.0:42424
BIND_ADDR={{BIND}}
DATABASE_URL=postgres://moments_ro@magrathea.kosherinata.internal:5432/moments?sslmode=verify-full&sslrootcert=/etc/pki/ca-trust/source/anchors/root-internal.pem&sslcert=/etc/pki/tls/misc/{{HOSTNAME}}.pem&sslkey=/etc/pki/tls/private/{{HOSTNAME}}.pem

View File

@@ -10,6 +10,7 @@ SEARCH_POLL_INTERVAL_SECS=86400
GITEA_HOST=git.lair.cafe
GITEA_USER=grenade
GITEA_TOKEN={{GITEA_TOKEN}}
GITEA_POLL_INTERVAL_SECS=600
HG_HOST=hg-edge.mozilla.org

View File

@@ -2,5 +2,5 @@
<service>
<short>moments-api</short>
<description>moments read-only HTTP API</description>
<port protocol="tcp" port="42424"/>
<port protocol="tcp" port="{{API_PORT}}"/>
</service>

View File

@@ -3,7 +3,7 @@ environments:
prod:
components:
api:
hosts: [anjie.kosherinata.internal]
hosts: [nikola.kosherinata.internal]
config:
bind: 0.0.0.0:42424
db_role: moments_ro
@@ -11,7 +11,7 @@ environments:
db_port: 5432
db_name: moments
worker:
hosts: [anjie.kosherinata.internal]
hosts: [frootmig.kosherinata.internal]
config:
db_role: moments_rw
db_host: magrathea.kosherinata.internal
@@ -27,10 +27,10 @@ environments:
bugzilla_email: rthijssen@mozilla.com
secrets:
GITHUB_TOKEN: github.com/grenade/admin-token
# GITEA_TOKEN, BUGZILLA_API_KEY: optional, omit unless required.
GITEA_TOKEN: git.lair.cafe/grenade/admin-token
web:
hosts: [oolon.kosherinata.internal]
config:
server_name: rob.tn
root: /var/www/rob.tn
api_upstream: http://anjie.kosherinata.internal:42424
api_upstream: http://nikola.kosherinata.internal:42424

View File

@@ -1,18 +1,18 @@
upstream moments_api {
server anjie.kosherinata.internal:42424 max_fails=3 fail_timeout=30s;
server {{API_UPSTREAM_ADDR}} max_fails=3 fail_timeout=30s;
keepalive 8;
}
server {
server_name rob.tn;
server_name {{SERVER_NAME}};
listen 443 ssl;
http2 on;
ssl_certificate /etc/letsencrypt/live/rob.tn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/rob.tn/privkey.pem;
ssl_certificate /etc/letsencrypt/live/{{SERVER_NAME}}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{SERVER_NAME}}/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
root /var/www/rob.tn;
root {{DOCROOT}};
index index.html;
location / {
@@ -28,7 +28,7 @@ server {
location /api/ {
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://moments_api;
proxy_pass {{API_UPSTREAM_SCHEME}}://moments_api;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
@@ -38,6 +38,6 @@ server {
proxy_connect_timeout 5s;
}
access_log /var/log/nginx/rob.tn.access.log;
error_log /var/log/nginx/rob.tn.error.log;
access_log /var/log/nginx/{{SERVER_NAME}}.access.log;
error_log /var/log/nginx/{{SERVER_NAME}}.error.log;
}