Confirm client idenity with an OIDC identity provider
Implementing zero-trust architecture requires providing identity-based access to services within an organization. OpenID Connect (OIDC) allows clients to confirm their identity through an identity provider. Vault 1.9.0 introduced the ability to configure Vault as an OIDC identity provider with authorization code flow.
In this tutorial, you will setup Vault as an OIDC provider. To demonstrate this feature, you will configure Boundary to leverage Vault as an identity provider and perform secure authentication.
Note
Boundary operates as a confidential OIDC client in this tutorial. To configure a public OIDC client, refer to the OIDC Provider documentation.
Prerequisites
To perform the tasks described in this tutorial, you need to have:
A Boundary environment. Refer to the Getting Started tutorial to install Boundary.
A Vault environment of version 1.10 or later. Refer to the Vault install guide to install Vault locally or create a Vault cluster on HCP.
Note
This feature was first introduced in Vault 1.9 as a Technical Preview feature. As of Vault 1.10, it is generally available.
Policy requirements
For the purpose of this tutorial, you will use root
token to work with Vault
running in development mode.
When you are working with non-development Vault environment, your token policy must include the following permissions:
# To create an entity and entity alias. Enable and configure Vault as an OIDC provider
path "identity/*" {
capabilities = [ "create", "read", "update", "delete", "list" ]
}
# To enable userpass auth method
path "sys/auth/userpass" {
capabilities = [ "create", "read", "update", "delete" ]
}
# To create a new user, "end-user" for userpass
path "auth/userpass/users/*" {
capabilities = [ "create", "read", "update", "delete", "list" ]
}
If you are not familiar with policies, complete the policies tutorial.
Lab setup
Start Vault
In another terminal, start a Vault dev server with root
as the root token.
$ vault server -dev -dev-root-token-id root
The Vault dev server defaults to running at 127.0.0.1:8200
. The server is
initialized and unsealed.
Insecure operation
Do not run a Vault dev server in production. This approach starts a Vault server with an in-memory database and runs in an insecure way.
Open another terminal session, and export an environment variable for the
vault
CLI to address the Vault server.
$ export VAULT_ADDR=http://127.0.0.1:8200
Export an environment variable for the vault
CLI to authenticate with the
Vault server.
$ export VAULT_TOKEN=root
Note
For these tasks, you can use Vault's root token. However, it is recommended that root tokens are only used for enough initial setup or in emergencies. As a best practice, use an authentication method or token that meets the policy requirements.
The Vault server is ready.
Configure Vault authentication
Vault auth methods authenticate, assign identity and policies to a client. When Vault acts as an OIDC provider, it is the source of identity and these auth methods verify that identity.
Enable the userpass auth method at the default path.
$ vault auth enable userpass
Create a policy granting the user read capabilities on the authorization endpoint.
vault policy write oidc-auth - << EOF path "identity/oidc/provider/my-provider/authorize" { capabilities = [ "read" ] } EOF
Example expected output:
Success! Uploaded policy: oidc-auth
Create a user named
end-user
with the passwordpassword
, and add theoidc-auth
policy.$ vault write auth/userpass/users/end-user \ password="password" \ token_policies="oidc-auth" \ token_ttl="1h"
Example expected output:
Success! Data written to: auth/userpass/users/end-user
This user authenticates with Vault and is assigned the default access policy along with the oidc-auth policy.
Learn More
For more information refer to the Userpass Auth Method documentation.
Create Vault identity entity and group
A client may have multiple accounts with various identity providers that are enabled on the Vault server. Vault clients can be mapped as entities and their corresponding accounts with authentication providers can be mapped as aliases.
Create an identity entity with details about the
end-user
.$ vault write identity/entity \ name="end-user" \ metadata="email=vault@hashicorp.com" \ metadata="phone_number=123-456-7890" \ disabled=false
The
end-user
metadata contains their email address and phone number.Create a variable named
ENTITY_ID
that stores the ID assigned to the entity.$ ENTITY_ID=$(vault read -field=id identity/entity/name/end-user)
Create an identity group with the name
engineering
with end-user as a member.$ vault write identity/group \ name="engineering" \ member_entity_ids="$ENTITY_ID"
Create a variable named
GROUP_ID
that stores the ID assigned to the group.$ GROUP_ID=$(vault read -field=id identity/group/name/engineering)
The
end-user
entity is a member of theengineering
group. An entity alias maps an entity to client of an authentication method. This mapping requires the entity ID and the authentication accessor ID.Create a variable named
USERPASS_ACCESSOR
that stores the accessor value of the userpass authentication method.$ USERPASS_ACCESSOR=$(vault auth list -detailed -format json | jq -r '.["userpass/"].accessor')
Create an entity alias that maps the
end-user
entity with theend-user
userpass user.$ vault write identity/entity-alias \ name="end-user" \ canonical_id="$ENTITY_ID" \ mount_accessor="$USERPASS_ACCESSOR"
The entity and the userpass user are aliases of one another.
Learn More
Explore identity in the entities and groups tutorial.
Create a Vault OIDC client
A Vault OIDC client connects a resource called an OIDC assignment, an encryption key, client callback URL and the time-to-live on verification together.
An OIDC assignment describes the list of the Vault entities and groups allowed to authenticate with this client.
Create an assignment named
my-assignment
that authorize theend-user
entity and andengineering
group.$ vault write identity/oidc/assignment/my-assignment \ entity_ids="${ENTITY_ID}" \ group_ids="${GROUP_ID}"
The Vault OIDC authentication process requires an encryption key to sign and verify the JSON web tokens (JWT) that are produced by the authentication flow.
Create a key named
my-key
.$ vault write identity/oidc/key/my-key \ allowed_client_ids="*" \ verification_ttl="2h" \ rotation_period="1h" \ algorithm="RS256"
The key is usable by all Vault OIDC clients as
allowed_client_ids
is set to*
.Create an OIDC client named
boundary
.$ vault write identity/oidc/client/boundary \ redirect_uris="http://127.0.0.1:9200/v1/auth-methods/oidc:authenticate:callback" \ assignments="my-assignment" \ key="my-key" \ id_token_ttl="30m" \ access_token_ttl="1h"
The
redirect_uris
describe the callback URL for the client. The valuehttp://127.0.0.1:9200/v1/auth-methods/oidc:authenticate:callback
is the address of a Boundary service running on its default port. Theassignments
limits access the entities and groups defined inmy-assignment
. Theid_token_ttl
sets the expiration on the ID token to 30 minutes. Theaccess_token_ttl
sets the expiration of the Access token to 1 hour.Create a variable named CLIENT_ID to store the
client_id
field of theboundary
client.$ CLIENT_ID=$(vault read -field=client_id identity/oidc/client/boundary)
Create a Vault OIDC provider
A Vault OIDC provider supports one or more clients and Vault OIDC scopes. These scopes define metadata claims expressed in a template. Claims are key-value pairs that contain information about a user and the OIDC service.
Create a variable named
USER_SCOPE_TEMPLATE
that stores the user scope template.$ USER_SCOPE_TEMPLATE='{ "username": {{identity.entity.name}}, "contact": { "email": {{identity.entity.metadata.email}}, "phone_number": {{identity.entity.metadata.phone_number}} } }'
This template retrieves the
email
andphone_number
from the metadata of an entity.Define a Vault OIDC scope named
user
with the user scope template.$ vault write identity/oidc/scope/user \ description="The user scope provides claims using Vault identity entity metadata" \ template="$(echo ${USER_SCOPE_TEMPLATE} | base64 -)"
Create a variable named
GROUPS_SCOPE_TEMPLATE
that stores the group scope template.$ GROUPS_SCOPE_TEMPLATE='{ "groups": {{identity.entity.groups.names}} }'
This template retrieves the names of all the groups defined.
Define a Vault OIDC scope named
groups
with the groups scope template.$ vault write identity/oidc/scope/groups \ description="The groups scope provides the groups claim using Vault group membership" \ template="$(echo ${GROUPS_SCOPE_TEMPLATE} | base64 -)"
The Vault OIDC provider requires a list of client IDs and scopes.
Create an Vault OIDC provider named
my-provider
.$ vault write identity/oidc/provider/my-provider \ allowed_client_ids="${CLIENT_ID}" \ scopes_supported="groups,user"
The provider grants the
boundary
client access.Display the Vault OIDC configuration endpoint.
$ curl -s $VAULT_ADDR/v1/identity/oidc/provider/my-provider/.well-known/openid-configuration | jq
Show Vault OIDC public keys.
$ curl -s $VAULT_ADDR/v1/identity/oidc/provider/my-provider/.well-known/keys | jq
Start Boundary
Boundary enables secure access to dynamic infrastructure. The development server brings up a fully functioning instance of Boundary. Refer to the Install Boundary tutorial if you are not familiar with installing Boundary.
In another terminal, start Boundary in development mode.
$ boundary dev ==> Boundary server configuration: [Controller] AEAD Key Bytes: CRVcb/JPLVZT1eTKzfL9D/LXJjveX99lpxp3NLQ7YDo= [Recovery] AEAD Key Bytes: hmdKvtT3ygiYrC35okvPTbTNLX64EL5nH66gE3/na+E= [Worker-Auth] AEAD Key Bytes: sNDhJIQy/9yR7AAGX3C8FN9aBjcCqFSZ+v+mTmIYetI= [Recovery] AEAD Type: aes-gcm [Root] AEAD Type: aes-gcm [Worker-Auth] AEAD Type: aes-gcm Cgo: disabled Controller Public Cluster Addr: 127.0.0.1:9201 Dev Database Container: bold_curran Dev Database Url: postgres://postgres:password@localhost:55001/boundary?sslmode=disable Generated Admin Login Name: admin Generated Admin Password: password Generated Host Catalog Id: hcst_1234567890 Generated Host Id: hst_1234567890 Generated Host Set Id: hsst_1234567890 Generated Oidc Auth Method Id: amoidc_1234567890 Generated Org Scope Id: o_1234567890 Generated Password Auth Method Id: ampw_1234567890 Generated Project Scope Id: p_1234567890 Generated Target Id: ttcp_1234567890 Generated Unprivileged Login Name: user Generated Unprivileged Password: password Listener 1: tcp (addr: "127.0.0.1:9200", cors_allowed_headers: "[]", cors_allowed_origins: "[*]", cors_enabled: "true", max_request_duration: "1m30s", purpose: "api") Listener 2: tcp (addr: "127.0.0.1:9201", max_request_duration: "1m30s", purpose: "cluster") Listener 3: tcp (addr: "127.0.0.1:9202", max_request_duration: "1m30s", purpose: "proxy") Log Level: info Mlock: supported: false, enabled: false Version: Boundary v0.6.2 Version Sha: 07c5c00f557ccc6d58ac065fa6c267f576860ac2 Worker Public Proxy Addr: 127.0.0.1:9202 ...snip...
Switch back to the other terminal and authenticate with the Boundary as the admin user.
$ boundary authenticate password \ -auth-method-id ampw_1234567890 \ -login-name admin \ -password password
The Boundary server is ready.
Configure Boundary OIDC auth
Boundary supports authentication through OIDC.
Create a variable named
ISSUER
that stores theissuer
field of the Vault OIDC provider namedmy-provider
.$ ISSUER=$(curl -s $VAULT_ADDR/v1/identity/oidc/provider/my-provider/.well-known/openid-configuration | jq -r .issuer)
Create a variable named
CLIENT_SECRET
that stores theclient_secret
field of Vault OIDC client namedboundary
.$ CLIENT_SECRET=$(vault read -field=client_secret identity/oidc/client/boundary)
Create a new oidc authentication method configured to use the Vault OIDC provider.
$ boundary auth-methods create oidc \ -issuer "${ISSUER}" \ -client-id "${CLIENT_ID}" \ -client-secret "${CLIENT_SECRET}" \ -signing-algorithm RS256 \ -api-url-prefix "http://127.0.0.1:9200" \ -claims-scopes "groups" \ -claims-scopes "user" \ -max-age 20 \ -name "Vault OIDC Provider"
Create a variable named
AUTH_METHOD_ID
that stores the ID of the authentication method.$ AUTH_METHOD_ID="$(boundary auth-methods list -format json | jq -r '.items[] | select(.name == "Vault OIDC Provider") | .id')"
Change the state of the auth method to
active-public
.$ boundary auth-methods change-state oidc \ -id "${AUTH_METHOD_ID}" \ -state active-public
Enable the auth method to operate in the
global
scope.$ boundary scopes update \ -primary-auth-method-id "${AUTH_METHOD_ID}" \ -id global
Boundary OIDC authentication is configured and ready for the end-user
to
authenticate.
Authenticate with Boundary
Launch the Boundary UI at http://127.0.0.1:9200 to authenticate.
By default the Boundary UI places you in the
Global
scope.Select Vault OIDC Provider.
Click Authenticate
Boundary redirects to the Vault authentication page in a new window.
Navigate to the Vault authentication window created by Boundary.
Pop-up Blocking
If you do not see the new window ensure that the browser has not blocked it.
Select Username from the Method drop-down list.
Enter
end-user
in the Username text field.Enter
password
in the Password text field.Click Sign In.
The authentication process completes and redirects back to Boundary.
Next steps
In this tutorial, you configured Vault as an OIDC provider with Boundary as a client. Learn more about configuring Vault as an OIDC provider by reading the documentation. Boundary is able to provide access to dynamic infrastructure.
To learn more about Boundary, visit Boundary Learn.