Files
Trey t c77ff07ce9
Backend CI / Test (push) Has been cancelled
Backend CI / Contract Tests (push) Has been cancelled
Backend CI / Lint (push) Has been cancelled
Backend CI / Secret Scanning (push) Has been cancelled
Backend CI / Build (push) Has been cancelled
fix(security): remediate 2026-05-12 audit findings (Stages 2–5)
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>
2026-05-16 22:28:33 -05:00

117 lines
3.8 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: honeydue
labels:
app.kubernetes.io/name: api
app.kubernetes.io/part-of: honeydue
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app.kubernetes.io/name: api
template:
metadata:
labels:
app.kubernetes.io/name: api
app.kubernetes.io/part-of: honeydue
spec:
serviceAccountName: api
# Explicit pod-level opt-out (audit F11) — defense-in-depth on top of
# the ServiceAccount-level setting in rbac.yaml.
automountServiceAccountToken: false
imagePullSecrets:
- name: gitea-credentials
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: api
image: IMAGE_PLACEHOLDER # Replaced by 03-deploy.sh
imagePullPolicy: IfNotPresent # audit CODE-L4 — explicit; images are SHA/digest-pinned
ports:
- containerPort: 8000
protocol: TCP
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
envFrom:
- configMapRef:
name: honeydue-config
# Audit CODE-F8: secrets are NOT injected as environment variables.
# Env vars are readable for the life of the pod via /proc/<pid>/environ
# and leak into crash dumps / child processes. honeydue-secrets is
# mounted read-only at /etc/honeydue/secrets (mode 0400) and the Go
# config layer (config.loadFileSecrets) reads each key from its file.
# Non-secret config still arrives via the configMapRef above.
volumeMounts:
- name: app-secrets
mountPath: /etc/honeydue/secrets
readOnly: true
- name: apns-key
mountPath: /secrets/apns
readOnly: true
- name: tmp
mountPath: /tmp
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: "1"
memory: 512Mi
startupProbe:
httpGet:
path: /api/health/
port: 8000
# Schema migrations run separately in the honeydue-migrate Job
# *before* this Deployment rolls — the api itself does not migrate
# (it only verifies goose_db_version at boot). Cold start still
# pays the DB pool warm-up + Redis connect + APNs/FCM client init
# before /api/health/ goes green. 48 × 5s = 240s grace keeps the
# probe from killing a still-starting replica.
failureThreshold: 48
periodSeconds: 5
readinessProbe:
httpGet:
path: /api/health/
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
livenessProbe:
httpGet:
path: /api/health/
port: 8000
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 10
volumes:
# Audit CODE-F8: the whole honeydue-secrets Secret, projected as files.
# defaultMode 0400 → readable only by the container's runAsUser (1000).
- name: app-secrets
secret:
secretName: honeydue-secrets
defaultMode: 0400
- name: apns-key
secret:
secretName: honeydue-apns-key
items:
- key: apns_auth_key.p8
path: apns_auth_key.p8
- name: tmp
emptyDir:
sizeLimit: 64Mi