177 lines
7.2 KiB
Markdown
177 lines
7.2 KiB
Markdown
# Requesting a client certificate from a remote Vault server with Microsoft Entra ID authentication
|
|
|
|
This guide shows how an administrator configures the Vault OIDC authentication method with Microsoft Entra ID as the identity provider, and how a user logs in with the Entra ID account to a remote Vault server and gets a client certificate signed by the Vault CA.
|
|
|
|
## Prerequisites
|
|
|
|
- The Vault PKI configured as described in step 1a of the main `README.md`: the `pki_int` mount and the `wif-client` role (`vault_configure.sh`).
|
|
- A Vault server reachable over HTTPS. The examples use `https://vault.example.com:8200`.
|
|
- Permissions to register applications in the Entra ID tenant.
|
|
- `vault` and `openssl` on the workstation of the administrator and of the user, and a web browser on the workstation of the user.
|
|
|
|
The commands are written for bash.
|
|
|
|
## 1. Register the application in Entra ID
|
|
|
|
Role: **Administrator**
|
|
|
|
1. Register an application for Vault in the tenant.
|
|
2. Add the redirect URIs of the type "Web":
|
|
|
|
- `http://localhost:8250/oidc/callback` for the CLI login.
|
|
- `https://vault.example.com:8200/ui/vault/auth/oidc/oidc/callback` for the Vault UI login.
|
|
|
|
3. Record the **Application (client) ID** and the **Directory (tenant) ID**.
|
|
4. Under **Certificates & secrets**, create a client secret and record its value.
|
|
5. Under **Token configuration**, select **Add groups claim**, select the group types to include (**Security groups**), and under **Customize token properties by type** select **Group ID** in the **ID** section. The `groups` claim of the ID token then contains the object IDs of the groups of the user.
|
|
6. Create a security group of the users who may request certificates, add the users to it, and record the **Object ID** of the group.
|
|
7. Under **API permissions**, add the Microsoft Graph delegated permission `User.Read` and grant the admin consent. Entra ID omits the groups from the token of users who are members of more than 200 groups, and Vault then reads them through Graph.
|
|
|
|
## 2. Configure Vault
|
|
|
|
Role: **Administrator**
|
|
|
|
Point the CLI at the server and authenticate with an administrator token:
|
|
|
|
```bash
|
|
export VAULT_ADDR=https://vault.example.com:8200
|
|
export VAULT_TOKEN=<admin-token>
|
|
```
|
|
|
|
1. Create a policy that allows signing certificate requests with the `wif-client` role and nothing else.
|
|
2. Enable the OIDC authentication method and configure Entra ID as the provider.
|
|
3. Create a role that accepts only members of the group and attaches the policy. The token is valid for 15 minutes and can be renewed up to 1 hour.
|
|
|
|
`pki-client.hcl`:
|
|
|
|
```hcl
|
|
path "pki_int/sign/wif-client" {
|
|
capabilities = ["update"]
|
|
}
|
|
```
|
|
|
|
`oidc-config.json`:
|
|
|
|
```json
|
|
{
|
|
"oidc_discovery_url": "https://login.microsoftonline.com/<tenant-id>/v2.0",
|
|
"oidc_client_id": "<client-id>",
|
|
"oidc_client_secret": "<client-secret>",
|
|
"default_role": "entra-pki",
|
|
"provider_config": {
|
|
"provider": "azure"
|
|
}
|
|
}
|
|
```
|
|
|
|
`oidc-role.json`:
|
|
|
|
```json
|
|
{
|
|
"role_type": "oidc",
|
|
"user_claim": "sub",
|
|
"allowed_redirect_uris": [
|
|
"http://localhost:8250/oidc/callback",
|
|
"https://vault.example.com:8200/ui/vault/auth/oidc/oidc/callback"
|
|
],
|
|
"groups_claim": "groups",
|
|
"oidc_scopes": ["profile"],
|
|
"bound_claims": {"groups": ["<group-object-id>"]},
|
|
"token_policies": ["pki-client"],
|
|
"token_ttl": "15m",
|
|
"token_max_ttl": "1h"
|
|
}
|
|
```
|
|
|
|
```bash
|
|
vault policy write pki-client pki-client.hcl
|
|
|
|
vault auth enable oidc
|
|
|
|
vault write auth/oidc/config @oidc-config.json
|
|
|
|
vault write auth/oidc/role/entra-pki @oidc-role.json
|
|
```
|
|
|
|
Delete `oidc-config.json` after the configuration is written, because it contains the client secret.
|
|
|
|
The redirect URIs in the role must match the ones registered in Entra ID exactly, including the scheme, the port and the trailing path. The `bound_claims` entry limits the login to the members of the group, the other Entra ID users get an error at login.
|
|
|
|
The CLI cannot express the `bound_claims` map as `key=value` arguments, which is why the configuration is read from JSON files.
|
|
|
|
> **Note:** The `wif-client` role accepts any common name (`allow_any_name=true`). Every member of the group can therefore request a certificate with any subject CN, including the CN that other users or workloads rely on as the WiF subject. Restrict the role (`allowed_domains`, `allow_bare_domains`, `enforce_hostnames`) when the users must not choose their CN freely.
|
|
|
|
## 3. Request a certificate
|
|
|
|
Role: **User**
|
|
|
|
1. Set the address of the Vault server. Set `VAULT_CACERT` as well when the server certificate is issued by a private CA.
|
|
|
|
```bash
|
|
export VAULT_ADDR=https://vault.example.com:8200
|
|
export VAULT_CACERT=/path/to/server-ca.pem
|
|
```
|
|
|
|
2. Unset `VAULT_TOKEN`, because a token in this environment variable takes precedence over the token stored by `vault login` and the CLI would keep using it. Then log in with the Entra ID account. The `skip_browser=true` parameter stops the CLI from launching the browser. The CLI prints the Entra ID sign-in link instead, waits on `localhost:8250` for the redirect, and after the sign-in stores the token in the token helper file (`~/.vault-token`).
|
|
|
|
```bash
|
|
unset VAULT_TOKEN
|
|
vault login -method=oidc skip_browser=true role=entra-pki
|
|
```
|
|
|
|
Open the printed link in the browser, or in the browser profile or the private window, signed in with the account that is a member of the group. Entra ID signs in with the session that the browser already has, without asking. A token of another account fails with `claim "groups" does not match any associated bound claim values`, because that account is not a member of the group.
|
|
|
|
The optional `callbackhost`, `callbackport`, `listenaddress` and `mount` parameters change the callback listener. The redirect URI built from them must be registered in Entra ID and listed in `allowed_redirect_uris` of the role.
|
|
|
|
3. Generate the client key and the CSR locally. The key is never sent to Vault.
|
|
|
|
```bash
|
|
openssl req \
|
|
-newkey rsa:4096 \
|
|
-keyout client_key.pem \
|
|
-noenc \
|
|
-subj "/CN=test-client" \
|
|
-out client.csr
|
|
```
|
|
|
|
4. Sign the CSR with the Vault CA.
|
|
|
|
```bash
|
|
vault write -field=certificate pki_int/sign/wif-client \
|
|
csr=@client.csr \
|
|
common_name=test-client \
|
|
ttl=8760h > client_cert.pem
|
|
```
|
|
|
|
5. Check the certificate.
|
|
|
|
```bash
|
|
openssl x509 -in client_cert.pem -noout -subject -issuer -dates -ext extendedKeyUsage
|
|
```
|
|
|
|
6. Revoke the token when the work is done.
|
|
|
|
```bash
|
|
vault token revoke -self
|
|
```
|
|
|
|
The user token can only sign with the `wif-client` role. Reading the role or any other path returns `permission denied`.
|
|
|
|
## Next steps
|
|
|
|
1. Create the credentials configuration `cred.json` for `client_cert.pem` and `client_key.pem`. The names come from `env.sh`.
|
|
|
|
```bash
|
|
gcloud iam workload-identity-pools create-cred-config \
|
|
"projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL_ID}/providers/${PROVIDER_ID}" \
|
|
--service-account="${SA_EMAIL}" \
|
|
--credential-cert-path=client_cert.pem \
|
|
--credential-cert-private-key-path=client_key.pem \
|
|
--output-file=cred.json
|
|
```
|
|
|
|
2. Activate the service account with `gcloud auth login --cred-file=cred.json`.
|
|
3. Run the WIF tests.
|
|
|
|
Example test commands are in `test_wif.sh`.
|