Ship the railiance01 deploy package (OpenBao/ESO custody, NetworkPolicy, probes), provider failure classification and suppression, T04 unit and live proof, and non-secret NK-WP-0024 hand-back evidence. Workplan finished. |
||
|---|---|---|
| .. | ||
| clustersecretstore.yaml.template | ||
| configmap.yaml | ||
| deployment.yaml | ||
| externalsecret.yaml | ||
| kustomization.yaml | ||
| namespace.yaml | ||
| networkpolicies.yaml | ||
| openbao-eso-token-apply.sh | ||
| pvc.yaml | ||
| README.md | ||
| service.yaml | ||
| serviceaccount.yaml | ||
email-connect transactional receiver — railiance01
Deploy the narrow invitation/verification HTTP receiver so user-engine can send mail without holding SMTP credentials, and so unrelated workloads cannot reach the send API.
Target cluster: railiance01 only (KUBECONFIG=~/.kube/config-hosteurope).
Stable in-cluster URL after apply:
http://email-connect.email-connect.svc.cluster.local:8080
Ownership split
| Concern | Owner | Where |
|---|---|---|
| Package, manifests, probes, NetworkPolicy, rollback | email-connect |
this directory |
| OpenBao path, ESO role/policy, ClusterSecretStore | railiance-platform |
CCR-2026-0010, openbao/policies/ |
| Credential routing catalog | ops-warden |
email-connect-transactional |
Never paste SMTP passwords, ingest tokens, or OpenBao tokens into Git, State Hub, workplans, logs, or chat. Route first:
warden route find "email-connect SMTP transactional" --json
warden route show email-connect-transactional --json
Runtime secrets (OpenBao → ESO)
| Item | Value |
|---|---|
| KV path | platform/workloads/email-connect/transactional |
| Fields | EMAIL_CONNECT_INGEST_TOKEN, EMAIL_CONNECT_SMTP_USERNAME, EMAIL_CONNECT_SMTP_PASSWORD |
| Read / ESO policy | external-secrets-email-connect (+ workload-kv-read-email-connect-transactional for operator OIDC) |
| ClusterSecretStore | openbao-email-connect (namespace-limited; token auth like activity-core/reuse) |
| ESO token Secret | external-secrets/openbao-email-connect-eso-token (via openbao-eso-token-apply.sh) |
| ExternalSecret | email-connect/email-connect-runtime → Secret email-connect-runtime |
| ops-warden catalog | email-connect-transactional (draft until provisioned) |
Non-secret config (host, port, sender, portal URL) lives in ConfigMap
email-connect-config.
Operator provision (attended, after CCR approval)
# Values from mode-0600 files only; shred after.
bao kv put platform/workloads/email-connect/transactional \
EMAIL_CONNECT_INGEST_TOKEN=@/path/to/ingest.token \
EMAIL_CONNECT_SMTP_USERNAME=@/path/to/smtp.user \
EMAIL_CONNECT_SMTP_PASSWORD=@/path/to/smtp.pass
user-engine must receive the same ingest token under its own runtime secret
path so it can call Authorization: Bearer … without ever seeing SMTP fields.
Network posture
- Default deny ingress and egress in
email-connect. - Ingress: only pods labeled
app.kubernetes.io/name=user-enginein namespaceuser-engine, TCP 8080. - Egress: kube-system DNS + TCP 587 to the public internet (IONOS STARTTLS). No cluster-lateral egress and no general HTTPS.
Bearer auth on /v1/* is the second gate; NetworkPolicy is the outer gate.
Apply
Prerequisites:
- CCR-2026-0010 approved.
- KV secret provisioned at
platform/workloads/email-connect/transactional(fields non-empty; do not log values). - ESO delivery token + ClusterSecretStore on railiance01 (token pattern matches
openbao-activity-core/openbao-reuse, not Kubernetes auth yet). - Image available on the node/registry at the digest pinned in
deployment.yaml.
export KUBECONFIG=~/.kube/config-hosteurope
# 1) Policy + limited ESO child token → external-secrets Secret
./deploy/k8s/railiance/openbao-eso-token-apply.sh
# 2) Namespace-scoped ClusterSecretStore (platform add-on copy)
kubectl apply -f ~/railiance-platform/argocd/platform-addons/openbao-secretstore/openbao-email-connect.clustersecretstore.yaml
# 3) Optional: load a freshly built image when the registry is not yet updated
# docker build -f Containerfile -t forgejo.coulomb.social/coulomb/email-connect:local .
# docker save … | ssh railiance01 sudo k3s ctr -n k8s.io images import -
# 4) Package
kubectl apply -k deploy/k8s/railiance
kubectl -n email-connect rollout status deployment/email-connect
kubectl -n email-connect get pods,svc,networkpolicy,externalsecret,secret
Expect ExternalSecret SecretSynced and a Ready pod. If the Secret is missing,
do not create a bootstrap Secret with values from chat — fix OpenBao/ESO.
Smoke (no SMTP proof yet)
From a user-engine pod (or a temporary pod in that namespace with the same labels — NetworkPolicy will otherwise drop the packet):
# Health only (no auth)
kubectl -n user-engine exec deploy/user-engine -- \
wget -qO- http://email-connect.email-connect.svc.cluster.local:8080/healthz
# Auth check: wrong token must be 401; correct token is exercised by T04.
Negative checks:
- A pod in another namespace cannot reach port 8080 (connection timeout/reset).
- user-engine runtime must not contain
EMAIL_CONNECT_SMTP_*keys.
Rollback
kubectl -n email-connect rollout undo deployment/email-connect
kubectl -n email-connect rollout status deployment/email-connect
revisionHistoryLimit: 5 keeps prior digests. SQLite on the PVC is additive;
rolling back does not clear delivery/idempotency rows (by design).
Done criteria (EMAIL-WP-0004-T03)
- user-engine calls the cluster-local receiver without possessing SMTP credentials
- Unrelated workloads cannot open TCP 8080 on the receiver
- SMTP password and ingest token exist only in OpenBao + the scoped K8s Secret
- Probes green; NetworkPolicy default-deny + SMTP-only egress applied
- Documented rollback path works against the previous revision