Trying to Authenticate with Vault using OpenID Connect (OIDC) using Dex

Trying to Authenticate with Vault using OpenID Connect (OIDC) using Dex

ยท

19 min read

I was playing with Vault OpenID Connect login method / auth method along with Dex. I wanted to share my learnings here of what I tried out

Please note that you would be able to take these learnings to use Vault with OpenID Connect as a login method with a different OpenID Connect provider too, if not Dex

One of the things that I did was - I tried out Dex's getting started guide. I built dex from source, but you can also use their container images. Let's see how one can use the container images. For example I'm using the container image hosted on GitHub Container Registry and I'm using the latest distroless container image, it's version is -

$ docker run --rm -it ghcr.io/dexidp/dex:latest-distroless dex version
Dex Version: 5d64dc7a4c2358cad781b88768480af6643e8e26-dirty
Go Version: go1.21.6
Go OS/ARCH: linux amd64

The next thing I did was run dex in local with dev (development) config that comes with the source code. The one I'm using is this - https://github.com/dexidp/dex/blob/5d64dc7a4c2358cad781b88768480af6643e8e26/examples/config-dev.yaml , which looks like this -

# DEPRECATED: use config.yaml.dist and config.dev.yaml examples in the repository root.
# TODO: keep this until all references are updated.

# The base path of dex and the external name of the OpenID Connect service.
# This is the canonical URL that all clients MUST use to refer to dex. If a
# path is provided, dex's HTTP service will listen at a non-root URL.
issuer: http://127.0.0.1:5556/dex

# The storage configuration determines where dex stores its state. Supported
# options include SQL flavors and Kubernetes third party resources.
#
# See the documentation (https://dexidp.io/docs/storage/) for further information.
storage:
  type: sqlite3
  config:
    file: examples/dex.db

  # type: mysql
  # config:
  #   host: localhost
  #   port: 3306
  #   database: dex
  #   user: mysql
  #   password: mysql
  #   ssl:
  #     mode: "false"

  # type: postgres
  # config:
  #   host: localhost
  #   port: 5432
  #   database: dex
  #   user: postgres
  #   password: postgres
  #   ssl:
  #     mode: disable

  # type: etcd
  # config:
  #   endpoints:
  #     - http://localhost:2379
  #   namespace: dex/

  # type: kubernetes
  # config:
  #   kubeConfigFile: $HOME/.kube/config

# Configuration for the HTTP endpoints.
web:
  http: 0.0.0.0:5556
  # Uncomment for HTTPS options.
  # https: 127.0.0.1:5554
  # tlsCert: /etc/dex/tls.crt
  # tlsKey: /etc/dex/tls.key

# Configuration for dex appearance
# frontend:
#   issuer: dex
#   logoURL: theme/logo.png
#   dir: web/
#   theme: light

# Configuration for telemetry
telemetry:
  http: 0.0.0.0:5558
  # enableProfiling: true

# Uncomment this block to enable the gRPC API. This values MUST be different
# from the HTTP endpoints.
# grpc:
#   addr: 127.0.0.1:5557
#   tlsCert: examples/grpc-client/server.crt
#   tlsKey: examples/grpc-client/server.key
#   tlsClientCA: examples/grpc-client/ca.crt

# Uncomment this block to enable configuration for the expiration time durations.
# Is possible to specify units using only s, m and h suffixes.
# expiry:
#   deviceRequests: "5m"
#   signingKeys: "6h"
#   idTokens: "24h"
#   refreshTokens:
#     reuseInterval: "3s"
#     validIfNotUsedFor: "2160h" # 90 days
#     absoluteLifetime: "3960h" # 165 days

# Options for controlling the logger.
# logger:
#   level: "debug"
#   format: "text" # can also be "json"

# Default values shown below
# oauth2:
    # grantTypes determines the allowed set of authorization flows.
#   grantTypes:
#     - "authorization_code"
#     - "refresh_token"
#     - "implicit"
#     - "password"
#     - "urn:ietf:params:oauth:grant-type:device_code"
#     - "urn:ietf:params:oauth:grant-type:token-exchange"
    # responseTypes determines the allowed response contents of a successful authorization flow.
    # use ["code", "token", "id_token"] to enable implicit flow for web-only clients.
#   responseTypes: [ "code" ] # also allowed are "token" and "id_token"
    # By default, Dex will ask for approval to share data with application
    # (approval for sharing data from connected IdP to Dex is separate process on IdP)
#   skipApprovalScreen: false
    # If only one authentication method is enabled, the default behavior is to
    # go directly to it. For connected IdPs, this redirects the browser away
    # from application to upstream provider such as the Google login page
#   alwaysShowLoginScreen: false
    # Uncomment the passwordConnector to use a specific connector for password grants
#   passwordConnector: local

# Instead of reading from an external storage, use this list of clients.
#
# If this option isn't chosen clients may be added through the gRPC API.
staticClients:
- id: example-app
  redirectURIs:
  - 'http://127.0.0.1:5555/callback'
  name: 'Example App'
  secret: ZXhhbXBsZS1hcHAtc2VjcmV0
#  - id: example-device-client
#    redirectURIs:
#      - /device/callback
#    name: 'Static Client for Device Flow'
#    public: true
connectors:
- type: mockCallback
  id: mock
  name: Example
# - type: google
#   id: google
#   name: Google
#   config:
#     issuer: https://accounts.google.com
#     # Connector config values starting with a "$" will read from the environment.
#     clientID: $GOOGLE_CLIENT_ID
#     clientSecret: $GOOGLE_CLIENT_SECRET
#     redirectURI: http://127.0.0.1:5556/dex/callback
#     hostedDomains:
#     - $GOOGLE_HOSTED_DOMAIN

# Let dex keep a list of passwords which can be used to login to dex.
enablePasswordDB: true

# A static list of passwords to login the end user. By identifying here, dex
# won't look in its underlying storage for passwords.
#
# If this option isn't chosen users may be added through the gRPC API.
staticPasswords:
- email: "admin@example.com"
  # bcrypt hash of the string "password": $(echo password | htpasswd -BinC 10 admin | cut -d: -f2)
  hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
  username: "admin"
  userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"

Note: This configuration yaml is deprecated according to the comments in the yaml file, but this is what I used for playing around. You can consider using the config.yaml.dist and config.dev.yaml examples in the repository root.

Next, I tried out the example-app for demo purposes - https://dexidp.io/docs/getting-started/#running-a-client . This example app comes as part of the dex source code

Let me show how I tried out authenticating with the example app using dex and then show I did it with Vault using dex. I built the example app using make examples

So, let's run dex and the example app now and do the authentication

$ ./bin/dex version

$ ./bin/dex serve examples/config-dev.yaml

Running dex looks like this -

$ ./bin/dex version
Dex Version: 5d64dc7a4c2358cad781b88768480af6643e8e26
Go Version: go1.21.5
Go OS/ARCH: darwin amd64

$ ./bin/dex serve examples/config-dev.yaml
time="2024-01-17T07:20:58Z" level=info msg="Dex Version: 5d64dc7a4c2358cad781b88768480af6643e8e26, Go Version: go1.21.5, Go OS/ARCH: darwin amd64"
time="2024-01-17T07:20:58Z" level=info msg="config issuer: http://127.0.0.1:5556/dex"
time="2024-01-17T07:20:58Z" level=info msg="config storage: sqlite3"
time="2024-01-17T07:20:58Z" level=info msg="config static client: Example App"
time="2024-01-17T07:20:58Z" level=info msg="config connector: mock"
time="2024-01-17T07:20:58Z" level=info msg="config connector: local passwords enabled"
time="2024-01-17T07:20:58Z" level=info msg="config refresh tokens rotation enabled: true"
time="2024-01-17T07:20:58Z" level=info msg="keys expired, rotating"
time="2024-01-17T07:20:58Z" level=info msg="keys rotated, next rotation: 2024-01-17 13:20:58.461861 +0000 UTC"
time="2024-01-17T07:20:58Z" level=info msg="listening (telemetry) on 0.0.0.0:5558"
time="2024-01-17T07:20:58Z" level=info msg="listening (http) on 0.0.0.0:5556"

That's using the locally built dex , though you can use container images too

Next, I ran the local example app in a new terminal

$ ./bin/example-app 
2024/01/17 12:52:23 listening on http://127.0.0.1:5555

Then I went to http://localhost:5555 in my browser and clicked Login . This is how it looks like -

On clicking Login, you will see this -

The URL on this page looks like this - http://127.0.0.1:5556/dex/auth?client_id=example-app&redirect_uri=http%3A%2F%2F127.0.0.1%3A5555%2Fcallback&response_type=code&scope=openid+profile+email+offline_access&state=I+wish+to+wash+my+irish+wristwatch

This page is served by the dex application, notice that it's port is 5556 , which is where the dex application server is listening ๐Ÿ‘‚ at

You can click Log in with Example or Log in with Email. For Log in with Example, it looks like this -

An example URL for this page looks like this - http://127.0.0.1:5556/dex/approval?req=sr3vrmobts4kiuuzhj5ltdctd&hmac=inl0VmV_FFPsSDXj2PvPByn-EOBWFDkW7SQ0QnMvBfo

This page is also served by the dex application, notice that it's port is 5556 , which is where the dex application server is listening ๐Ÿ‘‚ at

And then once you click Grant Access to grant access to example app, you will see this -

An example URL for this page looks like this - http://127.0.0.1:5555/callback?code=i25za5p4os4ckam62kho6iu2r&state=I+wish+to+wash+my+irish+wristwatch

This page is served by example app (application), notice that it's port is 5555 , which is where the example app server is listening ๐Ÿ‘‚ at

You can also click Redeem refresh token button on this page and you will see the ID Token, Access Token, Claims and the Refresh Token change

Now, let's go login to Vault, instead of an example application. Let's login to Vault using Dex using OpenID Connect

First thing I did was, I ran Vault in local. The version of Vault I used was this -

$ vault version
Vault v1.15.4 (9b61934559ba31150860e618cf18e816cbddc630), built 2023-12-04T17:45:28Z

I ran Vault in dev / development mode, because I was just playing with it. This is the command to run Vault in development mode, with root as the root token

$ vault server -dev -dev-root-token-id root

Running the above Vault command will look like this

~ $ vault version
Vault v1.15.4 (9b61934559ba31150860e618cf18e816cbddc630), built 2023-12-04T17:45:28Z
~ $ vault server -dev -dev-root-token-id root
==> Vault server configuration:

Administrative Namespace: 
             Api Address: http://127.0.0.1:8200
                     Cgo: disabled
         Cluster Address: https://127.0.0.1:8201
   Environment Variables: BASH_SILENCE_DEPRECATION_WARNING, EDITOR, GODEBUG, GOPATH, GOROOT, HISTSIZE, HOME, LC_CTYPE, LOGNAME, LaunchInstanceID, OLDPWD, PATH, PS1, PWD, SECURITYSESSIONID, SHELL, SHLVL, SSH_AUTH_SOCK, TERM, TERM_PROGRAM, TERM_PROGRAM_VERSION, TERM_SESSION_ID, TMPDIR, USER, XPC_FLAGS, XPC_SERVICE_NAME, _, __CFBundleIdentifier
              Go Version: go1.21.4
              Listener 1: tcp (addr: "127.0.0.1:8200", cluster address: "127.0.0.1:8201", max_request_duration: "1m30s", max_request_size: "33554432", tls: "disabled")
               Log Level: 
                   Mlock: supported: false, enabled: false
           Recovery Mode: false
                 Storage: inmem
                 Version: Vault v1.15.4, built 2023-12-04T17:45:28Z
             Version Sha: 9b61934559ba31150860e618cf18e816cbddc630

==> Vault server started! Log data will stream in below:

2024-01-17T13:11:25.004+0530 [INFO]  proxy environment: http_proxy="" https_proxy="" no_proxy=""
2024-01-17T13:11:25.005+0530 [INFO]  incrementing seal generation: generation=1
2024-01-17T13:11:25.005+0530 [WARN]  no `api_addr` value specified in config or in VAULT_API_ADDR; falling back to detection if possible, but this value should be manually set
2024-01-17T13:11:25.005+0530 [INFO]  core: Initializing version history cache for core
2024-01-17T13:11:25.005+0530 [INFO]  events: Starting event system
2024-01-17T13:11:25.005+0530 [INFO]  core: security barrier not initialized
2024-01-17T13:11:25.006+0530 [INFO]  core: security barrier initialized: stored=1 shares=1 threshold=1
2024-01-17T13:11:25.006+0530 [INFO]  core: post-unseal setup starting
2024-01-17T13:11:25.014+0530 [INFO]  core: loaded wrapping token key
2024-01-17T13:11:25.014+0530 [INFO]  core: successfully setup plugin runtime catalog
2024-01-17T13:11:25.014+0530 [INFO]  core: successfully setup plugin catalog: plugin-directory=""
2024-01-17T13:11:25.014+0530 [INFO]  core: no mounts; adding default mount table
2024-01-17T13:11:25.016+0530 [INFO]  core: successfully mounted: type=cubbyhole version="v1.15.4+builtin.vault" path=cubbyhole/ namespace="ID: root. Path: "
2024-01-17T13:11:25.016+0530 [INFO]  core: successfully mounted: type=system version="v1.15.4+builtin.vault" path=sys/ namespace="ID: root. Path: "
2024-01-17T13:11:25.017+0530 [INFO]  core: successfully mounted: type=identity version="v1.15.4+builtin.vault" path=identity/ namespace="ID: root. Path: "
2024-01-17T13:11:25.020+0530 [INFO]  core: successfully mounted: type=token version="v1.15.4+builtin.vault" path=token/ namespace="ID: root. Path: "
2024-01-17T13:11:25.021+0530 [INFO]  rollback: Starting the rollback manager with 256 workers
2024-01-17T13:11:25.021+0530 [INFO]  rollback: starting rollback manager
2024-01-17T13:11:25.021+0530 [INFO]  core: restoring leases
2024-01-17T13:11:25.023+0530 [INFO]  identity: entities restored
2024-01-17T13:11:25.023+0530 [INFO]  identity: groups restored
2024-01-17T13:11:25.023+0530 [INFO]  expiration: lease restore complete
2024-01-17T13:11:25.023+0530 [INFO]  core: Recorded vault version: vault version=1.15.4 upgrade time="2024-01-17 07:41:25.023499 +0000 UTC" build date=2023-12-04T17:45:28Z
2024-01-17T13:11:25.425+0530 [INFO]  core: post-unseal setup complete
2024-01-17T13:11:25.425+0530 [INFO]  core: root token generated
2024-01-17T13:11:25.425+0530 [INFO]  core: pre-seal teardown starting
2024-01-17T13:11:25.425+0530 [INFO]  rollback: stopping rollback manager
2024-01-17T13:11:25.425+0530 [INFO]  core: pre-seal teardown complete
2024-01-17T13:11:25.426+0530 [INFO]  core.cluster-listener.tcp: starting listener: listener_address=127.0.0.1:8201
2024-01-17T13:11:25.426+0530 [INFO]  core.cluster-listener: serving cluster requests: cluster_listen_address=127.0.0.1:8201
2024-01-17T13:11:25.426+0530 [INFO]  core: post-unseal setup starting
2024-01-17T13:11:25.426+0530 [INFO]  core: loaded wrapping token key
2024-01-17T13:11:25.426+0530 [INFO]  core: successfully setup plugin runtime catalog
2024-01-17T13:11:25.426+0530 [INFO]  core: successfully setup plugin catalog: plugin-directory=""
2024-01-17T13:11:25.428+0530 [INFO]  core: successfully mounted: type=system version="v1.15.4+builtin.vault" path=sys/ namespace="ID: root. Path: "
2024-01-17T13:11:25.428+0530 [INFO]  core: successfully mounted: type=identity version="v1.15.4+builtin.vault" path=identity/ namespace="ID: root. Path: "
2024-01-17T13:11:25.428+0530 [INFO]  core: successfully mounted: type=cubbyhole version="v1.15.4+builtin.vault" path=cubbyhole/ namespace="ID: root. Path: "
2024-01-17T13:11:25.430+0530 [INFO]  core: successfully mounted: type=token version="v1.15.4+builtin.vault" path=token/ namespace="ID: root. Path: "
2024-01-17T13:11:25.430+0530 [INFO]  rollback: Starting the rollback manager with 256 workers
2024-01-17T13:11:25.430+0530 [INFO]  rollback: starting rollback manager
2024-01-17T13:11:25.430+0530 [INFO]  core: restoring leases
2024-01-17T13:11:25.431+0530 [INFO]  identity: entities restored
2024-01-17T13:11:25.431+0530 [INFO]  identity: groups restored
2024-01-17T13:11:25.431+0530 [INFO]  expiration: lease restore complete
2024-01-17T13:11:25.431+0530 [INFO]  core: post-unseal setup complete
2024-01-17T13:11:25.431+0530 [INFO]  core: vault is unsealed
2024-01-17T13:11:25.435+0530 [INFO]  expiration: revoked lease: lease_id=auth/token/root/hb164f6b0bd11fb14a2bb1c1d9479fd4a5f1502cc27e4a59ae9140cbd27def48e
2024-01-17T13:11:25.443+0530 [INFO]  core: successful mount: namespace="" path=secret/ type=kv version=""
WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory
and starts unsealed with a single unseal key. The root token is already
authenticated to the CLI, so you can immediately begin using Vault.

You may need to set the following environment variables:

    $ export VAULT_ADDR='http://127.0.0.1:8200'

The unseal key and root token are displayed below in case you want to
seal/unseal the Vault or re-authenticate.

Unseal Key: 2sECtH4dtdZtpLy1MHLdYzORB/Pczw0eoUqFBJ4q8ac=
Root Token: root

Development mode should NOT be used in production installations!

Next thing I did was to configure Vault. Let's look at how to do it

In a new terminal, I did the following -

$ export VAULT_ADDR='http://127.0.0.1:8200'

This sets the Vault's API URL for Vault CLI

Next, login to Vault using root as root token, so that you can configure the Vault as a root user. Use the following to do login using Vault CLI

$ vault login

It will looks like this once logged in with root root token -

$ vault login
Token (will be hidden): 
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                  Value
---                  -----
token                root
token_accessor       XCpGikVy7Ediq3DM4fcM4tZt
token_duration       โˆž
token_renewable      false
token_policies       ["root"]
identity_policies    []
policies             ["root"]

Next, let's switch gears โš™๏ธ and do some dex stuff. First, stop the already running dex application and change the configuration of dex in config-dev.yaml and then rerun the dex application

In dex configuration yaml file, add a static client for vault app under staticClients -

- id: vault
  redirectURIs:
  - 'http://localhost:8200/ui/vault/auth/oidc/oidc/callback'
  - 'http://localhost:8250/oidc/callback'
  name: 'Vault App'
  secret: someVaultSecret

My Vault is running in HTTP mode, and not HTTPS and it's Web UI is running at 8200 port and OIDC auth server will run at 8250 port by default, hence the URLs. These redirect URIs are allowed redirect URIs that the client can request to redirect to - one is for Vault login through Vault CLI and another is for Vault login through Vault Web UI

config-dev.yaml will look like this as a whole -

# DEPRECATED: use config.yaml.dist and config.dev.yaml examples in the repository root.
# TODO: keep this until all references are updated.

# The base path of dex and the external name of the OpenID Connect service.
# This is the canonical URL that all clients MUST use to refer to dex. If a
# path is provided, dex's HTTP service will listen at a non-root URL.
issuer: http://127.0.0.1:5556/dex

# The storage configuration determines where dex stores its state. Supported
# options include SQL flavors and Kubernetes third party resources.
#
# See the documentation (https://dexidp.io/docs/storage/) for further information.
storage:
  type: sqlite3
  config:
    file: examples/dex.db

  # type: mysql
  # config:
  #   host: localhost
  #   port: 3306
  #   database: dex
  #   user: mysql
  #   password: mysql
  #   ssl:
  #     mode: "false"

  # type: postgres
  # config:
  #   host: localhost
  #   port: 5432
  #   database: dex
  #   user: postgres
  #   password: postgres
  #   ssl:
  #     mode: disable

  # type: etcd
  # config:
  #   endpoints:
  #     - http://localhost:2379
  #   namespace: dex/

  # type: kubernetes
  # config:
  #   kubeConfigFile: $HOME/.kube/config

# Configuration for the HTTP endpoints.
web:
  http: 0.0.0.0:5556
  # Uncomment for HTTPS options.
  # https: 127.0.0.1:5554
  # tlsCert: /etc/dex/tls.crt
  # tlsKey: /etc/dex/tls.key

# Configuration for dex appearance
# frontend:
#   issuer: dex
#   logoURL: theme/logo.png
#   dir: web/
#   theme: light

# Configuration for telemetry
telemetry:
  http: 0.0.0.0:5558
  # enableProfiling: true

# Uncomment this block to enable the gRPC API. This values MUST be different
# from the HTTP endpoints.
# grpc:
#   addr: 127.0.0.1:5557
#   tlsCert: examples/grpc-client/server.crt
#   tlsKey: examples/grpc-client/server.key
#   tlsClientCA: examples/grpc-client/ca.crt

# Uncomment this block to enable configuration for the expiration time durations.
# Is possible to specify units using only s, m and h suffixes.
# expiry:
#   deviceRequests: "5m"
#   signingKeys: "6h"
#   idTokens: "24h"
#   refreshTokens:
#     reuseInterval: "3s"
#     validIfNotUsedFor: "2160h" # 90 days
#     absoluteLifetime: "3960h" # 165 days

# Options for controlling the logger.
# logger:
#   level: "debug"
#   format: "text" # can also be "json"

# Default values shown below
# oauth2:
    # grantTypes determines the allowed set of authorization flows.
#   grantTypes:
#     - "authorization_code"
#     - "refresh_token"
#     - "implicit"
#     - "password"
#     - "urn:ietf:params:oauth:grant-type:device_code"
#     - "urn:ietf:params:oauth:grant-type:token-exchange"
    # responseTypes determines the allowed response contents of a successful authorization flow.
    # use ["code", "token", "id_token"] to enable implicit flow for web-only clients.
#   responseTypes: [ "code" ] # also allowed are "token" and "id_token"
    # By default, Dex will ask for approval to share data with application
    # (approval for sharing data from connected IdP to Dex is separate process on IdP)
#   skipApprovalScreen: false
    # If only one authentication method is enabled, the default behavior is to
    # go directly to it. For connected IdPs, this redirects the browser away
    # from application to upstream provider such as the Google login page
#   alwaysShowLoginScreen: false
    # Uncomment the passwordConnector to use a specific connector for password grants
#   passwordConnector: local

# Instead of reading from an external storage, use this list of clients.
#
# If this option isn't chosen clients may be added through the gRPC API.
staticClients:
- id: example-app
  redirectURIs:
  - 'http://127.0.0.1:5555/callback'
  name: 'Example App'
  secret: ZXhhbXBsZS1hcHAtc2VjcmV0
- id: vault
  redirectURIs:
  - 'http://localhost:8200/ui/vault/auth/oidc/oidc/callback'
  - 'http://localhost:8250/oidc/callback'
  name: 'Vault App'
  secret: someVaultSecret
#  - id: example-device-client
#    redirectURIs:
#      - /device/callback
#    name: 'Static Client for Device Flow'
#    public: true
connectors:
- type: mockCallback
  id: mock
  name: Example
# - type: google
#   id: google
#   name: Google
#   config:
#     issuer: https://accounts.google.com
#     # Connector config values starting with a "$" will read from the environment.
#     clientID: $GOOGLE_CLIENT_ID
#     clientSecret: $GOOGLE_CLIENT_SECRET
#     redirectURI: http://127.0.0.1:5556/dex/callback
#     hostedDomains:
#     - $GOOGLE_HOSTED_DOMAIN

# Let dex keep a list of passwords which can be used to login to dex.
enablePasswordDB: true

# A static list of passwords to login the end user. By identifying here, dex
# won't look in its underlying storage for passwords.
#
# If this option isn't chosen users may be added through the gRPC API.
staticPasswords:
- email: "admin@example.com"
  # bcrypt hash of the string "password": $(echo password | htpasswd -BinC 10 admin | cut -d: -f2)
  hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
  username: "admin"
  userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"

Note: This configuration yaml is deprecated according to the comments in the yaml file, but this is what I used for playing around. You can consider using the config.yaml.dist and config.dev.yaml examples in the repository root.

Now, run dex application with this modified configuration yaml file, like this

$ ./bin/dex serve examples/config-dev.yaml

It will look like this -

$ ./bin/dex serve examples/config-dev.yaml
time="2024-01-17T09:43:39Z" level=info msg="Dex Version: 5d64dc7a4c2358cad781b88768480af6643e8e26, Go Version: go1.21.5, Go OS/ARCH: darwin amd64"
time="2024-01-17T09:43:39Z" level=info msg="config issuer: http://127.0.0.1:5556/dex"
time="2024-01-17T09:43:39Z" level=info msg="config storage: sqlite3"
time="2024-01-17T09:43:39Z" level=info msg="config static client: Example App"
time="2024-01-17T09:43:39Z" level=info msg="config static client: Vault App"
time="2024-01-17T09:43:39Z" level=info msg="config connector: mock"
time="2024-01-17T09:43:39Z" level=info msg="config connector: local passwords enabled"
time="2024-01-17T09:43:39Z" level=info msg="config refresh tokens rotation enabled: true"
time="2024-01-17T09:43:39Z" level=info msg="listening (telemetry) on 0.0.0.0:5558"
time="2024-01-17T09:43:39Z" level=info msg="listening (http) on 0.0.0.0:5556"

Next, let's again switch gears โš™๏ธ and do some Vault stuff now. List policies in Vault

Next, create a Hashicorp Configuration Language (HCL) file to create a reader policy in Vault. We can use tee for this, or you can use echo or some text editor too, like vim or Visual Studio Code etc

$ tee reader.hcl <<EOF
# Read permission on the k/v secrets
path "/secret/*" {
    capabilities = ["read", "list"]
}
EOF

$ cat reader.hcl
# Read permission on the k/v secrets
path "/secret/*" {
    capabilities = ["read", "list"]
}

Next, let's create this policy in Vault. This policy will be attached by default to users who authenticate with Vault using Dex, so that they have read access - to read secret values and list secrets

$ vault policy write reader reader.hcl

On successful creation, it will look like this -

$ vault policy write reader reader.hcl
Success! Uploaded policy: reader

You can now list the policies to verify it's created

$ vault policy list

It will look like this -

$ vault policy list
default
reader
root

You can also read the policy to verify it

$ vault policy read reader

It will look like this -

$ vault policy read reader
# Read permission on the k/v secrets
path "/secret/*" {
    capabilities = ["read", "list"]
}

Now that we have created this Vault policy, let's do the configuration in Vault so that we can allow users to login to Vault / authenticate with Vault using OpenID Connect (OIDC) through Dex

First, enable OpenID Connect Authentication (Auth) Method in Vault like this -

$ vault auth enable oidc

It will look like this -

$ vault auth enable oidc
Success! Enabled oidc auth method at: oidc/

Next, let's configure this OpenID Connect (OIDC) Authentication Method in Vault, like this -

$ export OIDC_DOMAIN=http://127.0.0.1:5556/dex
$ export OIDC_CLIENT_ID=vault
$ export OIDC_CLIENT_SECRET=someVaultSecret

$ vault write auth/oidc/config \
         oidc_discovery_url="$OIDC_DOMAIN" \
         oidc_client_id="$OIDC_CLIENT_ID" \
         oidc_client_secret="$OIDC_CLIENT_SECRET" \
         default_role="reader"

It will look like this -

$ vault write auth/oidc/config \
         oidc_discovery_url="$OIDC_DOMAIN" \
         oidc_client_id="$OIDC_CLIENT_ID" \
         oidc_client_secret="$OIDC_CLIENT_SECRET" \
         default_role="reader"

Success! Data written to: auth/oidc/config

Next, configure a reader role in the OpenID Connect Authentication Method in Vault, like this -

$ vault write auth/oidc/role/reader \
      bound_audiences="$OIDC_CLIENT_ID" \
      allowed_redirect_uris="http://localhost:8200/ui/vault/auth/oidc/oidc/callback" \
      allowed_redirect_uris="http://localhost:8250/oidc/callback" \
      user_claim="sub" \
      token_policies="reader"

It will look like this -

$ vault write auth/oidc/role/reader \
      bound_audiences="$OIDC_CLIENT_ID" \
      allowed_redirect_uris="http://localhost:8200/ui/vault/auth/oidc/oidc/callback" \
      allowed_redirect_uris="http://localhost:8250/oidc/callback" \
      user_claim="sub" \
      token_policies="reader"

Success! Data written to: auth/oidc/role/reader

Next, let's login to Vault / authenticate with Vault using Dex, all from the CLI using Vault CLI. We will also login to Vault using Vault Web UI next

To login to Vault using Vault CLI, open a new terminal and run this

$ export VAULT_ADDR='http://127.0.0.1:8200'
$ vault login -method=oidc role="reader" port=8250

It opens up a browser with a link that takes you to the Dex page for logging in

The URL of the web page looks like this - http://127.0.0.1:5556/dex/auth?client_id=vault&code_challenge=XDDpdfW-iHyUq46SzS3-AhiwmgI-IPdYQ3mpNnVChzs&code_challenge_method=S256&nonce=n_iNF4xTj7oQHQiXngf5Sv&redirect_uri=http%3A%2F%2Flocalhost%3A8250%2Foidc%2Fcallback&response_type=code&scope=openid&state=st_zDd4ag15rQVKim15i4Bt

Next, you see a screen like the above and then you can click on Log in with Example or you can Log in with Email . I'm going to use Log in with Example for now and on clicking it, I see the below page

On succesfully doing everything and clicking Grant Access, you will see a success page like the below

On noticing the logs of the Vault CLI's login command, you will see something like this -

$ vault login -method=oidc role="reader" port=8250
Complete the login via your OIDC provider. Launching browser to:

    http://127.0.0.1:5556/dex/auth?client_id=vault&code_challenge=4tq4JNfaM1iJXZCnSR1cnu3zdWCMkVCF70-T8Piub-E&code_challenge_method=S256&nonce=n_3gYGHcjKbCSIn2O0k5Hz&redirect_uri=http%3A%2F%2Flocalhost%3A8250%2Foidc%2Fcallback&response_type=code&scope=openid&state=st_3Dol6CY9ye8LmEXpPdZL


Waiting for OIDC authentication to complete...
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                  Value
---                  -----
token                hvs.CAESIBU9tnRmt4OAuSGJOwy_qYtKxVJJHZIPZ6MltBhgXcHMGh4KHGh2cy5uZnpSYlFPZjIydVhQSlJ5OHJRNzI5Skw
token_accessor       omZp9R2hAL8O3uZg5ehjPLow
token_duration       768h
token_renewable      true
token_policies       ["default" "reader"]
identity_policies    []
policies             ["default" "reader"]
token_meta_role      reader

Notice how the user's token has default policy and reader policy too, and how other details are shown regarding the Vault token

That's all, that's how you authenticate to Vault using Dex. Let's also try Vault Web UI Authentication using Open ID Connect using Dex

First, go to http://localhost:8200/ and choose OIDC as Method, or go directly to http://localhost:8200/ui/vault/auth?with=oidc

Next, type reader in role name field's value

Finally click Sign in with OIDC Provider . It will show a popup window like this

Now choose Log in with Email or Log in with Example

Let's choose Log in with Example and everything would be good to go. You will see a request for Granting Access to Vault to get your information. Something like this

Once you click Grant Access, you should see the Vault Web UI, something like this -

So, yeah, that's it. That's how you authenticate with Vault using OpenID Connect (OIDC) using Dex

Finally, to checkout the token and it's capabilities, just do this -

Once you Copy token , take the token and go to the terminal where you were logged in as Root. Since we ran vault login in between, it would have overwridden the $HOME/.vault-token file, which contains the Vault token of not the root user, but of the new user logged in using Dex

Let's login to root user using root as the Vault login token

$ vault login

It will look like this -

Finally, let's look at the details about this token

$ vault token lookup hvs.CAESIPnvmVM-K8Lg7AYyItg_pBkY2QXbsRPBksFe3OYXY29QGh4KHGh2cy5Pd0ZudHdXdUNjVFQ3ZXF4aGljb0JFV2k

The output will look something similar to this -

$ vault token lookup hvs.CAESIPnvmVM-K8Lg7AYyItg_pBkY2QXbsRPBksFe3OYXY29QGh4KHGh2cy5Pd0ZudHdXdUNjVFQ3ZXF4aGljb0JFV2k
Key                  Value
---                  -----
accessor             y8Be17HEvkHJrcmKy229SMwZ
creation_time        1705486392
creation_ttl         768h
display_name         oidc-Cg0wLTM4NS0yODA4OS0wEgRtb2Nr
entity_id            02ce6133-a38a-f3c3-f68e-cae86191a3ee
expire_time          2024-02-18T15:43:12.986916+05:30
explicit_max_ttl     0s
id                   hvs.CAESIPnvmVM-K8Lg7AYyItg_pBkY2QXbsRPBksFe3OYXY29QGh4KHGh2cy5Pd0ZudHdXdUNjVFQ3ZXF4aGljb0JFV2k
issue_time           2024-01-17T15:43:12.798795+05:30
last_renewal         2024-01-17T15:45:38.986916+05:30
last_renewal_time    1705486538
meta                 map[role:reader]
num_uses             0
orphan               true
path                 auth/oidc/oidc/callback
policies             [default reader]
renewable            true
ttl                  767h51m57s
type                 service

Look at how it has reader and default Vault policies attached to it, which shows what level of access it has :)

That's all, that's how you work with Vault, OpenID Connect (OIDC) and Dex

ย