一、安全功能流程概述
安全功能主要涉及到如下几个模块:vault, vault-worker , security-api-gateway, kong
下面是各模块功能介绍:
- vault:生成root CA 证书与私钥、edgex-vault证书与私钥(支持https用)
- vault-worker:(若不存在)生成kong证书与私钥,并保存到vault中去;向consul发起心跳检测,看vault是否存活
- security-api-gateway:根据token,从vault服务器中获取证书,并上传到kong服务器;管理kong用户、生成JWT字串
- kong:实现edgexfoundry各微服务的安全访问(获取了JWT,通JWT字串访问后端微服务)
1、vault启动时,生成root CA 证书与私钥,并生成edgex-vault证书与私钥
2、worker启动时,每定时(3分钟)执行如下动作
1.1、探测试vault是否活着,并注册到consul
1.2、上面vault活着的话,则执行vault-kong.sh脚本,它主要动作如下:
1. 2.1 看是vault服务器上是否存在edgex-kong证书(如果本地edgex-kong证书没有,则先生成edgex-kong证书),没有的话就上传到vault
3、security-api-gateway :初始化kong(从vault获取证书密钥,并上传到kong)
二、功能代码详细解读
2.1 镜像edgexfoundry/docker-edgex-vault制作过程
2.1.1 Dockerfile.vault文件
#vault官方基础镜像
FROM vault:0.10.2
LABEL license='SPDX-License-Identifier: Apache-2.0' \
copyright='Copyright (c) 2018: ForgeRock'
USER root
WORKDIR /vault/config
#使用tls 方式,将vault注册到consul
COPY local-tls.json ./local.json
RUN chmod 777 /vault/config/local.json
# Vault PKI/TLS setup/config and X.509 materials
WORKDIR /vault
COPY pki-setup.sh .
COPY pki-setup-config-vault.env .
# install pre-requisites for pki setup: bash/openssl, pki/tls setup and housekeeping
RUN apk --no-cache update && \
apk --no-cache add bash openssl && \ #安装bash openssl工具,用于生成证书
chown -R vault:vault /vault && \
chmod 777 /vault/config/local.json && \
chmod 744 pki-setup* && \
./pki-setup.sh pki-setup-config-vault.env && \ #生成根证书密钥,及vault证书密钥,后面会详述
chown -R vault:vault /vault/pki && \
chmod 750 /vault/pki && \
chmod 640 /vault/pki/*/* && \
apk del --purge bash openssl && \ #安全起见,删除bash openssl工具、删除脚本,也可以减小镜像的空间大小
rm -f /vault/pki-setup.sh /vault/pki-setup-config-vault.env && \
rm -f /var/cache/apk/*
# Set the default path for Docker exec sessions
WORKDIR /
我们结合docker-compose.yml文件,看vault服务一段,如下
vault:
image: edgexfoundry/docker-edgex-vault:security
container_name: edgex-vault
hostname: edgex-vault
networks:
- edgex-network
ports:
- "8200:8200"
cap_add:
- "IPC_LOCK"
command: "server" #vault容器启动后,执行server命令,拉起vault服务
environment:
- 'VAULT_ADDR=https://edgex-vault:8200' #vault地外服务地址
- 'VAULT_CONFIG_DIR=/vault/config' # vault启动配置文件,镜像制作是local.json已拷贝到此目录
- 'VAULT_UI=true' # 开启UI 。在浏览器访问https://localhost:8200/ui ,可打开管理界面
volumes:
- vault-config:/vault/config
- vault-pki:/vault/pki
- vault-file:/vault/file
- vault-logs:/vault/logs
depends_on:
- volume
- consul
2.1.1 local-tls.json文件
#tcp监听配置
listener "tcp" {
address = "edgex-vault:8200" #访问地址
tls_disable = "0" #开启tls,即https访问
cluster_address = "edgex-vault:8201" #集群地址
tls_min_version = "tls12"
tls_client_ca_file ="/vault/pki/EdgeXFoundryTrustCA/EdgeXFoundryTrustCA.pem" #客户端访问的证书
tls_cert_file ="/vault/pki/EdgeXFoundryTrustCA/edgex-vault.pem" #支持https用的证书
tls_key_file = "/vault/pki/EdgeXFoundryTrustCA/edgex-vault.priv.key" #支持https用的密钥
}
#注册consul的配置
backend "consul" {
path = "vault/" #consul的services显示的名字
address = "edgex-core-consul:8500" #consul服务地址
scheme = "http" #注册使用的协议
redirect_addr = "https://edgex-vault:8200" #真实的地址,对应上段的address ,通过consul访问vault时,转发到此地址
cluster_addr = "https://edgex-vault:8201" # 对应上段的cluster_address ,意义同上行
}
default_lease_ttl = "168h"
max_lease_ttl = "720h"
2.1.2 pki-setup.sh 与 pki-setup-config-vault.env文件
在Dockerfile.vault 文件中,有一行 ./pki-setup.sh pki-setup-config-vault.env && \ ,下面了解一下执行过程。
2.1.2.1 pki-setup.sh文件
#!/bin/bash
# pki-setup.sh 用法,即help
function usage() {
echo "Usage : pki-setup.sh </path/to/config-script>"
echo "Example: pki-setup.sh pki-setup-config.env"
echo " "
}
function initEnv() {
# --------------------------------------------
DEBUG=$1 # Debug switch: "debug-off" or "debug-on"
#openssl是否存在
OPENSSL_BIN=$(which openssl)
if [[ $? == 1 ]]; then
echo "ERROR: openssl binary not found or not in the path"
exit
fi
echo ">> openssl binary found: ${OPENSSL_BIN}"
echo ">>" $(openssl version)
echo ""
# --------------------------------------------
#将pki-setup-config-vault.env 或 pki-setup-config-kong.env里定义的内容装入到shell变量
source $1 # </path/to/config-script>
# --------------------------------------------
}
function initCA() {
# ---------------------------------------------
# CA (self signing) name, keystore alias and file (.pem .priv.key)
CA_NAME=${ORG}$1 #值为:EdgeXFoundryTrustCA
# CA root certificate subject
# CA_C
# CA_ST
# CA_L
# CA_O
CA_CN=${ORG}" "$1 #值为:EdgeXFoundry TrustCA
CA_HOME=${SETUP_PKI}/${CA_NAME} #值为:/vault/pki/EdgeXFoundryTrustCA
CERT_EXTENSIONS="ssl_server.ext" #扩展文件,在签发证书请求(signCertReq)时需要
if [[ ${CREATE_CA} == "true" ]]; then #setup vault需要,kong时不需要
# Create configuration directory (CA_HOME)
echo ">> Creating a new Root CA Certificate"
rm -rf ${CA_HOME}
mkdir -p ${CA_HOME} #创建目录:/vault/pki/EdgeXFoundryTrustCA
chmod -R 755 ${SETUP_PKI}
# Generate an initial serial number for signed/generated certificates
echo `date +%s` > ${CA_HOME}/${CA_NAME}.srl #以当前时间,生成初始序号,放入EdgeXFoundryTrustCA.srl文件,在签发证书求(signCertReq)时需要
# Create a certificate extensions file:
# The generated server certificate will be for TLS server usage only!
cat > ${CA_HOME}/${CERT_EXTENSIONS} <<EOF #向扩展ssl_server.ext文件里写内容
nsCertType=server #证书类型为server
EOF
else
echo ">> Reusing existing Root CA Certificate"
# Certificate serial number is incremented during CA signing process
fi
# ---------------------------------------------
}
#定义FQDN ,当DOMAIN为local时,FQDN 值为 HOST (即:edgex-vault),否则为 edgex-vault.XXX (XXX 代表DOMAIN),
#本例FQDN 为edgex-vault 或 edgex-kong ,此值在生成证书求证时要用到,作为CN的值
function initVaultServer() {
# --------------------------------------------
# Vault FQDN, files (.pem .req .priv.key)
if [[ ${DOMAIN} == "local" ]]; then
FQDN=$1
else
FQDN=$1.${DOMAIN}
fi
# Vault TLS certificate subject
# TLS_C
# TLS_ST
# TLS_L
# TLS_O
TLS_CN=${FQDN}
# ---------------------------------------------
}
#创建CA ,生成EdgeXFoundryTrustCA.pem 与 EdgeXFoundryTrustCA.priv.key 文件
function genRootCA() {
# Generate Root CA private key (RSA/4096) and trusted Root CA certificate (1825 days = 5 years)
${OPENSSL_BIN} req -x509 -sha256 -nodes -days 1825 -newkey rsa:4096 \
-subj "/C=${CA_C}/ST=${CA_ST}/L=${CA_L}/O=${CA_O}/CN=${CA_CN}/emailAddress=${CA_NAME}@${DOMAIN}" \
-keyout ${CA_HOME}/${CA_NAME}.priv.key -out ${CA_HOME}/${CA_NAME}.pem
# Generate Root CA private key (ECDSA/secp384r1) and trusted Root CA certificate
#${OPENSSL_BIN} req -x509 -sha256 -nodes -days 1825 -newkey ec:secp384r1 \
# -subj "/C=${CA_C}/ST=${CA_ST}/L=${CA_L}/O=${CA_O}/CN=${CA_CN}/emailAddress=${CA_NAME}@${DOMAIN}" \
# -keyout ${CA_HOME}/${CA_NAME}.priv.key -out ${CA_HOME}/${CA_NAME}.pem
if [[ ${DEBUG} == "debug-on" ]]; then
echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
echo " CA Signing Certificate"
echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
${OPENSSL_BIN} x509 -in ${CA_HOME}/${CA_NAME}.pem -noout -text
echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
fi
}
#生成edgex-vault.req 或 edgex-kong.req证书请求文件
function genVaultCertReq() {
# Generate TLS server private key (RSA/4096)
${OPENSSL_BIN} genrsa -out ${CA_HOME}/${FQDN}.priv.key 4096
# Generate TLS server private key (ECDSA/secp384r1)
#${OPENSSL_BIN} ecparam -genkey -name secp384r1 -out ${CA_HOME}/${FQDN}.priv.key
# Generate server certificate signing request for TLS server certificate (CN must equal the FQDN)
${OPENSSL_BIN} req -sha256 -new -key ${CA_HOME}/${FQDN}.priv.key \
-out ${CA_HOME}/${FQDN}.req \
-subj "/C=${TLS_C}/ST=${TLS_ST}/L=${TLS_L}/O=${TLS_O}/CN=${TLS_CN}/emailAddress=admin@${DOMAIN}" #TLS_CN为edgex-vault
if [[ ${DEBUG} == "debug-on" ]]; then
echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
echo " TLS Server Certificate Request for: ${FQDN}"
echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
${OPENSSL_BIN} req -in ${CA_HOME}/${FQDN}.req -noout -text
echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
fi
}
#签发证书,生成edgex-vault.pem edgex-vault.priv.key 或 edgex-kong.pem edgex-kong.priv.key
#输入证书请求文件(edgex-vault.req 或 edgex-kong.req), CA 的扩展文件 与 CA的序列文件srl
function signVaultCertReq() {
# CA signs TLS server certificate request with its trusted root certificate,
# and creates the final TLS server certificate (1825 days = 5 years)
${OPENSSL_BIN} x509 -sha256 -req -in ${CA_HOME}/${FQDN}.req \
-CA ${CA_HOME}/${CA_NAME}.pem -CAkey ${CA_HOME}/${CA_NAME}.priv.key \
-CAserial ${CA_HOME}/${CA_NAME}.srl \
-extfile ${CA_HOME}/${CERT_EXTENSIONS} \
-days 1825 -outform PEM -out ${CA_HOME}/${FQDN}.pem
if [[ ${DEBUG} == "debug-on" ]]; then
echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
echo " TLS Server Certificate for: ${FQDN}"
echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
${OPENSSL_BIN} x509 -in ${CA_HOME}/${FQDN}.pem -noout -text
echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
fi
}
#将签发后证书与密钥,存储为PKCS12格式,此格式的好处是需要密码访问
#即生成 edgex-kong.p12 或 edgex-vault.p12 文件
function storeToPKCS12() {
# Create PKCS12 container for TLS server certificate and SK
# SK: Secret Key aka Private key
# PK: Public Key
#
${OPENSSL_BIN} pkcs12 -export -in ${CA_HOME}/${FQDN}.pem \
-inkey ${CA_HOME}/${FQDN}.priv.key -out ${CA_HOME}/${FQDN}.p12 \
-name ${FQDN} -password pass{PKCS12_PASSWORD}
}
function houseKeeping() {
chmod 644 ${SETUP_PKI}/${CA_NAME}/*
}
# ----------------- Common Functions ------------------------
#===================================== MAIN ====================================
# pki-setup.sh arguments check
# 检测pki-setup.sh 后是否有参数 ,没有的话,若调用usage函数显法用法
if [[ $# != 1 ]]; then
echo "SYNTAX ERROR"
usage
exit
fi
# 检测pki-setup.sh 后是的参数文件是否存在,即pki-setup-config-vault.env 或 pki-setup-config-kong.env
if [[ ! -f $1 ]]; then
echo "FILE $1 NOT FOUND"
exit
fi
# 环境初始化:判断openssl是否安装、将pki-setup-config-vault.env 或 pki-setup-config-kong.env里定义的内容装入到shell变量
echo ">> Create binary paths and folder paths"
initEnv $1
echo ">> Prepare Root CA Certificate"
# 初始化CA: 1、创建/vault/pki/EdgeXFoundryTrustCA目录 2、准备好 EdgeXFoundryTrustCA.srl 与 ssl_server.ext,它们用于签发(子)证书
initCA TrustCA
echo ">> Prepare Vault Server TLS certificate"
#定义FQDN ,当DOMAIN为local时,FQDN 值为 HOST (即:edgex-vault 或 edgex-kong),否则为 edgex-vault.XXX (XXX 代表DOMAIN),
#本例FQDN 的值为edgex-vault 或 edgex-kong ,此值在生成证书求证时要用到,作为CN的值
initVaultServer ${HOST} #HOST值是:edgex-vault 或 edgex-kong
if [[ ${CREATE_CA} == "true" ]]; then #edgex-vault 需要创建CA
echo ">> Generate Root CA private key and trusted root certificate"
genRootCA debug-off #创建CA ,生成EdgeXFoundryTrustCA.pem 与 EdgeXFoundryTrustCA.priv.key 文件
fi
echo ">> Generate TLS server private key"
echo ">> Generate server certificate signing request for TLS server certificate (CN must equal the FQDN)"
#生成edgex-vault.req 或 edgex-kong.req证书请求文件
genVaultCertReq debug-off
echo ">> Root CA signs TLS server certificate request with its trusted root certificate,"
echo ">> and creates the final TLS server certificate"
#签发证书,生成edgex-vault.pem edgex-vault.priv.key 或 edgex-kong.pem edgex-kong.priv.key
#输入证书请求文件(edgex-vault.req 或 edgex-kong.req), CA 的扩展文件 与 CA的序列文件srl
signVaultCertReq debug-off
echo ">> Create PKCS12 containers for TLS server certificate and SK"
#将签发后证书与密钥,存储为PKCS12格式,此格式的好处是需要密码访问
#即生成 edgex-kong.p12 或 edgex-vault.p12 文件
storeToPKCS12
#安全起见,限制目录权限/vault/pki/EdgeXFoundryTrustCA
houseKeeping
echo ">> PKI setup script completed."
exit
#EOF
===========================================
vault镜像从上面文件,可以归纳实现了如下功能:
- 自建了CA,并用此CA签发了vault证书,另外一个kong证书也类似生成
- local-tls.json定义了vault采用tls 方式访问,注册到consul
2.2 镜像edgexfoundry/docker-edgex-vault-worker制作过程
2.2.1 Dockerfile.vault-worker文件
#使用alpine基础镜像,这是个很小的linux操作系统,只有几M大
FROM alpine:latest
LABEL license='SPDX-License-Identifier: Apache-2.0' \
copyright='Copyright (c) 2018: ForgeRock'
USER root
# Vault init and unseal scripts
WORKDIR /vault
# Copy main launcher [CMD]
COPY vault-worker.sh .
# Copy init-unseal process
COPY vault-init-unseal.sh .
# Copy PKI/TLS script/config for Kong
COPY pki-setup.sh .
COPY pki-setup-config-kong.env .
# Copy PKI/TLS setup and Vault import for Kong
COPY vault-kong.sh .
# Create a vault user and group so the IDs get set the same way
# Set the files ownership and rights
# Install pre-requisites for pki setup: bash/curl/jq/dig/openssl
RUN addgroup vault && \
adduser -S -G vault vault && \
chmod 700 vault-*.sh pki-setup* && \
chown vault:vault vault-*.sh pki-setup* && \
apk --no-cache update && \
apk --no-cache add bash curl jq bind-tools openssl && \
rm -f /var/cache/apk/*
# Set the operation command
ENTRYPOINT ["/bin/bash", "-c"]
#容器启动后执行vault-worker.sh
CMD ["/vault/vault-worker.sh"]
2.2.2 vault-worker.sh文件
#!/bin/bash
while true
do
# Init/Unseal processes
/vault/vault-init-unseal.sh
# If Vault init/unseal was OK... eventually prepare materials for Kong
if [[ $? == 0 ]]; then
/vault/vault-kong.sh # 如果/vault/vault-init-unseal.sh执行成功则执行此步
fi
sleep ${WATCHDOG_DELAY} #此变量来自docker-compose.yaml文件中的environment定义,定为3分种循环执行一次
done
exit
#EOF
我们结合docker-compose.yml文件,看 vault-worker服务一段,如下
vault-worker:
image: edgexfoundry/docker-edgex-vault-worker:security
container_name: edgex-vault-worker
hostname: edgex-vault-worker
networks:
- edgex-network
environment:
- 'WATCHDOG_DELAY=3m' #这个在vault-worker.sh有引用
volumes:
- vault-pki:/vault/pki
- vault-file:/vault/file
depends_on:
- volume
- consul
- vault
2.2.3 vault-init-unseal.sh文件
#功能:实现vault初始化、启封、检查vault是否注册到consul
#!/bin/bash
# ----------------- Common Functions ------------------------
#
function houseKeeping() {
rm -f ${_TMP}
rm -f ${_PAYLOAD_INIT}
rm -f ${_PAYLOAD_UNSEAL}
}
#初始化vault
function vaulInitialization() {
echo ">> (1) Vault Initialization Process"
# Create the Vault init request payload with only 1 key and obviously threshold 1
#创建payload-init.json文件,写入如下内容
cat > ${_PAYLOAD_INIT} <<EOF
{
"secret_shares": 1,
"secret_threshold": 1
}
EOF
# ---------------------------------------------------------------------------
# Vault Initialization API
# ---------------------------------------------------------------------------
curl -sw 'HTTP-STATUS: %{http_code}\n' ${_TLS} ${_REDIRECT} \
--request PUT \
--data @${_PAYLOAD_INIT} \ #
${_HTTP_SCHEME}://${_VAULT_SVC}{_VAULT_PORT}/v1/sys/init > ${_TMP}
#上面命令真实值为:curl sw 'HTTP-STATUS: %{http_code}\n' --cacert /vault/pki/EdgeXFoundryTrustCA/EdgeXFoundryTrustCA.pem --location \
# --request PUT \
# --data /vault/file/payload-init.json https://edgex-vault:8200/v1/sys/init > /vault/file/_tmp.vault
#说明: https://edgex-vault:8200 在docker-compose.yaml中的vault服务中environment 定义:VAULT_ADDR
# EdgeXFoundryTrustCA.pem 在local-tls.json中tls_client_ca_file定义的要一致
}
# ---------------------------------------------------------------------------
# Check http status code returned by init request
result=$(tail -1 ${_TMP} | grep "HTTP-STATUS:" | cut -d' ' -f2)
case ${result} in
# If Vault initialization OK
#
# 执行上面的curl后,若初始化成功,会返回类似如下的字串
# {"keys":["8e70bcf6ba046b59857cba1ec6495c58b53f6c60e37f871e53b4b391ff43ec59"],"keys_base64":["jnC89roEa1mFfLoexklcWLU/bGDjf4ceU7Szkf9D7Fk="],"root_token":"6e2e099f-e5a4-028b-6b84-f11d1fb1ad9d"}
# http响应状态码为200时,说明成功,将返回内容中的keys的值放入_INIT_KEY ,root_token的值放入 _ROOT_TOKEN
"200")
# let's grab the init key
_INIT_KEY=$(head -1 ${_TMP} | jq -r '.keys | .[0]')
# let's grab the root token
_ROOT_TOKEN=$(head -1 ${_TMP} | jq -r '.root_token')
# save the key and the root token JSON (strip HTTP-STATUS)
#resp-init.json存放类似 {"keys":["8e70bcf6ba046b59857cba1ec6495c58b53f6c60e37f871e53b4b391ff43ec59"],"keys_base64":["jnC89roEa1mFfLoexklcWLU/bGDjf4ceU7Szkf9D7Fk="],"root_token":"6e2e099f-e5a4-028b-6b84-f11d1fb1ad9d"}
head -1 ${_TMP} | jq '.' > ${_RESP_INIT}
chown vault:vault ${_RESP_INIT}
echo ">> Vault successfully initialized"
;;
# If Vault already initialized
#
# Example:
# {"errors":["Vault is already initialized"]}
# http响应状态码为400时,说明Vault 已经初始化过,直接从resp-init.json取出_INIT_KEY 与 _ROOT_TOKEN
"400")
# let's grab the error message in case...
result=$(head -1 ${_TMP} | jq -r '.errors | .[0]')
echo ">> ${result}"
# let's go unseal... but before we need:
# 1) Check previous init response JSON still exists
if [[ -f ${_RESP_INIT} ]]; then
# 2) Grab the init key from previous init
_INIT_KEY=$(cat ${_RESP_INIT} | jq -r '.keys | .[0]')
# 3) Grab the root token from previous init
_ROOT_TOKEN=$(cat ${_RESP_INIT} | jq -r '.root_token')
else
echo ">> Vault initialization error!"
echo ">> Previous init response file not found: ${_RESP_INIT}"
_EXIT="1"
fi
;;
#
# Unattended error...
#
*)
echo ">> Vault initialization unattended error"
_EXIT="1"
;;
esac
# Handle the error exit use case for Vault init process
if [[ ${_EXIT} == "1" ]]; then
echo "==> Vault init request response:"
cat ${_TMP}
echo ">>"
fi
return ${_EXIT}
}
# vault初始化后,默认是密封的,需要正确启封才可用
function vaultUnsealing() {
echo ">> (2) Vault Unseal Process"
# https://www.vaultproject.io/api/system/seal-status.html
# Create the Vault unseal request payload with the unseal key
#创建payload-unseal.json文件,写入{ "key": "${_INIT_KEY}"}
cat > ${_PAYLOAD_UNSEAL} <<EOF
{
"key": "${_INIT_KEY}" #初始化vault时获理的值_INIT_KEY
}
EOF
# ---------------------------------------------------------------------------
# Vault Unsealing API
# ---------------------------------------------------------------------------
curl -sw 'HTTP-STATUS: %{http_code}\n' ${_TLS} ${_REDIRECT} \
--request PUT \
--data @${_PAYLOAD_UNSEAL} \
${_HTTP_SCHEME}://${_VAULT_SVC}{_VAULT_PORT}/v1/sys/unseal > ${_TMP}
#上面命令真实值为:curl sw 'HTTP-STATUS: %{http_code}\n' --cacert /vault/pki/EdgeXFoundryTrustCA/EdgeXFoundryTrustCA.pem --location \
# --request PUT \
# --data /vault/file/payload-unseal.json https://edgex-vault:8200/v1/sys/init > /vault/file/_tmp.vault
# ---------------------------------------------------------------------------
# Check http status code returned by unseal request
result=$(tail -1 ${_TMP} | grep "HTTP-STATUS:" | cut -d' ' -f2)
# If Vault unsealing OK
#
# Example:
# {"type":"shamir","sealed":false,"t":1,"n":1,"progress":0,"nonce":"","version":"0.10.2","cluster_name":"vault-cluster-1df0f671","cluster_id":"10f1a1eb-ad7a-511f-06af-aab9c370e412"}
# HTTP-STATUS: 200
#
# Remark: unsealing Vault when already unsealed generates same output response with code 200
# http响应状态码为200时,说明成功
if [[ ${result} == "200" ]]; then
# let's grab the sealed state boolean
result=$(head -1 ${_TMP} | jq -r '.sealed')
if [[ ${result} == "false" ]]; then #"sealed":false 说明启封状态,可以用了
# save the unseal JSON response (strip HTTP-STATUS)
#保存到resp-unseal.json,内容类似 {"type":"shamir","sealed":false,"t":1,"n":1,"progress":0,"nonce":"","version":"0.10.2","cluster_name":"vault-cluster-1df0f671","cluster_id":"10f1a1eb-ad7a-511f-06af-aab9c370e412"}
head -1 ${_TMP} | jq '.' > ${_RESP_UNSEAL} #保存到resp-unseal.json
chown vault:vault ${_RESP_UNSEAL}
echo ">> Vault successfully unsealed"
else
echo ">> Vault unseal ok but incoherent sealed status!"
_EXIT="1"
fi
else
echo ">> Vault unseal error!"
_EXIT="1"
fi
# Handle the error exit use case for Vault unseal process
if [[ ${_EXIT} == "1" ]]; then
echo "==> Vault unseal request response:"
cat ${_TMP}
echo ">>"
fi
return ${_EXIT}
}
# 检测vault是否成功注册到consul
function vaultRegistered() {
sleep 3 # Allow Consul DNS table refresh
echo ">> Check Vault is registered as a service in Consul"
echo ">> DNS requests to Consul service on port 8600/tcp"
echo -n "vault.service.consul: "
dig +short +tcp -p8600 @edgex-core-consul vault.service.consul #正常时返回“edgex-vault.”
echo -n "active.vault.service.consul: "
dig +short +tcp -p8600 @edgex-core-consul active.vault.service.consul #正常时返回“edgex-vault.”
echo ""
return ${_EXIT} #此段始终返回0,这个可能有问题,没有对上面的输出判断来改变_EXIT的值
}
# ----------------- Common Functions ------------------------
#===================================== MAIN INIT ===============================
# Variables and parameters
_VAULT_DIR="/vault"
_VAULT_CONFIG_DIR="${_VAULT_DIR}/config"
_VAULT_PKI_DIR="${_VAULT_DIR}/pki"
_VAULT_FILE_DIR="${_VAULT_DIR}/file"
_PAYLOAD_INIT="${_VAULT_FILE_DIR}/payload-init.json"
_PAYLOAD_UNSEAL="${_VAULT_FILE_DIR}/payload-unseal.json"
_RESP_INIT="${_VAULT_FILE_DIR}/resp-init.json"
_RESP_UNSEAL="${_VAULT_FILE_DIR}/resp-unseal.json"
_VAULT_CONFIG="${_VAULT_CONFIG_DIR}/local.json"
_TMP="${_VAULT_FILE_DIR}/_tmp.vault"
_EXIT="0"
_CA="EdgeXFoundryTrustCA"
_CA_DIR="${_VAULT_PKI_DIR}/${_CA}"
_CA_PEM="${_CA_DIR}/${_CA}.pem"
_TLS=" --cacert ${_CA_PEM}"
_REDIRECT=" --location" # If HTTP temporary redirect (HTTP STATUS 307) follow it
_HTTP_SCHEME="https"
_VAULT_SVC="edgex-vault"
_EDGEX_DOMAIN=""
_VAULT_PORT="8200"
#===================================== MAIN PROC ===============================
# 0) Cleanup up previous temp files and payloads
houseKeeping
# 1) Init Vault
#初始化Vault
vaulInitialization
# Handle the error exit use case for Vault init process
if [[ $? == "0" ]]; then
# 2) Unseal Vault
#Vault 启封
vaultUnsealing
# Handle the error exit use case for Vault unseal process
if [[ $? == "0" ]]; then
# 4) Check Vault was successfully registered as a Consul service
vaultRegistered
fi
fi
# 5) Cleanup up temp files and payloads
houseKeeping
exit ${_EXIT}
#EOF
进入vault容器命令
- docker exec -it bb28361c0bac /bin/sh
复制代码
token存储在:/vault/file/resp-init.json
2.2.4 vault-kong.sh文件
#功能:将 edgex-kong.pem、edgex-kong.priv.key 存储到vault中
#!/bin/bash
function houseKeeping() {
rm -f ${_TMP}
rm -f ${_PAYLOAD_KONG}
}
# ----------------- Common Functions ------------------------
#===================================== MAIN ====================================
# Variables and parameters
_VAULT_DIR="/vault"
_VAULT_CONFIG_DIR="${_VAULT_DIR}/config"
_VAULT_PKI_DIR="${_VAULT_DIR}/pki"
_VAULT_FILE_DIR="${_VAULT_DIR}/file"
_PAYLOAD_INIT="${_VAULT_FILE_DIR}/payload-init.json"
_PAYLOAD_UNSEAL="${_VAULT_FILE_DIR}/payload-unseal.json"
_PAYLOAD_KONG="${_VAULT_FILE_DIR}/payload-kong.json"
_RESP_INIT="${_VAULT_FILE_DIR}/resp-init.json"
_RESP_UNSEAL="${_VAULT_FILE_DIR}/resp-unseal.json"
_VAULT_CONFIG="${_VAULT_CONFIG_DIR}/local.json"
_TMP="${_VAULT_FILE_DIR}/_tmp.vault"
_EXIT="0"
_CA="EdgeXFoundryTrustCA"
_CA_DIR="${_VAULT_PKI_DIR}/${_CA}"
_CA_PEM="${_CA_DIR}/${_CA}.pem"
_TLS=" --cacert ${_CA_PEM}"
_KONG_SVC="edgex-kong"
_KONG_PEM="${_CA_DIR}/${_KONG_SVC}.pem"
_KONG_SK="${_CA_DIR}/${_KONG_SVC}.priv.key"
_REDIRECT=" --location" # If HTTP temporary redirect (HTTP STATUS 307) follow it
_HTTP_SCHEME="https"
_VAULT_SVC="edgex-vault"
_EDGEX_DOMAIN=""
_VAULT_PORT="8200"
_VAULT_API_PATH_KONG="/v1/secret/edgex/pki/tls/${_KONG_SVC}"
houseKeeping # temp files and payloads
# Generate Kong PKI/TLS materials if they haven't been already...
#如果edgex-kong.pem 或 edgex-kong.priv.key 文件不存在,则由CA创建签发
if [[ (! -f ${_KONG_PEM}) || (! -f ${_KONG_SK}) ]]; then
echo ">> (3) Create PKI materials for Kong TLS server certificate"
/vault/pki-setup.sh /vault/pki-setup-config-kong.env
chown vault:vault ${_CA_DIR}/${_KONG_SVC}.*
else
echo ">> (3) PKI materials for Kong TLS server certificate already created"
#查看证书信息
openssl x509 -noout -subject -in ${_KONG_PEM}
openssl x509 -noout -issuer -in ${_KONG_PEM}
fi
echo ""
echo ">> (4) Fetch the Vault Root Token"
#从resp-init.json文件中获取Vault Root Token
_ROOT_TOKEN=$(cat ${_RESP_INIT} | jq -r '.root_token')
echo ">> (5) Test if the Kong Key/Value already exists"
#检测Kong在vault中是否存在
curl -sw 'HTTP-STATUS: %{http_code}\n' ${_TLS} ${_REDIRECT} \
--header "X-Vault-Token: ${_ROOT_TOKEN}" \
--request GET \
${_HTTP_SCHEME}://${_VAULT_SVC}{_VAULT_PORT}${_VAULT_API_PATH_KONG} > ${_TMP}
#上面命令真实值为:curl sw 'HTTP-STATUS: %{http_code}\n' --cacert /vault/pki/EdgeXFoundryTrustCA/EdgeXFoundryTrustCA.pem --location \
# --header "X-Vault-Token: 6e2e099f-e5a4-028b-6b84-f11d1fb1ad9d" \
# --request GET \
# https://edgex-vault:8200/v1/secret/edgex/pki/tls/edgex-kong > /vault/file/_tmp.vault
# Check http status code returned by get request
result=$(tail -1 ${_TMP} | grep "HTTP-STATUS:" | cut -d' ' -f2)
case ${result} in
# HTTP-STATUS: 200 -> key found
"200")
echo "==> Key/Value already in Vault, done!"
;;
# HTTP-STATUS: 404 -> Key not found
#如果Kong不存在vault中,则post edgex-kong.pem、edgex-kong.priv.key到vault中去(存储起来)
"404")
echo ">> (6) Create the Kong JSON with TLS certificate and private key (base64 encoded)"
jq -n --arg cert "$(cat ${_KONG_PEM}|base64)" \ #将edgex-kong.pem base64编码后赋给变量cert
--arg sk "$(cat ${_KONG_SK}|base64)" \ #将edgex-kong.priv.key base64编码后赋给变量sk
'{certcert,keysk}' > ${_PAYLOAD_KONG} #生成payload-kong.json
echo ">> (7) Load the Kong JSON PKI materials in Vault"
echo "$(cat ${_PAYLOAD_KONG})"
curl -sw 'HTTP-STATUS: %{http_code}\n' ${_TLS} ${_REDIRECT} \
--header "X-Vault-Token: ${_ROOT_TOKEN}" \
--header "Content-Type: application/json" \
--request POST \
--data @${_PAYLOAD_KONG} \
${_HTTP_SCHEME}://${_VAULT_SVC}{_VAULT_PORT}${_VAULT_API_PATH_KONG} > ${_TMP}
#上面命令真实值为:curl sw 'HTTP-STATUS: %{http_code}\n' --cacert /vault/pki/EdgeXFoundryTrustCA/EdgeXFoundryTrustCA.pem --location \
# --header "X-Vault-Token: 6e2e099f-e5a4-028b-6b84-f11d1fb1ad9d" \
# --header "Content-Type: application/json" \ # --request POST \
# --data /vault/file/payload-kong.json \
# https://edgex-vault:8200/v1/secret/edgex/pki/tls/edgex-kong > /vault/file/_tmp.vault
# Check http status code returned by post request
result=$(tail -1 ${_TMP} | grep "HTTP-STATUS:" | cut -d' ' -f2)
#如果返回http 状代码为204,说明保存到vault成功
if [[ ${result} == "204" ]]; then
echo "==> Key/Value successfully written in Vault, done!"
else
echo "==> Error while writing Key/Value in Vault!"
_EXIT="1"
fi
;;
*)
echo "==> Unattended Error while reading Key/Value"
_EXIT="1"
;;
esac
# Handle the error exit use case
if [[ ${_EXIT} == "1" ]]; then
echo "==> Vault request response:"
cat ${_TMP}
echo ">>"
fi
echo ""
houseKeeping # temp files and payloads
exit ${_EXIT}
#EOF
2.3 edgexfoundry/docker-edgex-proxy-go镜像
首先,将security-api-gateway项目源码拷到ubuntn服务器edgexsecurity 目录下,如下结构:
- myEdgex@instance-nbpv5z80:~/gopath/src$ ls
- edgexsecurity github.com
- myEdgex@instance-nbpv5z80:~/gopath/src/edgexsecurity$ ls
- build.bat CONTRIBUTING.md Docker Dockerfile.orgin glide.yaml Makefile vendor version.go
- build.sh core Dockerfile glide.lock LICENSE README.md VERSION
复制代码
然后执行如下命令,安装go依赖,vendor会存放起来以备后用
- glide install
复制代码
2.3.1 Dockerfile.gobuildinside文件
我们采用在镜像内编译的方法,所以解读一下这个文件,此文件我作了修改,内容如下:
#第一阶段,编译为可执行:edgexproxy
#golang:1.9-alpine作为基础镜像,即带有golang的alpine linux操作系统
FROM golang:1.9-alpine AS builder
#待编译的源码务必要放在/go/src下,否由vendor不起作用
RUN mkdir -p /go/src/edgexsecurity
WORKDIR /go/src/edgexsecurity
COPY . .
#这样会很慢,会访问网络,屏蔽了如下两行
#RUN apk add make
#RUN go get github.com/dghubble/sling && go get github.com/BurntSushi/toml && go get github.com/edgexfoundry/edgex-go/pkg/clients/logging && go get github.com/dgrijalva/jwt-go
#务必要以CGO_ENABLED=0方式编译,否则会报错:standard_init_linux.go:190: exec user process caused "no such file or directory"
RUN cd core && CGO_ENABLED=0 go build -o edgexproxy #编译好的edgexproxy可执行文件放在core目录下
COPY Docker/res/configuration.toml core/res/
#第二阶段,制作真正的镜像,从第一阶段中拷文件
FROM scratch
LABEL license='SPDX-License-Identifier: Apache-2.0' \
copyright='Copyright (c) 2018: Dell, Cavium'
#edgexproxy 必须放在与res同级目录,否则找不到configuration.toml,因为main.go文件中这样定义相对目录configFileLocation := flag.String("configfile", "res/configuration.toml", "configuration file")
#除非你在ENTRYPOINT 中指定configfile
WORKDIR /edgexsecurity/core
COPY --from=builder /go/src/edgexsecurity/core/edgexproxy /edgexsecurity/core/
COPY --from=builder /go/src/edgexsecurity/core/res/configuration.toml /edgexsecurity/core/res/configuration.toml
ENTRYPOINT ["./edgexproxy","--init=true"]
至此,镜像文件已制作完毕!
2.3.2 security-api-gateway (简称edgexproxy) 是如何工作的及使用?
请参考:security-api-gateway 模块详解