Using Vault as an OIDC provider for single sign-on
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, and Nomad 1.5.0 introduced support for OIDC as a single sign-on method. With Nomad 1.5.0, you can use OIDC to authenticate users and map their permissions to Nomad ACL roles and policies.
In this tutorial, you will setup Vault as an OIDC provider and Nomad as its client.
Note: Nomad 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 Nomad environment. Refer to the get started tutorials to install Nomad and create a cluster.
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 the root
token to work with Vault
running in development mode.
When you are working with a 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 providerpath "identity/*" { capabilities = [ "create", "read", "update", "delete", "list" ]}# To enable userpass auth methodpath "sys/auth/userpass" { capabilities = [ "create", "read", "update", "delete" ]}# To create a new user, "end-user" for userpasspath "auth/userpass/users/*" { capabilities = [ "create", "read", "update", "delete", "list" ]}
Refer to the Vault policies tutorial for more information.
Lab setup
Start Vault
In your terminal, start a Vault development server with root
as the root token.
$ vault server -dev -dev-root-token-id root
The Vault development server defaults to running at 127.0.0.1:8200
. The server is now
initialized and unsealed.
Warning
Do not run a Vault development server in production. This approach starts a Vault server with an in-memory database and is only for testing purposes.
Open another terminal session, and export an environment variable for the address to the Vault server.
$ export VAULT_ADDR=http://127.0.0.1:8200
Export an environment variable for the Vault token.
$ export VAULT_TOKEN=root
Note: For these tasks, you can use Vault's root token. However, we recommend that you use root tokens only for the 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 and 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 user named
end-user
with the passwordpassword
.$ vault write auth/userpass/users/end-user \ password="password" \ token_ttl="1h"
This user authenticates with Vault and is assigned the default access 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" \ disabled=false
Create an environment 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
and addend-user
as a member.$ vault write identity/group \ name="engineering" \ member_entity_ids="$ENTITY_ID"
Create an environment 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
user.$ vault write identity/entity-alias \ name="end-user" \ canonical_id="$ENTITY_ID" \ mount_accessor="$USERPASS_ACCESSOR"
The entity and the user are aliases of one another.
Learn More: Learn more about 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, a client callback URL and a 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 authorizes theend-user
entity 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
nomad
.$ vault write identity/oidc/client/nomad \ redirect_uris="http://localhost:4649/oidc/callback,http://localhost:4200/ui/settings/tokens" \ assignments="my-assignment" \ key="my-key" \ id_token_ttl="30m" \ access_token_ttl="1h"
The
redirect_uris
flag describes the callback URL for the client, the value is the address of a Nomad service running on its default port. Theassignments
flag limits access to only the entities and groups defined inmy-assignment
. Theid_token_ttl
flag sets the expiration on the ID token to 30 minutes. Theaccess_token_ttl
flag sets the expiration of the access token to 1 hour.Create an environment variable named CLIENT_ID to store the
client_id
field of thenomad
client.$ CLIENT_ID=$(vault read -field=client_id identity/oidc/client/nomad)
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 an environment variable named
USER_SCOPE_TEMPLATE
that stores the user scope template.$ USER_SCOPE_TEMPLATE='{"username": {{identity.entity.name}}}'
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 an environment 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)"
Create a Vault OIDC provider named
my-provider
and provide it a list of client IDs and scopes. The provider grants access to thenomad
client.$ vault write identity/oidc/provider/my-provider \ allowed_client_ids="${CLIENT_ID}" \ scopes_supported="groups"
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 Nomad
The Nomad development agent brings up an instance of Nomad with a server and client. Refer to the start a cluster tutorial for more information.
In another terminal, start a Nomad agent in development mode with ACL enabled.
$ sudo nomad agent -dev -acl-enabled==> No configuration files loaded==> Starting Nomad agent...==> Nomad agent configuration: Advertise Addrs: HTTP: 127.0.0.1:4646; RPC: 127.0.0.1:4647; Serf: 127.0.0.1:4648 Bind Addrs: HTTP: [127.0.0.1:4646]; RPC: 127.0.0.1:4647; Serf: 127.0.0.1:4648 Client: true Log Level: DEBUG Region: global (DC: dc1) Server: true Version: 1.5.0...snip...
The Nomad server is ready.
Configure Nomad OIDC auth
Create an environment 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 an environment variable named
CLIENT_SECRET
that stores theclient_secret
field of the Vault OIDC client namednomad
.$ CLIENT_SECRET=$(vault read -field=client_secret identity/oidc/client/nomad)
Run the bootstrap process, store the value of the management token, and export it as the
NOMAD_TOKEN
environment variable.$ NOMAD_TOKEN=$(nomad acl bootstrap -json | jq -r .SecretID)$ export NOMAD_TOKEN
Create a Nomad policy that allows read access to the "default" namespace.
Create a file named
acl_policy_engineering_read.hcl
, add the following contents, and save the file.namespace "default" { policy = "read"}node { policy = "read"}
Apply the policy.
$ nomad acl policy apply engineering-read acl_policy_engineering_read.hcl
Create a corresponding role that contains the policies to assign to engineers.
$ nomad acl role create \ -name=engineering-read \ -policy=engineering-read
Create a configuration for the OIDC auth method.
Create a file named
acl_auth_method.json
, add the following contents, replace$ISSUER
,$CLIENT_ID
and$CLIENT_SECRET
with the values stored in their respective environment variables, and save the file.{ "OIDCDiscoveryURL": $ISSUER, "OIDCClientID": $CLIENT_ID, "OIDCClientSecret": $CLIENT_SECRET, "BoundAudiences": [$CLIENT_ID], "OIDCScopes": ["groups"], "AllowedRedirectURIs": [ "http://localhost:4649/oidc/callback", "http://localhost:4646/ui/settings/tokens" ], "ListClaimMappings": { "groups": "roles" }}
The
ListClaimMappings
field specifies which claims of the OIDC provider should be mapped to which ACL concepts of Nomad. The Vault "group" created before will map to a "role" in Nomad.Create a new OIDC authentication method and configure it to use the Vault OIDC provider.
$ nomad acl auth-method create \ -default=true \ -name=vault \ -token-locality=global \ -max-token-ttl="10m" \ -type=oidc \ -config @acl_auth_method.json
Create a binding rule to evaluate OIDC claims into Nomad policies and roles.
$ nomad acl binding-rule create \ -auth-method=vault \ -bind-type=role \ -bind-name="engineering-read" \ -selector="engineering in list.roles"
Nomad OIDC authentication is configured and ready for the end-user
to
authenticate.
Authenticate with Nomad
Authenticate to Nomad. The -method
flag is optional since vault
has been configured as the default method.
$ nomad login -method=vault
The Vault login screen will open in your browser. Select Username from the
Method dropdown selection and enter end-user
and password
as the credentials.
The nomad login
command confirms the authentication was successful and
displays the Nomad ACL token generated.
Successfully logged in via OIDC and vaultAccessor ID = 48008488-21ae-35d7-f1d6-0066a7eb902aSecret ID = d7a803e6-abd6-a80e-dd95-5f88d904cc9eName = vaultType = clientGlobal = trueCreate Time = 2023-01-13 10:06:35.933899 +0000 UTCExpiry Time = 2023-01-13 10:16:35.933899 +0000 UTCCreate Index = 19Modify Index = 19Policies = []RolesID Name197deab1-a963-91fc-2936-05923eecf0c0 engineering-read
Next steps
In this tutorial, you configured Vault as an OIDC provider with Nomad as a client. Learn more about configuring Vault as an OIDC provider.
To learn more about Nomad ACL, check out the access control tutorials.