diff --git a/sso-mfa/k8s/network-policies/netpol-mfa.yaml b/sso-mfa/k8s/network-policies/netpol-mfa.yaml index f3f81f1..df81a8a 100644 --- a/sso-mfa/k8s/network-policies/netpol-mfa.yaml +++ b/sso-mfa/k8s/network-policies/netpol-mfa.yaml @@ -4,6 +4,7 @@ # INGRESS: Traefik (kube-system) → privacyIDEA :8080 (user-facing portal) # INGRESS: KeyCape (sso) → privacyIDEA :8080 (Provider API calls) # EGRESS: privacyIDEA → databases :5432 (PostgreSQL) +# EGRESS: privacyIDEA → sso/lldap :3890 (LDAP resolver for realm) # EGRESS: all pods → kube-dns :53 (UDP+TCP) # # Everything else is denied. @@ -90,6 +91,33 @@ spec: - port: 5432 protocol: TCP --- +# ── privacyIDEA → LLDAP :3890 ──────────────────────────────────────────────── +# privacyIDEA's LDAP resolver binds to LLDAP to resolve users in the coulomb realm. +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-egress-to-lldap + namespace: mfa + labels: + net-kingdom/component: mfa +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: privacyidea + policyTypes: + - Egress + egress: + - to: + - namespaceSelector: + matchLabels: + net-kingdom/component: sso + podSelector: + matchLabels: + app.kubernetes.io/name: lldap + ports: + - port: 3890 + protocol: TCP +--- # ── Traefik → ACME HTTP-01 solver pods :8089 ───────────────────────────────── apiVersion: networking.k8s.io/v1 kind: NetworkPolicy diff --git a/sso-mfa/k8s/network-policies/netpol-sso.yaml b/sso-mfa/k8s/network-policies/netpol-sso.yaml index bf0fde5..1be22d9 100644 --- a/sso-mfa/k8s/network-policies/netpol-sso.yaml +++ b/sso-mfa/k8s/network-policies/netpol-sso.yaml @@ -173,6 +173,33 @@ spec: - port: 3890 protocol: TCP --- +# ── privacyIDEA (mfa ns) → LLDAP :3890 ────────────────────────────────────── +# privacyIDEA's LDAP resolver binds to LLDAP to resolve users in the coulomb realm. +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-privacyidea-to-lldap + namespace: sso + labels: + net-kingdom/component: sso +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: lldap + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + net-kingdom/component: mfa + podSelector: + matchLabels: + app.kubernetes.io/name: privacyidea + ports: + - port: 3890 + protocol: TCP +--- # ── KeyCape egress → Authelia + LLDAP (within sso namespace) ───────────────── apiVersion: networking.k8s.io/v1 kind: NetworkPolicy diff --git a/sso-mfa/k8s/privacyidea/bootstrap-realm.sh b/sso-mfa/k8s/privacyidea/bootstrap-realm.sh index 6e493f5..dd9dfe5 100755 --- a/sso-mfa/k8s/privacyidea/bootstrap-realm.sh +++ b/sso-mfa/k8s/privacyidea/bootstrap-realm.sh @@ -90,6 +90,8 @@ info "Authenticated as pi-admin (token obtained)" pi_api() { # pi_api [json-body] + # Content-Type is only set on requests with a body — Werkzeug 3.x raises + # BadRequest if Content-Type: application/json is sent on a bodyless GET. local method="$1"; local path="$2"; local body="${3:-}" if [[ -n "$body" ]]; then curl -sf -X "$method" "$PI_URL$path" \ @@ -99,7 +101,6 @@ pi_api() { else curl -sf -X "$method" "$PI_URL$path" \ -H "Authorization: $PI_TOKEN" \ - -H "Content-Type: application/json" \ 2>/dev/null || echo "CURL_FAILED" fi } @@ -163,7 +164,7 @@ echo "Step 2: Testing resolver '$RESOLVER_NAME' ..." TEST_RESP=$(pi_api GET "/resolver/$RESOLVER_NAME") if [[ "$TEST_RESP" != "CURL_FAILED" ]]; then RESOLVER_TYPE=$(echo "$TEST_RESP" | python3 -c \ - "import sys,json; d=json.load(sys.stdin); r=d.get('result',{}).get('value',{}).get('data',{}); print(list(r.values())[0].get('type','') if r else '')" \ + "import sys,json; d=json.load(sys.stdin); r=d.get('result',{}).get('value',{}); print(list(r.values())[0].get('type','') if r else '')" \ 2>/dev/null || echo "") if [[ "$RESOLVER_TYPE" == "ldapresolver" ]]; then ok "Resolver '$RESOLVER_NAME' exists and is type ldapresolver" @@ -217,12 +218,13 @@ check_result "Default realm set to '$REALM_NAME'" "$RESP" || true # ── 6. Create self-enrollment policy ───────────────────────────────────────── echo "" echo "Step 6: Creating self-enrollment policy ..." -# Allows users in the coulomb realm to self-enroll TOTP tokens. +# Allows users in the coulomb realm to self-enroll TOTP tokens via the self-service portal. +# Scope must be 'user' (not 'enrollment') — self-service actions live in the user scope. # The WebUI self-service portal is at pink-account.coulomb.social. ENROLL_POLICY=$(python3 -c " import json body = { - 'scope': 'enrollment', + 'scope': 'user', 'action': 'enrollTOTP, delete, disable', 'realm': '$REALM_NAME', 'user': '*',