Secret Store
There are all kinds of secrets used within EdgeX Foundry micro services, such as tokens, passwords, certificates etc. The secret store serves as the central repository to keep these secrets. The developers of other EdgeX Foundry micro services utilize the secret store to create, store and retrieve secrets relevant to their corresponding micro service. The communications the between secret store and other micro services are secured by TLS.
Currently the EdgeX Foundry secret store is implemented with Vault, a HashiCorp open source software product.
Vault is a tool for securely accessing secrets. A secret is anything that you want to tightly control access to, such as API keys, passwords, database credentials, service credentials, or certificates. Vault provides a unified interface to any secret, while providing tight access control and multiple authentication mechanisms (token, LDAP, etc.). Vault adds on key rolling, revocation rules, time-to-live access tokens, secure storage, Shamir Secret Sharing based unlocking mechanism, high availability and detailed auditing.
Vault can use several backend systems (filesystem, databases, Consul, Etcd, S3, Azure, etc.) to securely store every sensitive asset. The current EdgeX Foundry implementation of Vault is using Consul, another HashiCorp open source software product. Consul is a distributed service mesh to connect, secure, and configure services across any runtime platform and public or private cloud. Consul uses a consensus protocol to provide Consistency as defined by CAP. The consensus protocol is based on "Raft: In search of an Understandable Consensus Algorithm". For a visual explanation of Raft, see The Secret Lives of Data.
The seamless integration of Vault and Consul provides a strong yet simple infrastructure to setup a reliable high availability architecture (Vault failover nodes, Consul Clustering) for the EdgeX Foundry Security services in production.
The key features of Vault are:
- Secure Secret Storage: Arbitrary key/value secrets can be stored in Vault. Vault encrypts these secrets prior to writing them to persistent storage, so gaining access to the raw storage isn't enough to access your secrets. Authentication mechanisms (internal and/or external) and authorizations based upon policies provide access management.
- Dynamic Secrets: Vault can generate secrets on-demand for some systems, automatically revoking them after the lease is up.
- Data Encryption: Vault can encrypt and decrypt data without storing it.
- Leasing and Renewal: All secrets in Vault have a lease associated with them (automatic revocation). However, clients can renew leases via built-in renew APIs.
- Revocation: Vault has built-in support for secret revocation. Single secrets, but also a tree of secrets. Revocation assists in key rolling as well as locking down systems in the case of an intrusion.
Start the Secret Store
Start the Secret Store with Docker Compose and a Docker Compose manifest file. The whole set of Docker Compose files for Geneva release can be found here:
Note
This example uses the Geneva Release. There are later EdgeX releases.
The Compose file starts the entire EdgeX Foundry platform with Redis including the security services.
The command to start EdgeX Foundry platform including the Secret Store and API gateway related services is:
sh> docker-compose up -d
For a pristine run, it is strongly recommended to thoroughly clean up any Docker artifacts remaining from the current run.
sh> docker-compose down -v
The "down" operation will remove containers, network and adding the -v option it will also remove persistent volumes:
Stopping edgex-vault ... done
Stopping edgex-core-consul ... done
Stopping edgex-files ... done
Removing edgex-vault-worker ... done
Removing edgex-vault ... done
Removing edgex-config-seed ... done
Removing edgex-core-consul ... done
Removing edgex-files ... done
Removing network security-secret-store_edgex-network
Removing volume security-secret-store_db-data
Removing volume security-secret-store_log-data
Removing volume security-secret-store_consul-config
Removing volume security-secret-store_consul-data
Removing volume security-secret-store_vault-config
Removing volume security-secret-store_vault-file
Removing volume security-secret-store_vault-logs
Troubleshooting steps
For debugging purpose, the Secret Store services can be started individually with these commands. A pre-requisite being to carefully follow the invocation sequence in order to avoid any dependency failure. Lines starting with # (hashtag) are contextual comments to explicit the purpose of the above corresponding command:
Clean-up
sh> cd <path-to-EdgeX-Foundry-Secret-Store>
# <...>/security-secret-store/
sh> docker-compose down -v
sh> docker-compose ps
# Check no previous container is running
sh> docker volume ls
# Check and remove any previous persistent and/or unused volumes
sh> docker volume prune
sh> docker volume rm <volume-name>
sh> docker network ls
# Check and remove the previous EdgeX Foundry Docker network
sh> docker network rm edgex-network
Start the first service: volume (platform volume initializations)
sh> docker-compose up -d volume
Sample output:
Creating network "security-secret-store_edgex-network" with driver "bridge"
Creating volume "security-secret-store_db-data" with default driver
Creating volume "security-secret-store_log-data" with default driver
Creating volume "security-secret-store_consul-config" with default driver
Creating volume "security-secret-store_consul-data" with default driver
Creating volume "security-secret-store_vault-config" with default driver
Creating volume "security-secret-store_vault-file" with default driver
Creating volume "security-secret-store_vault-logs" with default driver
Creating edgex-files ... done
Start the second service: consul (Consul is Vault store backend)
sh> docker-compose up -d consul
Sample output:
edgex-files is up-to-date
Creating edgex-core-consul ... done
Display and inspect consul service logs: important lines are highlighted
sh> docker-compose logs consul
Sample output:
Attaching to edgex-core-consul
edgex-core-consul | ==> Starting Consul agent...
edgex-core-consul | ==> Consul agent running!
edgex-core-consul | Version: 'v1.1.0'
edgex-core-consul | Node ID: '371cbce6-02a8-65f6-ddea-6df5c40a4c50'
edgex-core-consul | Node name: 'edgex-core-consul'
edgex-core-consul | Datacenter: 'dc1' (Segment: '<all>')
edgex-core-consul | Server: true (Bootstrap: false)
edgex-core-consul | Client Addr: [0.0.0.0] (HTTP: 8500, HTTPS: -1, DNS: 8600)
edgex-core-consul | Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
edgex-core-consul | Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false
edgex-core-consul |
edgex-core-consul | ==> Log data will now stream in as it occurs:
edgex-core-consul |
edgex-core-consul | 2019/01/13 13:25:06 [DEBUG] agent: Using random ID "371cbce6-02a8-65f6-ddea-6df5c40a4c50" as node ID
edgex-core-consul | 2019/01/13 13:25:06 [INFO] raft: Initial configuration (index=1): [{Suffrage:Voter ID:371cbce6-02a8-65f6-ddea-6df5c40a4c50 Address:127.0.0.1:8300}]
edgex-core-consul | 2019/01/13 13:25:06 [INFO] raft: Node at 127.0.0.1:8300 [Follower] entering Follower state (Leader: "")
edgex-core-consul | 2019/01/13 13:25:06 [INFO] serf: EventMemberJoin: edgex-core-consul.dc1 127.0.0.1
edgex-core-consul | 2019/01/13 13:25:06 [INFO] serf: EventMemberJoin: edgex-core-consul 127.0.0.1
edgex-core-consul | 2019/01/13 13:25:06 [INFO] consul: Adding LAN server edgex-core-consul (Addr: tcp/127.0.0.1:8300) (DC: dc1)
edgex-core-consul | 2019/01/13 13:25:06 [INFO] consul: Handled member-join event for server "edgex-core-consul.dc1" in area "wan"
edgex-core-consul | 2019/01/13 13:25:06 [INFO] agent: Started DNS server 0.0.0.0:8600 (tcp)
edgex-core-consul | 2019/01/13 13:25:06 [INFO] agent: Started DNS server 0.0.0.0:8600 (udp)
edgex-core-consul | 2019/01/13 13:25:06 [INFO] agent: Started HTTP server on [::]:8500 (tcp)
edgex-core-consul | 2019/01/13 13:25:06 [INFO] agent: started state syncer
edgex-core-consul | 2019/01/13 13:25:06 [WARN] raft: Heartbeat timeout from "" reached, starting election
edgex-core-consul | 2019/01/13 13:25:06 [INFO] raft: Node at 127.0.0.1:8300 [Candidate] entering Candidate state in term 2
edgex-core-consul | 2019/01/13 13:25:06 [DEBUG] raft: Votes needed: 1
edgex-core-consul | 2019/01/13 13:25:06 [DEBUG] raft: Vote granted from 371cbce6-02a8-65f6-ddea-6df5c40a4c50 in term 2. Tally: 1
edgex-core-consul | 2019/01/13 13:25:06 [INFO] raft: Election won. Tally: 1
edgex-core-consul | 2019/01/13 13:25:06 [INFO] raft: Node at 127.0.0.1:8300 [Leader] entering Leader state
edgex-core-consul | 2019/01/13 13:25:06 [INFO] consul: cluster leadership acquired
Start the third service: config-seed (platform configuration initializations)
sh> docker-compose up -d config-seed
Sample output:
edgex-files is up-to-date
edgex-core-consul is up-to-date
Creating edgex-config-seed ... done
Display and inspect the created container states: important lines are highlighted
sh> docker-compose ps
Sample output:
Name Command State Ports
---------------------------------------------------------------------------------------------------------------------------------------edgex-config-seed /bin/sh -c /edgex/cmd/conf ... Exit 0 edgex-core-consul docker-entrypoint.sh agent ... Up 8300/tcp, 8301/tcp, 8301/udp, 8302/tcp, 8302/udp, 0.0.0.0:8400->8400/tcp, 0.0.0.0:8500->8500/tcp, 0.0.0.0:8600->8600/tcp, 8600/udp edgex-files /bin/sh -c /usr/bin/tail - ... Up
Note
Line 3: edgex-config-seed
service has exited after successful
processing (exit code 0)
Start the fourth service: vault (Vault tool)
Note
Vault will be uninitialized and unsealed upon success. The
vault-worker
service will process the initialization and unsealing
tasks.
sh> docker-compose up -d vault
Sample output:
edgex-files is up-to-date
edgex-core-consul is up-to-date
Creating edgex-vault ... done
Display and inspect "vault" service logs: important lines are highlighted
sh> docker-compose logs vault
Sample output:
Attaching to edgex-vault
edgex-vault | ==> Vault server configuration:
edgex-vault |
edgex-vault | Api Address: https://edgex-vault:8200
edgex-vault | Cgo: disabled
edgex-vault | Cluster Address: https://edgex-vault:8201
edgex-vault | Listener 1: tcp (addr: "edgex-vault:8200", cluster address: "edgex-vault:8201", tls: "enabled")
edgex-vault | Log Level: info
edgex-vault | Mlock: supported: true, enabled: true
edgex-vault | Storage: consul (HA available)
edgex-vault | Version: Vault v0.10.2
edgex-vault | Version Sha: 3ee0802ed08cb7f4046c2151ec4671a076b76166
edgex-vault |
edgex-vault | ==> Vault server started! Log data will stream in below:
edgex-vault |
Note
Line 4 & 7: Vault API endpoint on port 8200 (lines 4 and 7).
Line 7: Vault has TLS enabled.
Line 10: Vault backend storage is Consul.
Start the fifth service: vault-worker (Vault init/unseal process and setups)
sh> docker-compose up -d vault-worker
Sample output:
edgex-files is up-to-date
edgex-core-consul is up-to-date
edgex-vault is up-to-date
Creating edgex-vault-worker ... done
Display and inspect "vault-worker" service logs: important lines are highlighted
sh> docker-compose logs vault-worker
Sample output:
Attaching to edgex-vault-worker
edgex-vault-worker | INFO: 2019/01/13 13:35:42 successful loading the rootCA cert.
edgex-vault-worker | INFO: 2019/01/13 13:35:43 {"keys":["564b9444eebe28b393c21a4dca1e32835b7dc27f5da03b73d22b666cb20224a9"],"keys_base64":["VkuURO6+KLOTwhpNyh4yg1t9wn9doDtz0itmbLICJKk="],"recovery_keys":null,"recovery_keys_base64":null,"root_token":"01dbbae4-353a-8cdf-8189-4d50e5535a6f"}
edgex-vault-worker | INFO: 2019/01/13 13:35:43 Vault has been initialized successfully.
edgex-vault-worker | INFO: 2019/01/13 13:35:43 Vault has been unsealed successfully.
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Vault Health Check HTTP Status: 200 OK (StatusCode: 200)
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Verifying Admin policy file hash (SHA256).
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Vault policy file checksum (SHA256): 5ce8d58cf7d931735f6532742f677c109a91a263bcefe9aef73ab2a69f4b43d3
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Reading Admin policy file.
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Importing Vault Admin policy.
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Import Policy Successfull.
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Reading Kong policy file.
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Importing Vault Kong policy.
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Import Policy Successfull.
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Creating Vault Admin token.
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Create Token Successfull.
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Creating Vault Kong token.
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Create Token Successfull.
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Successful on reading certificate from v1/secret/edgex/pki/tls/edgex-kong.
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Cert&key are not in the secret store yet, will need to upload them.
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Load cert&key pair from volume successfully, now will upload to secret store.
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Trying to upload cert&key to secret store.
edgex-vault-worker | INFO: 2019/01/13 13:35:48 Successful to add certificate to the secret store.
Display and inspect "vault" service logs: important lines are highlighted
sh> docker-compose logs vault
Sample output:
Attaching to edgex-vault
edgex-vault | ==> Vault server configuration:
edgex-vault |
edgex-vault | Api Address: https://edgex-vault:8200
edgex-vault | Cgo: disabled
edgex-vault | Cluster Address: https://edgex-vault:8201
edgex-vault | Listener 1: tcp (addr: "edgex-vault:8200", cluster address: "edgex-vault:8201", tls: enabled")
edgex-vault | Log Level: info
edgex-vault | Mlock: supported: true, enabled: true
edgex-vault | Storage: consul (HA available)
edgex-vault | Version: Vault v0.10.2
edgex-vault | Version Sha: 3ee0802ed08cb7f4046c2151ec4671a076b76166
edgex-vault |
edgex-vault | ==> Vault server started! Log data will stream in below:
edgex-vault |
edgex-vault | 2019-01-13T13:35:42.549Z [INFO ] core: security barrier not initialized
edgex-vault | 2019-01-13T13:35:42.551Z [INFO ] core: security barrier not initialized
edgex-vault | 2019-01-13T13:35:42.554Z [INFO ] core: security barrier initialized: shares=1 threshold=1
edgex-vault | 2019-01-13T13:35:42.575Z [INFO ] core: post-unseal setup starting
edgex-vault | 2019-01-13T13:35:42.583Z [INFO ] core: loaded wrapping token key
edgex-vault | 2019-01-13T13:35:42.583Z [INFO ] core: successfully setup plugin catalog: plugin-directory=
edgex-vault | 2019-01-13T13:35:42.584Z [INFO ] core: no mounts; adding default mount table
edgex-vault | 2019-01-13T13:35:42.585Z [INFO ] core: successfully mounted backend: type=kv path=secret/
edgex-vault | 2019-01-13T13:35:42.586Z [INFO ] core: successfully mounted backend: type=cubbyhole path=cubbyhole/
edgex-vault | 2019-01-13T13:35:42.586Z [INFO ] core: successfully mounted backend: type=system path=sys/
edgex-vault | 2019-01-13T13:35:42.586Z [INFO ] core: successfully mounted backend: type=identity path=identity/
edgex-vault | 2019-01-13T13:35:42.593Z [INFO ] core: restoring leases
edgex-vault | 2019-01-13T13:35:42.593Z [INFO ] rollback: starting rollback manager
edgex-vault | 2019-01-13T13:35:42.594Z [INFO ] expiration: lease restore complete
edgex-vault | 2019-01-13T13:35:42.596Z [INFO ] identity: entities restored
edgex-vault | 2019-01-13T13:35:42.597Z [INFO ] identity: groups restored
edgex-vault | 2019-01-13T13:35:42.597Z [INFO ] core: post-unseal setup complete
edgex-vault | 2019-01-13T13:35:42.597Z [INFO ] core: core/startClusterListener: starting listener: listener_address=172.19.0.4:8201
edgex-vault | 2019-01-13T13:35:42.597Z [INFO ] core: core/startClusterListener: serving cluster requests: cluster_listen_address=172.19.0.4:8201
edgex-vault | 2019-01-13T13:35:42.600Z [INFO ] core: root token generated
edgex-vault | 2019-01-13T13:35:42.600Z [INFO ] core: pre-seal teardown starting
edgex-vault | 2019-01-13T13:35:42.600Z [INFO ] core: stopping cluster listeners
edgex-vault | 2019-01-13T13:35:42.600Z [INFO ] core: shutting down forwarding rpc listeners
edgex-vault | 2019-01-13T13:35:42.600Z [INFO ] core: forwarding rpc listeners stopped
edgex-vault | 2019-01-13T13:35:43.099Z [INFO ] core: rpc listeners successfully shut down
edgex-vault | 2019-01-13T13:35:43.099Z [INFO ] core: cluster listeners successfully shut down
edgex-vault | 2019-01-13T13:35:43.100Z [INFO ] rollback: stopping rollback manager
edgex-vault | 2019-01-13T13:35:43.100Z [INFO ] core: pre-seal teardown complete
edgex-vault | 2019-01-13T13:35:43.105Z [INFO ] core: vault is unsealed
edgex-vault | 2019-01-13T13:35:43.105Z [INFO ] core: entering standby mode
edgex-vault | 2019-01-13T13:35:43.109Z [INFO ] core: acquired lock, enabling active operation
edgex-vault | 2019-01-13T13:35:43.134Z [INFO ] core: post-unseal setup starting
edgex-vault | 2019-01-13T13:35:43.135Z [INFO ] core: loaded wrapping token key
edgex-vault | 2019-01-13T13:35:43.135Z [INFO ] core: successfully setup plugin catalog: plugin-directory=
edgex-vault | 2019-01-13T13:35:43.137Z [INFO ] core: successfully mounted backend: type=kv path=secret/
edgex-vault | 2019-01-13T13:35:43.137Z [INFO ] core: successfully mounted backend: type=system path=sys/
edgex-vault | 2019-01-13T13:35:43.137Z [INFO ] core: successfully mounted backend: type=identity path=identity/
edgex-vault | 2019-01-13T13:35:43.137Z [INFO ] core: successfully mounted backend: type=cubbyhole path=cubbyhole/
edgex-vault | 2019-01-13T13:35:43.141Z [INFO ] core: restoring leases
edgex-vault | 2019-01-13T13:35:43.142Z [INFO ] rollback: starting rollback manager
edgex-vault | 2019-01-13T13:35:43.142Z [INFO ] expiration: lease restore complete
edgex-vault | 2019-01-13T13:35:43.143Z [INFO ] identity: entities restored
edgex-vault | 2019-01-13T13:35:43.143Z [INFO ] identity: groups restored
edgex-vault | 2019-01-13T13:35:43.144Z [INFO ] core: post-unseal setup complete
edgex-vault | 2019-01-13T13:35:43.144Z [INFO ] core: core/startClusterListener: starting listener: listener_address=172.19.0.4:8201
edgex-vault | 2019-01-13T13:35:43.144Z [INFO ] core: core/startClusterListener: serving cluster requests: cluster_listen_address=172.19.0.4:8201
Note
Line 18: Vault initialization successful.
Line 35: Vault root token generated.
Line 44: Vault unsealing successful.
Line 50: Vault key/value store secret successfully mounted.
Line 60 & 61: Vault successfully started.
Display and inspect the created container states: important lines are highlighted
sh> docker-compose ps
Sample output:
Name Command State Ports
---------------------------------------------------------------------------------------------------------------------------------------edgex-config-seed /bin/sh -c /edgex/cmd/conf ... Exit 0 edgex-core-consul docker-entrypoint.sh agent ... Up 8300/tcp, 8301/tcp, 8301/udp, 8302/tcp, 8302/udp, 0.0.0.0:8400->8400/tcp, 0.0.0.0:8500->8500/tcp, 0.0.0.0:8600->8600/tcp, 8600/udp edgex-files /bin/sh -c /usr/bin/tail - ... Up edgex-vault docker-entrypoint.sh serve ... Up 0.0.0.0:8200->8200/tcp edgex-vault-worker ./edgex-vault-worker --ini ... Exit 0
Note
Line 9: edgex-vault-worker
service has exited after successful
processing (exit code 0)
Display and inspect the created container volumes: important lines are highlighted
sh> docker volume ls
DRIVER VOLUME NAME
local security-secret-store_consul-config
local security-secret-store_consul-data
local security-secret-store_db-data
local security-secret-store_log-data
local security-secret-store_vault-config
local security-secret-store_vault-file
local security-secret-store_vault-logs
Display and inspect the container network
(security-secret-store_edgex-network
): important lines are highlighted
sh> docker network ls
NETWORK ID NAME DRIVER SCOPE
63227826fbc7 bridge bridge local
60763abffde3 host host local
1d236ab1dbbd none null local
0a7f7266d102 security-secret-store_edgex-network bridge local
Using Consul Web UI
For learning and verification purposes one might use the Consul Web UI interface to gather and double check specific Vault informations.
Consul Web UI endpoint port is exposed by the Docker compose file. EdgeX Foundry platform uses the Consul default port number 8500. It is normally not recommended to expose Consul UI port number in production, at least the UI should not be accessible from outside the platform environment. However, because all the Vault secrets are encrypted before being transmitted and stored in the Consul backend, having access to Consul is not sufficient to access any secrets, the vault data encryption/decryption key would be absolutely necessary.
Open a Web browser on http://<EdgeX Consul Server>:8500/ui
.
On the screenshot below, after selecting SERVICES
and Vault
, the UI
will show the various Vault status (heartbeat and init/unseal states),
coloring the boxes in green, orange or red depending on the level of
importance (info, warning, error). By clicking each of the right side
status indicators, more information will be accessible in order to
better inspect any situation.
As a practical example, we are going to navigate the Consul structure
for Vault in order to check if the API Gateway (Kong) TLS certificate
and private key were fetched and stored accordingly during the
vault-worker
process.
First select KEY/VALUE
menu, and then select vault
root structure:
We are now going to navigate deeper in the vault
tree structure to
reach and display the EdgeX Kong TLS assets. Continue by selecting
logical/
:
{.align-center width="348px" height="287px"}
Then select d7809b...
an arbitrary UID generated and created by Consul
during Vault registration:
{.align-center width="377px" height="216px"}
Select edgex/
:
{.align-center width="419px" height="228px"}
Select pki/
:
{.align-center width="417px" height="222px"}
Select tls/
:
{.align-center width="418px" height="237px"}
Select edgex-kong/
:
{.align-center width="423px" height="233px"}
And we are now finally able to display the encrypted Vault secret containing the API Gateway (Kong) TLS server certificate and its corresponding private key. As you can see on the screenshot below the Vault key/value is encrypted and totally opaque to Consul, the Vault data encryption key (DEK) would be necessary to decrypt these secrets. Each Vault secret is encrypted before being transmitted to Consul node(s).
Shell Access to Consul Container and Using Consul CLI
sh> docker exec -it -e PS1='\u@\h:\w \$ ' edgex-core-consul sh
root@edgex-core-consul:/ # consul members
Node Address Status Type Build Protocol DC Segment
edgex-core-consul 127.0.0.1:8301 alive server 1.1.0 2 dc1 <all>
root@edgex-core-consul:/ # consul catalog nodes
Node ID Address DC
edgex-core-consul e49af36a 127.0.0.1 dc1
root@edgex-core-consul:/ # consul catalog services
consul
edgex-mongo
vault
Note
Line 5: Shows the Consul node status alive
(1 node in EdgeX default
configuration).
Line 9: Shows the Consul nodes (1 node in EdgeX default configuration).
Lines 12-14: Show the Consul registered services.
Configuring the Secret Store
Vault server configuration is essentially concentrated in one JSON file
named local.json
. This file was prepared during the Vault Docker image
build process. In the eventuality of a change, the Vault server
container should be accessed to then modify the JSON file. The absolute
path being /vault/config/local.json
. To reload the new configuration
simply send Vault PID a HUP signal to trigger a configuration reload.
Sample Vault server configuration file:
listener "tcp" {
address = "edgex-vault:8200"
tls_disable = "0"
cluster_address = "edgex-vault:8201"
tls_min_version = "tls12"
tls_client_ca_file ="/vault/config/pki/EdgeXFoundryCA/EdgeXFoundryCA.pem"
tls_cert_file ="/vault/config/pki/EdgeXFoundryCA/edgex-vault.pem"
tls_key_file = "/vault/config/pki/EdgeXFoundryCA/edgex-vault.priv.key"
}
backend "consul" {
path = "vault/"
address = "edgex-core-consul:8500"
scheme = "http"
redirect_addr = "https://edgex-vault:8200"
cluster_addr = "https://edgex-vault:8201"
}
default_lease_ttl = "168h"
max_lease_ttl = "720h"
The listener
clause refers to Vault server process (port, TLS and
server name), the backend
clause refers to the storage backend (i.e.
Consul).
To modify this configuration file, execute a shell session in the running Vault container:
sh> docker exec -it -e PS1='\u@\h:\w \$ ' -e VAULT_CAPATH='/vault/config/pki/EdgeXFoundryCA/EdgeXFoundryCA.pem' edgex-vault sh
root@edgex-vault:/vault # ls -l
total 12
drwxr-xr-x 4 vault vault 4096 Jan 13 13:34 config
drwxr-xr-x 2 vault vault 4096 Jun 7 2018 file
drwxr-xr-x 2 vault vault 4096 Jun 7 2018 logs
Pay attention to the VAULT_CAPATH
environment variable passed to the
session. This is necessary in order to run succesful Vault CLI command.
Every Vault CLI command is a wrapper of the Vault HTTP API. The Vault
server is configured with TLS using X.509 PKI materials generated and
signed by a local self-signed CA (EdgeXFoundryCA). Therefore, in order
for each Vault CLI command (or to that extent cURL commands) to verify
the Vault server TLS certificate, the self-signing CA root certificate
would have to be known by the CLI command interpreter. This
VAULT_CAPATH
variable is checked by every Vault CLI commands,
alternatively each Vault CLI command can specify an option with the same
certificate path if the variable is not set.
The self-signed Root CA certificate path can be found in the Vault
configuration file (see above local.json), with parameter
tls_client_ca_file ="/vault/config/pki/EdgeXFoundryCA/EdgeXFoundryCA.pem"
.
The local.json
configuration file can be read and modified within the
running container:
<root@edgex-vault>:/vault \# cat config/local.json
listener "tcp" {
address = "edgex-vault:8200"
tls\_disable = "0"
cluster\_address ="edgex-vault:8201"
tls\_min\_version = "tls12"
tls\_client\_ca\_file="/vault/config/pki/EdgeXFoundryCA/EdgeXFoundryCA.pem"
tls\_cert\_file="/vault/config/pki/EdgeXFoundryCA/edgex-vault.pem"
tls\_key\_file = "/vault/config/pki/EdgeXFoundryCA/edgex-vault.priv.key"
}
backend "consul" {
path = "vault/"
address = "edgex-core-consul:8500"
scheme = "http"
redirect\_addr ="<https://edgex-vault:8200>"
cluster\_addr ="<https://edgex-vault:8201>"
}
default\_lease\_ttl = "168h"
max\_lease\_ttl = "720h"
A sample Vault CLI command to check Vault status:
root@edgex-vault:/vault # vault status
Key Value
--- -----
Seal Type shamir
Sealed false
Total Shares 1
Threshold 1
Version 0.10.2
Cluster Name vault-cluster-57b3c4ed
Cluster ID fe6d18bf-fa9c-0d52-3278-bca0390af023
HA Enabled true
HA Cluster https://edgex-vault:8201
HA Mode active
All the X.509 PKI materials including the self-signing CA are located
under /vault/config/pki/EdgeXFoundryCA
.
root@edgex-vault:/vault # ls -l config/pki/EdgeXFoundryCA/
total 24
-rw-r--r-- 1 vault vault 956 Dec 5 14:05 EdgeXFoundryCA.pem
-r-------- 1 vault vault 306 Dec 5 14:05 EdgeXFoundryCA.priv.key
-rw-r--r-- 1 vault vault 989 Dec 5 14:05 edgex-kong.pem
-rw------- 1 vault vault 306 Dec 5 14:05 edgex-kong.priv.key
-rw-r--r-- 1 vault vault 1001 Dec 5 14:05 edgex-vault.pem
-rw------- 1 vault vault 306 Dec 5 14:05 edgex-vault.priv.key
Note
Line 3: self-signing root CA certificate.
Line 4: self-signing root CA private key.
Line 5: API Gateway (Kong) TLS server certificate.
Line 6: API Gateway (Kong) TLS server certificate private key.
Line 7: Vault TLS server certificate.
Line 8: Vault TLS server certificate private key.
The CA name (EdgeXFoundryCA) was defined by the pkisetup tool during the Vault image build process. This tool is also responsible for all the TLS server configuration and creation tasks.
If you are willing to change any of the Vault X.509 PKI assets or configuration parameters you will have to modify the pkisetup-vault.json file and rebuild a new Vault Docker image.
Similarly to Vault, each EdgeX Foundry service having a TLS server certificate and private key had its X.509 PKI assets generated and signed during the Vault Docker image build process. Therefore, the API Gateway (Kong) configuration file named pkisetup-kong.json would have to be modified accordingly. A new Vault Docker image would have to be built.
The Vault
Dockerfile
contains the pkisetup
executions, see below for a corresponding
excerpt (highlighted lines):
# Create assets folder (needed for unseal key/s, root token and tmp)
# Run CA/Vault and Kong PKI/TLS setups and peform housekeeping tasks
RUN mkdir /vault/config/assets && \
chown -R vault:vault /vault && \
chmod 644 /vault/config/local.json && \
chmod 744 pkisetup* && \
./pkisetup --config pkisetup-vault.json && \
echo "" && \
./pkisetup --config pkisetup-kong.json && \
chown -R vault:vault /vault/config/pki && \
rm -f /vault/pkisetup /vault/pkisetup-vault.json /vault/pkisetup-kong.json
EdgeX Foundry Docker environment implements a basic Vault/Consul
architecture that does not provide high availability guaranties. Only
one Consul server and one Vault server will be running. In a more
sophisticated production environment it would be possible to build a
reliable high availability infrastructure regarding Consul and Vault. To
facilitate the setup of a minimal failover architecture the
security-secret-store
repository provides a sample folder named
Full-Architecture-Prototype
that contains necessary materials (scripts, helpers, configurations,
etc.) to achieve that goal.
These samples describe an architecture design with two Vault servers in failover mode (active/standby), using each one a Consul client, which subsequently connects to a Consul cluster of 3 nodes (minimal Raft concensus quorum). The Consul clients and servers (nodes) have redundant paths.
Using the Secret Store
1st alternative: executing a shell session in the active Vault container to run Vault CLI commands.
See paragraph Configuring the Secret Store to have more details on
the VAULT_CAPATH
environment variable.
See HashiCorp Vault API documentation for further details on syntax and usage (https://www.vaultproject.io/api/).
Execute a shell session in the running Vault container:
sh> docker exec -it -e PS1='\u@\h:\w \$ ' -e VAULT_CAPATH='/vault/config/pki/EdgeXFoundryCA/EdgeXFoundryCA.pem' edgex-vault sh
Locate the assets
folder, and the resp-init.json
file:
root@edgex-vault:/vault # ls -l config/assets/
total 12
-rw-r--r-- 1 root root 366 Jan 13 13:35 admin-token.json
-rw-r--r-- 1 root root 365 Jan 13 13:35 kong-token.json
-rw-r--r-- 1 root root 241 Jan 13 13:35 resp-init.json
Inspect the resp-init.json
file to grab the Vault Root Token:
root@edgex-vault:/vault # cat config/assets/resp-init.json
{
"keys":["564b9444eebe28b393c21a4dca1e32835b7dc27f5da03b73d22b666cb20224a9"],
"keys_base64":["VkuURO6+KLOTwhpNyh4yg1t9wn9doDtz0itmbLICJKk="],
"recovery_keys":null,
"recovery_keys_base64":null,
"root_token":"01dbbae4-353a-8cdf-8189-4d50e5535a6f"
}
Login to Vault using Vault CLI and the gathered Root Token:
root@edgex-vault:/vault # vault login 01dbbae4-353a-8cdf-8189-4d50e5535a6f
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.
Key Value
--- -----
token 01dbbae4-353a-8cdf-8189-4d50e5535a6f
token_accessor 4d5eabf7-8710-81b1-b6a4-9ba17fdfdeb7
token_duration ∞
token_renewable false
token_policies [root]
Perform an introspection lookup
on the current token login:
root@edgex-vault:/vault # vault token lookup
Key Value
--- -----
accessor 4d5eabf7-8710-81b1-b6a4-9ba17fdfdeb7
creation_time 1547386542
creation_ttl 0
display_name root
entity_id n/a
expire_time <nil>
explicit_max_ttl 0
id 01dbbae4-353a-8cdf-8189-4d50e5535a6f
meta <nil>
num_uses 0
orphan true
path auth/token/root
policies [root]
ttl 0
Note
Lines 9 & 10: the Root Token is the only token that has no expiration enforcement rules (Time to Live TTL counter).
Perform a check on the current token login to display the corresponding capabilities (policies):
root@edgex-vault:/vault # vault token capabilities 01dbbae4-353a-8cdf-8189-4d50e5535a6f
root
Perform a list
request to display the currently mounted secret
backends:
root@edgex-vault:/vault # vault secrets list
Path Type Accessor Description
---- ---- -------- -----------
cubbyhole/ cubbyhole cubbyhole_ad070930 per-token private secret storage
identity/ identity identity_5397dc2f identity store
secret/ kv kv_2362c227 key/value secret storage
sys/ system system_410e4276 system endpoints used for control, policy and debugging
Note
Line 5: EdgeX Foundry platform is using the Key/Value secret storage
named secret
Let's drill down into the secret
k/v storage and walk through a
predefined hierarchical tree structure (path).
Note
the pkisetup
tool used during the Vault Docker image build process
generates all the related X.509 TLS materials. The vault-worker
service is storing each service materials into Vault using arbitrary
paths, setting up access policies accordingly.
For example, the API Gateway (Kong) service X.509 TLS materials:
root@edgex-vault:/vault # vault list secret
Keys
----
edgex/
root@edgex-vault:/vault # vault list secret/edgex
Keys
----
pki/
root@edgex-vault:/vault # vault list secret/edgex/pki
Keys
----
tls/
root@edgex-vault:/vault # vault list secret/edgex/pki/tls
Keys
----
edgex-kong
Displaying the API gateway (Kong) service X.509 TLS materials (TLS
certificate cert
& corresponding private key key
):
root@edgex-vault:/vault # vault read secret/edgex/pki/tls/edgex-kong
Key Value
--- -----
refresh_interval 168h
cert -----BEGIN CERTIFICATE-----
MIICrjCCAjWgAwIBAgIQDvZxhmU3nyG4cwXlQesMFDAKBggqhkjOPQQDAzB7MQsw
CQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28x
FzAVBgNVBAoTDkVkZ2VYRm91bmRyeUNBMRUwEwYDVQQLEwxFZGdlWEZvdW5kcnkxt
FzAVBgNVBAMTDkVkZ2VYRm91bmRyeUNBMB4XDTE4MTIwNTE0MDUyOFoXDTI4MTIw
NTE0MDUyOFowazELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1T
YW4gRnJhbmNpc2NvMRMwEQYDVQQKEwplZGdleC1rb25nMQ0wCwYDVQQLEwRLb25n
MRMwEQYDVQQDEwplZGdleC1rb25nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE2dnb
EboXET1TjzmWKFv3A0wklwNbs9t9JLT0ecpQr64a277UnTAQhgCv2e2/x9EP4eta
gSlz5PCqdAykWW0URIEPSwUKWmx4x1DBwyUD2oDOPsFrywIVEC3DlqQAL6huo4GN
MIGKMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMB
Af8EAjAAMB8GA1UdIwQYMBaAFFX63XbmPNpLceOJYyt2Y+LfW/gxMDQGA1UdEQQt
MCuCCmVkZ2V4LWtvbmeCEGVkZ2V4LWtvbmcubG9jYWyBC2FkbWluQGxvY2FsMAoG
CCqGSM49BAMDA2cAMGQCMCaH3sSKq6nlr6hBJx82wYEiK4slMbySiQZg5mLcwrsQ
tIPGcQ2lgBdQYzI3ymOS5gIwNhpQmo/p3hkoFzA4rxIAZx/GUgZan51JlXW0rpgz
4HerRLe55EmvF10mF7VCGOXe
-----END CERTIFICATE-----
key -----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDC6BRUXqkJbey765+8b
Oib2qG/jbai2rzp0+NQyJv4ijAyYjJlxhVGggZqPPBy8baqhZANiAATZ2dsRuhcR
PVOPOZYoW/cDTCSXA1uz230ktPR5ylCvrhrbvtSdMBCGAK/Z7b/H0Q/h61qBKXPk
8Kp0DKRZbRREgQ9LBQpabHjHUMHDJQPagM4+wWvLAhUQLcOWpAAvqG4=
-----END PRIVATE KEY-----
Note
These two key values are in PEM format.
2nd alternative: using the Vault Web UI.
Open a browser session on https://<EdgeX Vault Server>:8200
, accept
the self-signed TLS server certificate and sign-in with the Root Token
(see above 1st alternative to learn how to fetch this token):
{.align-center width="606px" height="504px"}
Upper left corner of the current Vault UI session, the sign-out menu displaying the current token name:
{.align-center width="275px" height="156px"}
Select the Vault secret backend:
Navigate the API Gateway (Kong) service X.509 TLS materials path (edgex/pki/tls/edgex-kong):
The Vault UI also allows entering Vault CLI commands (see above 1st alternative) using an embedded console:
3rd alternative: directly using the Vault HTTP API with cURL commands.
See paragraph Configuring the Secret Store to have more details on
the --cacert
option (identical purpose as the VAULT_CAPATH
environment variable for Vault CLI).
See paragraph Using the Secret Store to have more details on
gathering the Vault Root Token (ref:
/vault/config/assets/resp-init.json
).
See HashiCorp Vault API documentation for further details on syntax and usage (https://www.vaultproject.io/api/).
Displaying (GET) the API gateway (Kong) service X.509 TLS materials (TLS certificate cert & corresponding private key key):
curl -s --cacert /vault/config/pki/EdgeXFoundryCA/EdgeXFoundryCA.pem \
--location \
--header "X-Vault-Token: 01dbbae4-353a-8cdf-8189-4d50e5535a6f" \
--request GET \
https://edgex-vault:8200/v1/secret/edgex/pki/tls/edgex-kong | jq
Note
Line 2: the --location
option allows following a redirection
(necessary when using a Vault cluster)
Line 5: the Vault API path prefix /v1/secret
and the API Gateway X.509
TLS materials k/v /edgex/pki/tls/edgex-kong
.
Line 5: the jq
tool is a lightweight and flexible command-line JSON
processor (https://stedolan.github.io/jq/) allowing JSON pretty
printing in the terminal.
Sample JSON returned:
{
"request_id": "eaa80a1b-0d31-8d11-6ce1-8d9aa3ac6a19",
"lease_id": "",
"renewable": false,
"lease_duration": 604800,
"data": {
"cert": "-----BEGIN CERTIFICATE-----\nMIICrjCCAjWgAwIBAgIQDvZxhmU3nyG4cwXlQesMFDAKBggqhkjOPQQDAzB7MQsw\nCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28x\nFzAVBgNVBAoTDkVkZ2VYRm91bmRyeUNBMRUwEwYDVQQLEwxFZGdlWEZvdW5kcnkx\nFzAVBgNVBAMTDkVkZ2VYRm91bmRyeUNBMB4XDTE4MTIwNTE0MDUyOFoXDTI4MTIw\nNTE0MDUyOFowazELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1T\nYW4gRnJhbmNpc2NvMRMwEQYDVQQKEwplZGdleC1rb25nMQ0wCwYDVQQLEwRLb25n\nMRMwEQYDVQQDEwplZGdleC1rb25nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE2dnb\nEboXET1TjzmWKFv3A0wklwNbs9t9JLT0ecpQr64a277UnTAQhgCv2e2/x9EP4eta\ngSlz5PCqdAykWW0URIEPSwUKWmx4x1DBwyUD2oDOPsFrywIVEC3DlqQAL6huo4GN\nMIGKMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMB\nAf8EAjAAMB8GA1UdIwQYMBaAFFX63XbmPNpLceOJYyt2Y+LfW/gxMDQGA1UdEQQt\nMCuCCmVkZ2V4LWtvbmeCEGVkZ2V4LWtvbmcubG9jYWyBC2FkbWluQGxvY2FsMAoG\nCCqGSM49BAMDA2cAMGQCMCaH3sSKq6nlr6hBJx82wYEiK4slMbySiQZg5mLcwrsQ\ntIPGcQ2lgBdQYzI3ymOS5gIwNhpQmo/p3hkoFzA4rxIAZx/GUgZan51JlXW0rpgz\n4HerRLe55EmvF10mF7VCGOXe\n-----END CERTIFICATE-----\n",
"key": "-----BEGIN PRIVATE KEY-----\nMIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDC6BRUXqkJbey765+8b\nOib2qG/jbai2rzp0+NQyJv4ijAyYjJlxhVGggZqPPBy8baqhZANiAATZ2dsRuhcR\nPVOPOZYoW/cDTCSXA1uz230ktPR5ylCvrhrbvtSdMBCGAK/Z7b/H0Q/h61qBKXPk\n8Kp0DKRZbRREgQ9LBQpabHjHUMHDJQPagM4+wWvLAhUQLcOWpAAvqG4=\n-----END PRIVATE KEY-----\n"
},
"wrap_info": null,
"warnings": null,
"auth": null
}
Note
Lines 7 & 8: the two key values (TLS certificate cert
& corresponding
private key key
) are in PEM format
(https://tools.ietf.org/html/rfc1421).
Displaying (LIST) the root key path in the Vault secret
backend for
the EdgeX Foudry platform (edgex
):
curl -s --cacert /vault/config/pki/EdgeXFoundryCA/EdgeXFoundryCA.pem \
--location \
--header "X-Vault-Token: 01dbbae4-353a-8cdf-8189-4d50e5535a6f" \
--request LIST \
https://edgex-vault:8200/v1/secret | jq
Sample JSON returned:
{
"request_id": "0e0ea024-176d-21b3-73cb-99f17729b230",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"keys": [
"edgex/"
]
},
"wrap_info": null,
"warnings": null,
"auth": null
}
Displaying (GET) the Vault seal status
(API path: /v1/sys/seal-status
):
curl -s --cacert /vault/config/pki/EdgeXFoundryCA/EdgeXFoundryCA.pem \
--location \
--header "X-Vault-Token: 01dbbae4-353a-8cdf-8189-4d50e5535a6f" \
--request GET \
https://edgex-vault:8200/v1/sys/seal-status | jq
Sample JSON returned:
{
"type": "shamir",
"sealed": false,
"t": 1,
"n": 1,
"progress": 0,
"nonce": "",
"version": "0.10.2",
"cluster_name": "vault-cluster-57b3c4ed",
"cluster_id": "fe6d18bf-fa9c-0d52-3278-bca0390af023"
}
Note
Line 3: Vault is unsealed therefore available and ready for requests.
Line 4 & 5: Vault Shamir Secret Sharing default configuration for EdgeX Foundry: 1 share with threshold 1 (no sharding).
See also
Some of the command used in implementing security services have man-style documentation:
- security-file-token-provider - Generate Vault tokens for EdgeX services
- security-secrets-setup - Creates an on-device public-key infrastructure (PKI) to secure microservice secret management