Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
267 lines
9.5 KiB
YAML
267 lines
9.5 KiB
YAML
apiVersion: v1
|
|
kind: ServiceAccount
|
|
metadata:
|
|
name: keycape-factor-eso
|
|
namespace: sso
|
|
labels:
|
|
app.kubernetes.io/part-of: net-kingdom-sso-mfa
|
|
automountServiceAccountToken: false
|
|
---
|
|
apiVersion: v1
|
|
kind: ServiceAccount
|
|
metadata:
|
|
name: keycape-factor-renewer
|
|
namespace: sso
|
|
labels:
|
|
app.kubernetes.io/part-of: net-kingdom-sso-mfa
|
|
automountServiceAccountToken: false
|
|
---
|
|
apiVersion: external-secrets.io/v1
|
|
kind: ClusterSecretStore
|
|
metadata:
|
|
name: openbao-keycape-factor-read
|
|
spec:
|
|
conditions:
|
|
- namespaces:
|
|
- sso
|
|
provider:
|
|
vault:
|
|
server: http://openbao.openbao.svc.cluster.local:8200
|
|
path: platform
|
|
version: v2
|
|
auth:
|
|
kubernetes:
|
|
mountPath: kubernetes
|
|
role: keycape-factor-workload-kv-read
|
|
serviceAccountRef:
|
|
name: keycape-factor-eso
|
|
namespace: sso
|
|
audiences:
|
|
- openbao
|
|
---
|
|
apiVersion: external-secrets.io/v1
|
|
kind: ExternalSecret
|
|
metadata:
|
|
name: keycape-factor-read
|
|
namespace: sso
|
|
labels:
|
|
app.kubernetes.io/part-of: net-kingdom-sso-mfa
|
|
spec:
|
|
refreshInterval: 1m
|
|
secretStoreRef:
|
|
kind: ClusterSecretStore
|
|
name: openbao-keycape-factor-read
|
|
target:
|
|
name: keycape-factor-read
|
|
creationPolicy: Owner
|
|
data:
|
|
- secretKey: admin-token
|
|
remoteRef:
|
|
key: workloads/net-kingdom/keycape-factor-read
|
|
property: TOKEN
|
|
- secretKey: expires-at
|
|
remoteRef:
|
|
key: workloads/net-kingdom/keycape-factor-read
|
|
property: EXPIRES_AT
|
|
---
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: keycape-factor-renewer-2cf770000e
|
|
namespace: sso
|
|
labels:
|
|
app.kubernetes.io/part-of: net-kingdom-sso-mfa
|
|
immutable: true
|
|
data:
|
|
renew.py: "\"\"\"Dedicated Kubernetes renewal worker. Only sanitized outcome metadata\
|
|
\ is emitted.\"\"\"\nimport base64,json,sys,time,urllib.request,urllib.error,urllib.parse\n\
|
|
from pathlib import Path\nBAO=\"http://openbao.openbao.svc.cluster.local:8200/v1\"\
|
|
\nPI=\"http://privacyidea.mfa.svc.cluster.local:8080\"\nISSUER=\"platform/data/workloads/net-kingdom/keycape-factor-issuer\"\
|
|
\nTARGET=\"platform/data/workloads/net-kingdom/keycape-factor-read\"\nUSER=\"\
|
|
keycape-factor-reader\"\nPROVENANCE=\"RPF-WP-0040\"\nclass Failure(Exception):pass\n\
|
|
class NoRedirect(urllib.request.HTTPRedirectHandler):\n def redirect_request(self,*args,**kwargs):return\
|
|
\ None\n\ndef http(url,body=None,headers=None,method=None,form=False):\n data=None\n\
|
|
\ headers=dict(headers or {})\n if body is not None:\n data=(urllib.parse.urlencode(body)\
|
|
\ if form else json.dumps(body)).encode()\n headers[\"Content-Type\"]=\"application/x-www-form-urlencoded\"\
|
|
\ if form else \"application/json\"\n req=urllib.request.Request(url,data=data,headers=headers,method=method)\n\
|
|
\ try:\n with urllib.request.build_opener(NoRedirect()).open(req,timeout=15)\
|
|
\ as r:\n raw=r.read(1048577)\n if len(raw)>1048576:raise Failure()\n return\
|
|
\ r.status,json.loads(raw) if raw else {}\n except urllib.error.HTTPError as e:return\
|
|
\ e.code,{}\n\ndef success(code,data):\n if code!=200 or not data.get(\"result\"\
|
|
,{}).get(\"status\"):raise Failure()\n return data[\"result\"][\"value\"]\n\n\
|
|
def run():\n session=None;receipt={\"success\":False,\"phase\":\"workload_login\"\
|
|
}\n try:\n jwt=Path(\"/var/run/keycape-factor/token\").read_text().strip()\n\
|
|
\ code,body=http(BAO+\"/auth/kubernetes/login\",{\"role\":\"keycape-factor-renewer\"\
|
|
,\"jwt\":jwt})\n if code!=200:raise Failure()\n session=body[\"auth\"][\"client_token\"\
|
|
];headers={\"X-Vault-Token\":session}\n receipt[\"phase\"]=\"issuer_custody\"\
|
|
\n code,body=http(BAO+\"/\"+ISSUER,headers=headers)\n if code!=200:raise Failure()\n\
|
|
\ issuer=body[\"data\"][\"data\"]\n if issuer.get(\"REQUEST\")!=PROVENANCE or\
|
|
\ issuer.get(\"USERNAME\")!=USER:raise Failure()\n receipt[\"phase\"]=\"provider_authentication\"\
|
|
\n value=success(*http(PI+\"/auth\",{\"username\":USER,\"password\":issuer[\"\
|
|
PASSWORD\"]},form=True))\n if value.get(\"role\")!=\"admin\" or value.get(\"\
|
|
username\")!=USER:raise Failure()\n token=value[\"token\"];claims=json.loads(base64.urlsafe_b64decode(token.split('.')[1]+'==='));expiry=int(claims[\"\
|
|
exp\"])\n if not 300<expiry-time.time()<=7200:raise Failure()\n receipt[\"phase\"\
|
|
]=\"provider_scope\"\n provider_headers={\"Authorization\":token}\n listing=success(*http(PI+\"\
|
|
/token/?tokenrealm=coulomb&active=True&pagesize=1\",headers=provider_headers))\n\
|
|
\ if not isinstance(listing.get(\"tokens\"),list) or not isinstance(listing.get(\"\
|
|
count\"),int):raise Failure()\n code,_=http(PI+\"/policy/\",headers=provider_headers)\n\
|
|
\ if code not in (401,403):raise Failure()\n receipt[\"phase\"]=\"publish\"\n\
|
|
\ code,body=http(BAO+\"/\"+TARGET,headers=headers)\n if code!=200 or body[\"\
|
|
data\"][\"data\"].get(\"REQUEST\")!=PROVENANCE:raise Failure()\n version=body[\"\
|
|
data\"][\"metadata\"][\"version\"]\n code,_=http(BAO+\"/\"+TARGET,{\"options\"\
|
|
:{\"cas\":version},\"data\":{\"TOKEN\":token,\"EXPIRES_AT\":expiry,\"REQUEST\"\
|
|
:PROVENANCE}},headers=headers)\n if code!=200:raise Failure()\n code,body=http(BAO+\"\
|
|
/\"+TARGET,headers=headers)\n if code!=200 or body[\"data\"][\"data\"][\"TOKEN\"\
|
|
]!=token:raise Failure()\n receipt.update(success=True,phase=\"complete\",expires_at=expiry,kv_version=body[\"\
|
|
data\"][\"metadata\"][\"version\"],cross_user_factor_visible=listing[\"count\"\
|
|
]>0)\n except Exception:pass\n finally:\n if session:\n try:\n code,_=http(BAO+\"\
|
|
/auth/token/revoke-self\",headers={\"X-Vault-Token\":session},method=\"POST\"\
|
|
)\n receipt[\"session_revoked\"]=code in (200,204)\n except Exception:receipt[\"\
|
|
session_revoked\"]=False\n if not receipt[\"session_revoked\"]:receipt[\"success\"\
|
|
]=False;receipt[\"phase\"]=\"session_cleanup\"\n return receipt\nif __name__==\"\
|
|
__main__\":\n receipt=run();print(json.dumps(receipt));raise SystemExit(0 if receipt[\"\
|
|
success\"] else 1)\n"
|
|
---
|
|
apiVersion: batch/v1
|
|
kind: CronJob
|
|
metadata:
|
|
name: keycape-factor-renewer
|
|
namespace: sso
|
|
labels:
|
|
app.kubernetes.io/part-of: net-kingdom-sso-mfa
|
|
spec:
|
|
schedule: '*/10 * * * *'
|
|
suspend: false
|
|
concurrencyPolicy: Forbid
|
|
startingDeadlineSeconds: 120
|
|
successfulJobsHistoryLimit: 2
|
|
failedJobsHistoryLimit: 3
|
|
jobTemplate:
|
|
spec:
|
|
activeDeadlineSeconds: 120
|
|
backoffLimit: 1
|
|
ttlSecondsAfterFinished: 3600
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app.kubernetes.io/name: keycape-factor-renewer
|
|
spec:
|
|
serviceAccountName: keycape-factor-renewer
|
|
automountServiceAccountToken: false
|
|
restartPolicy: Never
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
runAsUser: 65534
|
|
runAsGroup: 65534
|
|
fsGroup: 65534
|
|
seccompProfile:
|
|
type: RuntimeDefault
|
|
containers:
|
|
- name: renew
|
|
image: python:3.12-slim@sha256:d764629ce0ddd8c71fd371e9901efb324a95789d2315a47db7e4d27e78f1b0e9
|
|
command:
|
|
- python3
|
|
- /worker/renew.py
|
|
env:
|
|
- name: PYTHONDONTWRITEBYTECODE
|
|
value: '1'
|
|
securityContext:
|
|
allowPrivilegeEscalation: false
|
|
readOnlyRootFilesystem: true
|
|
capabilities:
|
|
drop:
|
|
- ALL
|
|
resources:
|
|
requests:
|
|
cpu: 10m
|
|
memory: 24Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 64Mi
|
|
volumeMounts:
|
|
- name: worker
|
|
mountPath: /worker
|
|
readOnly: true
|
|
- name: identity
|
|
mountPath: /var/run/keycape-factor
|
|
readOnly: true
|
|
volumes:
|
|
- name: worker
|
|
configMap:
|
|
name: keycape-factor-renewer-2cf770000e
|
|
- name: identity
|
|
projected:
|
|
defaultMode: 288
|
|
sources:
|
|
- serviceAccountToken:
|
|
path: token
|
|
audience: openbao
|
|
expirationSeconds: 600
|
|
---
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: keycape-factor-renewer
|
|
namespace: sso
|
|
labels:
|
|
app.kubernetes.io/part-of: net-kingdom-sso-mfa
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: keycape-factor-renewer
|
|
policyTypes:
|
|
- Ingress
|
|
- Egress
|
|
ingress: []
|
|
egress:
|
|
- to:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: openbao
|
|
ports:
|
|
- port: 8200
|
|
protocol: TCP
|
|
- to:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: mfa
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: privacyidea
|
|
ports:
|
|
- port: 8080
|
|
protocol: TCP
|
|
- to:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: kube-system
|
|
ports:
|
|
- port: 53
|
|
protocol: UDP
|
|
- port: 53
|
|
protocol: TCP
|
|
---
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-keycape-factor-renewal
|
|
namespace: mfa
|
|
labels:
|
|
app.kubernetes.io/part-of: net-kingdom-sso-mfa
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: privacyidea
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: sso
|
|
podSelector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: keycape-factor-renewer
|
|
ports:
|
|
- port: 8080
|
|
protocol: TCP
|