HashiCorp Vault with AppRole

Securing Secrets using Vault and AppRole Auth

Security is an essential and a core part of operations and thus keeping secrets secured is vital. Unfortunately, for many an organization, this is often not a priority.

Securing secrets with Vault

The secret artifacts can be secured in HashiCorp Vault using the AppRole method.

  • Setting up KV Secrets v2 store and the AppRole method
  • Creating a admin role that will be used to create secrets and an dgraph application role (see below)
  • Testing access to the secrets using the dgraph application role
  • Configuration the application Dgraph (see below) to use the dgraph application role.
  • Walk-through in testing features (ACL and encrypted export and backup) enabled through Dgraph vault configuration.
export VAULT_VERSION="1.7.0"
export DGRAPH_VERSION="v21.03.0"

Prerequisites

These instructions will use shell commands that are tested in Bash.

Docker Compose setup

Use the following docker-compose.yaml configuration for this guide.

docker-compose.yaml
cat <<-EOF > .env
DGRAPH_VERSION=v21.03.0
VAULT_VERSION=1.7.0
EOF
mkdir -p {vault,dgraph}
touch vault/config.hcl \
dgraph/{vault_secret_id,vault_role_id,alpha.yaml}
.
├── .env
├── dgraph
│ ├── alpha.yaml
│ ├── vault_role_id
│ └── vault_secret_id
├── docker-compose.yaml
└── vault
└── config.hcl

Vault service

For the Vault service, use the following configuration and save it as ./vault/config.hcl:

vault/config.hcl
## launch vault server
docker-compose up --detach "vault"

Unseal Vault Service

Once the Vault service is ready, initialize a fresh new Vault environment with:

## initialize vault and copy secrets down
docker exec -t vault vault operator init
## unseal vault using copied secrets
docker exec -ti vault vault operator unseal
docker exec -ti vault vault operator unseal
docker exec -ti vault vault operator unseal
export VAULT_ROOT_TOKEN="<root-token>"
export VAULT_ADDRESS="127.0.0.1:8200"

Configure Vault with root privileges

This guide will use Vault’s RESTful API rather than the vault command. This may seem more complex, but it is the easiest way to access the Vault service and also demonstrates how to interact with the Vault server.

Create AppRole and KV Secrets

Configure AppRole:

curl --silent \
--header "X-Vault-Token: $VAULT_ROOT_TOKEN" \
--request POST \
--data '{"type": "approle"}' \
$VAULT_ADDRESS/v1/sys/auth/approle
curl --silent \
--header "X-Vault-Token: $VAULT_ROOT_TOKEN" \
--request POST \
--data '{ "type": "kv-v2" }' \
$VAULT_ADDRESS/v1/sys/mounts/secret

Create the admin policy

We need to create a policy that establishes an admin persona. Save the file below as ./vault/policy_admin.hcl.

vault/policy_admin.hcl
cat <<EOF > ./vault/policy_admin.json
{
"policy": "$(sed -e ':a;N;$!ba;s/\n/\\n/g' \
-e 's/"/\\"/g' \
vault/policy_admin.hcl)"
}
EOF
curl --silent \
--header "X-Vault-Token: $VAULT_ROOT_TOKEN" \
--request PUT --data @./vault/policy_admin.json \
http://$VAULT_ADDRESS/v1/sys/policies/acl/admin
curl --silent \
--header "X-Vault-Token: $VAULT_ROOT_TOKEN" \
--request GET \
http://$VAULT_ADDRESS/v1/sys/policies/acl/admin | jq

Create the admin role

Now that the admin policy is uploaded, create the admin role that uses this policy for the admin persona using the root token:

## create the admin role with an attached policy
curl --silent \
--header "X-Vault-Token: $VAULT_ROOT_TOKEN" \
--request POST \
--data '{
"token_policies": "admin",
"token_ttl": "1h",
"token_max_ttl": "4h"
}' \
http://$VAULT_ADDRESS/v1/auth/approle/role/admin
## verify the role
curl --silent \
--header "X-Vault-Token: $VAULT_ROOT_TOKEN" \
--request GET \
http://$VAULT_ADDRESS/v1/auth/approle/role/admin | jq

Retreive Admin Token

For this point forward, we will use the admin persona to setup secrets and the application persona called dgraph. We need to retreive the admin token by logging into Vault with the admin persona.

VAULT_ADMIN_ROLE_ID=$(curl --silent \
--header "X-Vault-Token: $VAULT_ROOT_TOKEN" \
http://$VAULT_ADDRESS/v1/auth/approle/role/admin/role-id \
| jq -r '.data.role_id'
)
VAULT_ADMIN_SECRET_ID=$(curl --silent \
--header "X-Vault-Token: $VAULT_ROOT_TOKEN" \
--request POST \
http://$VAULT_ADDRESS/v1/auth/approle/role/admin/secret-id \
| jq -r '.data.secret_id'
)
export VAULT_ADMIN_TOKEN=$(curl --silent \
--request POST \
--data "{ \"role_id\": \"$VAULT_ADMIN_ROLE_ID\", \"secret_id\": \"$VAULT_ADMIN_SECRET_ID\" }" \
http://$VAULT_ADDRESS/v1/auth/approle/login \
| jq -r '.auth.client_token'
)

Configure Vault with admin persona

For this section, the admin token (VAULT_ADMIN_TOKEN) will be used to upload secrets and create the dgraph application policy and role.

Save Secrets

There are two secrets used, the encryption key and HMAC secret file, which can uploaded using example JSON below (saved as ./vault/payload_alpha_secrets.json):

vault/payload_alpha_secrets
curl --silent \
--header "X-Vault-Token: $VAULT_ADMIN_TOKEN" \
--request POST \
--data @./vault/payload_alpha_secrets.json \
http://$VAULT_ADDRESS/v1/secret/data/dgraph/alpha | jq

Create Dgraph application policy

The application policy is limited to reading the secrets (save this as ./vault/policy_dgraph.hcl):

vault/policy_dgraph.hcl
## convert policies to json format
cat <<EOF > ./vault/policy_dgraph.json
{
"policy": "$(sed -e ':a;N;$!ba;s/\n/\\n/g' \
-e 's/"/\\"/g' \
vault/policy_dgraph.hcl)"
}
EOF
## create the dgraph policy
curl --silent
\
--header "X-Vault-Token: $VAULT_ADMIN_TOKEN" \
--request PUT --data @./vault/policy_dgraph.json \
http://$VAULT_ADDRESS/v1/sys/policies/acl/dgraph
## verify the policy
curl --silent \
--header "X-Vault-Token: $VAULT_ADMIN_TOKEN" \
--request GET \
http://$VAULT_ADDRESS/v1/sys/policies/acl/dgraph | jq

Create Dgraph application role

Create the dgraph application role:

## create the dgraph role with an attached policy
curl --silent \
--header "X-Vault-Token: $VAULT_ADMIN_TOKEN" \
--request POST \
--data '{ "token_policies": "dgraph", "token_ttl": "1h", "token_max_ttl": "4h" }' \
http://$VAULT_ADDRESS/v1/auth/approle/role/dgraph
## verify the role
curl --silent \
--header "X-Vault-Token: $VAULT_ADMIN_TOKEN" \
--request GET \
http://$VAULT_ADDRESS/v1/auth/approle/role/dgraph | jq

Retreive the Dgraph application token

Fetch the dgraph role-id:

VAULT_DGRAPH_ROLE_ID=$(curl --silent \
--header "X-Vault-Token: $VAULT_ADMIN_TOKEN" \
http://$VAULT_ADDRESS/v1/auth/approle/role/dgraph/role-id \
| jq -r '.data.role_id'
)
VAULT_DGRAPH_SECRET_ID=$(curl --silent \
--header "X-Vault-Token: $VAULT_ADMIN_TOKEN" \
--request POST \
http://$VAULT_ADDRESS/v1/auth/approle/role/dgraph/secret-id \
| jq -r '.data.secret_id'
)
export VAULT_DGRAPH_TOKEN=$(curl --silent \
--request POST \
--data "{ \"role_id\": \"$VAULT_DGRAPH_ROLE_ID\", \"secret_id\": \"$VAULT_DGRAPH_SECRET_ID\" }" \
http://$VAULT_ADDRESS/v1/auth/approle/login \
| jq -r '.auth.client_token'
)
echo $VAULT_DGRAPH_ROLE_ID > ./dgraph/vault_role_id
echo $VAULT_DGRAPH_SECRET_ID > ./dgraph/vault_secret_id

Verify Secrets with Dgraph application persona

Using the dgraph token (VAULT_DGRAPH_TOKEN), verify that secrets can be retreived:

curl --silent \
--header "X-Vault-Token: $VAULT_DGRAPH_TOKEN" \
--request GET \
http://$VAULT_ADDRESS/v1/secret/data/dgraph/alpha | jq

Dgraph Service

The Dgraph Alpha service will need to be configured to use Vault to fetch secrets for the encryption and ACL features. Save the following to ./dgraph/alpha.yaml:

dgraph/alpha.yaml
docker network inspect "${PWD##*/}_default" | jq '.[].Containers'
docker-compose up --detach
docker logs alpha1
# print a list of features enabled
curl --silent http://localhost:8080/health \
| jq -r '.[].ee_features | .[]' \
| sed 's/^/* /' \
| grep --color --extended-regexp 'acl|encrypt.*|$'
* acl
* multi_tenancy
* encryption_at_rest
* encrypted_backup_restore
* encrypted_export

* cdc

Testing Dgraph features

This area demonstrates how to directly test the Dgraph ACL (access control lists) and encyption at rest features when enabled through the vault configuration.

Testing Dgraph ACL Feature

The ACL feature is only enabled if a secret was saved. We can tested it by using the default administrative user (groot) and password (password) with RESTful request to /login path:

DGRAPH_ADMIN_USER="groot"
DGRAPH_ADMIN_PSWD="password"
export DGRAPH_ALPHA_ADDRESS="localhost:8080"
## login using RESTful API
export DGRAPH_ACCESS_TOKEN=$(curl --silent \
--request POST \
--data "{
\"userid\": \"$DGRAPH_ADMIN_USER\",
\"password\": \"$DGRAPH_ADMIN_PSWD\",
\"namespace\": 0
}" \
http://$DGRAPH_ALPHA_ADDRESS/login \
| grep -oP '(?<=accessJWT":")[^"]*'
)

Testing Dgraph export operation

The export feature will export the whole Dgraph database in RDF or JSON formats. When encyption at rest is enabled, the exported database will be encypted.

dgraph/export.graphql
curl --silent \
--header "Content-Type: application/graphql" \
--header "X-Dgraph-AccessToken: $DGRAPH_ACCESS_TOKEN" \
--request POST \
--upload-file ./dgraph/export.graphql \
http://$DGRAPH_ALPHA_ADDRESS/admin | jq
./dgraph/export
└── dgraph.r8.u0419.1810
├── g01.gql_schema.gz
├── g01.json.gz
└── g01.schema.gz
find ./dgraph/export/ -name '*.gz' \
| xargs -n 1 file | awk -F/ '{print $NF}'
g01.schema.gz: data
g01.json.gz: data
g01.gql_schema.gz: data
g01.schema.gz: gzip compressed data, max speed, original size modulo 2^32 381
g01.json.gz: gzip compressed data, max speed, original size modulo 2^32 5
g01.gql_schema.gz: gzip compressed data, max speed, original size modulo 2^32 5

Testing Dgraph backup operation

The binary backups feature will, as expected, backup the Dgraph database with either full or incremental backups. This can be used to restore the database to a Dgraph cluster. When encyption at rest is enabled, the backups will be encrypted.

dgraph/backup.graphql
curl --silent \
--header "Content-Type: application/graphql" \
--header "X-Dgraph-AccessToken: $DGRAPH_ACCESS_TOKEN" \
--request POST \
--upload-file ./dgraph/backup.graphql \
http://$DGRAPH_ALPHA_ADDRESS/admin | jq
./dgraph/backups
├── dgraph.20210419.181116.903
│ └── r8-g1.backup
└── manifest.json
find ./dgraph/backups/ -name '*.backup' \
| xargs -n 1 file | awk -F/ '{print $NF}'
r8-g1.backup: data
r8-g1.backup: snappy framed data

Resources

The source code files for this tutorial can be found from here:

Conclusion

There you have it, a full overiew of saving secrets using AppRole auth method and KV Secrets v2 store using with an application service, Dgraph, that has direct support for this feature.

curl --silent \
--header "X-Vault-Token: $VAULT_ADMIN_TOKEN" \
--request POST \
--data '{
"token_policies": "dgraph",
"token_ttl": "1h",
"token_max_ttl": "4h",
"bind_secret_id": false,
"bound_cidr_list": [
"10.0.0.0/8",
"172.0.0.0/8",
"192.168.0.0/16",
"127.0.0.1/32"
]
}' \
http://$VAULT_ADDRESS/v1/auth/approle/role/dgraph

Linux NinjaPants Automation Engineering Mutant — exploring DevOps, o11y, k8s, progressive deployment (ci/cd), cloud native infra, infra as code