Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NOISSUE - Vault integration as an addon #1266

Merged
merged 2 commits into from
Oct 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,22 @@ MF_CERTS_SIGN_RSA_BITS=2048
MF_CERTS_VAULT_HOST=
MF_CERTS_VAULT_PKI_PATH=pki_int
tritao marked this conversation as resolved.
Show resolved Hide resolved
MF_CERTS_VAULT_ROLE=agent
MF_CERTS_VAULT_TOKEN=s.nArgw6xn3uIOfA7nfKk8LFaW
MF_CERTS_VAULT_TOKEN=

### Vault
MF_VAULT_HOST=vault
MF_VAULT_PORT=8200
MF_VAULT_UNSEAL_KEY_1=
MF_VAULT_UNSEAL_KEY_2=
MF_VAULT_UNSEAL_KEY_3=
MF_VAULT_TOKEN=
MF_VAULT_CA_NAME=mainflux
MF_VAULT_CA_ROLE_NAME=mainflux
MF_VAULT_CA_DOMAIN_NAME=mainflux.com
MF_VAULT_CA_OU='Mainflux Cloud'
MF_VAULT_CA_ORG='Mainflux Company'
MF_VAULT_CA_COUNTRY=Serbia
MF_VAULT_CA_LOC=BG

### LoRa
MF_LORA_ADAPTER_LOG_LEVEL=debug
Expand Down
1 change: 1 addition & 0 deletions docker/addons/vault/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
data
tritao marked this conversation as resolved.
Show resolved Hide resolved
100 changes: 100 additions & 0 deletions docker/addons/vault/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
This is Vault service deployment to be used with Mainflux.

When the Vault service is started, some initialization steps need to be done to set things up.

## Setup

The following scripts are provided, which work on the running Vault service in Docker.

1. `vault-init.sh`

Calls `vault operator init` to perform the initial vault initialization and generates
a `data/secrets` file which contains the Vault unseal keys and root tokens.

After this step, the corresponding Vault environment variables (`MF_VAULT_TOKEN`, `MF_VAULT_UNSEAL_KEY_1`,
`MF_VAULT_UNSEAL_KEY_2`, `MF_VAULT_UNSEAL_KEY_3`) should be updated in `.env` file.

Example contents for `data/secrets`:

```
Unseal Key 1: Ay0YZecYJ2HVtNtXfPootXK5LtF+JZoDmBb7IbbYdLBI
Unseal Key 2: P6hb7x2cglv0p61jdLyNE3+d44cJUOFaDt9jHFDfr8Df
Unseal Key 3: zSBfDHzUiWoOzXKY1pnnBqKO8UD2MDLuy8DNTxNtEBFy
Unseal Key 4: 5oJuDDuMI0I8snaw/n4VLNpvndvvKi6JlkgOxuWXqMSz
Unseal Key 5: ZhsUkk2tXBYEcWgz4WUCHH9rocoW6qZoiARWlkE5Epi5

Initial Root Token: s.V2hdd00P4bHtUQnoWZK2hSaS

Vault initialized with 5 key shares and a key threshold of 3. Please securely
distribute the key shares printed above. When the Vault is re-sealed,
restarted, or stopped, you must supply at least 3 of these keys to unseal it
before it can start servicing requests.

Vault does not store the generated master key. Without at least 3 key to
reconstruct the master key, Vault will remain permanently sealed!

It is possible to generate new unseal keys, provided you have a quorum of
existing unseal keys shares. See "vault operator rekey" for more information.
bash-4.4

Use 3 out of five keys presented and put it into .env file and than start the composition again Vault should be in unsealed state ( take a note that this is not recommended in terms of security, this is deployment for development) A real production deployment can use Vault auto unseal mode where vault gets unseal keys from some 3rd party KMS ( on AWS for example)
```

2. `vault-unseal.sh`

This can be run after the initialization to unseal Vault, which is necessary for it to be used to store and/or get
secrets.

The unseal environment variables need to be set in `.env` for the script to work.

This script should not be necessary to run after the initial setup, since the Vault service unseals itself when
starting the container.

3. `vault-set-pki.sh`

This script is used to generate the root certificate, intermediate certificate and HTTPS server certificate.
After it runs, it copes the necessary certificates and keys to the `docker/ssl/certs` folder.

The CA parameters are obtained from the environment variables starting with `MF_VAULT_CA` in `.env` file.

## Vault CLI

It can also be useful to run the Vault CLI for inspection and administration work.

This can be done directly using the Vault image in Docker: `docker run -it mainflux/vault:latest vault`

```
Usage: vault <command> [args]

Common commands:
read Read data and retrieves secrets
write Write data, configuration, and secrets
delete Delete secrets and configuration
list List data or secrets
login Authenticate locally
agent Start a Vault agent
server Start a Vault server
status Print seal and HA status
unwrap Unwrap a wrapped secret

Other commands:
audit Interact with audit devices
auth Interact with auth methods
debug Runs the debug command
kv Interact with Vault's Key-Value storage
lease Interact with leases
monitor Stream log messages from a Vault server
namespace Interact with namespaces
operator Perform operator-specific tasks
path-help Retrieve API help for paths
plugin Interact with Vault plugins and catalog
policy Interact with policies
print Prints runtime configurations
secrets Interact with secrets engines
ssh Initiate an SSH session
token Interact with tokens
```

### Vault Web UI

The Vault Web UI is accessible by default on `http://localhost:8200/ui`.
10 changes: 10 additions & 0 deletions docker/addons/vault/config.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
storage "file" {
path = "/vault/file"
}

listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = 1
}

ui = true
42 changes: 42 additions & 0 deletions docker/addons/vault/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright (c) Mainflux
# SPDX-License-Identifier: Apache-2.0

# This docker-compose file contains optional Vault service for Mainflux platform.
# Since this is optional, this file is dependent of docker-compose file
# from <project_root>/docker. In order to run these services, execute command:
# docker-compose -f docker/docker-compose.yml -f docker/addons/vault/docker-compose.yml up
# from project root. Vault default port (8200) is exposed, so you can use Vault CLI tool for
# vault inspection and administration, as well as access the UI.

version: '3.7'

networks:
docker_mainflux-base-net:
external: true

volumes:
mainflux-vault-volume:

services:
vault:
image: vault:latest
container_name: mainflux-vault
ports:
- ${MF_VAULT_PORT}:8200
networks:
- docker_mainflux-base-net
volumes:
- mainflux-vault-volume:/vault/file
- mainflux-vault-volume:/vault/logs
- ./config.hcl:/vault/config/config.hcl
- ./entrypoint.sh:/entrypoint.sh
environment:
VAULT_ADDR: http://127.0.0.1:${MF_VAULT_PORT}
MF_VAULT_PORT: ${MF_VAULT_PORT}
MF_VAULT_UNSEAL_KEY_1: ${MF_VAULT_UNSEAL_KEY_1}
MF_VAULT_UNSEAL_KEY_2: ${MF_VAULT_UNSEAL_KEY_2}
MF_VAULT_UNSEAL_KEY_3: ${MF_VAULT_UNSEAL_KEY_3}
entrypoint: /bin/sh
command: /entrypoint.sh
cap_add:
- IPC_LOCK
23 changes: 23 additions & 0 deletions docker/addons/vault/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/dumb-init /bin/sh

VAULT_CONFIG_DIR=/vault/config

docker-entrypoint.sh server &
VAULT_PID=$!

sleep 2

echo $MF_VAULT_UNSEAL_KEY_1
echo $MF_VAULT_UNSEAL_KEY_2
echo $MF_VAULT_UNSEAL_KEY_3

if [[ ! -z "${MF_VAULT_UNSEAL_KEY_1}" ]] &&
[[ ! -z "${MF_VAULT_UNSEAL_KEY_2}" ]] &&
[[ ! -z "${MF_VAULT_UNSEAL_KEY_3}" ]]; then
echo "Unsealing Vault"
vault operator unseal ${MF_VAULT_UNSEAL_KEY_1}
vault operator unseal ${MF_VAULT_UNSEAL_KEY_2}
vault operator unseal ${MF_VAULT_UNSEAL_KEY_3}
fi

wait $VAULT_PID
10 changes: 10 additions & 0 deletions docker/addons/vault/vault-init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash
set -euo pipefail

vault() {
docker exec -it mainflux-vault vault "$@"
}

mkdir -p data

vault operator init 2>&1 | tee >(sed -r 's/\x1b\[[0-9;]*m//g' > data/secrets)
142 changes: 142 additions & 0 deletions docker/addons/vault/vault-set-pki.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/bin/bash
set -euo pipefail

scriptdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
export MAINFLUX_DIR=$scriptdir/../../../

cd $scriptdir

readDotEnv() {
set -o allexport
source $MAINFLUX_DIR/.env
set +o allexport
}

vault() {
docker exec -it mainflux-vault vault "$@"
}

vaultEnablePKI() {
vault secrets enable -path pki_${MF_VAULT_CA_NAME} pki
vault secrets tune -max-lease-ttl=87600h pki_${MF_VAULT_CA_NAME}
}

vaultAddRoleToSecret() {
vault write pki_${MF_VAULT_CA_NAME}/roles/${MF_VAULT_CA_NAME} \
allow_any_name=true \
max_ttl="4300h" \
default_ttl="4300h" \
generate_lease=true
}

vaultGenerateRootCACertificate() {
echo "Generate root CA certificate"
vault write -format=json pki_${MF_VAULT_CA_NAME}/root/generate/exported \
common_name="\"$MF_VAULT_CA_DOMAIN_NAME CA Root\"" \
ou="\"$MF_VAULT_CA_OU\""\
organization="\"$MF_VAULT_CA_ORG\"" \
country="\"$MF_VAULT_CA_COUNTRY\"" \
locality="\"$MF_VAULT_CA_LOC\"" \
ttl=87600h | tee >(jq -r .data.certificate >data/${MF_VAULT_CA_NAME}_ca.crt) \
>(jq -r .data.issuing_ca >data/${MF_VAULT_CA_NAME}_issuing_ca.crt) \
>(jq -r .data.private_key >data/${MF_VAULT_CA_NAME}_ca.key)
}

vaultGenerateIntermediateCAPKI() {
echo "Generate Intermediate CA PKI"
export NAME_PKI_INT_PATH="pki_int_$MF_VAULT_CA_NAME"
vault secrets enable -path=${NAME_PKI_INT_PATH} pki
vault secrets tune -max-lease-ttl=43800h ${NAME_PKI_INT_PATH}
}

vaultGenerateIntermediateCSR() {
echo "Generate intermediate CSR"
vault write -format=json ${NAME_PKI_INT_PATH}/intermediate/generate/exported \
common_name="$MF_VAULT_CA_DOMAIN_NAME Intermediate Authority" \
| tee >(jq -r .data.csr >data/${MF_VAULT_CA_NAME}_int.csr) \
>(jq -r .data.private_key >data/${MF_VAULT_CA_NAME}_int.key)
}

vaultSignIntermediateCSR() {
echo "Sign intermediate CSR"
docker cp data/${MF_VAULT_CA_NAME}_int.csr mainflux-vault:/vault/${MF_VAULT_CA_NAME}_int.csr
vault write -format=json pki_${MF_VAULT_CA_NAME}/root/sign-intermediate \
csr=@/vault/${MF_VAULT_CA_NAME}_int.csr \
| tee >(jq -r .data.certificate >data/${MF_VAULT_CA_NAME}_int.crt) \
>(jq -r .data.issuing_ca >data/${MF_VAULT_CA_NAME}_int_issuing_ca.crt)
}

vaultInjectIntermediateCertificate() {
echo "Inject Intermediate Certificate"
docker cp data/${MF_VAULT_CA_NAME}_int.crt mainflux-vault:/vault/${MF_VAULT_CA_NAME}_int.crt
vault write ${NAME_PKI_INT_PATH}/intermediate/set-signed certificate=@/vault/${MF_VAULT_CA_NAME}_int.crt
}

vaultGenerateIntermediateCertificateBundle() {
echo "Generate intermediate certificate bundle"
cat data/${MF_VAULT_CA_NAME}_int.crt data/${MF_VAULT_CA_NAME}_ca.crt \
> data/${MF_VAULT_CA_NAME}_int_bundle.crt
}

vaultSetupIssuingURLs() {
echo "Setup URLs for CRL and issuing"
VAULT_ADDR=http://$MF_VAULT_HOST:$MF_VAULT_PORT
vault write ${NAME_PKI_INT_PATH}/config/urls \
issuing_certificates="$VAULT_ADDR/v1/${NAME_PKI_INT_PATH}/ca" \
crl_distribution_points="$VAULT_ADDR/v1/${NAME_PKI_INT_PATH}/crl"
}

vaultSetupCARole() {
echo "Setup CA role"
vault write ${NAME_PKI_INT_PATH}/roles/${MF_VAULT_CA_ROLE_NAME} \
allow_subdomains=true \
allow_any_name=true \
max_ttl="720h"
}

vaultGenerateServerCertificate() {
echo "Generate server certificate"
vault write -format=json ${NAME_PKI_INT_PATH}/issue/${MF_VAULT_CA_ROLE_NAME} \
common_name="$MF_VAULT_CA_DOMAIN_NAME" ttl="8670h" \
| tee >(jq -r .data.certificate >data/${MF_VAULT_CA_DOMAIN_NAME}.crt) \
>(jq -r .data.private_key >data/${MF_VAULT_CA_DOMAIN_NAME}.key)
}

vaultCleanupFiles() {
docker exec mainflux-vault sh -c 'rm -rf /vault/*.{crt,csr}'
}

if ! command -v jq &> /dev/null
then
echo "jq command could not be found, please install it and try again."
exit
fi

readDotEnv

mkdir -p data

vault login ${MF_VAULT_TOKEN}

vaultEnablePKI
vaultAddRoleToSecret
vaultGenerateRootCACertificate
vaultGenerateIntermediateCAPKI
vaultGenerateIntermediateCSR
vaultSignIntermediateCSR
vaultInjectIntermediateCertificate
vaultGenerateIntermediateCertificateBundle
vaultSetupIssuingURLs
vaultSetupCARole
vaultGenerateServerCertificate
vaultCleanupFiles

echo "Copying certificate files"

cp -v data/${MF_VAULT_CA_DOMAIN_NAME}.crt ${MAINFLUX_DIR}/docker/ssl/certs/mainflux-server.crt
cp -v data/${MF_VAULT_CA_DOMAIN_NAME}.key ${MAINFLUX_DIR}/docker/ssl/certs/mainflux-server.key
cp -v data/${MF_VAULT_CA_NAME}_int.key ${MAINFLUX_DIR}/docker/ssl/certs/ca.key
cp -v data/${MF_VAULT_CA_NAME}_int.crt ${MAINFLUX_DIR}/docker/ssl/certs/ca.crt
cp -v data/${MF_VAULT_CA_NAME}_int_bundle.crt ${MAINFLUX_DIR}/docker/ssl/bundle.pem

exit 0
22 changes: 22 additions & 0 deletions docker/addons/vault/vault-unseal.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

#!/bin/bash
set -euo pipefail

scriptdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
export MAINFLUX_DIR=$scriptdir/../../../

readDotEnv() {
set -o allexport
source $MAINFLUX_DIR/.env
set +o allexport
}

vault() {
docker exec -it mainflux-vault vault "$@"
}

readDotEnv

vault operator unseal ${MF_VAULT_UNSEAL_KEY_1}
vault operator unseal ${MF_VAULT_UNSEAL_KEY_2}
vault operator unseal ${MF_VAULT_UNSEAL_KEY_3}