diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index 1b52b8e..ff5a160 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -13,8 +13,8 @@ | workplan | NK-WP-0009 | backlog | — | workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md | | workplan | NK-WP-0011 | backlog | — | workplans/NK-WP-0011-enterprise-federation-saml.md | | workplan | NK-WP-0021 | finished | — | workplans/NK-WP-0021-activity-core-ops-sso-operators.md | -| workplan | NK-WP-0022 | backlog | — | workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md | -| workplan | NK-WP-0023 | ready | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md | +| workplan | NK-WP-0022 | active | — | workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md | +| workplan | NK-WP-0023 | active | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md | | task | ADHOC-2026-07-02-T01 | done | — | workplans/ADHOC-2026-07-02.md | | task | ADHOC-2026-07-02-T02 | done | — | workplans/ADHOC-2026-07-02.md | | task | NET-WP-0020-T01 | done | — | workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md | @@ -49,8 +49,8 @@ | task | NK-WP-0022-T06 | wait | — | workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md | | task | NK-WP-0022-T07 | wait | — | workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md | | task | NK-WP-0022-T08 | wait | — | workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md | -| task | NK-WP-0023-T01 | todo | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md | -| task | NK-WP-0023-T02 | wait | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md | +| task | NK-WP-0023-T01 | done | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md | +| task | NK-WP-0023-T02 | progress | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md | | task | NK-WP-0023-T03 | wait | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md | | task | NK-WP-0023-T04 | wait | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md | | task | NK-WP-0023-T05 | wait | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md | diff --git a/sso-mfa/k8s/user-engine/README.md b/sso-mfa/k8s/user-engine/README.md new file mode 100644 index 0000000..4a25820 --- /dev/null +++ b/sso-mfa/k8s/user-engine/README.md @@ -0,0 +1,26 @@ +# user-engine portal on reef-railiance + +This is a stateful `rail-kubernetes` platform workload. It intentionally has +no public Ingress until the KeyCape authorization-code/PKCE edge and +`user-engine-portal` client are configured. Direct access to protected routes +must remain impossible because the application accepts identity only from a +trusted edge marker plus verified claims. + +For the current pre-production bootstrap the image is imported directly into +k3s and uses `imagePullPolicy: Never`. Replace it with the immutable Forgejo +OCI digest after the OpenBao package-publisher lane is available. + +The CloudNativePG operator creates `user-engine-pg-app`, including its `uri` +field. `user-engine-runtime` contains only the generated edge marker and must +be replaced by an ExternalSecret before public exposure. + +```bash +kubectl apply -f runtime.yaml +kubectl -n user-engine rollout status deployment/user-engine +kubectl -n user-engine get cluster,pod,service,networkpolicy +``` + +Rollback sets the Deployment image to the preceding immutable digest. +Database migrations are additive and run before serving; restore uses the +standard CNPG recovery contract once the offsite object-store reference is +attached. diff --git a/sso-mfa/k8s/user-engine/runtime.yaml b/sso-mfa/k8s/user-engine/runtime.yaml new file mode 100644 index 0000000..d552491 --- /dev/null +++ b/sso-mfa/k8s/user-engine/runtime.yaml @@ -0,0 +1,142 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: user-engine + labels: + railiance.io/workload-class: platform +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: user-engine-pg + namespace: user-engine +spec: + instances: 1 + imageName: ghcr.io/cloudnative-pg/postgresql:17.5 + storage: + size: 5Gi + bootstrap: + initdb: + database: user_engine + owner: user_engine + resources: + requests: {cpu: 100m, memory: 256Mi} + limits: {cpu: "1", memory: 1Gi} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user-engine + namespace: user-engine + labels: &labels + app.kubernetes.io/name: user-engine + app.kubernetes.io/component: portal +spec: + replicas: 1 + selector: + matchLabels: {app.kubernetes.io/name: user-engine} + template: + metadata: + labels: *labels + spec: + automountServiceAccountToken: false + securityContext: + runAsNonRoot: true + seccompProfile: {type: RuntimeDefault} + containers: + - name: portal + image: user-engine:portal-c27012a + imagePullPolicy: Never + ports: [{name: http, containerPort: 8080}] + env: + - name: USER_ENGINE_DATABASE_URL + valueFrom: + secretKeyRef: {name: user-engine-pg-app, key: uri} + - name: USER_ENGINE_PROXY_SECRET + valueFrom: + secretKeyRef: {name: user-engine-runtime, key: proxy-secret} + - {name: USER_ENGINE_LOGIN_URL, value: "https://kc.coulomb.social/"} + - {name: USER_ENGINE_OIDC_ISSUER, value: "https://kc.coulomb.social"} + - {name: USER_ENGINE_OIDC_AUDIENCE, value: user-engine-portal} + - {name: USER_ENGINE_PUBLIC_REGISTRATION, value: "false"} + securityContext: + allowPrivilegeEscalation: false + capabilities: {drop: ["ALL"]} + readOnlyRootFilesystem: true + resources: + requests: {cpu: 50m, memory: 64Mi} + limits: {cpu: 500m, memory: 256Mi} + startupProbe: + httpGet: {path: /readyz, port: http} + failureThreshold: 30 + periodSeconds: 5 + readinessProbe: + httpGet: {path: /readyz, port: http} + periodSeconds: 10 + livenessProbe: + httpGet: {path: /healthz, port: http} + periodSeconds: 20 +--- +apiVersion: v1 +kind: Service +metadata: {name: user-engine, namespace: user-engine} +spec: + selector: {app.kubernetes.io/name: user-engine} + ports: [{name: http, port: 8080, targetPort: http}] +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: {name: user-engine-default-deny, namespace: user-engine} +spec: + podSelector: {} + policyTypes: [Ingress, Egress] +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: {name: user-engine-runtime, namespace: user-engine} +spec: + podSelector: + matchLabels: {app.kubernetes.io/name: user-engine} + policyTypes: [Ingress, Egress] + ingress: + - from: + - namespaceSelector: + matchLabels: {kubernetes.io/metadata.name: kube-system} + ports: [{protocol: TCP, port: 8080}] + egress: + - to: + - podSelector: + matchLabels: {cnpg.io/cluster: user-engine-pg} + ports: [{protocol: TCP, port: 5432}] + - to: + - namespaceSelector: + matchLabels: {kubernetes.io/metadata.name: kube-system} + ports: [{protocol: UDP, port: 53}, {protocol: TCP, port: 53}] +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: {name: user-engine-postgres, namespace: user-engine} +spec: + podSelector: + matchLabels: {cnpg.io/cluster: user-engine-pg} + policyTypes: [Ingress, Egress] + ingress: + - from: + - podSelector: + matchLabels: {app.kubernetes.io/name: user-engine} + ports: [{protocol: TCP, port: 5432}] + - from: + - namespaceSelector: + matchLabels: {kubernetes.io/metadata.name: cnpg-system} + ports: + - {protocol: TCP, port: 5432} + - {protocol: TCP, port: 8000} + - {protocol: TCP, port: 9187} + egress: + # CNPG instance manager must read its Cluster resource during bootstrap. + # K3s network policy evaluates the API service after DNAT on port 6443. + - ports: [{protocol: TCP, port: 6443}] + - to: + - namespaceSelector: + matchLabels: {kubernetes.io/metadata.name: kube-system} + ports: [{protocol: UDP, port: 53}, {protocol: TCP, port: 53}] diff --git a/workplans/NK-WP-0023-user-engine-portal-platform-integration.md b/workplans/NK-WP-0023-user-engine-portal-platform-integration.md index 75d891f..ffbfb14 100644 --- a/workplans/NK-WP-0023-user-engine-portal-platform-integration.md +++ b/workplans/NK-WP-0023-user-engine-portal-platform-integration.md @@ -98,6 +98,14 @@ runtime references, NetworkPolicies, TLS ingress, backups, observability, resource limits, rollout/rollback, and availability evidence. Do not place this stateful platform control surface on scale-to-zero Knative. +Implementation evidence (2026-07-27): the internal-only portal foundation is +running on `reef-railiance` as `Deployment/user-engine` with dedicated healthy +`Cluster/user-engine-pg`, default-deny NetworkPolicies, immutable directly +imported image `user-engine:portal-c27012a`, and successful `/readyz` probes. +Public ingress remains intentionally absent until T03 supplies the verified +KeyCape OIDC edge. Forgejo OCI publication is pending restoration of the +OpenBao package-publisher capability; no credential was bypassed or exposed. + ## T06 - Prove role-scoped administration and failure safety ```task