b54493f785
BE-3 observability: expose the worker's Prometheus metrics on :6060/metrics (apns/fcm/asynq histograms + a new cache_ops_total counter were recorded all along but never scraped — which is why those dashboard panels read empty); add the worker containerPort, the vmagent worker scrape job, and two additive NetworkPolicies. Instrument cache Get/Set hit/miss. BE-2 retention: three periodic Asynq cleanup crons mirroring the reminder-log cleanup — notifications (90d), webhook dedup log (180d), audit_log (365d). BE-1 GDPR data export: POST /api/auth/export/ enqueues a low-priority Asynq job that gathers all of the user's data (owned residences + their tasks/contractors/ documents/share-codes, plus profile/notifications/prefs/push-tokens/subscription/ audit log), zips one JSON file per category, and emails it as an attachment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
146 lines
4.5 KiB
YAML
146 lines
4.5 KiB
YAML
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: worker
|
|
namespace: honeydue
|
|
labels:
|
|
app.kubernetes.io/name: worker
|
|
app.kubernetes.io/part-of: honeydue
|
|
spec:
|
|
# Asynq's Scheduler is a singleton — running >1 replica fires every cron
|
|
# task once per replica (duplicate daily digests, onboarding emails, etc.).
|
|
# Keep at 1 until asynq.PeriodicTaskManager with Redis leader election is
|
|
# wired in cmd/worker/main.go.
|
|
replicas: 1
|
|
strategy:
|
|
type: RollingUpdate
|
|
rollingUpdate:
|
|
maxUnavailable: 0
|
|
maxSurge: 1
|
|
selector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: worker
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app.kubernetes.io/name: worker
|
|
app.kubernetes.io/part-of: honeydue
|
|
spec:
|
|
serviceAccountName: worker
|
|
# 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: worker
|
|
image: IMAGE_PLACEHOLDER # Replaced by 03-deploy.sh
|
|
imagePullPolicy: IfNotPresent # audit CODE-L4 — explicit; images are SHA/digest-pinned
|
|
ports:
|
|
# health + Prometheus /metrics (in-cluster only; scraped by vmagent)
|
|
- name: metrics
|
|
containerPort: 6060
|
|
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: 500m
|
|
memory: 512Mi
|
|
livenessProbe:
|
|
exec:
|
|
command: ["pgrep", "-f", "/app/worker"]
|
|
initialDelaySeconds: 15
|
|
periodSeconds: 30
|
|
timeoutSeconds: 5
|
|
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
|
|
---
|
|
# Allow vmagent to scrape the worker's /metrics on :6060 (default-deny-all is in
|
|
# force; the worker otherwise receives no ingress). Additive — see node-exporter.
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-ingress-to-worker-metrics
|
|
namespace: honeydue
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: worker
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
- from:
|
|
- podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: vmagent
|
|
ports:
|
|
- port: 6060
|
|
protocol: TCP
|
|
---
|
|
# vmagent's base egress policy only opens :8000/:8080 to the pod CIDR; this
|
|
# additive policy opens :6060 for the worker scrape (leaves the base untouched).
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-egress-from-vmagent-to-worker
|
|
namespace: honeydue
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: vmagent
|
|
policyTypes:
|
|
- Egress
|
|
egress:
|
|
- to:
|
|
- ipBlock:
|
|
cidr: 10.42.0.0/16
|
|
ports:
|
|
- port: 6060
|
|
protocol: TCP
|