diff --git a/apps/wave-0/jackett-oauth2-proxy-credentials.sops.yaml b/apps/wave-0/jackett-oauth2-proxy-credentials.sops.yaml new file mode 100644 index 0000000..8f1920e --- /dev/null +++ b/apps/wave-0/jackett-oauth2-proxy-credentials.sops.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Secret +metadata: + name: jackett-oauth2-proxy + namespace: jackett +type: Opaque +stringData: + client-id: ENC[AES256_GCM,data:n5h6NG0emA==,iv:d6lvZfgRq6+IKNpsSoRa5a38R2f2+tWBO+j9QKDThMY=,tag:P+cvQXlWR4aZs7a9/lVuMQ==,type:str] + client-secret: ENC[AES256_GCM,data:WL5G7PuVTdHQoe7Zp2eiE9ppxSGE0+W2RMfISuwqD6JA9auG97ZYnPUjG8HNx7sosOvsmqeE9kONK23IqIeaQw==,iv:kSP10UKeDv/8BsgX36+uKwEY+LBovcicce1RkD14M4g=,tag:fEUsVuajWPxAmAo8Nnrrrg==,type:str] + cookie-secret: ENC[AES256_GCM,data:WChW9YLhzG9Nwp8dCWTFtirufXxGk3Ep9HXpfqcNz69PXQW3RaMC4EJpw2U=,iv:f6obIerkog8Q2gfj1tVL259LOl5jj59VE29KnJwmCXQ=,tag:s0FwNtU6vffKgDcGUNUhCg==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age175l8mdpdfu7hq0aml3exs3gykarwjmuvr23sq56s3wjejnyk44wsp4mfzg + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLWVptVHc4aFl0MXRWTHVP + cC9qcVoyZUV2TGZnTEpHY3RLOU8vQnNNNm00ClZRejE2bzFaWXNSUmQ5MEZtV3VK + djB2TXJqQjVUOE93QjN6U0lMVDBEbm8KLS0tIFFTZnB0c2pCZWZMZ084WEIxT2tW + WUxxemJHdWRZbnBpL280dzlpMmhwSW8Kra0bkx3iJYQi+3rnpU/89fJbJHzQjKi2 + ZWNwYhLpULw1xNo3xbCm+PUp5f30kpyMKQld2qaLOZAqWI5vNl9Byw== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-08-31T13:41:37Z" + mac: ENC[AES256_GCM,data:zU9nWCrSomAXAZ/Ve8U3yLHnoxhu8nkQ7523neJTGv810ONzAAZ7Gr8nFHR7bTqz2QwuGYD3P1ypdqYnKLM8ri2Zf1LDo/Su9ptnvQhUbfXKlRorcW6uAWsViCwvTM05wZNkdzZfza7tGwJQVTD6hc0cP+/7Y7JnpT5FEstK254=,iv:oggTRjcWqAJizA/a22+mtslkEWka8hUG0wudM8obO4g=,tag:i0TtNmqGe+b5t4iq8LpsqA==,type:str] + pgp: [] + encrypted_regex: ^(data|stringData)$ + version: 3.9.4 diff --git a/apps/wave-0/jackett.yaml b/apps/wave-0/jackett.yaml index 87a143e..f80c721 100644 --- a/apps/wave-0/jackett.yaml +++ b/apps/wave-0/jackett.yaml @@ -84,6 +84,70 @@ spec: volumeMounts: - name: config mountPath: /data/config + - name: oauth2-proxy + image: quay.io/oauth2-proxy/oauth2-proxy:v7.8.1 + imagePullPolicy: IfNotPresent + args: + - --http-address=0.0.0.0:4180 + - --reverse-proxy=true + - --provider=oidc + - --oidc-issuer-url=https://keycloak.home.keithjackson.dev/realms/homelab + - --upstream=http://127.0.0.1:9117 + - --email-domain=* + - --scope=openid email profile + - --oidc-groups-claim=groups + - --allowed-group=media-users + - --skip-provider-button=true + - --redirect-url=https://jackett.home.keithjackson.dev/oauth2/callback + - --cookie-domain=.home.keithjackson.dev + - --whitelist-domain=.home.keithjackson.dev + - --cookie-secure=true + - --pass-access-token=true + - --set-xauthrequest=true + env: + - name: OAUTH2_PROXY_CLIENT_ID + valueFrom: + secretKeyRef: + name: jackett-oauth2-proxy + key: client-id + - name: OAUTH2_PROXY_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: jackett-oauth2-proxy + key: client-secret + - name: OAUTH2_PROXY_COOKIE_SECRET + valueFrom: + secretKeyRef: + name: jackett-oauth2-proxy + key: cookie-secret + ports: + - name: http + containerPort: 4180 + protocol: TCP + resources: + requests: + cpu: 25m + memory: 64Mi + limits: + memory: 128Mi + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /ping + port: http + initialDelaySeconds: 5 + periodSeconds: 15 + readinessProbe: + httpGet: + path: /ping + port: http + initialDelaySeconds: 5 + periodSeconds: 15 volumes: - name: config persistentVolumeClaim: @@ -104,6 +168,9 @@ spec: - name: port-9117 port: 9117 targetPort: 9117 + - name: authproxy + port: 4180 + targetPort: 4180 --- # CiliumNetworkPolicy reciprocal ingress allow (R-NET-02): jackett-allow-radarr-9117 apiVersion: cilium.io/v2 @@ -166,7 +233,7 @@ spec: - ingress toPorts: - ports: - - port: '9117' + - port: '4180' protocol: TCP --- # CiliumNetworkPolicy world egress allow (egress class governs the path; R-NET-05): jackett-allow-internet-egress @@ -214,7 +281,7 @@ spec: value: / backendRefs: - name: jackett - port: 9117 + port: 4180 --- # ServiceMonitor (R-OBS-01) apiVersion: monitoring.coreos.com/v1 diff --git a/apps/wave-0/kustomization.yaml b/apps/wave-0/kustomization.yaml index 27659a4..bd47530 100644 --- a/apps/wave-0/kustomization.yaml +++ b/apps/wave-0/kustomization.yaml @@ -8,6 +8,7 @@ kind: Kustomization resources: - gitea-bootstrap-secret.sops.yaml - gitea.yaml +- jackett-oauth2-proxy-credentials.sops.yaml - jackett.yaml - nextcloud-db-app.sops.yaml - nextcloud-db-backup-credentials.sops.yaml @@ -15,6 +16,7 @@ resources: - pihole-oauth2-proxy-credentials.sops.yaml - pihole.yaml - plex.yaml +- qbittorrent-oauth2-proxy-credentials.sops.yaml - qbittorrent.yaml - semaphore-db-app.sops.yaml - semaphore-db-backup-credentials.sops.yaml diff --git a/apps/wave-0/qbittorrent-oauth2-proxy-credentials.sops.yaml b/apps/wave-0/qbittorrent-oauth2-proxy-credentials.sops.yaml new file mode 100644 index 0000000..f5bcbfc --- /dev/null +++ b/apps/wave-0/qbittorrent-oauth2-proxy-credentials.sops.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Secret +metadata: + name: qbittorrent-oauth2-proxy + namespace: qbittorrent +type: Opaque +stringData: + client-id: ENC[AES256_GCM,data:ytfVRDabEfRM8NI=,iv:ERtFy5iss10id/cGLMZDmIAazkYtFG/spkxRYkoT2pw=,tag:FzOepKeYT05BBrz6RlnACQ==,type:str] + client-secret: ENC[AES256_GCM,data:klZzTdyAqQgG+1s074/0Bh5ybuHehiEo/RYceRGAGwuUrgCMb0AmYHCrE9gvtnPkSOBqWvFnPnorWkERc+V73g==,iv:nOUO/p+WnI5657I3Lm6zQAsumkscjF7J0fK0EVFHGo0=,tag:dF3AHYP4sxDChroRer9kMQ==,type:str] + cookie-secret: ENC[AES256_GCM,data:jVpsZjYHRrPvblfsb2f9OuneqoZ0cwcTLKrPb8XJ5oves1PmHtN5njrkk4o=,iv:bNjUK/ar4nniR7AjJ9fsGVkKd8nxwphkptmhFaYh9Ek=,tag:2axf+AshSdIvvqwyIfhq6g==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age175l8mdpdfu7hq0aml3exs3gykarwjmuvr23sq56s3wjejnyk44wsp4mfzg + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAreWdPdUxiYXBxYWxZNlhy + WHFTZmo1RW1McXJJTjlWeTBCd2Y4WVU2TVRNCnExbEQrQXZCS2RRaGxJQlQ5eHpY + Z1UrZWlubUJpRC9YeEhJZW8zNVdLUVUKLS0tIGtmblFDRzk3ZC8wWVo2ZHJHWmEx + OW9DRUpaSlFrbHNDTGYvQUdyamh2bWsK5VvV4cl1iNXYECnSk3fi1ZuAPKGN+n8B + g51lT4gi03KA67ARzwVGVBfP5E1Cdmx4rJhi305tG+Ug99VyLBCUfQ== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-08-31T13:42:02Z" + mac: ENC[AES256_GCM,data:7GdlUKnZ/ST7EOhfPB6I2e2wyVHWmZ2G554yBcZQzqoZZWZ1hn+lkosNJxWD7E0hRId6P320EKj+Sf0g2ur3Mna5zii1GKFJHGtDEoccKvJQRx2JMkZlEC/x6xwc4BeJNtvidECVRvKC2WdrByRMr9rrI8NfXpmWH4e7iz1Xac0=,iv:UqsNOPRwzT+Dt043ldxIubkAAkPr0/nvjxlgVPiXIbw=,tag:7Wex5Rng3Nwtr8wxkFJD9Q==,type:str] + pgp: [] + encrypted_regex: ^(data|stringData)$ + version: 3.9.4 diff --git a/apps/wave-0/qbittorrent.yaml b/apps/wave-0/qbittorrent.yaml index 02c66c6..573794a 100644 --- a/apps/wave-0/qbittorrent.yaml +++ b/apps/wave-0/qbittorrent.yaml @@ -84,6 +84,70 @@ spec: volumeMounts: - name: config mountPath: /data/config + - name: oauth2-proxy + image: quay.io/oauth2-proxy/oauth2-proxy:v7.8.1 + imagePullPolicy: IfNotPresent + args: + - --http-address=0.0.0.0:4180 + - --reverse-proxy=true + - --provider=oidc + - --oidc-issuer-url=https://keycloak.home.keithjackson.dev/realms/homelab + - --upstream=http://127.0.0.1:8080 + - --email-domain=* + - --scope=openid email profile + - --oidc-groups-claim=groups + - --allowed-group=media-users + - --skip-provider-button=true + - --redirect-url=https://qbittorrent.home.keithjackson.dev/oauth2/callback + - --cookie-domain=.home.keithjackson.dev + - --whitelist-domain=.home.keithjackson.dev + - --cookie-secure=true + - --pass-access-token=true + - --set-xauthrequest=true + env: + - name: OAUTH2_PROXY_CLIENT_ID + valueFrom: + secretKeyRef: + name: qbittorrent-oauth2-proxy + key: client-id + - name: OAUTH2_PROXY_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: qbittorrent-oauth2-proxy + key: client-secret + - name: OAUTH2_PROXY_COOKIE_SECRET + valueFrom: + secretKeyRef: + name: qbittorrent-oauth2-proxy + key: cookie-secret + ports: + - name: http + containerPort: 4180 + protocol: TCP + resources: + requests: + cpu: 25m + memory: 64Mi + limits: + memory: 128Mi + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /ping + port: http + initialDelaySeconds: 5 + periodSeconds: 15 + readinessProbe: + httpGet: + path: /ping + port: http + initialDelaySeconds: 5 + periodSeconds: 15 volumes: - name: config persistentVolumeClaim: @@ -104,6 +168,9 @@ spec: - name: port-8080 port: 8080 targetPort: 8080 + - name: authproxy + port: 4180 + targetPort: 4180 --- # CiliumNetworkPolicy reciprocal ingress allow (R-NET-02): qbittorrent-allow-radarr-8080 apiVersion: cilium.io/v2 @@ -166,7 +233,7 @@ spec: - ingress toPorts: - ports: - - port: '8080' + - port: '4180' protocol: TCP --- # CiliumNetworkPolicy world egress allow (egress class governs the path; R-NET-05): qbittorrent-allow-internet-egress @@ -214,7 +281,7 @@ spec: value: / backendRefs: - name: qbittorrent - port: 8080 + port: 4180 --- # ServiceMonitor (R-OBS-01) apiVersion: monitoring.coreos.com/v1 diff --git a/apps/wave-1/kustomization.yaml b/apps/wave-1/kustomization.yaml index a0c54d7..2e207b8 100644 --- a/apps/wave-1/kustomization.yaml +++ b/apps/wave-1/kustomization.yaml @@ -10,7 +10,9 @@ resources: - gitea-actions.yaml - nextcloud-bootstrap-secret.sops.yaml - nextcloud.yaml +- radarr-oauth2-proxy-credentials.sops.yaml - radarr.yaml - semaphore-bootstrap-secret.sops.yaml - semaphore.yaml +- sonarr-oauth2-proxy-credentials.sops.yaml - sonarr.yaml diff --git a/apps/wave-1/radarr-oauth2-proxy-credentials.sops.yaml b/apps/wave-1/radarr-oauth2-proxy-credentials.sops.yaml new file mode 100644 index 0000000..a1a7831 --- /dev/null +++ b/apps/wave-1/radarr-oauth2-proxy-credentials.sops.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Secret +metadata: + name: radarr-oauth2-proxy + namespace: radarr +type: Opaque +stringData: + client-id: ENC[AES256_GCM,data:vFLxqzxt,iv:6k0ZjKP8rvg56ZFPWEGdyj+FLgK+LBOE0pLBWfC0bjA=,tag:LiZb5BasJAEWM1c3I8vFTA==,type:str] + client-secret: ENC[AES256_GCM,data:JzfHF8JF4YUxBxiC738N4UsMlcCOvxn4/AE2B4meZNGAZb2uhG4n51uEY8OQsbsM0Ni4qM2Wi17KuLDwAicfUA==,iv:A3AnUhYxw/IWfvfcZf6MgC5u37dMoNToIWxRG9AxEJQ=,tag:1OY2pWGTbCxPYYdaiBa/oA==,type:str] + cookie-secret: ENC[AES256_GCM,data:LPt5nghLfoNvr7yBes8rLeEK3M6UVIO1h9POHMOAj8iZX3l2RJJa4w8GcSg=,iv:Q5+7zH8Tb1SWe92B0pxekfMgOXlBG8nixWn/ho4oJw4=,tag:D+41dBFPVTGj86xv5/GEng==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age175l8mdpdfu7hq0aml3exs3gykarwjmuvr23sq56s3wjejnyk44wsp4mfzg + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHVG5MWVRyOVl1RWFpOExC + SjZKcEd1ZlFnZVZ5NmtyUlQzRGI1b1o3dG04CkwrTUNjd3crM1VzaUdPczliSDY1 + a3dONnBFWk8yOW5kL2xES1RZbUN6L3cKLS0tIG9hWG9Cbm01RlMrcC9zMWkxeFgr + ZnRGRUVTTS81SFRyenNsZUZEcG1TUm8K5XUGP/1NVsoaFcBZUER3/zMPTtCuVisT + LrbSmTkIyVfzjN/vRpRH92b1VzENJvhGhRghhQaKY0rbG34i6yh3Vw== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-08-31T13:42:25Z" + mac: ENC[AES256_GCM,data:TxdPz2zE6+ypgQG2b0Rp3VFiPL7LLls4NNkC7YQ/bVCLvyvaXdGsEWkiHJE+Po7hcESmZRt/NMS25mEXPMfvlSCltwFUTcrxoZDS0NI27kIzbtceTbxmznt/dMS/iZm21lCgA6Aag9ByLjWusPLkRKWhep+G+0G54qtfQzv3s80=,iv:44f0KikrpKpnSaLLyzTvnCfrwEXylRgVIlKMtSpMyNc=,tag:jDx43n0usDtbwzo8KzDBqw==,type:str] + pgp: [] + encrypted_regex: ^(data|stringData)$ + version: 3.9.4 diff --git a/apps/wave-1/radarr.yaml b/apps/wave-1/radarr.yaml index d8aa2b0..38a6186 100644 --- a/apps/wave-1/radarr.yaml +++ b/apps/wave-1/radarr.yaml @@ -84,6 +84,70 @@ spec: volumeMounts: - name: config mountPath: /data/config + - name: oauth2-proxy + image: quay.io/oauth2-proxy/oauth2-proxy:v7.8.1 + imagePullPolicy: IfNotPresent + args: + - --http-address=0.0.0.0:4180 + - --reverse-proxy=true + - --provider=oidc + - --oidc-issuer-url=https://keycloak.home.keithjackson.dev/realms/homelab + - --upstream=http://127.0.0.1:7878 + - --email-domain=* + - --scope=openid email profile + - --oidc-groups-claim=groups + - --allowed-group=media-users + - --skip-provider-button=true + - --redirect-url=https://radarr.home.keithjackson.dev/oauth2/callback + - --cookie-domain=.home.keithjackson.dev + - --whitelist-domain=.home.keithjackson.dev + - --cookie-secure=true + - --pass-access-token=true + - --set-xauthrequest=true + env: + - name: OAUTH2_PROXY_CLIENT_ID + valueFrom: + secretKeyRef: + name: radarr-oauth2-proxy + key: client-id + - name: OAUTH2_PROXY_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: radarr-oauth2-proxy + key: client-secret + - name: OAUTH2_PROXY_COOKIE_SECRET + valueFrom: + secretKeyRef: + name: radarr-oauth2-proxy + key: cookie-secret + ports: + - name: http + containerPort: 4180 + protocol: TCP + resources: + requests: + cpu: 25m + memory: 64Mi + limits: + memory: 128Mi + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /ping + port: http + initialDelaySeconds: 5 + periodSeconds: 15 + readinessProbe: + httpGet: + path: /ping + port: http + initialDelaySeconds: 5 + periodSeconds: 15 volumes: - name: config persistentVolumeClaim: @@ -104,6 +168,9 @@ spec: - name: port-7878 port: 7878 targetPort: 7878 + - name: authproxy + port: 4180 + targetPort: 4180 --- # CiliumNetworkPolicy egress allow (R-NET-02): radarr-allow-qbittorrent-8080 apiVersion: cilium.io/v2 @@ -188,7 +255,7 @@ spec: - ingress toPorts: - ports: - - port: '7878' + - port: '4180' protocol: TCP --- # CiliumNetworkPolicy world egress allow (egress class governs the path; R-NET-05): radarr-allow-internet-egress @@ -236,7 +303,7 @@ spec: value: / backendRefs: - name: radarr - port: 7878 + port: 4180 --- # ServiceMonitor (R-OBS-01) apiVersion: monitoring.coreos.com/v1 diff --git a/apps/wave-1/sonarr-oauth2-proxy-credentials.sops.yaml b/apps/wave-1/sonarr-oauth2-proxy-credentials.sops.yaml new file mode 100644 index 0000000..683e4ca --- /dev/null +++ b/apps/wave-1/sonarr-oauth2-proxy-credentials.sops.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Secret +metadata: + name: sonarr-oauth2-proxy + namespace: sonarr +type: Opaque +stringData: + client-id: ENC[AES256_GCM,data:JMYoJymH,iv:CM9HF71QZCeETzrOtkeTUs+BLTp/gGSExulXUP8yS/M=,tag:l73RaFQolO7+wZBo8nYCvw==,type:str] + client-secret: ENC[AES256_GCM,data:Fq271KdbtKWKWOe1Sun6+SLv/kSBsvAeFUhZIq6J2DNPGM4E8dgc2YANITcpezcJR07PAbSlUcS0L/qF6K+YhQ==,iv:/mVD1uOkweyO26RMV3qRQmLdyxXJR4I7p+OjYiiuSMU=,tag:9ZOKye5Sp2NHU2RXH1Y+wQ==,type:str] + cookie-secret: ENC[AES256_GCM,data:jMZZEi8fdh20BbPzCoVfwdS1g0/ma5n/QISU5A/gQCu/2Rkylt7mp798CUg=,iv:6IPqDNQyJHze2DUOQ93IwruWSge5nXjkVcQIOMfJSwY=,tag:cOKRcDKywV3PBOBrhoddRQ==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age175l8mdpdfu7hq0aml3exs3gykarwjmuvr23sq56s3wjejnyk44wsp4mfzg + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoNU5NbnJnV0dKRnpIN2dw + QmhBRWI4aEpWajRHVkxZcGxCRk1hMDBjSlRVCm91b0U2N08vNi8rSEFaVlRJazFL + bllPcm9EMUgrQ3ZlUDlrdEZpZE8zNmMKLS0tIDU0ajdycXZrazlQb3RVRGQyY25h + R3RDajltcmFvQWdLYWQ2Mjh0VkplaWsKJprnLNJYzMd0yHbxjsD8P66Wu+sefmaV + nduEgVQY0t4EsYN6iKVDLMUAp+o3gXU8QuMuBnFZznrmBghfG+ZruQ== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-08-31T13:42:49Z" + mac: ENC[AES256_GCM,data:ckVR9OyFvle0fLhBjHtJ6hH2St2CRJeu6abTCnqtxAfMDVXdbIkgHEesmN2kAgA7s3LkKJC2m7EFIpt3pO94VQNnZvC1KKD7qUSIfJN1DLVv78m8JbEQfVTJk/WgWy1ObxLriw5hDuMiEuOvKND+s6eqS5j6er4Q72lBsBpoEeY=,iv:uguThOQbR9Ks1qs1U0HO7EhUj1BT60fvM+3xVEC9hUU=,tag:m4P5nhlspCZyJRuOqffMog==,type:str] + pgp: [] + encrypted_regex: ^(data|stringData)$ + version: 3.9.4 diff --git a/apps/wave-1/sonarr.yaml b/apps/wave-1/sonarr.yaml index 6518a9a..39d8dd7 100644 --- a/apps/wave-1/sonarr.yaml +++ b/apps/wave-1/sonarr.yaml @@ -84,6 +84,70 @@ spec: volumeMounts: - name: config mountPath: /data/config + - name: oauth2-proxy + image: quay.io/oauth2-proxy/oauth2-proxy:v7.8.1 + imagePullPolicy: IfNotPresent + args: + - --http-address=0.0.0.0:4180 + - --reverse-proxy=true + - --provider=oidc + - --oidc-issuer-url=https://keycloak.home.keithjackson.dev/realms/homelab + - --upstream=http://127.0.0.1:8989 + - --email-domain=* + - --scope=openid email profile + - --oidc-groups-claim=groups + - --allowed-group=media-users + - --skip-provider-button=true + - --redirect-url=https://sonarr.home.keithjackson.dev/oauth2/callback + - --cookie-domain=.home.keithjackson.dev + - --whitelist-domain=.home.keithjackson.dev + - --cookie-secure=true + - --pass-access-token=true + - --set-xauthrequest=true + env: + - name: OAUTH2_PROXY_CLIENT_ID + valueFrom: + secretKeyRef: + name: sonarr-oauth2-proxy + key: client-id + - name: OAUTH2_PROXY_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: sonarr-oauth2-proxy + key: client-secret + - name: OAUTH2_PROXY_COOKIE_SECRET + valueFrom: + secretKeyRef: + name: sonarr-oauth2-proxy + key: cookie-secret + ports: + - name: http + containerPort: 4180 + protocol: TCP + resources: + requests: + cpu: 25m + memory: 64Mi + limits: + memory: 128Mi + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /ping + port: http + initialDelaySeconds: 5 + periodSeconds: 15 + readinessProbe: + httpGet: + path: /ping + port: http + initialDelaySeconds: 5 + periodSeconds: 15 volumes: - name: config persistentVolumeClaim: @@ -104,6 +168,9 @@ spec: - name: port-8989 port: 8989 targetPort: 8989 + - name: authproxy + port: 4180 + targetPort: 4180 --- # CiliumNetworkPolicy egress allow (R-NET-02): sonarr-allow-qbittorrent-8080 apiVersion: cilium.io/v2 @@ -188,7 +255,7 @@ spec: - ingress toPorts: - ports: - - port: '8989' + - port: '4180' protocol: TCP --- # CiliumNetworkPolicy world egress allow (egress class governs the path; R-NET-05): sonarr-allow-internet-egress @@ -236,7 +303,7 @@ spec: value: / backendRefs: - name: sonarr - port: 8989 + port: 4180 --- # ServiceMonitor (R-OBS-01) apiVersion: monitoring.coreos.com/v1 diff --git a/ci/render-golden.sh b/ci/render-golden.sh index 5996b92..4031f45 100755 --- a/ci/render-golden.sh +++ b/ci/render-golden.sh @@ -40,14 +40,7 @@ PYTHON="${PYTHON:-python3}" # by-product of `--all catalog` but must NOT have a golden; the gate ignores # their rendered output. Onboarding one = add its golden and drop it here. # -# CANARY DEFERRAL (forward-auth oauth2-proxy sidecar rollout, 2026-08-31): the renderer now -# emits a per-app oauth2-proxy sidecar for every forward-auth app (pihole/jackett/qbittorrent/ -# radarr/sonarr). pihole is the CANARY — its golden carries the sidecar and is verified fail-closed -# in-cluster FIRST. The other four are deferred to the ROLLOUT commit so partial progress is safe -# (their committed goldens stay sidecar-less and keep serving exactly as today). They are listed -# here TEMPORARILY so the canonical render's fresh (sidecar) output does not flag drift against the -# deferred sidecar-less goldens. The ROLLOUT commit re-renders their goldens and REMOVES them here. -: "${RENDER_GOLDEN_NOT_ONBOARDED:=keycloak vm-demo jackett qbittorrent radarr sonarr}" +: "${RENDER_GOLDEN_NOT_ONBOARDED:=keycloak vm-demo}" log "Stage: render==golden reproducibility (canonical render vs committed apps/)" diff --git a/platform/keycloak/keycloak-realm-clients.sops.yaml b/platform/keycloak/keycloak-realm-clients.sops.yaml index 6e4ae27..3e81068 100644 --- a/platform/keycloak/keycloak-realm-clients.sops.yaml +++ b/platform/keycloak/keycloak-realm-clients.sops.yaml @@ -13,6 +13,10 @@ stringData: headlamp-client-secret: ENC[AES256_GCM,data:hlQ5m6FeqwOqX9HTT8jC6NZmhQH0kMgUveMZ2Cfen1zLLxWfYXXC1QMBWSqrchY2EpyIr7vNPbFjbqffwiSDHQ==,iv:WS1F3gkWn18STl4c38AlCe7TU6jvZpl/eULf2TnjIZ4=,tag:n2BnQYrm4gZbLeemDBykjA==,type:str] crafty-client-secret: ENC[AES256_GCM,data:j9HAqIVlr+y8xZ6RvOvtQrVBv/mTLK89KYu1oQMkeSMsjyZdwZlLwW+Q7oiO1hqpyEG7ht5z3x6dJegN4LaxQQ==,iv:mnG5boz0M2A6hDdLKBaneUPH75Ih1RhM2R2d77VG7Vs=,tag:TGQKb7+4lnmhS3DZm6Wz1A==,type:str] pihole-client-secret: ENC[AES256_GCM,data:lxheNbIev1Y6O0b3LJWvyROizOU2ts3WwZTa32x+efJwEek3dTBBc/3Nn0m5GKVNXv9lqkio9+qPTlZTmKZbLw==,iv:mfoIGDpGxc+f445Ev8dJY1YMe6WmOurMCDwdz80Cjbk=,tag:FPA2Ln97wG89NnLWdvnP+Q==,type:str] + jackett-client-secret: ENC[AES256_GCM,data:YIXxL5x0VstT4iTW2G/rjdF9jh14log6GPojbs1RyZYnPBs5zDgwU9kCHD8EUGtcbXbz4hZall/imt9zU080JA==,iv:Y3DwFxrWv8gTW3kK9Gq6Un+WbG2VJs28yr6/hUIyenc=,tag:D7Q2wKkx8rTmpGbvKEoM0g==,type:str] + qbittorrent-client-secret: ENC[AES256_GCM,data:jXNg4mF9Li0ilsV53iYeVJCGavVGFjvLk9jKBrRgcH6id1QCzsjs0bbMqJO5nALWfKhD8+CEmYJRrfABYTFKZA==,iv:XbLkX7/tfB/DLeMriHA3Oo2M6hgF1Z6RbHBXmdwUJoA=,tag:gikgdU0ajNKZxxYEUGc1aw==,type:str] + radarr-client-secret: ENC[AES256_GCM,data:acsBXdewKB/gJNyoo1CsPLCvZe6fuJWbrKLim1/57OPnDHNoA+OvkQqt12Y4VMAXjzl7BWD2afFTIkIcaNGgaQ==,iv:VSvMaDZeuFyIAWpUoa++khvx07hY6g7X8PxORtjFTVI=,tag:jY5QSw6cMvUhCfX32RNgXA==,type:str] + sonarr-client-secret: ENC[AES256_GCM,data:6cCVadKAKh5YbZIPocXG8pNY/AZbo3zCUP/19a0aHK0fIx6QzvBBl7v2vTsY/tq5tiVx8Uzr6oQNo/xt6pIgIA==,iv:EBXBZgpgdt7GiIS0w/Uj/vNE9EBgSgrbaMOKP346GKA=,tag:I2skAB+fdfQTsMgcywOrAg==,type:str] sops: kms: [] gcp_kms: [] @@ -28,8 +32,8 @@ sops: YXJGUUJiTnFndFhDZDU1bmxXTENCbmsKuhb3RQ3LZmuTPAXEck8b6iuZ5vxLb9fV +fQELmU4kOTbZrPSxy+kpttLG5Cy5d6X2ieZXBf6opzpLTUhlBOkHA== -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-08-31T13:33:15Z" - mac: ENC[AES256_GCM,data:5wUiBt5+PxzAdGaKrf6wwx0TBjOC2doukzJSleRpYtN/Tf+ubMHNDa6C1sH/RFd83haPrws1ZoAedmyiBSwqwLhmslwil5ZHzbyaBs8fVrp/EcKgNmaUKfjv8AOj4q5MFWIb8eeKGCpt1GVGHlwg4ewfwRn+2mgPev1LKsNsaYc=,iv:X2APh51JZ16O17bz+cNLgXGdORaeYsdXvznbnVt8mwU=,tag:99ekaLyqhL4YPT34uo9xCA==,type:str] + lastmodified: "2026-08-31T13:42:49Z" + mac: ENC[AES256_GCM,data:/sOLb/XYQ2WB+mPgnTfYXD3y7xwDi926v0e2upBqgcCugtfKHg+Q1C9+XUiG2qE/iIBJyYVtOu9pmiMMNyT9hJ9vm7WFOC6gqukFMo42PjAI4I4PExfjVMKt76kKJ4OZZrKF2IKLjccZFuKDQhfADIR3zl6UNf/x5wz7XpXPEvI=,iv:iV4eOcRxsgTm1uNVMkMwsspj9glKCSZQlBciRY+MDJY=,tag:c72Pks1VJH+gmfP5FOQPSw==,type:str] pgp: [] encrypted_regex: ^(data|stringData)$ version: 3.9.4 diff --git a/platform/keycloak/keycloak-realm-import.yaml b/platform/keycloak/keycloak-realm-import.yaml index 1d1f1d9..b637814 100644 --- a/platform/keycloak/keycloak-realm-import.yaml +++ b/platform/keycloak/keycloak-realm-import.yaml @@ -47,6 +47,22 @@ spec: secret: name: keycloak-realm-clients key: pihole-client-secret + JACKETT_CLIENT_SECRET: + secret: + name: keycloak-realm-clients + key: jackett-client-secret + QBITTORRENT_CLIENT_SECRET: + secret: + name: keycloak-realm-clients + key: qbittorrent-client-secret + RADARR_CLIENT_SECRET: + secret: + name: keycloak-realm-clients + key: radarr-client-secret + SONARR_CLIENT_SECRET: + secret: + name: keycloak-realm-clients + key: sonarr-client-secret realm: realm: homelab enabled: true @@ -399,6 +415,119 @@ spec: "access.token.claim": "true" "userinfo.token.claim": "true" "claim.name": groups + # jackett/qbittorrent/radarr/sonarr — media stack UIs (catalog/*.yaml auth.mode forward-auth, + # auth.groups media-users). Each has a bare UI with no usable native OIDC, gated by a per-app + # oauth2-proxy REVERSE-PROXY sidecar (Option B, rendered by catalog/render.py) — these are those + # sidecars' confidential auth-code clients, modeled on the crafty/pihole clients. Redirect URI is + # the oauth2-proxy callback on each app's host; the `groups` mapper (bare names) lets the sidecar + # enforce --allowed-group=media-users (R-IDP-06). Secrets injected from the SOPS keycloak-realm- + # clients Secret via the *_CLIENT_SECRET placeholders (R-IDP-04); the SAME values are mirrored in + # apps/wave-*/-oauth2-proxy-credentials.sops.yaml. Provisioned live via kcadm 2026-08-31 and + # captured here as the durable GitOps source of truth (operator imports once; kcadm authoritative). + - clientId: jackett + name: Jackett (oauth2-proxy) + protocol: openid-connect + enabled: true + publicClient: false + standardFlowEnabled: true + directAccessGrantsEnabled: false + secret: "$(env:JACKETT_CLIENT_SECRET)" + redirectUris: + - https://jackett.home.keithjackson.dev/oauth2/callback + webOrigins: + - https://jackett.home.keithjackson.dev + defaultClientScopes: + - email + - profile + - roles + protocolMappers: + - name: groups + protocol: openid-connect + protocolMapper: oidc-group-membership-mapper + config: + "full.path": "false" + "id.token.claim": "true" + "access.token.claim": "true" + "userinfo.token.claim": "true" + "claim.name": groups + - clientId: qbittorrent + name: qBittorrent (oauth2-proxy) + protocol: openid-connect + enabled: true + publicClient: false + standardFlowEnabled: true + directAccessGrantsEnabled: false + secret: "$(env:QBITTORRENT_CLIENT_SECRET)" + redirectUris: + - https://qbittorrent.home.keithjackson.dev/oauth2/callback + webOrigins: + - https://qbittorrent.home.keithjackson.dev + defaultClientScopes: + - email + - profile + - roles + protocolMappers: + - name: groups + protocol: openid-connect + protocolMapper: oidc-group-membership-mapper + config: + "full.path": "false" + "id.token.claim": "true" + "access.token.claim": "true" + "userinfo.token.claim": "true" + "claim.name": groups + - clientId: radarr + name: Radarr (oauth2-proxy) + protocol: openid-connect + enabled: true + publicClient: false + standardFlowEnabled: true + directAccessGrantsEnabled: false + secret: "$(env:RADARR_CLIENT_SECRET)" + redirectUris: + - https://radarr.home.keithjackson.dev/oauth2/callback + webOrigins: + - https://radarr.home.keithjackson.dev + defaultClientScopes: + - email + - profile + - roles + protocolMappers: + - name: groups + protocol: openid-connect + protocolMapper: oidc-group-membership-mapper + config: + "full.path": "false" + "id.token.claim": "true" + "access.token.claim": "true" + "userinfo.token.claim": "true" + "claim.name": groups + - clientId: sonarr + name: Sonarr (oauth2-proxy) + protocol: openid-connect + enabled: true + publicClient: false + standardFlowEnabled: true + directAccessGrantsEnabled: false + secret: "$(env:SONARR_CLIENT_SECRET)" + redirectUris: + - https://sonarr.home.keithjackson.dev/oauth2/callback + webOrigins: + - https://sonarr.home.keithjackson.dev + defaultClientScopes: + - email + - profile + - roles + protocolMappers: + - name: groups + protocol: openid-connect + protocolMapper: oidc-group-membership-mapper + config: + "full.path": "false" + "id.token.claim": "true" + "access.token.claim": "true" + "userinfo.token.claim": "true" + "claim.name": groups # --- MFA for platform-admins (R-IDP-04): a browser flow whose OTP step is CONDITIONAL on the # platform-admin role. Non-admins log in without OTP; platform-admins must enroll/present TOTP. # Set as the realm browserFlow below. VERIFY at bring-up (see runbook): a malformed flow fails