Compare commits

..
14 Commits
Author SHA1 Message Date
security-bot 03cf6a0e45 chore(image-automation): bump gitea.renner.uno/gitea_admin/authentik-gate-sync:2026-11-01-1059
ci/woodpecker/cron/e2e Pipeline failed
ci/woodpecker/cron/security-scan Pipeline was successful
Updates managed by image-automation-controller.
2026-11-01 11:07:04 +00:00
k.renner 88576f7022 Merge pull request 'revert(authentik-gate-sync): roll back OIDC clients (#1535, #1536), gates are being deleted' (#1537) from revert/gate-sync-oidc into main
ci/woodpecker/push/build-ci-images Pipeline was successful
Reviewed-on: #1537
2026-11-01 10:59:29 +00:00
hermes-bot 8c85a49ae5 Revert "Merge pull request 'feat(authentik-gate-sync): OIDC clients from OIDCClient resources' (#1535) from feat/gate-sync-oidc into main"
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
This reverts commit 09a03dfd76, reversing
changes made to e518b46045.
2026-11-01 10:44:22 +00:00
hermes-bot 8fa68bc9cd Revert "Merge pull request 'fix(authentik-gate-sync): stop sweeping every gate as an OIDC client' (#1536) from fix/gate-sync-proxy-sweep into main"
This reverts commit 60dfcf5382, reversing
changes made to 1a99c02996.
2026-11-01 10:44:22 +00:00
security-bot 20d928aeba chore(image-automation): bump gitea.renner.uno/gitea_admin/authentik-gate-sync:2026-11-01-1014
Updates managed by image-automation-controller.
2026-11-01 10:37:04 +00:00
k.renner 60dfcf5382 Merge pull request 'fix(authentik-gate-sync): stop sweeping every gate as an OIDC client' (#1536) from fix/gate-sync-proxy-sweep into main
ci/woodpecker/push/build-ci-images Pipeline was successful
Reviewed-on: #1536
2026-11-01 10:13:55 +00:00
hermes-bot 5e814c525a fix(authentik-gate-sync): stop sweeping every gate as an OIDC client
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
Outage since 10:11Z: the first pass of image 2026-11-01-0959 (#1535)
removed all 14 gates ("removed OIDC client from Authentik", "14 objects
removed"), and every pass since recreates and removes them again.

ProxyProvider subclasses OAuth2Provider (authentik 2026.8.4
providers/proxy/models.py: class ProxyProvider(OutpostModel,
OAuth2Provider)), so GET providers/oauth2/ also lists every proxy
provider. owned_applications() yielded each gate a second time as an
OAuth2 application; it was not in the OIDC keep set, so the sweep
deleted its application and provider.

Both the sweep and ensure_oidc's provider lookup now take from
providers/oauth2/ only providers whose meta_model_name is
authentik_providers_oauth2.oauth2provider. The test fake now lists proxy
providers under providers/oauth2/ like the real API; the new tests
(gate-only and gate + OIDC client stable over two passes) fail against
the previous controller.
2026-11-01 10:12:58 +00:00
security-bot 1a99c02996 chore(image-automation): bump gitea.renner.uno/gitea_admin/authentik-gate-sync:2026-11-01-0959
Updates managed by image-automation-controller.
2026-11-01 10:07:04 +00:00
k.renner 09a03dfd76 Merge pull request 'feat(authentik-gate-sync): OIDC clients from OIDCClient resources' (#1535) from feat/gate-sync-oidc into main
ci/woodpecker/push/build-ci-images Pipeline was successful
Reviewed-on: #1535
Reviewed-by: Review Bot <review-bot@renner.uno>
2026-11-01 09:59:01 +00:00
hermes-bot f76defb1e1 fix(authentik-gate-sync): review of the OIDC clients
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
- Desired redirect URIs carry redirect_uri_type: authorization, which the
  API returns on every entry (RedirectURISerializer default in 2026.8.4);
  without it every pass would re-PATCH the provider.
- OIDC slugs and gate names are kept apart: a slug that is a gate name is
  refused, an existing application without this controller's OAuth2
  provider is refused, and the sweep keeps proxy and OAuth2 applications
  by their own sets, so an OIDCClient can neither take over nor delete a
  gate.
- Status is written only when it changed.
- Adoption of an unassigned oidc-<slug> provider is documented; removal
  logs say OIDC client.
- reconcile() takes k8s/ak for tests; new loop tests: create then quiet,
  duplicate slug, transient error keeps the client, sweep after the
  resource is gone, slug equal to a gate name.
2026-11-01 09:49:33 +00:00
hermes-bot 3e0557cfe8 feat(authentik-gate-sync): OIDC clients as OIDCClient resources, not Ingress annotations
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
OIDC is not part of the Ingress. The client is now declared by an
OIDCClient (auth.renner.uno/v1alpha1, CRD in
infrastructure/authentik-gate-sync-crds, like kuma-sync-crds) next to the
app: launchURL, redirectURIs, optional slug, group, authorizationFlow.
The controller writes a Ready condition and the issuer to its status.

The auth.renner.uno/oidc-* Ingress annotations are gone; RBAC gains
get/list/watch on oidcclients and patch on their status.
2026-11-01 09:31:43 +00:00
hermes-bot 8739efe026 feat(authentik-gate-sync): OIDC clients from auth.renner.uno/oidc-client
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
An Ingress annotated auth.renner.uno/oidc-client: <slug> gets a
confidential OAuth2/OpenID provider (client ID = slug, secret generated by
Authentik), an application with that slug and, with auth.renner.uno/group,
the same group expression policy as gates. auth.renner.uno/oidc-redirect-uri
pins strict redirect URIs on the Ingress host; default is any path on it.

Same fail-closed and ownership rules as gates: owned clients are found via
the OAuth2 provider list and meta_publisher and swept when their Ingress
goes. The service-account role gains the OAuth2 provider permissions plus
view on scope mappings and certificate key pairs.

So apps that log in through Authentik via OIDC (first: YouTrack, #1534)
need no blueprint entries; the groups stay managed in Authentik.
2026-11-01 09:22:25 +00:00
k.renner e518b46045 Merge pull request 'chore(ci): bump ci-claude image to 2.1.316' (#1533) from ci-image/claude-2.1.316 into main
Reviewed-on: #1533
2026-11-01 09:14:15 +00:00
claude-bot d7a6c2bdc7 chore(ci): bump ci-claude image to 2.1.316
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
2026-11-01 00:01:25 +00:00
18 changed files with 8 additions and 359 deletions
+4 -4
View File
@@ -35,25 +35,25 @@ when:
steps:
- name: model-selection-tests
depends_on: []
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.312
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.316
commands:
- sh scripts/ci/test-model-selection.sh
- name: pr-review-mistral-tests
depends_on: []
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.312
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.316
commands:
- sh scripts/ci/test-pr-review-mistral.sh
- name: promtool-rules-guard-tests
depends_on: []
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.312
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.316
commands:
- sh scripts/ci/test-promtool-rules.sh
- name: yaml-citations-guard-tests
depends_on: []
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.312
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.316
commands:
- sh scripts/ci/test-yaml-citations.sh
+1 -1
View File
@@ -26,7 +26,7 @@ steps:
# fetch-models.sh response mapping. No cluster, no secrets, no network — the
# ci-claude image just provides sh + jq. Keeps the flag-injection regression
# test from rotting (it exercises the exact injection shapes).
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.312
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.316
commands:
- sh scripts/ci/test-model-selection.sh
+1 -1
View File
@@ -70,7 +70,7 @@ steps:
# this step is skipped — otherwise we'd fork-bomb PRs.
when:
- branch: main
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.312
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.316
environment:
CLAUDE_CODE_OAUTH_TOKEN:
from_secret: claude_code_oauth_token
+1 -1
View File
@@ -30,7 +30,7 @@ when:
steps:
- name: update-audit-agent
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.312
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.316
environment:
CLAUDE_CODE_OAUTH_TOKEN:
from_secret: claude_code_oauth_token
+1 -1
View File
@@ -105,7 +105,7 @@ spec:
type: RuntimeDefault
containers:
- name: controller
image: gitea.renner.uno/gitea_admin/authentik-gate-sync:2026-10-29-1349 # {"$imagepolicy": "flux-system:authentik-gate-sync"}
image: gitea.renner.uno/gitea_admin/authentik-gate-sync:2026-11-01-1059 # {"$imagepolicy": "flux-system:authentik-gate-sync"}
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
-90
View File
@@ -1,90 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: youtrack
namespace: youtrack
labels:
app.kubernetes.io/name: youtrack
spec:
replicas: 1
# One process owns the database directory.
strategy:
type: Recreate
selector:
matchLabels:
app.kubernetes.io/name: youtrack
template:
metadata:
labels:
app.kubernetes.io/name: youtrack
spec:
automountServiceAccountToken: false
enableServiceLinks: false
securityContext:
runAsNonRoot: true
# The image's service account.
runAsUser: 13001
runAsGroup: 13001
fsGroup: 13001
seccompProfile:
type: RuntimeDefault
containers:
- name: youtrack
image: jetbrains/youtrack:2026.2.19728@sha256:c5e9cedc6fb1d7bcc9b3b56fc3f63e563623e1cd39396161eb70957991583950
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
ports:
- name: http
containerPort: 8080
resources:
requests:
cpu: 250m
memory: 2Gi
limits:
memory: 4Gi
startupProbe:
tcpSocket:
port: http
periodSeconds: 10
failureThreshold: 60
readinessProbe:
tcpSocket:
port: http
periodSeconds: 10
livenessProbe:
tcpSocket:
port: http
periodSeconds: 30
failureThreshold: 4
volumeMounts:
- name: youtrack
mountPath: /opt/youtrack/data
subPath: data
- name: youtrack
mountPath: /opt/youtrack/conf
subPath: conf
- name: youtrack
mountPath: /opt/youtrack/logs
subPath: logs
- name: youtrack
mountPath: /opt/youtrack/backups
subPath: backups
volumes:
- name: youtrack
persistentVolumeClaim:
claimName: youtrack
---
apiVersion: v1
kind: Service
metadata:
name: youtrack
namespace: youtrack
spec:
selector:
app.kubernetes.io/name: youtrack
ports:
- name: http
port: 8080
targetPort: http
-27
View File
@@ -1,27 +0,0 @@
# LAN-only (UDM record via dns.renner.uno/internal); no public label until login goes through Authentik.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: youtrack
namespace: youtrack
labels:
dns.renner.uno/internal: "true"
annotations:
cert-manager.io/cluster-issuer: letsencrypt-cluster
spec:
ingressClassName: traefik
tls:
- hosts:
- youtrack.renner.uno
secretName: youtrack-tls
rules:
- host: youtrack.renner.uno
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: youtrack
port:
number: 8080
@@ -1,7 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- pvc.yaml
- deployment.yaml
- ingress.yaml
- oidcclient.yaml
-13
View File
@@ -1,13 +0,0 @@
# Authentik OIDC client, created by authentik-gate-sync; issuer https://auth.renner.uno/application/o/youtrack/.
apiVersion: auth.renner.uno/v1alpha1
kind: OIDCClient
metadata:
name: youtrack
namespace: youtrack
spec:
launchURL: https://youtrack.renner.uno
group: YouTrack Users
redirectURIs:
# YouTrack generates the callback when the auth module is created; pin it strict once known.
- matchingMode: regex
url: ^https://youtrack\.renner\.uno/.*$
-17
View File
@@ -1,17 +0,0 @@
# YouTrack data, conf, logs and backups (subPaths); Velero youtrack-daily.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: youtrack
namespace: youtrack
labels:
app.kubernetes.io/name: youtrack
annotations:
kustomize.toolkit.fluxcd.io/prune: disabled
spec:
accessModes:
- ReadWriteOnce
storageClassName: ceph-block
resources:
requests:
storage: 20Gi
-28
View File
@@ -394,31 +394,3 @@ spec:
status.validationErrors.exists(e, e.startsWith('No backups found for schedule')))))
current: >-
has(status.phase) && (status.phase == 'Completed' || status.phase == 'FailedValidation')
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: restore-youtrack
namespace: flux-system
spec:
interval: 10m
timeout: 1h
sourceRef:
kind: GitRepository
name: flux-system
path: ./kubernetes/restore/youtrack
prune: true
wait: true
dependsOn:
- name: restore-gitea
- name: velero-backups-synced
- name: infrastructure-ceph-cluster
healthCheckExprs:
- apiVersion: velero.io/v1
kind: Restore
failed: >-
has(status.phase) && (status.phase in ['Failed', 'PartiallyFailed'] ||
(status.phase == 'FailedValidation' && !(has(status.validationErrors) &&
status.validationErrors.exists(e, e.startsWith('No backups found for schedule')))))
current: >-
has(status.phase) && (status.phase == 'Completed' || status.phase == 'FailedValidation')
-25
View File
@@ -1,25 +0,0 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: youtrack
namespace: flux-system
spec:
interval: 10m
retryInterval: 1m
timeout: 15m
sourceRef:
kind: GitRepository
name: flux-system
path: ./kubernetes/apps/youtrack
prune: true
wait: true
# OIDCClient Ready tracks Authentik (e.g. the group exists), not this apply.
healthCheckExprs:
- apiVersion: auth.renner.uno/v1alpha1
kind: OIDCClient
current: "true"
dependsOn:
- name: restore-youtrack
- name: infrastructure
- name: infrastructure-crds
- name: infrastructure-ceph-cluster
@@ -83,11 +83,6 @@ metadata:
---
apiVersion: v1
kind: Namespace
metadata:
name: youtrack
---
apiVersion: v1
kind: Namespace
metadata:
name: immich
---
@@ -32,7 +32,6 @@ resources:
- wordpress-ns.yaml
- ai-ns.yaml
- navidrome-ns.yaml
- youtrack-ns.yaml
- uptime-monitor-ns.yaml
- kuma-sync-ns.yaml
- cloudflare-lb-sync-ns.yaml
@@ -1,107 +0,0 @@
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: default-deny-ingress
namespace: youtrack
spec:
endpointSelector: {}
ingress:
- fromEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": youtrack
- fromEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": ingress
app.kubernetes.io/name: traefik
- fromEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": monitoring
- fromEntities:
- host
- remote-node
# ICMP — all standard ICMPv4 + ICMPv6 from any cluster pod. See
# home-assistant-ns.yaml for the rationale (CT-RELATED path not
# passing related ICMP empirically; per-namespace shape, mirroring
# commit 3c517a0's verified-safe pattern with the 66dbe9f type set).
- fromEndpoints:
- matchExpressions:
- key: io.kubernetes.pod.namespace
operator: Exists
icmps:
- fields:
- { type: 0, family: IPv4 }
- { type: 3, family: IPv4 }
- { type: 8, family: IPv4 }
- { type: 11, family: IPv4 }
- { type: 12, family: IPv4 }
- { type: 1, family: IPv6 }
- { type: 2, family: IPv6 }
- { type: 3, family: IPv6 }
- { type: 4, family: IPv6 }
- { type: 128, family: IPv6 }
- { type: 129, family: IPv6 }
- { type: 133, family: IPv6 }
- { type: 134, family: IPv6 }
- { type: 135, family: IPv6 }
- { type: 136, family: IPv6 }
- { type: 143, family: IPv6 }
egress:
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": youtrack
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": ingress
app.kubernetes.io/name: traefik
- toEndpoints:
- matchLabels:
"k8s:io.kubernetes.pod.namespace": kube-system
k8s-app: kube-dns
toPorts:
- ports:
- port: "53"
protocol: UDP
# host/remote-node: kubelet probes + node-local reach (Ceph mons on host net)
- toEntities:
- host
- remote-node
# ICMP — egress mirror; see ingress block above.
- toEndpoints:
- matchExpressions:
- key: io.kubernetes.pod.namespace
operator: Exists
icmps:
- fields:
- { type: 0, family: IPv4 }
- { type: 3, family: IPv4 }
- { type: 8, family: IPv4 }
- { type: 11, family: IPv4 }
- { type: 12, family: IPv4 }
- { type: 1, family: IPv6 }
- { type: 2, family: IPv6 }
- { type: 3, family: IPv6 }
- { type: 4, family: IPv6 }
- { type: 128, family: IPv6 }
- { type: 129, family: IPv6 }
- { type: 133, family: IPv6 }
- { type: 134, family: IPv6 }
- { type: 135, family: IPv6 }
- { type: 136, family: IPv6 }
- { type: 143, family: IPv6 }
---
# Outbound HTTPS: OIDC discovery/token/JWKS at auth.renner.uno (Traefik VIP, matched as traefik
# pods above) and JetBrains services (license, update check), mail-free.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: youtrack-egress-https
namespace: youtrack
spec:
endpointSelector: {}
egress:
- toEntities:
- world
toPorts:
- ports:
- port: "443"
protocol: TCP
@@ -369,23 +369,3 @@ spec:
labelSelector:
matchLabels:
app.kubernetes.io/name: synapse
---
apiVersion: velero.io/v1
kind: Schedule
metadata:
name: youtrack-daily
namespace: velero
labels:
app.kubernetes.io/part-of: homelab-backup
spec:
# Daily at 10:15 local, next free stagger slot after matrix-daily at 09:45.
schedule: "15 10 * * *"
useOwnerReferencesInBackup: false
template:
includedNamespaces:
- youtrack
includedResources:
- persistentvolumeclaims
storageLocation: default
ttl: 720h0m0s
snapshotMoveData: true
@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- restore.yaml
-7
View File
@@ -1,7 +0,0 @@
apiVersion: velero.io/v1
kind: Restore
metadata:
name: youtrack
namespace: velero
spec:
scheduleName: youtrack-daily