HashiCorp Vault Master Lab – Consul Secrets Engine + Userpass Authentication + Vault Policies + Dynamic Consul Credentials

Beginner-Friendly Hands-On Lab for Ubuntu Linux


1. Lab Objective

In this lab, we will build the following environment from scratch:

                    Ubuntu Linux Server
                           |
              +------------+-------------+
              |                          |
        HashiCorp Vault             HashiCorp Consul
          127.0.0.1:8200              127.0.0.1:8500
              |                          |
              |                          |
        userpass auth                    |
              |                          |
          student1                       |
              |                          |
     consul-engine-admin                 |
          Vault Policy                   |
              |                          |
              v                          |
      Consul Secrets Engine ------------+
              |
              |
      student-role
              |
              v
      Dynamic Consul Token
              |
              v
       Consul ACL Policy
          student-kv
              |
              v
        student/* KV

By the end of the lab, students will understand:

  • how to install Vault and Consul;
  • how to start Vault in development mode;
  • how to start Consul in development mode with ACLs;
  • how Consul ACL bootstrap works;
  • how to enable the Vault Consul secrets engine;
  • how Vault authenticates to Consul;
  • how to create a Consul ACL policy;
  • how to create a Vault ACL policy;
  • how to enable the Vault userpass authentication method;
  • how to create a Vault user;
  • how to attach a Vault policy to that user;
  • how to log in using username/password;
  • how to perform Create, Read, Update, List, and Delete operations on Consul secrets-engine roles;
  • how to generate short-lived Consul credentials;
  • how to use the dynamically generated Consul token;
  • how Vault leases work;
  • how to renew a lease;
  • how to revoke a lease;
  • how to confirm that the Consul credential stops working after revocation;
  • how Vault policies prevent access to unrelated secret engines.

The current Vault documentation is on the Vault 2.x release line. As of August 9, 2026, HashiCorp’s release notes list Vault 2.0.4, released August 4, 2026. The current Consul install material lists Consul 2.0.2.


2. Important Lab Warning

This tutorial intentionally uses:

Vault development mode
Consul development mode
HTTP instead of HTTPS
A known Vault root token
A locally stored Consul bootstrap token
A broad Vault policy

These choices make the environment easy for beginners to understand.

Do not use this architecture in production.

Vault development mode:

  • starts initialized;
  • starts unsealed;
  • stores its data in memory;
  • listens on 127.0.0.1:8200;
  • can use a predetermined root token;
  • loses its data when stopped.

HashiCorp explicitly identifies dev mode as an experimentation/development environment rather than a production deployment.


3. Lab Environment

Recommended environment:

Operating System : Ubuntu Server
Vault             : 2.0.4 or newer compatible 2.x
Consul            : 2.0.2 or newer compatible 2.x
Vault Address     : http://127.0.0.1:8200
Consul Address    : http://127.0.0.1:8500
Vault Root Token  : root
Vault User        : student1
Vault Password    : StudentLab@2026
Vault Policy      : consul-engine-admin
Vault Engine      : consul/
Consul Policy     : student-kv
Vault Consul Role : student-role
Consul KV Prefix  : student/

Everything in this tutorial runs on the same Ubuntu server.


4. Understanding the Two Different Policies

Before running commands, students should understand that this lab uses two completely different policy systems.

Vault Policy

Our Vault policy will be named:

consul-engine-admin

It controls what the Vault user student1 can do inside Vault.

For example:

Can student1 create a Vault Consul role?
Can student1 read the role?
Can student1 modify the role?
Can student1 delete the role?
Can student1 request a dynamic Consul token?

Vault policies map paths to capabilities such as:

create
read
update
delete
list

Vault’s current policy documentation maps these capabilities to operations on Vault paths.


Consul ACL Policy

Our Consul ACL policy will be named:

student-kv

This policy controls what a Consul token generated by Vault can do inside Consul.

We will allow the generated token to manage:

student/*

but not:

private/*
production/*
anything-else/*

A Consul write policy permits reading and modifying the matching resources.


5. Step 1 — Update Ubuntu

Run:

sudo apt update
sudo apt upgrade -y

Install utilities required during the lab:

sudo apt install -y \
  curl \
  wget \
  gpg \
  lsb-release \
  jq

Verify jq:

jq --version

Example:

jq-1.7

6. Step 2 — Add the Official HashiCorp Repository

Download the HashiCorp repository signing key:

wget -O- https://apt.releases.hashicorp.com/gpg \
  | sudo gpg --dearmor \
  -o /usr/share/keyrings/hashicorp-archive-keyring.gpg

Add the HashiCorp repository:

echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(grep -oP '(?<=UBUNTU_CODENAME=).*' /etc/os-release || lsb_release -cs) main" \
  | sudo tee /etc/apt/sources.list.d/hashicorp.list

Update the package list:

sudo apt update

HashiCorp publishes both Vault and Consul through its Ubuntu/Debian package repository.


7. Step 3 — Install Vault and Consul

Install both packages:

sudo apt install -y vault consul

8. Step 4 — Check Installed Versions

Run:

vault version

At the time this lab was validated against the release documentation, the current Vault release is:

Vault v2.0.4

Now check Consul:

consul version

Current Consul should be from the 2.0.x line, with the current install release being:

Consul v2.0.2

Consul’s version command is the supported way to inspect the installed version.


9. Step 5 — Create a Working Directory

Rather than scattering files around the server, create one directory for everything related to the lab.

Run:

mkdir -p "$HOME/vault-consul-lab"

Set an environment variable:

export LAB_DIR="$HOME/vault-consul-lab"

Verify:

echo "$LAB_DIR"

Expected:

/home/<your-user>/vault-consul-lab

Move into the directory:

cd "$LAB_DIR"

10. Step 6 — Configure Consul ACLs

Create:

cat > "$LAB_DIR/consul-lab.hcl" <<'EOF'
datacenter = "dc1"

acl = {
  enabled                  = true
  default_policy           = "deny"
  enable_token_persistence = true
}
EOF

Display the file:

cat "$LAB_DIR/consul-lab.hcl"

Expected:

datacenter = "dc1"

acl = {
  enabled                  = true
  default_policy           = "deny"
  enable_token_persistence = true
}

This enables the Consul ACL system and sets the default policy to deny. HashiCorp recommends deny for a new secured Consul environment.


11. Step 7 — Start Consul

First ensure another Consul process is not already running:

pgrep -a consul || true

For a fresh lab, there should normally be no output.

Start Consul:

nohup consul agent \
  -dev \
  -node=consul-lab \
  -config-file="$LAB_DIR/consul-lab.hcl" \
  > "$LAB_DIR/consul.log" 2>&1 &

Save the process ID:

echo $! > "$LAB_DIR/consul.pid"

Display the PID:

cat "$LAB_DIR/consul.pid"

Wait approximately two seconds:

sleep 2

Development mode runs an in-memory single-server Consul agent and is intended for rapid development and experimentation.


12. Step 8 — Verify Consul

Check the log:

tail -20 "$LAB_DIR/consul.log"

Check whether port 8500 is listening:

ss -lnt | grep 8500

You should see something similar to:

LISTEN ... 127.0.0.1:8500

Check the Consul leader:

curl -s http://127.0.0.1:8500/v1/status/leader

Expected output will resemble:

"127.0.0.1:8300"

The exact address may vary slightly.


13. Step 9 — Bootstrap the Consul ACL System

Consul ACLs are enabled, but no management token exists yet.

Run:

consul acl bootstrap -format=json \
  > "$LAB_DIR/consul-bootstrap.json"

Protect the file:

chmod 600 "$LAB_DIR/consul-bootstrap.json"

View the non-secret portions:

jq 'del(.SecretID)' "$LAB_DIR/consul-bootstrap.json"

The bootstrap command creates the initial token associated with Consul’s unrestricted global-management policy. Consul only permits the ACL bootstrap operation once for a given ACL state.


14. Step 10 — Capture the Consul Management Token

Extract the Secret ID:

export CONSUL_MGMT_TOKEN="$(
  jq -r '.SecretID' "$LAB_DIR/consul-bootstrap.json"
)"

Check that we captured something:

test -n "$CONSUL_MGMT_TOKEN" \
  && test "$CONSUL_MGMT_TOKEN" != "null" \
  && echo "Consul management token captured successfully."

Expected:

Consul management token captured successfully.

Do not print this token unnecessarily.

Configure the Consul CLI to use it:

export CONSUL_HTTP_TOKEN="$CONSUL_MGMT_TOKEN"

15. Step 11 — Test Consul Administrative Access

Run:

consul acl policy list

You should see built-in policies such as:

global-management
builtin/global-read-only

The exact displayed formatting may differ.


16. Step 12 — Create a Consul Policy for Vault-Generated Tokens

We do not want Vault to generate unrestricted Consul management tokens for students.

Instead, create a restricted Consul ACL policy.

Run:

cat > "$LAB_DIR/student-kv-policy.hcl" <<'EOF'
key_prefix "student/" {
  policy = "write"
}
EOF

Display it:

cat "$LAB_DIR/student-kv-policy.hcl"

Expected:

key_prefix "student/" {
  policy = "write"
}

This policy allows generated Consul tokens to read and modify keys underneath:

student/

Consul’s current ACL rules define write as permitting both read and modification operations.


17. Step 13 — Create the Consul ACL Policy

Run:

consul acl policy create \
  -name="student-kv" \
  -description="Vault generated tokens may manage student KV data" \
  -rules=@"$LAB_DIR/student-kv-policy.hcl"

A successful response will contain fields similar to:

ID:
Name:        student-kv
Description: Vault generated tokens may manage student KV data
Rules:
key_prefix "student/" {
  policy = "write"
}

Consul’s current CLI supports loading ACL policy rules from a file by prefixing the filename with @.


18. Step 14 — Read the Consul Policy

Run:

consul acl policy read -name="student-kv"

Verify that you see:

Name: student-kv

and:

key_prefix "student/" {
  policy = "write"
}

At this point the Consul side of the permission model exists.


19. Step 15 — Start Vault

Before starting Vault, check whether another Vault process exists:

pgrep -a vault || true

Start the Vault development server:

nohup vault server \
  -dev \
  -dev-root-token-id="root" \
  -dev-listen-address="127.0.0.1:8200" \
  > "$LAB_DIR/vault.log" 2>&1 &

Save the PID:

echo $! > "$LAB_DIR/vault.pid"

Wait:

sleep 2

HashiCorp’s current Vault server CLI supports -dev, -dev-root-token-id, and -dev-listen-address.


20. Step 16 — Configure the Vault CLI

Set the Vault server address:

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

Set the root token:

export VAULT_TOKEN='root'

Confirm:

echo "$VAULT_ADDR"

Expected:

http://127.0.0.1:8200

21. Step 17 — Verify Vault

Run:

vault status

Expected output should contain:

Initialized       true
Sealed            false

Because this is a development server, Vault starts initialized and unsealed automatically.


22. Step 18 — Confirm Root Access

Run:

vault token lookup

You should see:

policies    [root]

The root policy gives unrestricted access and should only be used for the administrative setup section of this lab. Root tokens have unrestricted Vault privileges.


23. Step 19 — Check Existing Secrets Engines

Run:

vault secrets list

You will see several built-in mounts.

The important point is that:

consul/

should not exist yet.


24. Step 20 — Enable the Consul Secrets Engine

Run:

vault secrets enable consul

Expected:

Success! Enabled the consul secrets engine at: consul/

Verify:

vault secrets list

You should now find:

consul/

The supported command for enabling the secrets engine at its default mount is:

vault secrets enable consul

25. Step 21 — Configure Vault to Communicate with Consul

Vault now has the plugin, but the plugin does not yet know:

Where is Consul?
What credential should Vault use to create Consul tokens?

Configure it:

vault write consul/config/access \
  address="127.0.0.1:8500" \
  scheme="http" \
  token="$CONSUL_MGMT_TOKEN"

Expected:

Success! Data written to: consul/config/access

Vault’s Consul secrets engine requires the Consul address and, for an already-bootstrapped Consul ACL environment, a Consul management token that Vault can use to create and revoke ACL tokens.


26. Step 22 — Stop Using the Consul Management Token Directly

This is an important lab practice.

Vault now has the credential it needs.

Remove the management credential from the active shell:

unset CONSUL_HTTP_TOKEN
unset CONSUL_MGMT_TOKEN

Verify:

env | grep CONSUL_HTTP_TOKEN || true

There should be no output.

From this point onward, we want students to work with credentials generated by Vault, not with the powerful Consul bootstrap credential.


27. Step 23 — Create the Vault Policy

Now create the policy that will eventually be attached to our Vault userpass user.

Run:

cat > "$LAB_DIR/consul-engine-admin.hcl" <<'EOF'
# Full CRUD-style access to everything underneath the
# Consul secrets engine mount.
#
# LAB ONLY: This includes sensitive Consul engine configuration.

path "consul/*" {
  capabilities = [
    "create",
    "read",
    "update",
    "delete",
    "list"
  ]
}

# Allow looking up an individual dynamic-secret lease.

path "sys/leases/lookup" {
  capabilities = ["update"]
}

# Allow renewal of an individual lease.

path "sys/leases/renew" {
  capabilities = ["update"]
}

path "sys/leases/renew/*" {
  capabilities = ["update"]
}

# Allow revocation of an individual lease.

path "sys/leases/revoke" {
  capabilities = ["update"]
}

path "sys/leases/revoke/*" {
  capabilities = ["update"]
}
EOF

28. Step 24 — Examine the Vault Policy

Run:

cat "$LAB_DIR/consul-engine-admin.hcl"

The central section is:

path "consul/*" {
  capabilities = [
    "create",
    "read",
    "update",
    "delete",
    "list"
  ]
}

This means:

CREATE  -> permitted
READ    -> permitted
UPDATE  -> permitted
DELETE  -> permitted
LIST    -> permitted

Vault policies are path based, and these capabilities map to the corresponding operations supported by a Vault endpoint.


29. Step 25 — Load the Policy into Vault

Run:

vault policy write \
  consul-engine-admin \
  "$LAB_DIR/consul-engine-admin.hcl"

Expected:

Success! Uploaded policy: consul-engine-admin

Vault supports loading an HCL ACL policy using vault policy write.


30. Step 26 — Read the Vault Policy

Run:

vault policy read consul-engine-admin

Verify the policy is displayed correctly.


31. Step 27 — List Vault Policies

Run:

vault policy list

You should see something similar to:

consul-engine-admin
default
root

32. Step 28 — Enable Userpass Authentication

Now enable username/password authentication.

Run:

vault auth enable userpass

Expected:

Success! Enabled userpass auth method at: userpass/

Verify:

vault auth list

You should find:

userpass/

Vault’s current documentation supports enabling the method with exactly:

vault auth enable userpass

33. Step 29 — Create One Vault User

Create:

Username: student1
Password: StudentLab@2026
Policy:   consul-engine-admin

Run:

vault write auth/userpass/users/student1 \
  password='StudentLab@2026' \
  policies='consul-engine-admin' \
  token_ttl='1h' \
  token_max_ttl='4h'

Expected:

Success! Data written to: auth/userpass/users/student1

Vault’s userpass configuration associates users with policies, and successful authentication produces a Vault token carrying those policies.


34. Step 30 — Read the User Configuration

Still using the root token, run:

vault read auth/userpass/users/student1

You should see information including:

token_policies
token_ttl
token_max_ttl

The password itself will not be returned.

The current Userpass API exposes reading user configuration but does not reveal the stored password.


35. Step 31 — Administrator Setup Is Complete

At this point root has completed:

Consul installation
Consul ACL setup
Consul ACL policy creation
Vault installation
Vault Consul secrets engine
Vault-to-Consul connection
Vault policy
Userpass authentication
Vault user creation

Now we switch identities.

Everything from the next step should be performed as:

student1

36. Step 32 — Remove Root from the Current Shell

This step is extremely important.

Run:

unset VAULT_TOKEN

Verify:

echo "${VAULT_TOKEN:-VAULT_TOKEN is not set}"

Expected:

VAULT_TOKEN is not set

If students accidentally leave:

VAULT_TOKEN=root

then their tests are meaningless because every command continues to execute as root.


37. Step 33 — Login Using Userpass

Run:

vault login -method=userpass \
  username='student1' \
  password='StudentLab@2026'

Expected output will contain something similar to:

token                 <generated-vault-token>
token_duration        1h
token_renewable       true
token_policies        ["consul-engine-admin" "default"]

The precise token will be different.

The supported CLI authentication form is:

vault login -method=userpass username=... password=...

38. Step 34 — Verify the Current Vault Identity

Run:

vault token lookup

Check:

display_name
policies
ttl

The policies should include:

consul-engine-admin
default

They should not include:

root

This is a critical checkpoint.


39. Step 35 — Verify the Student’s Vault Capabilities

Run:

vault token capabilities consul/roles/student-role

The output should contain capabilities similar to:

create, delete, list, read, update

Now check the credential endpoint:

vault token capabilities consul/creds/student-role

Again, the policy allows access.

Now test an unrelated Vault engine:

vault token capabilities secret/data/my-secret

Expected:

deny

This proves that the student is not a Vault administrator.

They have permissions on:

consul/*

but not arbitrary Vault paths.


40. Step 36 — First Negative Security Test

Try writing into Vault’s development KV engine:

vault kv put secret/not-allowed \
  username=student \
  password=test

This should fail with a permission error similar to:

permission denied

This failure is correct.

The student’s policy only gives access to:

consul/*

This demonstrates authorization working correctly.


41. Step 37 — Understand Vault Consul Roles

The Vault Consul secrets engine uses roles.

Think of a Vault role as a template that tells Vault:

When someone requests Consul credentials from this role, which Consul policy should be attached to the generated token and how long should the token live?

Our role will be:

student-role

and it will reference the Consul policy:

student-kv

Current Vault Consul API documentation supports creating/updating roles with consul_policies, assigning TTLs, reading roles, listing them, deleting them, and generating credentials from them.


42. Step 38 — CREATE a Consul Secrets-Engine Role

Run as student1:

vault write consul/roles/student-role \
  consul_policies='student-kv' \
  ttl='30m' \
  max_ttl='1h'

Expected:

Success! Data written to: consul/roles/student-role

We have just performed the CREATE operation.


43. Step 39 — READ the Role

Run:

vault read consul/roles/student-role

You should see fields corresponding to the role, including its Consul policy and TTL configuration.

This is the READ operation.


44. Step 40 — LIST the Roles

Run:

vault list consul/roles

Expected:

Keys
----
student-role

This is the LIST operation.

The Consul secrets engine explicitly supports LIST /consul/roles.


45. Step 41 — UPDATE the Role

Originally:

TTL     = 30 minutes
Max TTL = 1 hour

Change it to:

TTL     = 15 minutes
Max TTL = 30 minutes

Run:

vault write consul/roles/student-role \
  consul_policies='student-kv' \
  ttl='15m' \
  max_ttl='30m'

Expected:

Success! Data written to: consul/roles/student-role

This demonstrates UPDATE.


46. Step 42 — Verify the Update

Run:

vault read consul/roles/student-role

Verify the new TTL settings.


47. Step 43 — DELETE the Role

Run:

vault delete consul/roles/student-role

Expected:

Success! Data deleted (if it existed) at: consul/roles/student-role

This demonstrates DELETE.

The secrets engine’s current API supports DELETE /consul/roles/:name.


48. Step 44 — Confirm the Role Is Gone

Run:

vault list consul/roles

You may receive:

No value found at consul/roles

Alternatively, if other roles exist, student-role should be absent.


49. Step 45 — Recreate the Role

We need the role for the dynamic-credential portion of the lab.

Run:

vault write consul/roles/student-role \
  consul_policies='student-kv' \
  ttl='30m' \
  max_ttl='1h'

Verify:

vault read consul/roles/student-role

50. CRUD Checkpoint

Students have now successfully demonstrated:

CREATE
vault write consul/roles/student-role ...

READ
vault read consul/roles/student-role

UPDATE
vault write consul/roles/student-role ...

LIST
vault list consul/roles

DELETE
vault delete consul/roles/student-role

This is the core Vault ACL policy exercise.


51. Step 46 — Generate a Dynamic Consul Credential

Now request a Consul token.

Run:

vault read consul/creds/student-role

Vault should return information resembling:

Key                 Value
---                 -----
lease_id            consul/creds/student-role/<unique-id>
lease_duration      30m
lease_renewable     true
token               <dynamic-consul-token>

Exact fields and values may vary.

Every request creates a new Consul ACL token.

The current Consul secrets-engine credential endpoint is a read operation on:

consul/creds/:role

even though that read causes Vault to create a new credential. This is why the Vault policy requires read capability on that path.


52. Step 47 — Capture the Dynamic Credential Safely

Instead of manually copying the token, request JSON:

CREDS_JSON="$(
  vault read -format=json consul/creds/student-role
)"

Capture the Vault lease ID:

export LEASE_ID="$(
  printf '%s' "$CREDS_JSON" \
  | jq -r '.lease_id'
)"

Capture the generated Consul token:

export DYNAMIC_CONSUL_TOKEN="$(
  printf '%s' "$CREDS_JSON" \
  | jq -r '.data.token'
)"

Remove the JSON variable:

unset CREDS_JSON

53. Step 48 — Verify Variables Were Captured

Check the lease ID:

echo "$LEASE_ID"

It should resemble:

consul/creds/student-role/<unique-id>

Do not print the Consul token unnecessarily.

Instead verify that it exists:

test -n "$DYNAMIC_CONSUL_TOKEN" \
  && test "$DYNAMIC_CONSUL_TOKEN" != "null" \
  && echo "Dynamic Consul token captured successfully."

Expected:

Dynamic Consul token captured successfully.

54. Step 49 — Use the Vault-Generated Consul Token

We now use the generated token directly against Consul.

Write a KV value:

CONSUL_HTTP_TOKEN="$DYNAMIC_CONSUL_TOKEN" \
  consul kv put \
  student/application/database \
  "training-database"

Expected:

Success! Data written to: student/application/database

Depending on the Consul CLI version, successful KV writes may instead display:

Success! Data written

or:

true

The important result is success.


55. Step 50 — Read the Consul KV Value

Run:

CONSUL_HTTP_TOKEN="$DYNAMIC_CONSUL_TOKEN" \
  consul kv get student/application/database

Expected:

training-database

The Vault-generated Consul token works.


56. Step 51 — Update the Consul KV Value

Run:

CONSUL_HTTP_TOKEN="$DYNAMIC_CONSUL_TOKEN" \
  consul kv put \
  student/application/database \
  "production-style-training-value"

Read it:

CONSUL_HTTP_TOKEN="$DYNAMIC_CONSUL_TOKEN" \
  consul kv get student/application/database

Expected:

production-style-training-value

57. Step 52 — List Student Keys

Run:

CONSUL_HTTP_TOKEN="$DYNAMIC_CONSUL_TOKEN" \
  consul kv get -recurse student/

You should see the values underneath:

student/

A token with Consul write access on the matching prefix also has read/list functionality for that prefix.


58. Step 53 — Prove the Consul Token Is Restricted

Now try writing outside the permitted prefix:

CONSUL_HTTP_TOKEN="$DYNAMIC_CONSUL_TOKEN" \
  consul kv put \
  production/database/password \
  "should-not-work"

This should fail with an ACL/permission error.

That failure is correct.

Why?

Because the dynamically generated token has:

key_prefix "student/" {
  policy = "write"
}

It does not have:

key_prefix "production/" {
  policy = "write"
}

This demonstrates the difference between:

Vault authorization

and:

Consul authorization

59. Step 54 — Create a Value for the Lease Test

Create another key:

CONSUL_HTTP_TOKEN="$DYNAMIC_CONSUL_TOKEN" \
  consul kv put \
  student/lease-test \
  "credential-is-currently-valid"

Verify:

CONSUL_HTTP_TOKEN="$DYNAMIC_CONSUL_TOKEN" \
  consul kv get student/lease-test

Expected:

credential-is-currently-valid

Keep this key in Consul.

We will use it to prove credential revocation.


60. Step 55 — Understand Vault Leases

Dynamic secrets issued by Vault have leases.

Our lease ID looks similar to:

consul/creds/student-role/ABC123...

The lease tells Vault:

which dynamic secret was issued
when it was issued
how long it should live
whether it can be renewed
how Vault should revoke it

Vault’s lease system tracks dynamic secrets and automatically revokes them when their lease expires.


61. Step 56 — Lookup the Lease

Run:

vault lease lookup "$LEASE_ID"

Look for fields such as:

issue_time
expire_time
last_renewal_time
ttl

62. Step 57 — Renew the Dynamic Credential

Run:

vault lease renew \
  -increment=10m \
  "$LEASE_ID"

Expected output resembles:

lease_id          consul/creds/student-role/...
lease_duration    ...
lease_renewable   true

The requested increment is advisory; the backend may apply TTL limits configured by the role or mount.


63. Step 58 — Confirm the Token Still Works

Run:

CONSUL_HTTP_TOKEN="$DYNAMIC_CONSUL_TOKEN" \
  consul kv get student/lease-test

Expected:

credential-is-currently-valid

The token is still active.


64. Step 59 — Revoke the Dynamic Credential

Now manually revoke its Vault lease.

Run:

vault lease revoke \
  -sync \
  "$LEASE_ID"

Expected:

Success! Revoked lease: consul/creds/student-role/...

Using -sync makes the revocation synchronous rather than merely queueing the work in the background. The current Vault CLI exposes this option on vault lease revoke.


65. Step 60 — Prove the Consul Credential Was Revoked

Try the same Consul command using the old token:

CONSUL_HTTP_TOKEN="$DYNAMIC_CONSUL_TOKEN" \
  consul kv get student/lease-test

This should now fail with a Consul ACL/token error.

This failure is the expected result.

The sequence proves:

Vault generated the token
        ↓
Token worked in Consul
        ↓
Vault tracked it using a lease
        ↓
Vault revoked the lease
        ↓
Vault revoked the Consul token
        ↓
Old Consul token stopped working

Vault’s Consul secrets engine requires management-level Consul access precisely so that it can create and revoke these generated ACL tokens.


66. Step 61 — Request Another Dynamic Credential

Run:

vault read consul/creds/student-role

You will receive:

a different lease ID
a different Consul token

This demonstrates one of the major benefits of dynamic credentials:

Applications do not need to share one permanent Consul password/token.

New credentials can be requested when required and expired/revoked afterward.


67. Complete Authentication and Authorization Flow

Students should now understand this entire chain:

1. student1 supplies username/password
                     |
                     v
2. Vault userpass authenticates student1
                     |
                     v
3. Vault issues student1 a Vault token
                     |
                     v
4. Vault token contains consul-engine-admin policy
                     |
                     v
5. Policy permits operations against consul/*
                     |
                     v
6. student1 requests consul/creds/student-role
                     |
                     v
7. Vault reads student-role
                     |
                     v
8. student-role references Consul policy student-kv
                     |
                     v
9. Vault authenticates to Consul using its management token
                     |
                     v
10. Vault asks Consul to create a new ACL token
                     |
                     v
11. Consul attaches student-kv to that token
                     |
                     v
12. Vault returns the short-lived token to student1
                     |
                     v
13. Token can access student/*
                     |
                     v
14. Vault tracks the credential using a lease
                     |
                     v
15. Lease expiration/revocation causes the Consul token to be revoked

68. Vault CRUD Summary

CREATE

vault write consul/roles/student-role \
  consul_policies='student-kv' \
  ttl='30m' \
  max_ttl='1h'

READ

vault read consul/roles/student-role

UPDATE

vault write consul/roles/student-role \
  consul_policies='student-kv' \
  ttl='15m' \
  max_ttl='30m'

LIST

vault list consul/roles

DELETE

vault delete consul/roles/student-role

GENERATE DYNAMIC SECRET

vault read consul/creds/student-role

69. Userpass Command Summary

Enable Userpass

vault auth enable userpass

Create User

vault write auth/userpass/users/student1 \
  password='StudentLab@2026' \
  policies='consul-engine-admin' \
  token_ttl='1h' \
  token_max_ttl='4h'

Read User

Root/admin only in this lab:

vault read auth/userpass/users/student1

Login

vault login -method=userpass \
  username='student1' \
  password='StudentLab@2026'

The current userpass API also supports reading users, updating users/passwords/policies, listing users, and deleting them.


70. Consul Secrets Engine Command Summary

Enable Engine

vault secrets enable consul

Configure Connection

vault write consul/config/access \
  address="127.0.0.1:8500" \
  scheme="http" \
  token="<CONSUL-MANAGEMENT-TOKEN>"

Create Role

vault write consul/roles/student-role \
  consul_policies='student-kv' \
  ttl='30m' \
  max_ttl='1h'

Read Role

vault read consul/roles/student-role

List Roles

vault list consul/roles

Delete Role

vault delete consul/roles/student-role

Generate Credential

vault read consul/creds/student-role

These paths match the current Vault Consul secrets-engine API: configuration at consul/config/access, role management at consul/roles/:name, and credential generation at consul/creds/:name.


71. Vault Policy Used in This Lab

Complete file:

path "consul/*" {
  capabilities = [
    "create",
    "read",
    "update",
    "delete",
    "list"
  ]
}

path "sys/leases/lookup" {
  capabilities = ["update"]
}

path "sys/leases/renew" {
  capabilities = ["update"]
}

path "sys/leases/renew/*" {
  capabilities = ["update"]
}

path "sys/leases/revoke" {
  capabilities = ["update"]
}

path "sys/leases/revoke/*" {
  capabilities = ["update"]
}

72. Why This Vault Policy Is Intentionally Broad

The requirement for this particular lab is:

Give one user complete CRUD-style access to the Consul secrets engine.

Therefore:

path "consul/*"

is intentionally broad.

It also means the user could potentially change sensitive configuration underneath the Consul engine.

That makes this policy appropriate for:

training
labs
experimentation
administrative demonstrations

but usually not appropriate for a normal application in production.


73. Safer Production-Style Vault Policy

After students understand the broad policy, show them this safer version:

# Allow role management.

path "consul/roles" {
  capabilities = ["list"]
}

path "consul/roles/*" {
  capabilities = [
    "create",
    "read",
    "update",
    "delete"
  ]
}

# Allow credential generation.

path "consul/creds/*" {
  capabilities = ["read"]
}

# Do NOT give the user access to:
#
# consul/config/access

This illustrates an important Vault security principle:

Give the identity only the permissions it actually requires.

74. Student Verification Checklist

A student has successfully completed the lab when all of these conditions are true:

[ ] vault version works

[ ] consul version works

[ ] Consul listens on 127.0.0.1:8500

[ ] Consul ACL system has been bootstrapped

[ ] Consul policy student-kv exists

[ ] Vault listens on 127.0.0.1:8200

[ ] Vault is initialized

[ ] Vault is unsealed

[ ] consul/ secrets engine exists

[ ] Vault can communicate with Consul

[ ] consul-engine-admin Vault policy exists

[ ] userpass auth method exists

[ ] student1 exists

[ ] student1 can login

[ ] student1 receives consul-engine-admin

[ ] student1 does not have root policy

[ ] student1 cannot write to secret/

[ ] student1 can create student-role

[ ] student1 can read student-role

[ ] student1 can list roles

[ ] student1 can update student-role

[ ] student1 can delete student-role

[ ] student1 can generate a Consul token

[ ] generated token can write student/*

[ ] generated token can read student/*

[ ] generated token cannot write production/*

[ ] generated credential has a Vault lease

[ ] lease can be renewed

[ ] lease can be revoked

[ ] revoked Consul token stops working

75. Troubleshooting — Vault Connection Refused

Error:

connect: connection refused

Check:

pgrep -a vault

Check port:

ss -lnt | grep 8200

Check log:

tail -50 "$LAB_DIR/vault.log"

Check:

echo "$VAULT_ADDR"

It should be:

http://127.0.0.1:8200

76. Troubleshooting — Consul Connection Refused

Check process:

pgrep -a consul

Check port:

ss -lnt | grep 8500

Check log:

tail -50 "$LAB_DIR/consul.log"

Check leader:

curl -s http://127.0.0.1:8500/v1/status/leader

77. Troubleshooting — Consul ACL Bootstrap Fails

If:

consul acl bootstrap

reports that ACL bootstrapping has already occurred, you probably still have an existing Consul development server/state.

For this disposable lab, stop Consul:

kill "$(cat "$LAB_DIR/consul.pid")"

Confirm:

pgrep -a consul || true

Then restart the clean Consul development lab.

A Consul ACL environment can normally be bootstrapped only once.


78. Troubleshooting — Vault Permission Denied

Check which token you are using:

vault token lookup

Check policies:

vault token lookup -format=json \
  | jq '.data.policies'

Expected for the student:

[
  "consul-engine-admin",
  "default"
]

Check capability:

vault token capabilities consul/roles/student-role

79. Troubleshooting — Student Accidentally Still Has Root Access

Check:

echo "${VAULT_TOKEN:-not-set}"

If you see:

root

then:

unset VAULT_TOKEN

Login again:

vault login -method=userpass \
  username='student1' \
  password='StudentLab@2026'

Then:

vault token lookup

Make sure:

root

is absent from the policies.


80. Troubleshooting — Dynamic Consul Credential Cannot Be Generated

Check that the role exists:

vault read consul/roles/student-role

Check the Consul policy name:

student-kv

Check Vault’s logs:

tail -50 "$LAB_DIR/vault.log"

Check Consul logs:

tail -50 "$LAB_DIR/consul.log"

Typical causes are:

Vault cannot reach Consul
Vault has an invalid Consul management token
student-kv Consul policy does not exist
student-role is incorrectly configured

81. Troubleshooting — Generated Token Cannot Access student/

Make sure you are actually supplying the generated token:

test -n "$DYNAMIC_CONSUL_TOKEN" \
  && echo "Token variable exists."

Then:

CONSUL_HTTP_TOKEN="$DYNAMIC_CONSUL_TOKEN" \
  consul kv put student/test "hello"

If that fails, verify that the Consul role was created with:

consul_policies=student-kv

82. Troubleshooting — Wrong Password Entered Several Times

Vault includes user-lockout behavior for supported authentication methods including userpass.

Repeated failed authentication attempts can temporarily result in immediate permission-denied responses.

For a classroom environment, encourage students to copy the lab password correctly:

StudentLab@2026

rather than repeatedly guessing it.


83. Cleanup

Because both servers are operating in development mode, cleanup is simple.

Remove sensitive shell variables:

unset DYNAMIC_CONSUL_TOKEN
unset LEASE_ID
unset CONSUL_HTTP_TOKEN
unset CONSUL_MGMT_TOKEN
unset VAULT_TOKEN

Stop Vault:

kill "$(cat "$LAB_DIR/vault.pid")" 2>/dev/null || true

Stop Consul:

kill "$(cat "$LAB_DIR/consul.pid")" 2>/dev/null || true

Verify:

pgrep -a vault || true

and:

pgrep -a consul || true

Remove the lab files:

rm -rf "$LAB_DIR"

Optionally remove the locally cached Vault CLI token created by vault login:

rm -f "$HOME/.vault-token"

Unset:

unset LAB_DIR
unset VAULT_ADDR

Because development mode uses in-memory state, stopping the servers destroys the lab’s Vault and Consul runtime data.


84. What Students Should Remember

Authentication

Who are you?

In this lab:

userpass
username = student1
password = StudentLab@2026

Vault Authorization

What are you allowed to do in Vault?

Controlled by:

consul-engine-admin

Vault Consul Role

What type of Consul credential should Vault generate?

Controlled by:

student-role

Consul Authorization

What can the generated token do inside Consul?

Controlled by:

student-kv

Dynamic Secret

What credential did Vault dynamically create?

Answer:

A short-lived Consul ACL token.

Lease

How does Vault track the lifecycle of that credential?

Answer:

Using a Vault lease.

85. Final Architecture

                    +----------------------+
                    |       student1       |
                    +----------+-----------+
                               |
                               | username/password
                               v
                    +----------------------+
                    |   Vault Userpass     |
                    +----------+-----------+
                               |
                               | authenticates
                               v
                    +----------------------+
                    |    Vault Token       |
                    |                      |
                    | consul-engine-admin  |
                    +----------+-----------+
                               |
                               | permits access
                               v
                    +----------------------+
                    |  Consul Secret       |
                    |  Engine: consul/     |
                    +----------+-----------+
                               |
                               | uses
                               v
                    +----------------------+
                    |   student-role       |
                    |                      |
                    | Consul policy:       |
                    | student-kv           |
                    +----------+-----------+
                               |
                               | Vault talks to Consul
                               v
                    +----------------------+
                    |      Consul          |
                    |     ACL System       |
                    +----------+-----------+
                               |
                               | dynamically creates
                               v
                    +----------------------+
                    | Dynamic Consul Token |
                    +----------+-----------+
                               |
                               | student-kv
                               v
                    +----------------------+
                    | Consul KV            |
                    |                      |
                    | student/*  -> ALLOW  |
                    | other/*    -> DENY   |
                    +----------------------+

86. Master Lab Success

If the entire lab works correctly, the following sequence has been proven:

Ubuntu
  ↓
Vault
  ↓
userpass
  ↓
student1
  ↓
Vault Policy
  ↓
Consul Secrets Engine
  ↓
Vault Consul Role
  ↓
Dynamic Consul ACL Token
  ↓
Restricted Consul Permissions
  ↓
Vault Lease
  ↓
Renew
  ↓
Revoke
  ↓
Credential becomes invalid

That gives students an end-to-end introduction to Vault authentication, Vault authorization, the Consul dynamic secrets engine, Consul ACL authorization, dynamic credentials, TTLs, leases, renewal, and revocation in one coherent exercise.


Validation Notes

This guide has been syntax-checked against the current official Vault 2.x and Consul 2.0.x documentation, including the current Userpass API, Consul secrets-engine API, Vault ACL capability model, Consul ACL bootstrap and policy commands, and Vault lease renew/revoke commands. Vault’s current release notes identify 2.0.4 as the August 4, 2026 fix release.

The lab intentionally uses only currently documented fields such as consul_policies; older examples using legacy base64 policy, older Consul token types, or deprecated role fields have been avoided. Vault’s current Consul API specifically identifies older policy and policies role parameters as deprecated in favor of the current Consul-policy model.

The commands have been validated against the current documentation rather than executed on your particular Ubuntu host, so the first run on your server should also be treated as the final environment-specific runtime validation.

Related Posts

Go Tutorials: Go Methods

Go methods become much easier once you understand one idea: A method is simply a function attached to a type. And the important rule is: A receiver…

Read More

Go Tutorials: Modules, Packages, Subpackages, Submodules, and Workspaces

This tutorial gives you one complete mental model for: The most important idea is: And sometimes: These two designs are very different. 1. What is a Go…

Read More

Go Tutorials: Numeric Types Beginner Tutorial

Go has four main families of numeric types: The simplest way to remember them is: Type family Stores Example int Whole numbers, positive or negative -10, 0,…

Read More

Go Tutorials: Go Testing, Benchmarking, and Profiling

These three topics become much easier once you separate the questions they answer: Topic Main question Main Go tool Result Testing Does my code work correctly? go…

Read More

Go Tutorials: Generics

1. What are Generics? Generics let you write one piece of code that works with multiple types while keeping Go’s compile-time type safety. Suppose you want a…

Read More

Go Concurrency Management Made Simple

This tutorial covers: The goal is: One concept → one purpose → one complete runnable example. Every example is independent. Save any example as: and run: 0….

Read More