fix(privacyidea): bootstrap-realm scope fixes + netpol for PI→LLDAP

bootstrap-realm.sh:
- Remove Content-Type header from GET requests (Werkzeug 3.x BadRequest fix)
- Fix resolver type check — result path is result.value.<name>.type, not .data
- Fix self-enrollment policy scope: 'user' not 'enrollment' (PI 3.12)

NetworkPolicies:
- allow-egress-to-lldap (mfa ns): privacyIDEA → LLDAP :3890
- allow-privacyidea-to-lldap (sso ns): ingress from mfa/privacyIDEA → LLDAP :3890

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-25 02:10:53 +00:00
parent 88bbd585fd
commit afbf968c76
3 changed files with 61 additions and 4 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -90,6 +90,8 @@ info "Authenticated as pi-admin (token obtained)"
pi_api() {
# pi_api <method> <path> [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': '*',