Terraform module to configure Vault for GitHub OIDC authentication from Action runners on GitHub.com or self-hosted GitHub Enterprise Server.
OIDC authentication allows us to bind GitHub repositories (and subcomponents of a repository, such as a branch, ref, or environment) to a Vault role without needing to manage actual credentials that require a lifecycle system, integration into repo-level GitHub Secrets, or other organizational glue.
Explore GitHub OIDC and HashiCorp Vault use cases with this hands-on workshop: https://github.com/artis3n/course-vault-github-oidc.
Reference documents that help with understanding the process:
- https://www.digitalocean.com/blog/fine-grained-rbac-for-github-action-workflows-hashicorp-vault
- https://docs.github.com/en/enterprise-cloud@latest/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-hashicorp-vault
Once OIDC authentication is configured on a Vault server via this module, a GitHub repository can leverage hashicorp/vault-action to retrieve secrets from Vault with GitHub OIDC authentication. No secrets or credential management needed!
e.g.
- name: Import Secrets
uses: hashicorp/vault-action@v2
id: secrets
with:
exportEnv: false
url: https://<your-vault-URL>
path: github-actions
method: jwt
role: <vault_role_name>
secrets: |
secret/data/foo/bar fi | MY_SECRET
- name: Access secret
run: echo '${{steps.secrets.outputs.MY_SECRET }}' | my_command
- Usage
- Authors
- License
This module simplifies the creation of the JWT auth backend on Vault for this GitHub Action OIDC use case.
The module requires you to configure what repositories to bind to Vault roles and policies, and under what
conditions the respective repository should be granted access.
This is encapsulated by the oidc_bindings
variable.
Note
v2 of this module adopts Terraform 1.3's standardized support of optional object type attributes. Therefore, Terraform 1.3+ is required to use v2.0.0 or higher.
Users with Terraform 1.2 or earlier can use v1.1.0 of this module with the
module_variable_optional_attrs
experimental Terraform feature enabled.
Tutorial/example repo: https://github.com/artis3n/github-oidc-vault-example.
You can find several examples leveraging this module under examples/
:
- Basic usage
- Leveraging JSON files for distributed organization of repo bindings
- Adding custom additional claims per OIDC binding
- Leveraging this module on-prem with GitHub Enterprise Server
Basic example - one repo, separating secrets access by nonprod and prod pipelines.
module "github-vault-oidc" {
source = "digitalocean/github-oidc/vault"
version = "~> 2.1.0"
oidc_bindings = [
{
audience : "https://github.com/artis3n",
vault_role_name : "oidc-dev-role",
bound_subject : "repo:artis3n/github-oidc-vault-example:pull_request",
vault_policies : [
vault_policy.dev.name,
],
},
{
audience : "https://github.com/artis3n",
vault_role_name : "oidc-deploy-role",
bound_subject : "repo:artis3n/github-oidc-vault-example:ref:refs/heads/main",
vault_policies : [
vault_policy.deployment.name,
],
},
]
}
resource "vault_policy" "dev" {
name = "oidc-dev"
policy = data.vault_policy_document.dev.hcl
}
data "vault_policy_document" "dev" {
rule {
path = "secret/data/dev/foo"
capabilities = ["read"]
}
}
resource "vault_policy" "deployment" {
name = "oidc-deploy"
policy = data.vault_policy_document.deployment.hcl
}
data "vault_policy_document" "deployment" {
rule {
path = "secret/data/prod/bar"
capabilities = ["read"]
}
}
Enterprise Cloud organizations should strongly consider enabling the Unique Token URL feature for their organization.
If they do so, they should set the github_identity_provider
variable of this module to their enterprise's unique token URL.
This input variable must be a list of objects containing the following structure:
oidc_bindings = [
{
audience: '',
vault_role_name: '',
bound_subject: '',
vault_policies: [''],
}
]
There are additional, optional values you can include as well:
oidc_bindings = [
{
audience: '',
vault_role_name: '',
bound_subject: '',
vault_policies: [''],
# Optional below
user_claim: '',
additional_claims: [
{
x: '',
}
],
ttl: 0,
}
]
Descriptions for each parameter are below:
By default, the audience
must be the URL of the repository owner (e.g. https://github.com/digitalocean
).
The audience
can be customized by configuring whatever you'd like and using the jwtGithubAudience
parameter in
hashicorp/vault-action.
For example, from an organizational or audit perspective, you may desire to establish a naming scheme such as audience: "<company>:<org-unit>:<team-name>"
, e.g. digitalocean:security:product-security
.
The vault_role_name
must be the name of the Vault role you wish to create on the JWT auth backend.
Each Vault role should be configured for one repo subject - using the same Vault role with different configurations in the rest of
the parameters will cause this module to fail.
This is because you would otherwise silently overwrite the role configuration.
You may want to create multiple Vault roles for a single GitHub repository, e.g. a nonprod CI workflow that needs access to CI secrets, and a deployment workflow that publishes a release that needs production secrets.
The bound_subject
must be the sub
field from GitHub's OIDC token.
The bound subject can be constructed from various filters, such as a branch, tag, or specific environment.
See GitHub's documentation for examples.
vault_policies
must be a list of Vault policy strings to grant to the vault_role_name
Vault role being configured.
These can also come from vault_policy
resources.
Optional
The user_claim
is how you want Vault to uniquely identify this client.
This will be used as the name for the Identity entity alias created due to a successful login.
This means it will determine the auth.display_name
value in Vault audit logs.
This must be a field present in the GitHub JWT token.
Defaults to the value of the default_user_claim
variable if not provided.
We strongly recommend you keep a consistent format for auth.display_name
for monitoring of Vault's audit log.
Instead of changing the user_claim
for a specific role, consider modifying the default_user_claim
variable to apply a format change to all roles managed through this module.
Optional
additional_claims
must be a list of any additional claims you would like to enforce in the Vault role binding.
Each key
must be a field present in the GitHub JWT token.
For example, to leverage reusable workflows
with OIDC, you may wish to set your bound_subject
to repo:ORG_NAME/*
and add an additional claim of job_workflow_ref:ORG_NAME/REPO_NAME
pointing to the reusable workflow.
e.g.
oidc_bindings = [
{
audience: '...',
vault_role_name: '...',
bound_subject: "repo:digitalocean/*",
vault_policies: ['...'],
user_claim: '...',
additional_claims: [
{
job_workflow_ref: 'digitalocean/oidc-example/.github/workflows/deployment.yml@v1',
}
],
},
]
You can also specify a custom ttl
per role binding if you wish to customize beyond the default_ttl
variable.
This must be a number of seconds.
Optional
The default incremental time-to-live for generated tokens, in seconds.
Since most uses of hashicorp/vault-action
authenticate & retrieve secrets
in one step during a CI pipeline, the default for this variable is set to 5 minutes.
If you wish to customize the TTL for all roles, modify this variable.
You can also specify individual TTL requirements on individual roles that may have edge case needs for a different TTL.
See oidc_bindings.ttl
.
Optional
This is how you want Vault to uniquely identify this client.
This will be used as the name for the Identity entity alias created from a successful login.
This means it will determine the auth.display_name
value in Vault audit logs.
This must be a field prevent in the GitHub JWT token.
This is set to job_workflow_ref
by default.
Optional
By default, this role will generate a JWT auth backend on Vault at the path /github-actions
.
If you wish to customize the path created by this module, modify this variable.
Do not include a leading /
in the variable value (e.g. use github-actions
not /github-actions
).
At this time, this module expects to create and manage the JWT backend leveraged for GitHub OIDC auth. You cannot pass in a Terraform reference to an existing backend.
Optional
By default, this role will communicate with github.com for an OIDC JWT (https://token.actions.githubusercontent.com
).
If you are an Enterprise Cloud customer, you should configure a unique token URL and set this variable to your unique token URL.
https://token.actions.githubusercontent.com/<enterpriseSlug>
If you run GitHub Enterprise Server, you will need to configure your instance of GitHub as the identity provider and should modify this variable. This requires GitHub Enterprise Server version 3.5 or higher.
The format is: https://HOSTNAME/_services/token
.
Optional
The type of Vault token that should be generated. https://developer.hashicorp.com/vault/api-docs/auth/jwt#token_type
Because of the short TTLs and frequent use intended for authentication via this module, this module generates a batch token by default.
Name | Version |
---|---|
terraform | >= 1.3.0 |
vault | >= 3.4.1 |
Name | Version |
---|---|
vault | 3.12.0 |
No modules.
Name | Type |
---|---|
vault_jwt_auth_backend.github_oidc | resource |
vault_jwt_auth_backend_role.github_oidc_role | resource |
Name | Description | Type | Default | Required |
---|---|---|---|---|
oidc_bindings | A list of OIDC JWT bindings between GitHub repos and Vault roles. For each entry, you must include:audience : By default, this must be the URL of the repository owner (e.g. https://github.com/digitalocean ). This can be customized with the jwtGithubAudience parameter in hashicorp/vault-action . This is the bound audience (aud ) field from GitHub's OIDC token .vault_role_name : The name of the Vault role to generate under the OIDC auth backend.bound_subject : This is what is set in the sub field from GitHub's OIDC token . The bound subject can be constructed from various filters, such as a branch, tag, or specific environment . See GitHub's documentation for examples.vault_policies : A list of Vault policies you wish to grant to the generated token.user_claim : Optional. This is how you want Vault to uniquely identify this client. This will be used as the name for the Identity entity alias created due to a successful login. This must be a field present in the GitHub JWT token . Defaults to the default_user_claim variable if not provided. Consider the impact on reusable workflows if you are thinking of changing this value from the default.additional_claims : Optional. Any additional bound_claims to configure for this role. Claim keys must match a value in GitHub's OIDC token . Do not use this field for the sub claim. Use the bound_subject parameter instead.ttl : Optional. The default incremental time-to-live for the generated token, in seconds. Defaults to the default_ttl value but can be individually specified per binding with this value. |
list(object({ |
n/a | yes |
default_ttl | The default incremental time-to-live for generated tokens, in seconds. | number |
300 |
no |
default_user_claim | This is how you want Vault to uniquely identify this client. This will be used as the name for the Identity entity alias created due to a successful login. This must be a field present in the GitHub OIDC token . Consider the impact on reusable workflows if you are thinking of changing this value from the default. | string |
"job_workflow_ref" |
no |
github_identity_provider | The JWT authentication URL used for the GitHub OIDC trust configuration. If you are an Enteprise Cloud account, you should configure a unique token URL and set the result on this variable. If you are an Enterprise Server organization, you should provide a URL in the format: https://HOSTNAME/_services/token . This requires GitHub Enterprise Server version 3.5 or higher. See https://docs.github.com/en/enterprise-server@latest/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-hashicorp-vault#adding-the-identity-provider-to-hashicorp-vault. |
string |
"https://token.actions.githubusercontent.com" |
no |
oidc_auth_backend_path | The path to mount the OIDC auth backend. | string |
"github-actions" |
no |
token_type | The type of token to generate. This can be either batch or service . See https://developer.hashicorp.com/vault/api-docs/auth/jwt#token_type for more information. |
string |
"batch" |
no |
Name | Description |
---|---|
auth_backend_path | The path of the generated auth method. Use with a vault_auth_backend data source to retrieve any needed attributes from this resource. |
oidc_bindings_names | The Vault role names generated for each OIDC binding provided. This is a reflection of the vault_role_name value of each item in oidc-bindings . |
This module is maintained by Ari Kalfus with help from these excellent contributors.
Licensed under Apache 2.0. See LICENSE for full details.