Vault extension configuration for Drone v1.1

#1

Using Vault Approle to access secrets for Drone

This quick doc tries to help further understanding on connecting Vault and Drone for secrets. This doesn’t implement best practices for security, as it depends on your organization, and the Drone docs already describe limiting scope by repository and git event. I had some trouble getting started with the Vault extension, and I hope this helps anyone else in the future that also is implementing Drone and Vault.

My Setup:

  • Vault 1.1
  • Drone 1.1.0
  • GitHub Enterprise

For my scenario, I already have a secret share specifically for drone builds. My Vault is using the KV version 2 which uses secret/data/shared/drone instead of secret/shared/drone. The vault cli client and API differ in how it accesses the secret mount.

$ vault kv get secret/data/shared/drone/secret_username
No value found at secret/data/data/shared/drone/secret_username

Versus

$ vault kv get secret/shared/drone/secret_username
====== Metadata ======
Key              Value
---              -----
created_time     2019-04-17T19:02:16.871296361Z
deletion_time    n/a
destroyed        false
version          1

==== Data ====
Key      Value
---      -----
value    r00t_user

Create Vault HCL policy

# drone-shared.hcl

# Login with AppRole
path "auth/approle/login" {
  capabilities = ["create", "read"]
}

# Read shared data kv2
path "secret/data/shared/*" {
  capabilities = ["read", "list"]
}

# Read shared data
path "secret/shared/*" {
  capabilities = ["read", "list"]
}

$ vault policy write drone-shared ./drone-shared.hcl

$ vault write auth/approle/role/drone-shared token_ttl=48h period=8h policies=drone-shared

$ vault write auth/approle/role/drone-shared --policy=drone-shared

Key                      Value
---                      -----
bind_secret_id           true
bound_cidr_list          <nil>
local_secret_ids         false
period                   8h
policies                 [drone-shared]
secret_id_bound_cidrs    <nil>
secret_id_num_uses       0
secret_id_ttl            0s
token_bound_cidrs        <nil>
token_max_ttl            0s
token_num_uses           0
token_ttl                48h

Read and export ROLE_ID, SECRET_ID, and login

$ vault read auth/approle/role/drone-shared/role-id

Key Value
--- -----
role_id 0x0ba0c2-b2ea-5e0c-1eb0-1bd25b50dd0b

$ vault write -f auth/approle/role/drone-shared/secret-id
Key                   Value
---                   -----
secret_id             beef1-08b5-4e49-a922-89c7ffd47075
...

$ vault write -f auth/approle/login role_id=$ROLE_ID secret_id=$SECRET_ID
Key                     Value
---                     -----
token                   be071b75-0000-ffff-eeee-fffeeeee0000
token_accessor          e23ebb2b-37dc-3fac-8137-9a08fdaaa7c4
token_duration          8h
token_renewable         true
token_policies          ["default" "drone-shared"]
identity_policies       []
policies                ["default" "drone-shared"]
token_meta_role_name    drone-shared

Generate RPC secrets for communication between server, agent, and for secrets plugin.

# for drone rpc
$ openssl rand -hex 16
b1631ec6ccf4670e681a16476be8f014

# for vault
$ openssl rand -hex 16
734e405271785460ccdf97f0ebad7073

Basic setup using docker-compose

version: '2'

services:
  drone-server:
    image: drone/drone:1.1.0
    ports:
      - 1.2.3.4.5:80:80
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /srv/drone_v1/data:/data
    restart: always
    environment:
      - DRONE_OPEN=true
      - DRONE_USER_CREATE=username:ecray,admin:true
      - DRONE_ORGS=example
      - DRONE_AGENTS_ENABLED=true
      - DRONE_SERVER_PROTO=https
      - DRONE_SERVER_HOST=drone.example.com
      - DRONE_GITHUB_PRIVATE_MODE=true
      - DRONE_GITHUB_SERVER=https://github.example.com
      - DRONE_GITHUB_CLIENT_ID=000bbb3333
      - DRONE_GITHUB_CLIENT_SECRET=aaadddeee
      - DRONE_LOGS_DEBUG=true
      - DRONE_RPC_SECRET=b1631ec6ccf4670e681a16476be8f014
      - DRONE_SECRET_ENDPOINT=http://drone-vault:3000

  drone-vault:
    image: drone/vault:latest
    restart: always
    ports:
      - 3000:3000
    environment:
      - SECRET_KEY=734e405271785460ccdf97f0ebad7073
      - VAULT_ADDR=https://my.vault-endpoint.com:8200
      - VAULT_TOKEN_RENEWAL=8h
      - VAULT_TOKEN_TTL=48h
      - VAULT_TOKEN=be071b75-0000-ffff-eeee-fffeeeee0000
      - DRONE_RPC_SERVER=http://drone-server:80
      - DRONE_RPC_SECRET=b1631ec6ccf4670e681a16476be8f014
      - DEBUG=true

  drone-agent:
    image: drone/agent:1.1.0
    command: agent
    restart: always
    depends_on: [ drone-server, drone-vault ]
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - DRONE_RPC_SERVER=http://drone-server:80
      - DRONE_RPC_SECRET=b1631ec6ccf4670e681a16476be8f014
      - DRONE_SECRET_SECRET=734e405271785460ccdf97f0ebad7073
      - DRONE_SECRET_ENDPOINT=http://drone-vault:3000
      - DRONE_LOGS_TRACE=true

Check logs

$ docker-compose logs | grep drone-vault
Attaching to dronev1_drone-agent_1, dronev1_drone-server_1, dronev1_drone-vault_1
drone-vault_1   | time="2019-04-29T19:59:47Z" level=info msg="server listening on address :3000"
drone-vault_1   | time="2019-04-29T19:59:47Z" level=info msg="vault: token renewal enabled: 8h0m0s interval"

Test plugin connection works

$ docker run -it -e DRONE_SECRET_SECRET=ba234f000000000 \
               -e DRONE_SECRET_ENDPOINT=http://drone-vault:3000 \
               --network dronev1_default \
drone/cli:latest -- plugins secret get secret/data/shared/drone/secret_username value --repo ecray/i-hate-pants

r00t_user

Troubleshooting

If you are getting secret not found, you should verify that you have Vault auditing enabled and check the logs to see why its failing to find the secret in Vault. I was also getting post: no protocol schema supported for "". Which let me to believe that the drone-vault plugin needed to have the VAULT_ADDR declare http or https in it.

HTH