Vault OIDC SSH client certs
ActionsUse OIDC authentication to get SSH client certificates from Vault alt. OpenBao
v2.0
LatestBy andreaso
Tags
(2)This action uses GitHub's OIDC support to authenticate towards a HashiCorp Vault instance or an OpenBao instance, and to request a (short-lived) SSH client certificate from it.
jobs:
deploy:
permissions:
contents: read
id-token: write
# ...
steps:
# ...
- name: Generate SSH client certificate
if: github.ref == 'refs/heads/main'
id: ssh_cert
uses: andreaso/[email protected]
with:
vault_server: https://vault.example.com:8200
jwt_audience: vault.example.com
jwt_oidc_backend_path: github-oidc
jwt_oidc_role: example-user
ssh_backend_path: ssh-client-ca
ssh_role: github-actions-example
- name: Deploy site
if: github.ref == 'refs/heads/main'
run: >
rsync -e "ssh -i '$SSH_KEY_PATH'"
--verbose --recursive --delete-after --perms --chmod=D755,F644
build/ [email protected]:/var/www/site/
env:
SSH_KEY_PATH: ${{ steps.ssh_cert.outputs.key_path }}
Do note that all client certification configuration is expected to happen on the Vault end, given that that is where all the limitations can be enforced.
All the action's writes are to the ${{ runner.temp }}
directory. Hence as soon as the job is completed both the SSH
certificate and its private key will be automatically removed, even in
the case of a non-ephemeral runner.
resource "vault_jwt_auth_backend" "github" {
path = "github-oidc"
oidc_discovery_url = "https://token.actions.githubusercontent.com"
bound_issuer = "https://token.actions.githubusercontent.com"
}
resource "vault_mount" "ssh_ca" {
path = "ssh-client-ca"
type = "ssh"
}
resource "vault_ssh_secret_backend_ca" "ssh_ca" {
backend = vault_mount.ssh_ca.path
key_type = "ed25519"
generate_signing_key = true
}
resource "vault_ssh_secret_backend_role" "example" {
name = "github-actions-example"
backend = vault_mount.ssh_ca.path
max_ttl = "900"
key_type = "ca"
allow_user_certificates = true
allow_host_certificates = false
allowed_users = "[email protected]"
default_user = "[email protected]"
default_extensions = {}
allowed_user_key_config {
type = "ed25519"
lengths = [0]
}
}
data "vault_policy_document" "example" {
rule {
path = "${vault_mount.ssh_ca.path}/sign/${vault_ssh_secret_backend_role.example.name}"
capabilities = ["update"]
}
}
resource "vault_policy" "example" {
name = "example-policy"
policy = data.vault_policy_document.example.hcl
}
resource "vault_jwt_auth_backend_role" "example" {
backend = vault_jwt_auth_backend.github.path
role_type = "jwt"
role_name = "example-user"
token_max_ttl = "300"
token_policies = [vault_policy.example.name]
user_claim = "actor"
bound_audiences = ["vault.example.com"]
bound_claims = {
repository = "OWNER/REPO-NAME",
ref = "refs/heads/main",
}
}
output "ssh_ca" {
value = vault_ssh_secret_backend_ca.ssh_ca.public_key
}
# /etc/ssh/sshd_config
# ...
TrustedUserCAKeys /etc/ssh/sshd_user_ca.pub
AuthorizedPrincipalsFile /etc/ssh/user_principals/%u
# /etc/ssh/sshd_user_ca.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...
# /etc/ssh/user_principals/deployer
[email protected]
Vault OIDC SSH client certs is not certified by GitHub. It is provided by a third-party and is governed by separate terms of service, privacy policy, and support documentation.