Credenciales para GitHub Actions¶
Cómo consigue un workflow de GitHub Actions una credencial de kubelatch sin guardar ningún secreto en el repositorio.
Cómo funciona¶
GitHub Actions puede emitir un id_token OIDC firmado por GitHub, con el repositorio, la rama, el run_id y quien lanzó el job dentro. El workflow se lo pide a GitHub, se lo manda a kubelatch, y kubelatch lo cambia por una credencial de un bot, ya con su kubeconfig. No hay ningún secreto que guardar en GitHub ni que rotar.
sequenceDiagram
participant W as Workflow
participant G as GitHub
participant K as kubelatch
W->>G: pide un id_token (audience = KUBELATCH_URL)
G-->>W: id_token firmado
W->>K: POST /v1/ci/github-actions/token {token}
K->>K: comprueba el token y busca la trust rule
K-->>W: credencial + kubeconfig del bot (una vez)
W->>K: kubectl a través del proxy
Antes de empezar¶
Un administrador tiene que haber preparado esto antes (CI: trust rules):
- Un bot dado de alta, con al menos un permiso concedido en el cluster que necesitas.
- Una trust rule que haga coincidir tu repositorio (por sus ids numéricos, no por nombre) con ese bot, opcionalmente restringida a una rama (
ref) o a unenvironment. - Un
KUBELATCH_URLque el runner alcance por https, con un certificado en el que confíe (un kubelatch en un portátil o detrás de una red privada no sirve).
El workflow¶
-
Crea
.github/workflows/kubelatch.ymlen tu repositorio con este contenido. Es el ejemplo que trae el repositorio de kubelatch enexamples/github-actions/kubelatch.yml, sin los comentarios. Un administrador también puede pasarte el «Fragmento del workflow» de la pantalla «CI», que ya lleva la URL de tu kubelatch.name: kubectl via kubelatch on: push: branches: [main] workflow_dispatch: permissions: id-token: write # required: lets the job request the OIDC token contents: read env: KUBELATCH_URL: https://kubelatch.example.com # exactly KUBELATCH_BASE_URL: it is the token's audience jobs: pods: runs-on: ubuntu-latest # environment: prod # uncomment if the trust rule sets an environment steps: - name: Request the OIDC token for kubelatch run: | ID_TOKEN=$(curl -sSf -G \ -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ --data-urlencode "audience=$KUBELATCH_URL" \ "$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r .value) echo "::add-mask::$ID_TOKEN" echo "ID_TOKEN=$ID_TOKEN" >> "$GITHUB_ENV" - name: Exchange it for a kubelatch credential run: | RESPONSE=$(curl -sS -w '\n%{http_code}' -H 'Content-Type: application/json' \ -d "$(jq -n --arg token "$ID_TOKEN" '{token:$token}')" "$KUBELATCH_URL/v1/ci/github-actions/token") STATUS=$(printf '%s' "$RESPONSE" | tail -n1) BODY=$(printf '%s' "$RESPONSE" | sed '$d') if [ "$STATUS" != "201" ]; then echo "kubelatch answered $STATUS: $(printf '%s' "$BODY" | jq -r '.error // .')" >&2 exit 1 fi printf '%s' "$BODY" | jq -r .token | xargs -I{} echo "::add-mask::{}" (umask 077 && printf '%s' "$BODY" | jq -r .kubeconfig > "$RUNNER_TEMP/kubeconfig") chmod 600 "$RUNNER_TEMP/kubeconfig" echo "KUBECONFIG=$RUNNER_TEMP/kubeconfig" >> "$GITHUB_ENV" echo "credential expires at $(printf '%s' "$BODY" | jq -r .expires_at)" - name: kubectl through kubelatch run: | kubectl auth whoami # bot:<name> with the kubelatch:* groups of its grants kubectl get pods -n default -
Cambia
KUBELATCH_URLpor la URL de tu kubelatch. Tiene que coincidir letra por letra con elKUBELATCH_BASE_URLdel servidor: es la audiencia que kubelatch exige en el token. - Si la trust rule fija un
environment, quita el#de la líneaenvironment:y pon su nombre. Sin él, elid_tokenno lleva el claim que la trust rule espera y el canje responde403. - Ajusta el último paso a lo que necesites hacer en el cluster.
Qué hace cada paso¶
permissions:id-token: writees obligatorio. Sin él, el job no puede pedir elid_token.- Request the OIDC token: pide a GitHub un
id_tokencon la audienciaKUBELATCH_URLy lo enmascara en el log. - Exchange it: lo canjea en
POST /v1/ci/github-actions/token. Un201traecredential,token,kubeconfig,clustersyexpires_at, una sola vez. El paso enmascara el token y guarda el kubeconfig en$RUNNER_TEMP, nunca en~/.kube/configdel runner. - kubectl through kubelatch: a partir de aquí, kubectl usa ese kubeconfig a través del proxy.
Comprueba que funciona¶
Lanza el workflow (workflow_dispatch o un push a la rama configurada) y confirma:
- El job imprime
bot:<nombre-del-bot>enkubectl auth whoami. - La credencial aparece en «Credenciales» (para un administrador) con el nombre
github-actionsy una notaowner/repo@refs/heads/main · run <id> · <actor>. - Las peticiones quedan en la auditoría con ese bot como sujeto.
Relanzar el job pide un id_token nuevo (cada ejecución tiene su propio jti, así que un token capturado no sirve dos veces).
Si algo falla¶
401 token no válido: elid_tokenno pasa la verificación (firma, emisor, audiencia distinta deKUBELATCH_BASE_URL, caducado, o ya canjeado antes). Revisa queKUBELATCH_URLcoincida exactamente conKUBELATCH_BASE_URLy que no estés reusando un token de un job anterior.403 ninguna trust rule coincide ...: el mensaje trae losrepository_owner_id,repository_id,refyenvironmentdel token. Pide a un administrador que cree o ajuste la trust rule con esos valores, o que quiteref/environmentpara hacerla genérica.403nombrando al bot: el bot está deshabilitado o sin permisos activos en ningún cluster.- El runner no llega a kubelatch (
curl: (7)o(35)): falta alcance de red por https, o el certificado no es de confianza. Es un problema de despliegue, no del workflow: pide a un administrador que revise la guía Instalar.
El listado completo de errores está en Errores.