c77ff07ce9
Remediation of the 2026-05-12/13 audits (78 findings + cluster gaps), tracked in deploy-k3s/SECURITY.md, plus fixes from two independent post-remediation reviews. Auth & sessions: - SHA-256 hashed auth-token storage (C1); prior-token cache eviction on re-login (MEDIUM-1) - local Google JWKS verification, iss/aud/exp checks (C2/C3) - constant-time login + generic errors (L1/LIVE-L11/LIVE-L13) - per-account login lockout keyed on distinct source IPs (M5/MEDIUM-3) - verified-email gating, login rate limiting (LIVE-L19, H1-H3) IAP & webhooks: - Apple/Google cross-account replay protection (C5/C6/C10/C13, H5/H6) - migrations 000003-000006 (token hashing, IAP replay, audit_log + webhook_event_log table creation, append-only audit log) Authorization & races: - file-ownership owner-OR-member fix (C7), atomic share-code join (C9/H9), device-token reassignment (C8/LOW-3) Secrets & deploy: - secrets file-mounted at /etc/honeydue/secrets, not env (F8); Redis password out of the ConfigMap (HIGH-1); B2 keys reconciled - digest-pinned images, admin ingress hardening, CSP/HSTS, /metrics lockdown; kubeconfig 0600, etcd secrets-encryption, fail2ban + unattended-upgrades at provision; secret-rotation runbook Build, vet, and the full test suite (incl. -race) pass; the goose migration chain is verified against PostgreSQL 16. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
79 lines
2.9 KiB
YAML
79 lines
2.9 KiB
YAML
# One-shot migration Job. Runs goose against Neon's *direct* (non-pooler)
|
|
# endpoint, applies any pending migrations from /app/migrations (baked into
|
|
# the api image), exits.
|
|
#
|
|
# 03-deploy.sh deletes any prior Job, applies this one, waits for completion
|
|
# with `kubectl wait --for=condition=complete`, and rolls api/worker only
|
|
# after the Job succeeds. A Job failure aborts the whole deploy.
|
|
#
|
|
# We reuse the api image rather than build a separate one — the api Dockerfile
|
|
# already installs the goose CLI to /usr/local/bin/goose and copies the
|
|
# migrations directory to /app/migrations.
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata:
|
|
name: honeydue-migrate
|
|
namespace: honeydue
|
|
labels:
|
|
app.kubernetes.io/name: migrate
|
|
app.kubernetes.io/part-of: honeydue
|
|
spec:
|
|
backoffLimit: 0 # fail fast — no silent retries on a bad migration
|
|
ttlSecondsAfterFinished: 86400 # keep finished Job for 24h so logs are inspectable
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app.kubernetes.io/name: migrate
|
|
app.kubernetes.io/part-of: honeydue
|
|
spec:
|
|
restartPolicy: Never
|
|
# The migrate Job never calls the k8s API (audit F11).
|
|
automountServiceAccountToken: false
|
|
imagePullSecrets:
|
|
- name: gitea-credentials
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
runAsUser: 1000
|
|
runAsGroup: 1000
|
|
seccompProfile:
|
|
type: RuntimeDefault
|
|
containers:
|
|
- name: goose
|
|
image: IMAGE_PLACEHOLDER # Replaced by 03-deploy.sh — same as api
|
|
imagePullPolicy: IfNotPresent # audit CODE-L4 — explicit
|
|
command: ["/bin/sh", "-c"]
|
|
# DB_HOST in the ConfigMap points at the -pooler endpoint for runtime.
|
|
# goose's session-scoped advisory lock can't survive PgBouncer
|
|
# transaction-mode, so we strip the -pooler segment for migrations.
|
|
# `set -e` so any sub-command failure exits non-zero.
|
|
args:
|
|
- |
|
|
set -e
|
|
DIRECT_HOST=$(echo "$DB_HOST" | sed 's/-pooler\.\(.*\)$/.\1/')
|
|
echo "[migrate] running goose up against $DIRECT_HOST"
|
|
exec /usr/local/bin/goose \
|
|
-dir /app/migrations \
|
|
postgres "host=$DIRECT_HOST port=$DB_PORT user=$POSTGRES_USER password=$POSTGRES_PASSWORD dbname=$POSTGRES_DB sslmode=$DB_SSLMODE" \
|
|
up
|
|
securityContext:
|
|
allowPrivilegeEscalation: false
|
|
readOnlyRootFilesystem: true
|
|
capabilities:
|
|
drop: ["ALL"]
|
|
envFrom:
|
|
- configMapRef:
|
|
name: honeydue-config
|
|
env:
|
|
- name: POSTGRES_PASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: honeydue-secrets
|
|
key: POSTGRES_PASSWORD
|
|
resources:
|
|
requests:
|
|
cpu: 100m
|
|
memory: 64Mi
|
|
limits:
|
|
cpu: 500m
|
|
memory: 256Mi
|