Merge pull request 'fix(ci): remove kubernetes-deploy workflow that leaked secrets to logs' (#45) from fix/remove-leaky-k8s-deploy-workflow into main
Reviewed-on: #45
This commit was merged in pull request #45.
This commit is contained in:
@@ -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/
|
||||
@@ -1,5 +1,11 @@
|
||||
# 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
|
||||
|
||||
- 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.
|
||||
|
||||
@@ -182,7 +182,7 @@ fuj-management/
|
||||
│
|
||||
├── .gitea/workflows/
|
||||
│ ├── 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
|
||||
├── docs/ # Project documentation
|
||||
|
||||
@@ -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.
|
||||
|
||||
#### 2. Deploy to Kubernetes (`kubernetes-deploy.yaml`)
|
||||
#### 2. GitOps image update (`gitops-update.yaml`)
|
||||
|
||||
**Triggers**:
|
||||
- Push to any branch
|
||||
- Manual dispatch
|
||||
- Completion of a successful "Build and Push" run
|
||||
- Manual dispatch (with `tag`, `dry_run` and `uh_cli_version` inputs)
|
||||
|
||||
**Steps**:
|
||||
1. Checkout code
|
||||
2. Install kubectl
|
||||
3. Retrieve Kanidm token from HashiCorp Vault:
|
||||
- Authenticate to Vault via AppRole (`VAULT_ROLE_ID` / `VAULT_SECRET_ID`)
|
||||
- Fetch API token from `secret/data/gitea/gitea-ci`
|
||||
4. Exchange API token for K8s OIDC token via Kanidm:
|
||||
- POST to `https://idm.home.hrajfrisbee.cz/oauth2/token`
|
||||
- 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)
|
||||
1. Install `tea` and `uh-cli`
|
||||
2. Resolve the image tag of the Go image that was just built
|
||||
3. Open a PR in `kacerr/home-kubernetes` bumping the `fuj-management` Deployment (namespace `fuj`) to that image
|
||||
|
||||
The cluster picks the change up once that PR is merged. CI does not talk to the cluster directly.
|
||||
|
||||
> 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.
|
||||
|
||||
**Required secrets**:
|
||||
|
||||
| Secret | Purpose |
|
||||
|--------|---------|
|
||||
| `REGISTRY_TOKEN` | Docker registry authentication |
|
||||
| `VAULT_ROLE_ID` | HashiCorp Vault AppRole role ID |
|
||||
| `VAULT_SECRET_ID` | HashiCorp Vault AppRole secret ID |
|
||||
| `K8S_CA_CERT` | Kubernetes cluster CA certificate |
|
||||
| `GITOPS_TOKEN` | Gitea token with write and PR access to `home-kubernetes` |
|
||||
|
||||
### Infrastructure Topology
|
||||
|
||||
@@ -164,8 +159,7 @@ Gitea (git push / tag)
|
||||
├── build.yaml → Docker Build → Gitea Container Registry
|
||||
│ (gitea.home.hrajfrisbee.cz)
|
||||
│
|
||||
└── kubernetes-deploy.yaml → Vault → Kanidm → K8s Cluster
|
||||
(192.168.0.31:6443)
|
||||
└── gitops-update.yaml → PR in home-kubernetes → K8s Cluster
|
||||
```
|
||||
|
||||
This is a self-hosted infrastructure stack:
|
||||
|
||||
@@ -30,13 +30,18 @@ before new feature work starts. Companion to the Go rewrite tracker,
|
||||
|
||||
## 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 echoes the raw Vault login response, the Kanidm token response and the
|
||||
OIDC token response body to the log, and dumps the environment twice.
|
||||
- It uses `--insecure-skip-tls-verify=true`.
|
||||
- Delete or disable the workflow. Rotate the Vault AppRole secret and the
|
||||
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.
|
||||
- `ADULT_FEE_MONTHLY_RATE` ends at 2026-08 in `scripts/attendance.py` and
|
||||
`go/internal/domain/fees/fees.go`.
|
||||
|
||||
Reference in New Issue
Block a user