feat(auth): scaffold Ory Kratos identity service — phase 1 (infrastructure)
First phase of replacing the hand-rolled auth (internal/services/auth_service.go
et al.) with Ory Kratos. This commit is infrastructure only — Kratos will run
but nothing consumes it yet; the Go API still does its own auth until phase 2.
Adds deploy-k3s/manifests/kratos/:
- configmap.yaml — kratos.yml, identity schema, Google/Apple OIDC claim
mappers (no secrets in the ConfigMap)
- migrate-job.yaml — `kratos migrate sql`, run before the Deployment
- kratos.yaml — Deployment (x2), Service, NetworkPolicies
- ingress.yaml — auth.myhoneydue.com -> Kratos public API :4433
- README.md — operator prerequisites + deploy runbook
Wiring:
- 02-setup-secrets.sh creates kratos-secrets, gated on a config.yaml `kratos:`
block (DSN, cookie/cipher, SMTP URI, OIDC client secret, Apple key).
- 03-deploy.sh applies the Kratos manifests + runs the migrate Job, gated on
the kratos-secrets Secret existing.
Both gates mean the existing stack deploys completely unaffected until the
operator completes the prerequisites (Neon `kratos` DB, auth.myhoneydue.com
DNS, Apple/Google OAuth apps, Kratos image version). Pre-production, so no
user-data migration — see manifests/kratos/README.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
# Ory Kratos — identity service for honeyDue.
|
||||
#
|
||||
# Deployed only once the operator has completed the prerequisites in
|
||||
# kratos/README.md (Neon `kratos` database, auth.myhoneydue.com DNS, Apple +
|
||||
# Google OAuth apps, and the kratos-secrets Secret). Until then 03-deploy.sh
|
||||
# skips the Kratos apply, so the existing stack is unaffected.
|
||||
#
|
||||
# IMAGE: oryd/kratos uses CalVer (v25.x / v26.x). The tag below is a
|
||||
# fail-loud placeholder — set the current stable tag and pin a @sha256:
|
||||
# digest (like redis/vmagent) before deploying. See kratos/README.md.
|
||||
# The schema-migration Job is in migrate-job.yaml (run before this).
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: kratos
|
||||
namespace: honeydue
|
||||
labels:
|
||||
app.kubernetes.io/name: kratos
|
||||
app.kubernetes.io/part-of: honeydue
|
||||
spec:
|
||||
replicas: 2
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: 0
|
||||
maxSurge: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: kratos
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: kratos
|
||||
app.kubernetes.io/part-of: honeydue
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: kratos
|
||||
image: oryd/kratos:REPLACE_WITH_CURRENT_STABLE_TAG
|
||||
imagePullPolicy: IfNotPresent
|
||||
args:
|
||||
- serve
|
||||
- --config
|
||||
- /etc/kratos/kratos.yml
|
||||
- --watch-courier # send verification/recovery email in-process
|
||||
ports:
|
||||
- name: public
|
||||
containerPort: 4433
|
||||
- name: admin
|
||||
containerPort: 4434
|
||||
env:
|
||||
# Kratos is configured natively via env vars; secrets come from
|
||||
# the kratos-secrets Secret rather than the ConfigMap.
|
||||
- name: DSN
|
||||
valueFrom: { secretKeyRef: { name: kratos-secrets, key: dsn } }
|
||||
- name: SECRETS_COOKIE
|
||||
valueFrom: { secretKeyRef: { name: kratos-secrets, key: secrets_cookie } }
|
||||
- name: SECRETS_CIPHER
|
||||
valueFrom: { secretKeyRef: { name: kratos-secrets, key: secrets_cipher } }
|
||||
- name: COURIER_SMTP_CONNECTION_URI
|
||||
valueFrom: { secretKeyRef: { name: kratos-secrets, key: smtp_connection_uri } }
|
||||
# OIDC provider secrets — index must match the providers list
|
||||
# order in configmap.yaml (0 = google, 1 = apple).
|
||||
- name: SELFSERVICE_METHODS_OIDC_CONFIG_PROVIDERS_0_CLIENT_SECRET
|
||||
valueFrom: { secretKeyRef: { name: kratos-secrets, key: google_client_secret } }
|
||||
- name: SELFSERVICE_METHODS_OIDC_CONFIG_PROVIDERS_1_APPLE_PRIVATE_KEY
|
||||
valueFrom: { secretKeyRef: { name: kratos-secrets, key: apple_private_key } }
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/kratos
|
||||
readOnly: true
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health/ready
|
||||
port: 4434
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health/alive
|
||||
port: 4434
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 30
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: 512Mi
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: kratos-config
|
||||
- name: tmp
|
||||
emptyDir:
|
||||
sizeLimit: 64Mi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: kratos
|
||||
namespace: honeydue
|
||||
labels:
|
||||
app.kubernetes.io/name: kratos
|
||||
app.kubernetes.io/part-of: honeydue
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: kratos
|
||||
ports:
|
||||
- name: public
|
||||
port: 4433
|
||||
targetPort: 4433
|
||||
- name: admin
|
||||
port: 4434
|
||||
targetPort: 4434
|
||||
---
|
||||
# Ingress to Kratos: Traefik (the auth.myhoneydue.com IngressRoute) and the
|
||||
# honeyDue api pods (session whoami) may reach the public API :4433. The
|
||||
# admin API :4434 takes no cluster ingress — it is reachable only in-pod.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-ingress-to-kratos
|
||||
namespace: honeydue
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: kratos
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: api
|
||||
ports:
|
||||
- port: 4433
|
||||
protocol: TCP
|
||||
---
|
||||
# Kratos egress: DNS, the Neon Postgres database, SMTP, and HTTPS to the
|
||||
# OIDC providers (Apple/Google token + JWKS endpoints).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-egress-from-kratos
|
||||
namespace: honeydue
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: kratos
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- to:
|
||||
- namespaceSelector: {}
|
||||
ports:
|
||||
- port: 53
|
||||
protocol: UDP
|
||||
- port: 53
|
||||
protocol: TCP
|
||||
# Neon Postgres (external)
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 0.0.0.0/0
|
||||
except:
|
||||
- 10.42.0.0/16
|
||||
- 10.43.0.0/16
|
||||
ports:
|
||||
- port: 5432
|
||||
protocol: TCP
|
||||
# SMTP (Fastmail) + HTTPS to Apple/Google OIDC endpoints (external)
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 0.0.0.0/0
|
||||
except:
|
||||
- 10.42.0.0/16
|
||||
- 10.43.0.0/16
|
||||
ports:
|
||||
- port: 465
|
||||
protocol: TCP
|
||||
- port: 443
|
||||
protocol: TCP
|
||||
Reference in New Issue
Block a user