Integrating HashiCorp Vault with an existing LDAP system such as Active Directory is a convenient way to manage user authentication and authorization. Follow along below for an example of setting this up.
Note, I am piping curl output to jq for better formatting. Check it out here.
Updated - check the updates at the bottom of the post for a briefer setup.
Enable the LDAP Auth Method
API:
1
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request POST http://localhost:8200/v1/sys/auth/ldap --data '{"type":"ldap","description":"Login with LDAP"}'
|
Confirm
1
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request GET http://localhost:8200/v1/sys/auth | jq
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
{
"token/": {
"accessor": "auth_token_62e1836c",
"config": {
"default_lease_ttl": 0,
"force_no_cache": false,
"max_lease_ttl": 0,
"token_type": "default-service"
},
"description": "token based credentials",
"local": false,
"options": null,
"seal_wrap": false,
"type": "token",
"uuid": "19661fc1-15e2-05c6-492e-445480a3fd4a"
},
"ldap/": {
"accessor": "auth_ldap_e620cbc4",
"config": {
"default_lease_ttl": 0,
"force_no_cache": false,
"max_lease_ttl": 0,
"token_type": "default-service"
},
"description": "Login with LDAP",
"local": false,
"options": {},
"seal_wrap": false,
"type": "ldap",
"uuid": "001b8deb-52f2-72b6-edbe-5a15e06c9d8d"
},
"request_id": "5afff2e8-5a27-02e9-cd66-c1b567b79368",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"ldap/": {
"accessor": "auth_ldap_e620cbc4",
"config": {
"default_lease_ttl": 0,
"force_no_cache": false,
"max_lease_ttl": 0,
"token_type": "default-service"
},
"description": "Login with LDAP",
"local": false,
"options": {},
"seal_wrap": false,
"type": "ldap",
"uuid": "001b8deb-52f2-72b6-edbe-5a15e06c9d8d"
},
"token/": {
"accessor": "auth_token_62e1836c",
"config": {
"default_lease_ttl": 0,
"force_no_cache": false,
"max_lease_ttl": 0,
"token_type": "default-service"
},
"description": "token based credentials",
"local": false,
"options": null,
"seal_wrap": false,
"type": "token",
"uuid": "19661fc1-15e2-05c6-492e-445480a3fd4a"
}
},
"wrap_info": null,
"warnings": null,
"auth": null
}
|
Reference: https://www.vaultproject.io/api/system/auth.html#enable-auth-method
CLI:
Confirm:
1
2
3
4
5
6
|
vault auth list
Path Type Accessor Description
---- ---- -------- -----------
ldap/ ldap auth_ldap_e620cbc4 Login with LDAP
token/ token auth_token_62e1836c token based credentials
|
Reference: https://www.vaultproject.io/docs/auth/index.html#auth-methods
Note, there is an open issue regarding the LDAP auth method at the time of writing, which impacts group searching. To workaround the problem, do not define the upndomain attribute. Refer to the GitHub issue here: https://github.com/hashicorp/vault/issues/6325
API:
1
2
3
4
5
6
7
8
9
10
11
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request POST http://localhost:8200/v1/auth/ldap/config --data @- <<EOF
{
"url":"ldap://ec2-3-106-146-38.ap-southeast-2.compute.amazonaws.com:389",
"binddn":"CN=svc_vault_bind,OU=Service Accounts,DC=vaulttest,DC=internal",
"bindpass":"pass@word1",
"userdn":"OU=Vault Users,DC=vaulttest,DC=internal",
"userattr":"sAMAccountName",
"upndomain":"vaulttest.internal",
"groupdn":"OU=Security Groups,DC=vaulttest,DC=internal"
}
EOF
|
Confirm:
1
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request GET http://localhost:8200/v1/auth/ldap/config | jq
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
{
"request_id": "ea451a0b-0de6-0a25-7bca-f01c9745fb70",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"binddn": "CN=svc_vault_bind,OU=Service Accounts,DC=vaulttest,DC=internal",
"case_sensitive_names": false,
"certificate": "",
"deny_null_bind": true,
"discoverdn": false,
"groupattr": "cn",
"groupdn": "OU=Security Groups,DC=vaulttest,DC=internal",
"groupfilter": "(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))",
"insecure_tls": false,
"starttls": false,
"tls_max_version": "tls12",
"tls_min_version": "tls12",
"token_bound_cidrs": [],
"token_explicit_max_ttl": 0,
"token_max_ttl": 0,
"token_no_default_policy": false,
"token_num_uses": 0,
"token_period": 0,
"token_policies": [],
"token_ttl": 0,
"token_type": "default",
"upndomain": "vaulttest.internal",
"url": "ldap://ec2-3-106-146-38.ap-southeast-2.compute.amazonaws.com:389",
"use_pre111_group_cn_behavior": false,
"use_token_groups": false,
"userattr": "samaccountname",
"userdn": "OU=Vault Users,DC=vaulttest,DC=internal"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
|
Reference: https://www.vaultproject.io/api/auth/ldap/index.html#configure-ldap
CLI:
1
2
3
4
5
6
7
8
|
./vault write auth/ldap/config \
url="ldap://ec2-3-106-146-38.ap-southeast-2.compute.amazonaws.com:389" \
binddn="CN=svc_vault_bind,OU=Service Accounts,DC=vaulttest,DC=internal" \
bindpass="pass@word1" \
userdn="OU=Vault Users,DC=vaulttest,DC=internal" \
userattr="sAMAccountName" \
upndomain="vaulttest.internal" \
groupdn="OU=Security Groups,DC=vaulttest,DC=internal"
|
Confirm:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
vault read /auth/ldap/config
Key Value
--- -----
binddn CN=svc_vault_bind,OU=Service Accounts,DC=vaulttest,DC=internal
case_sensitive_names false
certificate n/a
deny_null_bind true
discoverdn false
groupattr cn
groupdn OU=Security Groups,DC=vaulttest,DC=internal
groupfilter (|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))
insecure_tls false
starttls false
tls_max_version tls12
tls_min_version tls12
token_bound_cidrs []
token_explicit_max_ttl 0s
token_max_ttl 0s
token_no_default_policy false
token_num_uses 0
token_period 0s
token_policies []
token_ttl 0s
token_type default
upndomain vaulttest.internal
url ldap://ec2-3-106-146-38.ap-southeast-2.compute.amazonaws.com:389
use_pre111_group_cn_behavior false
use_token_groups false
userattr samaccountname
userdn OU=Vault Users,DC=vaulttest,DC=internal
|
Reference: https://www.vaultproject.io/docs/auth/ldap.html
The following assumes a Key/Value V2 secret backend at /secret.
Policy policy-vault-users will be a simple read-only policy to /secret/*.
Policy policy-vault-admins will be a simple create, update, read, list, and delete policy to /secret/*.
API:
1
2
3
4
5
6
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request PUT http://localhost:8200/v1/sys/policies/acl/policy-vault-users --data @- <<EOF
{
"name":"policy-vault-users",
"policy":"path \"secret/*\" {capabilities = [\"read\", \"list\"]}"
}
EOF
|
1
2
3
4
5
6
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request PUT http://localhost:8200/v1/sys/policies/acl/policy-vault-admins --data @- <<EOF
{
"name":"policy-vault-admins",
"policy":"path \"secret/*\" {capabilities = [\"create\",\"update\", \"read\", \"list\", \"delete\"]}"
}
EOF
|
Confirm:
1
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request LIST http://localhost:8200/v1/sys/policies/acl | jq
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
{
"request_id": "5d5b41f0-1ee1-ffed-2a7d-d6700e4f2fd3",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"keys": [
"default",
"policy-vault-admins",
"policy-vault-users",
"root"
]
},
"wrap_info": null,
"warnings": null,
"auth": null
}
|
1
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request GET http://localhost:8200/v1/sys/policies/acl/policy-vault-users | jq
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
{
"request_id": "ca258cbf-ad82-920e-5bac-679d9805074f",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"name": "policy-vault-users",
"policy": "path \"secret/*\" {capabilities = [\"read\", \"list\"]}"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
|
Reference: https://www.vaultproject.io/api/system/policies.html#sys-policies-
CLI:
1
2
3
4
5
|
cat <<EOF | vault policy write policy-vault-users -
path "secret/*" {
capabilities = ["read", "list"]
}
EOF
|
1
2
3
4
5
|
cat <<EOF | vault policy write policy-vault-admins -
path "secret/*" {
capabilities = ["create", "update", "read", "list", "delete"]
}
EOF
|
Confirm:
1
2
3
4
|
default
policy-vault-admins
policy-vault-users
root
|
1
|
vault policy read policy-vault-users
|
1
2
3
|
path "secret/*" {
capabilities = ["read", "list"]
}
|
1
|
vault policy read policy-vault-admins
|
1
2
3
|
path "secret/*" {
capabilities = ["create", "update", "read", "list", "delete"]
}
|
Reference: https://www.vaultproject.io/docs/commands/policy/write.html & https://www.vaultproject.io/docs/concepts/policies.html
Create Identity Groups
The identity groups in Vault need to be created with a type of external to integrate with LDAP.
One group will be created for Vault Users and another for Vault Admins.
The groups will be created with the policies created previously.
1
2
3
4
5
6
7
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request POST http://localhost:8200/v1/identity/group --data @- <<EOF | jq
{
"name":"Vault Users",
"type":"external",
"policies":"policy-vault-users"
}
EOF
|
1
2
3
4
5
6
7
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request POST http://localhost:8200/v1/identity/group --data @- <<EOF | jq
{
"name":"Vault Admins",
"type":"external",
"policies":"policy-vault-admins"
}
EOF
|
Confirm:
1
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request LIST http://localhost:8200/v1/identity/group/id | jq
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
"request_id": "1f466da1-d7b2-18b4-380b-244b1b9f2424",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"key_info": {
"461b9477-0c66-2520-47c1-358bed5fcaac": {
"name": "Vault Admins",
"num_member_entities": 0,
"num_parent_groups": 0
},
"fea4254d-73e8-e0f7-852e-47ad3d18856f": {
"name": "Vault Users",
"num_member_entities": 0,
"num_parent_groups": 0
}
},
"keys": [
"461b9477-0c66-2520-47c1-358bed5fcaac",
"fea4254d-73e8-e0f7-852e-47ad3d18856f"
]
},
"wrap_info": null,
"warnings": null,
"auth": null
}
|
Reference: https://www.vaultproject.io/api/secret/identity/group.html#create-a-group
CLI:
1
2
3
|
vault write identity/group name="Vault Users" \
policies="policy-vault-users" \
type="external"
|
1
2
3
|
vault write identity/group name="Vault Admins" \
policies="policy-vault-admins" \
type="external"
|
Confirm:
1
|
vault list /identity/group/name
|
1
2
3
4
|
Keys
----
Vault Admins
Vault Users
|
1
|
vault read "/identity/group/name/Vault Users"
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
Key Value
--- -----
alias map[]
creation_time 2019-11-07T05:36:50.825746Z
id 2ae358f8-4520-82b3-9b67-148ee7bb609a
last_update_time 2019-11-07T05:36:50.825746Z
member_entity_ids []
member_group_ids <nil>
metadata <nil>
modify_index 1
name Vault Users
namespace_id root
parent_group_ids <nil>
policies [policy-vault-users]
type external
|
1
|
vault read "/identity/group/name/Vault Admins"
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
Key Value
--- -----
alias map[]
creation_time 2019-11-07T05:30:01.893194Z
id 461b9477-0c66-2520-47c1-358bed5fcaac
last_update_time 2019-11-07T05:30:01.893194Z
member_entity_ids []
member_group_ids <nil>
metadata <nil>
modify_index 1
name Vault Admins
namespace_id root
parent_group_ids <nil>
policies [policy-vault-admins]
type external
|
Reference: https://www.vaultproject.io/docs/secrets/identity/index.html#identity-groups
Create Group Aliases
Group aliases will be created for both of the LDAP security groups mapping them to the Vault identity groups. The group alias name must match the security group CN (common name) in LDAP.
First, determine the mount accessor for the LDAP auth method
API:
1
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request GET http://localhost:8200/v1/sys/auth | jq -r '.["ldap/"].accessor'
|
CLI:
1
|
vault auth list -format=json | jq -r '.["ldap/"].accessor'
|
Then determine the id of each of the groups (note from prior API/CLI calls or do the below).
API:
1
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request GET --url "http://localhost:8200/v1/identity/group/name/Vault%20Users" | jq ".data.id"
|
1
|
"2ae358f8-4520-82b3-9b67-148ee7bb609a"
|
1
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request GET --url "http://localhost:8200/v1/identity/group/name/Vault%20Admins" | jq ".data.id"
|
1
|
"461b9477-0c66-2520-47c1-358bed5fcaac"
|
CLI:
1
|
vault read "/identity/group/name/Vault Users" -format=json | jq ".data.id"
|
1
|
"2ae358f8-4520-82b3-9b67-148ee7bb609a"
|
1
|
vault read "/identity/group/name/Vault Admins" -format=json | jq ".data.id"
|
1
|
"461b9477-0c66-2520-47c1-358bed5fcaac"
|
Then create the group aliases
API:
1
2
3
4
5
6
7
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request POST http://localhost:8200/v1/identity/group-alias --data @- <<EOF | jq
{
"name":"Vault Users",
"mount_accessor":"auth_ldap_e620cbc4",
"canonical_id":"2ae358f8-4520-82b3-9b67-148ee7bb609a"
}
EOF
|
1
2
3
4
5
6
7
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request POST http://localhost:8200/v1/identity/group-alias --data @- <<EOF | jq
{
"name":"Vault Admins",
"mount_accessor":"auth_ldap_e620cbc4",
"canonical_id":"461b9477-0c66-2520-47c1-358bed5fcaac"
}
EOF
|
Confirm:
1
|
curl --header "X-Vault-Token: s.NQWnh1WyIJEvbcKcE3YGWLei" --request LIST http://localhost:8200/v1/identity/group-alias/id/ | jq
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
{
"request_id": "575942d3-2193-781a-3f62-fa1d6d101ec6",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"key_info": {
"08cd4481-5eff-fadd-bce4-08862cba03a5": {
"canonical_id": "461b9477-0c66-2520-47c1-358bed5fcaac",
"mount_accessor": "auth_ldap_e620cbc4",
"mount_path": "auth/ldap/",
"mount_type": "ldap",
"name": "Vault Admins"
},
"48859a80-b6a5-daf5-1b47-0f09aa2e9194": {
"canonical_id": "2ae358f8-4520-82b3-9b67-148ee7bb609a",
"mount_accessor": "auth_ldap_e620cbc4",
"mount_path": "auth/ldap/",
"mount_type": "ldap",
"name": "Vault Users"
}
},
"keys": [
"08cd4481-5eff-fadd-bce4-08862cba03a5",
"48859a80-b6a5-daf5-1b47-0f09aa2e9194"
]
},
"wrap_info": null,
"warnings": null,
"auth": null
}
|
CLI:
1
2
3
|
vault write /identity/group-alias name="Vault Users" \
mount_accessor="auth_ldap_e620cbc4" \
canonical_id="2ae358f8-4520-82b3-9b67-148ee7bb609a"
|
1
2
3
|
vault write /identity/group-alias name="Vault Admins" \
mount_accessor="auth_ldap_e620cbc4" \
canonical_id="461b9477-0c66-2520-47c1-358bed5fcaac"
|
Confirm:
1
|
vault list /identity/group-alias/id
|
1
2
3
4
|
Keys
----
08cd4481-5eff-fadd-bce4-08862cba03a5
d77609a5-0cf2-4e4a-fd4c-bb99257a86ec
|
1
|
vault read /identity/group-alias/id/08cd4481-5eff-fadd-bce4-08862cba03a5 -format=json
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
{
"request_id": "8705e6a5-89b8-0f74-c296-47cd3086e01d",
"lease_id": "",
"lease_duration": 0,
"renewable": false,
"data": {
"canonical_id": "461b9477-0c66-2520-47c1-358bed5fcaac",
"creation_time": "2019-11-07T10:18:38.125507Z",
"id": "08cd4481-5eff-fadd-bce4-08862cba03a5",
"last_update_time": "2019-11-07T10:46:57.690064Z",
"merged_from_canonical_ids": null,
"metadata": null,
"mount_accessor": "auth_ldap_e620cbc4",
"mount_path": "auth/ldap/",
"mount_type": "ldap",
"name": "Vault Admins",
"namespace_id": "root"
},
"warnings": null
}
|
1
|
vault read /identity/group-alias/id/d77609a5-0cf2-4e4a-fd4c-bb99257a86ec -format=json
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
{
"request_id": "9289cfe1-61b2-2f2b-fa7d-da77b82fd57c",
"lease_id": "",
"lease_duration": 0,
"renewable": false,
"data": {
"canonical_id": "2ae358f8-4520-82b3-9b67-148ee7bb609a",
"creation_time": "2019-11-07T11:04:11.588927Z",
"id": "d77609a5-0cf2-4e4a-fd4c-bb99257a86ec",
"last_update_time": "2019-11-07T11:04:11.588982Z",
"merged_from_canonical_ids": null,
"metadata": null,
"mount_accessor": "auth_ldap_e620cbc4",
"mount_path": "auth/ldap/",
"mount_type": "ldap",
"name": "Vault Users",
"namespace_id": "root"
},
"warnings": null
}
|
Conclusion
At this point, vaultuser can log in and see data in secret/, but it cannot create or update. User vaultadmin can log in and can perform any of the create, read, update, list, delete actions.
References
- Identity: Entities and Groups found here
Updated January 2020
Rather than using identity groups and group aliases you can simply configure LDAP groups.
Create the LDAP Group in Vault.
API:
1
|
curl --header "X-Vault-Token: xxx" --request POST https://localhost:8200/v1/auth/ldap/groups/SomeLDAPGroupName --data '{"policies": ["policy1", "policy2"]}' --insecure
|
Confirm:
1
|
curl --header "X-Vault-Token: xxx" --request GET https://localhost:8200/v1/auth/ldap/groups/SomeLDAPGroupName --insecure | jq
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
{
"request_id": "8d4f5858-5ae1-ce67-44d4-3e6278d20bec",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"policies": [
"policy1",
"policy2"
]
},
"wrap_info": null,
"warnings": null,
"auth": null
}
|
CLI:
1
|
vault write /auth/ldap/groups/SomeLDAPGroupName policies=policy1
|
Confirm:
1
2
3
4
|
vault.exe read /auth/ldap/groups/SomeLDAPGroupName
Key Value
--- -----
policies [policy1]
|