From 18034b7cd90cdd8f8839ea0bc574dc70b9eb0605 Mon Sep 17 00:00:00 2001 From: Erki Aas Date: Tue, 15 Sep 2026 22:23:56 +0300 Subject: [PATCH] Rotate gitea INTERNAL_TOKEN and JWT_SECRET into separate claims Post-breach (CVE-2026-59774) rotation. All three security secrets shared one generated value via the gitea-random SecretClaim, and the file-read exploit leaked it. Split INTERNAL_TOKEN and JWT_SECRET into their own claims (gitea-internal-token, gitea-oauth2-jwt) with fresh distinct values and repoint the StatefulSet at them. INTERNAL_TOKEN is the secret the attacker actually used against the still-internet-reachable /api/internal, so this closes reuse of any saved copy. Rotating JWT_SECRET invalidates gitea-issued OAuth2 and LFS tokens, so the OAuth2 integrations (Woodpecker, tea, credential helpers) need one re-login. SECRET_KEY is deliberately left on gitea-random: rotating it would break the 2 push mirrors (encrypted creds) and must be done in a window that re-enters those credentials. Co-Authored-By: Claude Opus 4.8 --- gitea/application.yaml | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/gitea/application.yaml b/gitea/application.yaml index 2729a3e..cbb47cb 100644 --- a/gitea/application.yaml +++ b/gitea/application.yaml @@ -16,6 +16,15 @@ spec: # Gitea DOES NOT go through Traefik. It has its own IP because ssh :22 would conflict with kube worker ssh. On its own IP, at the moment it doesn't flirt with Traefik — also has its own certificate. --- +# After the 2026-09 breach (CVE-2026-59774) all three secrets below leaked +# because they shared one generated value. INTERNAL_TOKEN and JWT_SECRET were +# moved to their own claims (gitea-internal-token, gitea-oauth2-jwt) and rotated; +# the StatefulSet now reads those two from the new claims, so the copies here are +# unused. SECRET_KEY is intentionally still sourced from this claim and NOT +# rotated: a new value would make data-at-rest (push-mirror creds, 2FA) +# undecryptable and break the 2 push mirrors, whose credentials must be re-entered +# by hand. Do not shrink this mapping: the operator would regenerate the plaintext +# and change SECRET_KEY. Rotate SECRET_KEY in a maintenance window instead. apiVersion: codemowers.cloud/v1beta1 kind: SecretClaim metadata: @@ -31,6 +40,26 @@ spec: value: "%(plaintext)s" --- apiVersion: codemowers.cloud/v1beta1 +kind: SecretClaim +metadata: + name: gitea-internal-token +spec: + size: 32 + mapping: + - key: GITEA__SECURITY__INTERNAL_TOKEN + value: "%(plaintext)s" +--- +apiVersion: codemowers.cloud/v1beta1 +kind: SecretClaim +metadata: + name: gitea-oauth2-jwt +spec: + size: 32 + mapping: + - key: GITEA__OAUTH2__JWT_SECRET + value: "%(plaintext)s" +--- +apiVersion: codemowers.cloud/v1beta1 kind: OIDCClient metadata: name: gitea @@ -220,12 +249,12 @@ spec: - name: GITEA__OAUTH2__JWT_SECRET valueFrom: secretKeyRef: - name: gitea-random + name: gitea-oauth2-jwt key: GITEA__OAUTH2__JWT_SECRET - name: GITEA__SECURITY__INTERNAL_TOKEN valueFrom: secretKeyRef: - name: gitea-random + name: gitea-internal-token key: GITEA__SECURITY__INTERNAL_TOKEN - name: GITEA__SECURITY__SECRET_KEY valueFrom: