Dgraph Vault Integration cannot authenticate using `bound_cidr_list`

Report a Dgraph Bug

Dgraph Alpha will not work when Vault dgraph role is configured with bound_cidr_list. In this mode, you do not need to supply a secret-id, only the role-id is needed.

What version of Dgraph are you using?

  • v20.11.2
  • should happen in v21.03.0 as well (modified for superflag config)

Have you tried reproducing the issue with the latest release?

  • yes

What is the hardware spec (RAM, OS)?

  • docker (dgraph image)

Steps to reproduce the issue (command/config used to run Dgraph).

Configuration

  • docker-compose.yaml
    # docker-compose.yaml
    version: "3.5"
    services:
      zero1:
        image: dgraph/dgraph:v20.11.2
        command: dgraph zero --my=zero1:5080 --replicas 1 --idx 1
        ports:
          - 6080:6080
        container_name: zero1
    
      alpha1:
        image: dgraph/dgraph:v20.11.2
        ports:
          - 8080:8080
          - 9080:9080
        environment:
          DGRAPH_ALPHA_CONFIG: /dgraph/config/config.yaml
        volumes:
          - ./dgraph_alpha_config.yaml:/dgraph/config/config.yaml
          - ./vault/secret_id:/dgraph/vault/secret_id
          - ./vault/role_id:/dgraph/vault/role_id
        command: dgraph alpha --my=alpha1:7080 --zero=zero1:5080
        container_name: alpha1
    
      vault:
        image: vault:1.6.3
        container_name: vault
        ports:
          - 8200:8200
        volumes:
          - ./vault/config.hcl:/vault/config/config.hcl
          - ./vault/data:/vault/data
        environment:
          VAULT_ADDR: http://127.0.0.1:8200
        entrypoint: vault server -config=/vault/config/config.hcl
        cap_add:
          - IPC_LOCK
    
  • dgraph_alpha_config.yaml
    vault_addr: http://vault:8200
    vault_field: enc_key
    vault_format: raw
    vault_path: secret/data/dgraph/enc_key
    vault_roleid_file: /dgraph/vault/role_id
    
  • ./vault/config.hcl
    storage "raft" {
        path = "/vault/data"
        node_id = "vault1"
    }
    
    listener "tcp" {
        address = "0.0.0.0:8200"
        tls_disable = "true"
    }
    
    api_addr = "http://127.0.0.1:8200"
    cluster_addr = "http://127.0.0.1:8201"
    ui = true
    disable_mlock = true
    

Steps for Vault

  1. Launch Unsealed Vault
    ## launch vault server
    docker-compose up --detach "vault"
    
    ## 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
    
  2. Enable AppRole + KV Secrets using token from vault operator init command
    export VAULT_ADDRESS="127.0.0.1:8200"
    
    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  
    
  3. Create Dgraph Policy
    curl --silent \
      --header "X-Vault-Token: $VAULT_ROOT_TOKEN" \
      --request PUT --data '{ "policy": "path \"secret/data/dgraph/*\" {\n  capabilities = [ \"read\", \"update\" ]\n}" }' \
      http://$VAULT_ADDRESS/v1/sys/policies/acl/dgraph
    
  4. Create Dgraph Role with Attached Policy w/ bound_cidr_list configured
    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"]
    }' \
     http://$VAULT_ADDRESS/v1/auth/approle/role/dgraph
    
  5. Save Secret
    curl --silent \
      --header "X-Vault-Token: $VAULT_ADMIN_TOKEN" \
      --request POST \
      --data @./vault/payload_enc_key.json \
      http://$VAULT_ADDRESS/v1/secret/data/dgraph/enc_key | jq
    
  6. Retrieve and Save 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'
    )
    
    echo $VAULT_DGRAPH_ROLE_ID > ./vault/role_id
    
  7. Start Dgraph
    docker-compose up -d
    

Expected behavior and actual result.

Actual Result

Dgraph refuses to work if vault_secretid_file is not configured.

I0317 04:16:15.464402      16 run.go:668] unable to read key vault_roleid_file and vault_secretid_file must both be specified

Expected Result

With bound_cidr_list, similar to Dgraph Alpha whitelist, Vault only needs a role-id, and will return a token.

For example:

## Get Token with ONLY role-id
export VAULT_DGRAPH_TOKEN=$(curl --silent \
  --request POST \
  --data "{ \"role_id\": \"$VAULT_DGRAPH_ROLE_ID\" }" \
  http://$VAULT_ADDRESS/v1/auth/approle/login | jq -r '.auth.client_token'
)

## Test Access to Secret with the Token
curl --silent \
  --header "X-Vault-Token: $VAULT_DGRAPH_TOKEN" \
  --request GET \
  http://$VAULT_ADDRESS/v1/secret/data/dgraph/enc_key | jq

This will return from Vault:

{
  "request_id": "c4e16c91-c1aa-b015-efef-08b5a71ef6d1",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "data": {
      "enc_key": "1234567890123456"
    },
    "metadata": {
      "created_time": "2021-03-17T04:12:59.3566335Z",
      "deletion_time": "",
      "destroyed": false,
      "version": 1
    }
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

Are there Vault docs that say that role-id isn’t needed when bound_cidr_list is set?

In the expected results, I demonstrate that it works with only role-id, and no secret-id.

Observe that with bind_secret_id set to false and bound_cidr_list with a list of addresses, you can get a token.

## Get Token with ONLY role-id
export VAULT_DGRAPH_TOKEN=$(curl --silent \
  --request POST \
  --data "{ \"role_id\": \"$VAULT_DGRAPH_ROLE_ID\" }" \
  http://$VAULT_ADDRESS/v1/auth/approle/login | jq -r '.auth.client_token'
)

This is different that when bind_secret_id set to true, where secret-id is required:

## Get Token with both role-id + secret-id when 
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'
)

The relevant docs: