fix(ci): remove kubernetes-deploy workflow that leaked secrets to logs #45

Merged
kacerr merged 1 commits from fix/remove-leaky-k8s-deploy-workflow into main 2026-10-08 11:49:02 +02:00
5 changed files with 25 additions and 131 deletions
Showing only changes of commit 5b162d0030 - Show all commits
-111
View File
@@ -1,111 +0,0 @@
name: Deploy to K8s
on:
workflow_dispatch:
push:
branches:
- '**'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Debug - print Gitea Actions environment
run: |
echo "=== All environment variables ==="
env | sort
echo ""
echo "=== GITHUB_* / GITEA_* / ACTIONS_* vars ==="
env | grep -E '^(GITHUB|GITEA|ACTIONS)_' | sort
- name: Install kubectl
run: |
curl -sfLO "https://dl.k8s.io/release/$(curl -sfL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
install kubectl /usr/local/bin/
- name: Get Kanidm token from Vault
id: vault
run: |
set -euxo pipefail
VAULT_AUTH_RESPONSE=$(curl -f --request POST \
--data '{"role_id":"${{ secrets.VAULT_ROLE_ID }}","secret_id":"${{ secrets.VAULT_SECRET_ID }}"}' \
https://vault.hrajfrisbee.cz/v1/auth/approle/login)
echo "Vault auth response: $VAULT_AUTH_RESPONSE" >&2
VAULT_TOKEN=$(echo "$VAULT_AUTH_RESPONSE" | jq -r '.auth.client_token')
# Read the kanidm API token
SECRET_RESPONSE=$(curl -f \
-H "X-Vault-Token: ${VAULT_TOKEN}" \
https://vault.hrajfrisbee.cz/v1/secret/data/gitea/gitea-ci)
echo "Secret response: $SECRET_RESPONSE" >&2
API_TOKEN=$(echo "$SECRET_RESPONSE" | jq -r '.data.data.token')
echo "::add-mask::${API_TOKEN}"
echo "api_token=${API_TOKEN}" >> "$GITHUB_OUTPUT"
- name: Exchange for K8s OIDC token via Kanidm
id: k8s
run: |
API_TOKEN="${{ steps.vault.outputs.api_token }}"
echo "api_token length: ${#API_TOKEN}" >&2
echo "api_token prefix (first 8 chars): ${API_TOKEN:0:8}..." >&2
HTTP_BODY=$(mktemp)
HTTP_STATUS=$(curl -sS -X POST "https://idm.home.hrajfrisbee.cz/oauth2/token" \
-d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "client_id=k8s" \
-d "subject_token=${API_TOKEN}" \
-d "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
-d "audience=k8s" \
-d "scope=openid groups" \
-o "$HTTP_BODY" -w "%{http_code}")
echo "HTTP status: $HTTP_STATUS" >&2
echo "Response body:" >&2
cat "$HTTP_BODY" >&2
RESPONSE=$(cat "$HTTP_BODY")
ID_TOKEN=$(echo "$RESPONSE" | jq -r '.id_token // empty')
if [ -z "$ID_TOKEN" ]; then
echo "::error::Kanidm token exchange failed (HTTP $HTTP_STATUS)"
exit 1
fi
echo "::add-mask::${ID_TOKEN}"
echo "id_token=${ID_TOKEN}" >> "$GITHUB_OUTPUT"
# Sanity check
# Warning - this is the part that is failing
# echo "$ID_TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq '{sub, groups, exp}'
- name: Debug - print environment before kubectl
run: env | sort
- name: Configure kubectl & deploy
run: |
echo "${{ secrets.K8S_CA_CERT }}" > /tmp/ca.crt
kubectl config set-cluster mycluster \
--server=https://192.168.0.31:6443 \
--insecure-skip-tls-verify=true
# --certificate-authority=/tmp/ca.crt \
kubectl config set-credentials gitea-ci \
--token="${{ steps.k8s.outputs.id_token }}"
kubectl config set-context gitea-ci \
--cluster=mycluster --user=gitea-ci
kubectl config use-context gitea-ci
kubectl auth whoami
kubectl get ns
# your deploy here
# kubectl apply -f k8s/
+6
View File
@@ -1,5 +1,11 @@
# Changelog # Changelog
## 2026-10-08 11:48 CEST — fix(ci): remove kubernetes-deploy workflow that leaked secrets to logs
- Deleted `.gitea/workflows/kubernetes-deploy.yaml`. It ran on every push to every branch, deployed nothing, and echoed the Vault login response, the Kanidm API token response and the OIDC token response to the job log.
- Deployment is handled by `gitops-update.yaml`. Repo secrets `VAULT_ROLE_ID`, `VAULT_SECRET_ID` and `K8S_CA_CERT` are now unused.
- Follow-up outside the repo: rotate the Vault AppRole secret and the Kanidm API token, and clear old job logs.
## 2026-10-08 11:34 CEST — fix(python): return months_to_show from /api/adults and /api/juniors ## 2026-10-08 11:34 CEST — fix(python): return months_to_show from /api/adults and /api/juniors
- Go already returned `months_to_show` in the JSON API; Python only passed it to its HTML templates. Backported the key so the API contract matches and `make parity` reports 3/3 routes. - Go already returned `months_to_show` in the JSON API; Python only passed it to its HTML templates. Backported the key so the API contract matches and `make parity` reports 3/3 routes.
+1 -1
View File
@@ -182,7 +182,7 @@ fuj-management/
│ │
├── .gitea/workflows/ ├── .gitea/workflows/
│ ├── build.yaml # CI: build + push Docker image │ ├── build.yaml # CI: build + push Docker image
│ └── kubernetes-deploy.yaml # CD: deploy to K8s cluster │ └── gitops-update.yaml # CD: open image-update PR in home-kubernetes
│ │
├── .secret/ # (gitignored) API credentials ├── .secret/ # (gitignored) API credentials
├── docs/ # Project documentation ├── docs/ # Project documentation
+12 -18
View File
@@ -129,32 +129,27 @@ The project uses two Gitea Actions workflows:
**Tag resolution**: Uses the git tag name. For manual dispatch, uses the provided input. **Tag resolution**: Uses the git tag name. For manual dispatch, uses the provided input.
#### 2. Deploy to Kubernetes (`kubernetes-deploy.yaml`) #### 2. GitOps image update (`gitops-update.yaml`)
**Triggers**: **Triggers**:
- Push to any branch - Completion of a successful "Build and Push" run
- Manual dispatch - Manual dispatch (with `tag`, `dry_run` and `uh_cli_version` inputs)
**Steps**: **Steps**:
1. Checkout code 1. Install `tea` and `uh-cli`
2. Install kubectl 2. Resolve the image tag of the Go image that was just built
3. Retrieve Kanidm token from HashiCorp Vault: 3. Open a PR in `kacerr/home-kubernetes` bumping the `fuj-management` Deployment (namespace `fuj`) to that image
- Authenticate to Vault via AppRole (`VAULT_ROLE_ID` / `VAULT_SECRET_ID`)
- Fetch API token from `secret/data/gitea/gitea-ci` The cluster picks the change up once that PR is merged. CI does not talk to the cluster directly.
4. Exchange API token for K8s OIDC token via Kanidm:
- POST to `https://idm.home.hrajfrisbee.cz/oauth2/token` > The earlier `kubernetes-deploy.yaml` workflow (Vault → Kanidm → `kubectl`) was removed on 2026-10-08. It never deployed anything and printed credentials to the job log.
- Token exchange using `urn:ietf:params:oauth:grant-type:token-exchange`
5. Configure kubectl with the OIDC token
6. Run `kubectl auth whoami` and `kubectl get ns` (deploy commands are commented out — WIP)
**Required secrets**: **Required secrets**:
| Secret | Purpose | | Secret | Purpose |
|--------|---------| |--------|---------|
| `REGISTRY_TOKEN` | Docker registry authentication | | `REGISTRY_TOKEN` | Docker registry authentication |
| `VAULT_ROLE_ID` | HashiCorp Vault AppRole role ID | | `GITOPS_TOKEN` | Gitea token with write and PR access to `home-kubernetes` |
| `VAULT_SECRET_ID` | HashiCorp Vault AppRole secret ID |
| `K8S_CA_CERT` | Kubernetes cluster CA certificate |
### Infrastructure Topology ### Infrastructure Topology
@@ -164,8 +159,7 @@ Gitea (git push / tag)
├── build.yaml → Docker Build → Gitea Container Registry ├── build.yaml → Docker Build → Gitea Container Registry
│ (gitea.home.hrajfrisbee.cz) │ (gitea.home.hrajfrisbee.cz)
│ │
└── kubernetes-deploy.yaml → Vault → Kanidm → K8s Cluster └── gitops-update.yaml → PR in home-kubernetes → K8s Cluster
(192.168.0.31:6443)
``` ```
This is a self-hosted infrastructure stack: This is a self-hosted infrastructure stack:
@@ -30,13 +30,18 @@ before new feature work starts. Companion to the Go rewrite tracker,
## Fix first ## Fix first
- [ ] **T1** Remove the secret leak in `.gitea/workflows/kubernetes-deploy.yaml`. - [x] **T1** Remove the secret leak in `.gitea/workflows/kubernetes-deploy.yaml`.
Workflow deleted on 2026-10-08. Still open for the owner: rotate the
credentials and remove the now-unused repo secrets (see below).
- It runs on every push to every branch and deploys nothing. - It runs on every push to every branch and deploys nothing.
- It echoes the raw Vault login response, the Kanidm token response and the - It echoes the raw Vault login response, the Kanidm token response and the
OIDC token response body to the log, and dumps the environment twice. OIDC token response body to the log, and dumps the environment twice.
- It uses `--insecure-skip-tls-verify=true`. - It uses `--insecure-skip-tls-verify=true`.
- Delete or disable the workflow. Rotate the Vault AppRole secret and the - Delete or disable the workflow. Rotate the Vault AppRole secret and the
Kanidm API token if anyone else can read the CI logs. Kanidm API token if anyone else can read the CI logs.
- After deletion the repo secrets `VAULT_ROLE_ID`, `VAULT_SECRET_ID` and
`K8S_CA_CERT` are unused and can be removed. Old job logs still contain
the leaked tokens until they are deleted or expire.
- [ ] **T2** Confirm the fee rates for 2026-09 onward. - [ ] **T2** Confirm the fee rates for 2026-09 onward.
- `ADULT_FEE_MONTHLY_RATE` ends at 2026-08 in `scripts/attendance.py` and - `ADULT_FEE_MONTHLY_RATE` ends at 2026-08 in `scripts/attendance.py` and
`go/internal/domain/fees/fees.go`. `go/internal/domain/fees/fees.go`.