feat(iam): add local-llm Authentik application with JWT auth
- Add local-llm OAuth2 provider and application to Authentik provisioning - Configure JWT-compatible OAuth2 provider (client_id: local-llm) - Generate client secret on first run, stored in llm-serving/local-llm-jwt Secret - Bind llm-admins group to local-llm application for admin access - Add RBAC for provisioning job to create secrets in llm-serving namespace - Output JWT issuer URL and certificate for local-llm token validation
This commit is contained in:
@@ -129,6 +129,20 @@ roleRef:
|
|||||||
name: authentik-provisioner
|
name: authentik-provisioner
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
---
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: authentik-provisioner
|
||||||
|
namespace: llm-serving
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: authentik-provisioner
|
||||||
|
namespace: iam
|
||||||
|
roleRef:
|
||||||
|
kind: ClusterRole
|
||||||
|
name: authentik-provisioner
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
---
|
||||||
apiVersion: batch/v1
|
apiVersion: batch/v1
|
||||||
kind: Job
|
kind: Job
|
||||||
metadata:
|
metadata:
|
||||||
|
|||||||
@@ -424,6 +424,16 @@ SERVICES = {
|
|||||||
"launch_url": "https://memory.riotpiao.com",
|
"launch_url": "https://memory.riotpiao.com",
|
||||||
"display_name": "Poimen Memory",
|
"display_name": "Poimen Memory",
|
||||||
},
|
},
|
||||||
|
"local-llm": {
|
||||||
|
# JWT auth for local LLM API access - service-to-service, no browser flow.
|
||||||
|
# Client validates JWT tokens issued by this provider using the public key.
|
||||||
|
"client_secret_source": ("llm-serving", "local-llm-jwt", "client-secret"),
|
||||||
|
"generate_if_missing": True,
|
||||||
|
"extra_secret_literals": {"client-id": "local-llm"},
|
||||||
|
"redirect_uris": [], # No browser flow, JWT/service-to-service only
|
||||||
|
"launch_url": "https://llm.riotpiao.com",
|
||||||
|
"display_name": "Local LLM",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
app_pks_for_binding = []
|
app_pks_for_binding = []
|
||||||
@@ -646,7 +656,7 @@ for name, app_pk in app_pks_for_binding:
|
|||||||
|
|
||||||
# Per-service admin groups are app-scoped (unlike homelab-admins' blanket
|
# Per-service admin groups are app-scoped (unlike homelab-admins' blanket
|
||||||
# binding above) - only grants visibility/access to that one application.
|
# binding above) - only grants visibility/access to that one application.
|
||||||
# portainer/kmsvc/temporal/llm-serving have no Authentik Application (no OIDC
|
# portainer/kmsvc/temporal have no Authentik Application (no OIDC
|
||||||
# login integration), so their groups exist for the "permissions" claim /
|
# login integration), so their groups exist for the "permissions" claim /
|
||||||
# future k8s RBAC only - nothing to bind here.
|
# future k8s RBAC only - nothing to bind here.
|
||||||
SERVICE_GROUP_TO_APP_SLUG = {
|
SERVICE_GROUP_TO_APP_SLUG = {
|
||||||
@@ -656,6 +666,7 @@ SERVICE_GROUP_TO_APP_SLUG = {
|
|||||||
"homarr-admins": "homarr",
|
"homarr-admins": "homarr",
|
||||||
"paperless-admins": "paperless",
|
"paperless-admins": "paperless",
|
||||||
"immich-admins": "immich",
|
"immich-admins": "immich",
|
||||||
|
"llm-admins": "local-llm",
|
||||||
}
|
}
|
||||||
for group_name, app_slug in SERVICE_GROUP_TO_APP_SLUG.items():
|
for group_name, app_slug in SERVICE_GROUP_TO_APP_SLUG.items():
|
||||||
app_pk = next((pk for n, pk in app_pks_for_binding if n == app_slug), None)
|
app_pk = next((pk for n, pk in app_pks_for_binding if n == app_slug), None)
|
||||||
@@ -674,6 +685,19 @@ for group_name, app_slug in SERVICE_GROUP_TO_APP_SLUG.items():
|
|||||||
)
|
)
|
||||||
print(f" {app_slug}: {group_name} bound")
|
print(f" {app_slug}: {group_name} bound")
|
||||||
|
|
||||||
|
# JWT configuration for local-llm
|
||||||
|
print("\n[JWT] Fetching Authentik signing key for local-llm...")
|
||||||
|
status, signing_key_res = api("GET", f"/api/v3/crypto/certificatekeypairs/{SIGNING_KEY_PK}/")
|
||||||
|
if status == 200:
|
||||||
|
jwt_cert = signing_key_res.get("certificate", "")
|
||||||
|
print(f" Public certificate available for JWT validation (base64-encoded below)\n")
|
||||||
|
import base64
|
||||||
|
cert_b64 = base64.b64encode(jwt_cert.encode()).decode()
|
||||||
|
print(f"Save this to local-llm config for JWT token validation:")
|
||||||
|
print(f" AUTHENTIK_JWT_CERT={cert_b64}")
|
||||||
|
print(f"\nJWT issuer URL: https://authentik.riotpiao.com/application/o/local-llm/")
|
||||||
|
print(f"Local-LLM credentials are stored in: kubectl -n llm-serving get secret local-llm-jwt")
|
||||||
|
|
||||||
print("\nDone. Summary:")
|
print("\nDone. Summary:")
|
||||||
print(" groups: homelab-admins (superuser) + " + ", ".join(SERVICE_ADMIN_GROUP_NAMES))
|
print(" groups: homelab-admins (superuser) + " + ", ".join(SERVICE_ADMIN_GROUP_NAMES))
|
||||||
print(" user: rock -> homelab-admins + all service admin groups")
|
print(" user: rock -> homelab-admins + all service admin groups")
|
||||||
|
|||||||
Reference in New Issue
Block a user