This action uses GitHub's OIDC support to authenticate towards a HashiCorp Vault 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
oidc_backend_path: github-oidc
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.
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
}
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]