Whoever controls your pipeline controls your deploy credentials; keyless auth and protection rules limit the blast radius.
id_tokens: makes GitLab issue a signed JWT to the job, with an audience you choose. The token's claims describe the job: project_path, ref, ref_protected, environment, pipeline_source and a sub such as project_path:acme/shop:ref_type:branch:ref:main. A cloud provider (AWS, GCP, Azure) or HashiCorp Vault configured to trust your GitLab instance as an OIDC issuer exchanges the token for short-lived credentials. No long-lived keys are stored.
The trust policy is where security lives: require the exact project_path, and for production also ref_protected: true or a specific environment. Matching only the issuer would let every project on GitLab.com assume your role.
Broader pipeline hardening:
- Protect the default and release branches and tags; protected variables, runners and environments all build on that.
- Merge requests from forks run pipelines in the fork by default, without your secrets. Running them in the parent project is an explicit, reviewed action.
CI_JOB_TOKENaccess is limited by the project's allowlist; keep it tight.- Pin included templates and components to versions; an attacker who can change a shared template changes every pipeline that includes it.
- Treat
.gitlab-ci.ymlchanges as security-sensitive and use code owners for them.
Example
deploy:
image:
name: amazon/aws-cli:2.27.0
entrypoint: [""] # the image's entrypoint is aws
id_tokens:
AWS_TOKEN:
aud: sts.amazonaws.com
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
environment: production
script:
- >
export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s"
$(aws sts assume-role-with-web-identity
--role-arn "$ROLE_ARN" --role-session-name "gl-$CI_JOB_ID"
--web-identity-token "$AWS_TOKEN" --duration-seconds 3600
--query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text))
- aws sts get-caller-identity
# AWS trust policy condition (excerpt):
# "gitlab.example.com:sub": "project_path:acme/shop:ref_type:branch:ref:main" Remember
- id_tokens issue a per-job JWT for keyless cloud or Vault login
- Restrict trust to project_path and protected refs
- Protected branches underpin protected variables, runners and environments
Watch out for
- A trust policy that only checks the issuer and audience, trusting every project on the instance.