【微服务安全】使用 Spring Boot、Kafka、Vault 和 Kubernetes 保护微服务间通信——第 3 部分:设置 Vault
Chinese, Simplified
- 第 1 部分:简介和架构
- 第 2 部分:设置 Kubernetes 和 Kafka
- 第 3 部分:设置 Vault <--本文
- 第 4 部分:构建微服务
- 第 5 部分:部署和测试
设置Vault
- 如果我们回想一下我们的架构,我们说过我们将提供端到端加密。虽然 Vault 将保护我们的信息,但谁在保护 Vault? - 为此,我们将使用经典的 TLS 连接 (HTTPS) 来保护我们与 Vault 的通信。我们将为此创建一个自签名证书。
使用 TLS 安装
- 以下步骤主要取自以下教程:https://www.vaultproject.io/docs/platform/k8s/helm/examples/standalone-…
- 在开始之前,让我们验证您是否拥有 OpenSSL。启动命令行控制台并运行以下命令
> openssl version
- 如果你有 openssl,你应该得到下面的响应(或类似的)。如果您没有 OpenSSL,请从此处 [https://www.openssl.org/] 下载并安装
LibreSSL 2.6.5
- 接下来,运行以下命令为我们的设置设置环境变量
> SERVICE=vault > NAMESPACE=default > SECRET_NAME=vault-server-tls > TMPDIR=/tmp
SERVICE:包含 Vault 的服务名称
NAMESPACE:运行 Vault 的 Kubernetes 命名空间
SECRET_NAME:包含 TLS 证书的 Kubernetes 密钥
TMPDIR:临时工作目录
- 然后,创建一个用于签名的密钥
> openssl genrsa -out ${TMPDIR}/vault.key 2048
- 我们现在将创建一个证书签名请求 (CSR)。首先,让我们创建一个 CSR 配置文件:
> cat <<EOF >${TMPDIR}/csr.conf [req] req_extensions = v3_req distinguished_name = req_distinguished_name [req_distinguished_name] [ v3_req ] basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = ${SERVICE} DNS.2 = ${SERVICE}.${NAMESPACE} DNS.3 = ${SERVICE}.${NAMESPACE}.svc DNS.4 = ${SERVICE}.${NAMESPACE}.svc.cluster.local IP.1 = 127.0.0.1 EOF
- 然后,我们将创建 CSR 文件本身
> openssl req -new -key ${TMPDIR}/vault.key -subj "/CN=${SERVICE}.${NAMESPACE}.svc" -out ${TMPDIR}/server.csr -config ${TMPDIR}/csr.conf
- 现在,我们将创建实际的证书。从命令行运行
> export CSR_NAME=vault-csr
- 然后我们将创建一个 csr.yaml 文件
> cat <<EOF >${TMPDIR}/csr.yaml apiVersion: certificates.k8s.io/v1beta1 kind: CertificateSigningRequest metadata: name: ${CSR_NAME} spec: groups: - system:authenticated request: $(cat ${TMPDIR}/server.csr | base64 | tr -d '\n') usages: - digital signature - key encipherment - server auth EOF
- 然后我们将在 Kubernetes 中创建一个证书签名请求
> kubectl create -f ${TMPDIR}/csr.yaml
- 要验证是否创建了证书签名请求,请运行以下命令。
> kubectl get csr ${CSR_NAME}
- 然后,批准 CSR。通过此命令,您已签署 CSR
> kubectl certificate approve ${CSR_NAME}
- 之后,将证书导出到名为 vault.crt 的文件中
> serverCert=$(kubectl get csr ${CSR_NAME} -o jsonpath='{.status.certificate}') > echo "${serverCert}" | openssl base64 -d -A -out ${TMPDIR}/vault.crt
- 同时导出 Kubernetes CA
> kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}' | base64 -d > ${TMPDIR}/vault.ca
- 创建一个秘密存储上面创建的所有文件
> kubectl create secret generic ${SECRET_NAME} --namespace ${NAMESPACE} --from-file=vault.key=${TMPDIR}/vault.key --from-file=vault.crt=${TMPDIR}/vault.crt --from-file=vault.ca=${TMPDIR}/vault.ca
- 接下来,我们将在 $PROJECTS/k8s 文件夹中创建一个名为 custom-values.yaml 的文件。该文件的内容应为:
global: enabled: true tlsDisable: false server: extraEnvironmentVars: VAULT_CACERT: /vault/userconfig/vault-server-tls/vault.ca extraVolumes: - type: secret name: vault-server-tls standalone: enabled: true config: | listener "tcp" { address = "[::]:8200" cluster_address = "[::]:8201" tls_cert_file = "/vault/userconfig/vault-server-tls/vault.crt" tls_key_file = "/vault/userconfig/vault-server-tls/vault.key" tls_client_ca_file = "/vault/userconfig/vault-server-tls/vault.ca" } storage "file" { path = "/vault/data" }
- 然后,我们将使用 Helm 安装通过我们的自签名 SSL 保护的独立 Vault。将命令行控制台指向文件夹 $PROJECTS/k8s 并运行:
> helm repo add hashicorp https://helm.releases.hashicorp.com > helm install vault -f custom-values.yaml hashicorp/vault
- 要验证 Vault 是否正在运行,请运行以下命令:
> kubectl get pods
- 您应该在 pod 列表中看到 vault
保险库 Kubernetes 服务
- 运行以下命令以获取服务列表
> kubectl get svc
- 您应该看到下面的列表。Vault服务的名称是“Vault”。由于 Vault 在默认集群中运行,因此 Vault 服务(在 Kubernetes 内)的完全限定域名应该是 vault.default.svc。
初始化Vault
- 要初始化 Vault,我们需要运行以下命令:
> kubectl exec -ti vault-0 -- vault operator init -format=json > cluster-keys.json
- 这将创建一个包含 5 个键的 json 文件。 json 文件的内容可能如下所示:
{ "keys": [ "dea...94", "0c0...10", "800...29", "88e...1e", "5by...8z" ], "keys_base64": [ "...", "...", "...", "...", "..." ], "root_token": "s.Tyu...d" }
开封保险库
- 初始化 Vault 时,它以密封模式运行。我们需要解封它才能使 Vault 有用。
- 您还需要在每次重新启动时解封 Vault
- 在上面的步骤(Initialize Vault)中,我们创建了一个包含 5 个密钥的子文件。我们需要提供 3 个密钥来解封 Vault。要解封,请运行以下命令:
> kubectl exec -ti vault-0 -- vault operator unseal
- 此命令将提示输入密钥。键入其中一个键并按回车键。
- 您将得到以下结果:
Key Value --- ----- Seal Type shamir Initialized true Sealed false Total Shares 5 Threshold 3 Version 1.5.2 Cluster Name vault-cluster-e4b6a573 Cluster ID 80...b HA Enabled false
- 每次使用不同的键重新运行以上命令 2 次
- 祝贺。你有开封Vault。
暴露 Vault web ui
- 接下来,我们将使用他之前在 Kafka 中使用的 port-forward 命令将 Vault 暴露给 Kubernetes 之外的世界。请注意,使用 Vault,我们通过端口转发而不是 pod 公开服务。
> kubectl port-forward service/vault 8200:8200
创建中转(transit )引擎
- Vault Transit 引擎是 Vault 提供的一种加密即服务工具。我们将使用它来加密我们的消息。
- 将浏览器指向地址 https://localhost:8200(注意它是 https)。您可能会看到一个对话框,您需要在其中接受自签名证书。单击确定。
- 然后您将看到下面的页面。在 Token 字段中,键入刚才在初始化期间创建的 cluster-key.json 文件中的 root_token 值。
- 然后,点击启用新引擎
- 选择过境,然后点击下一步
- 然后,在 Path 字段中输入“transit”,然后单击 Enable Engine
- 您将获得您的秘密中列出的运输引擎
- 在中转页面上,单击“创建加密密钥”
- 在“创建加密密钥”页面中,在名称字段中输入 my-encryption-key,然后单击创建加密密钥
- 您将看到已创建加密密钥
- 祝贺。您创建了一个 Transit 引擎
管理访问权限和策略
- 单击菜单策略。然后单击“创建 ACL 策略”
- 在名称中,输入 my-encrypt-policy。在策略中放入下面的代码。这将允许此策略仅加密以下数据。完成后,单击“创建策略”:
path "transit/decrypt/my-encryption-key" { capabilities = [ "update" ] }
- 再次单击策略 > 创建 ACL 策略。在 Name 中输入“my-encrypy-policy”,在 Policy 中输入下面的代码。然后点击“创建策略”
path "transit/encrypt/my-encryption-key" { capabilities = [ "update" ] }
- 您现在应该设置 2 个策略:
- 然后启动你的命令行控制台并运行下面的 curl 命令。请注意,下面的值 s.Tyu…d 是从上面的文件 cluster-keys.json 获得的根令牌的值。我们创建的策略 my-encrypt-policy 的值也在下面指定。 (我们在 curl 命令上使用选项 -k 因为我们使用的证书是自签名的。没有 -k,curl 命令将抱怨我们的证书)
> curl --header "X-Vault-Token: s.Tyu...d" --request POST --data '{"policies": ["my-encrypt-policy"]}' -k https://localhost:8200/v1/auth/token/create
- 你应该得到下面的回应。请注意客户端令牌的 values.O1...k。让我们称之为加密令牌
{ "request_id":"d5b47829-53f0-a35e-4b7c-0e9d8f4f3cbc", "lease_id":"", "renewable":false, "lease_duration":0, "data":null, "wrap_info":null, "warnings":null, "auth":{ "client_token":"s.O1sI3QhVvSmbG1lyfKSMXXFk", "accessor":"T...8", "policies":[ "default", "my-encrypt-policy" ], "token_policies":[ "default", "my-encrypt-policy" ], "metadata":null, "lease_duration":2764800, "renewable":true, "entity_id":"", "token_type":"service", "orphan":false } }
- 让我们重复相同的 curl 命令,这一次,我们将使用 my-decrypt-policy 策略
curl --header "X-Vault-Token: s.WuTNTDpBqsspinc6dlDN0cbz" --request POST --data '{"policies": ["my-decrypt-policy"]}' -k https://localhost:8200/v1/auth/token/create
- 我们应该看到下面的结果。注意客户端令牌(s.7x…Xv)。我们将此令牌称为解密器令牌
{ "request_id":"d5b47829-53f0-a35e-4b7c-0e9d8f4f3cbc", "lease_id":"", "renewable":false, "lease_duration":0, "data":null, "wrap_info":null, "warnings":null, "auth":{ "client_token":"s.7xdIhRPcJXFw2B1s6fKasHXv", "accessor":"TSNoAMVNwFEUzlmx8tjRh9w8", "policies":[ "default", "my-encrypt-policy" ], "token_policies":[ "default", "my-encrypt-policy" ], "metadata":null, "lease_duration":2764800, "renewable":true, "entity_id":"", "token_type":"service", "orphan":false }
测试 Vault 的加密即服务
- 因此,让我们测试一下我们的设置。首先,我们需要一个 base-64 字符串。打开命令行控制台并运行以下命令。这会将字符串编码为 base-64。当然,您可以使用“Hello world”以外的其他语言。
> echo -n 'Hello world'|openssl base64
- 你会得到
SGVsbG8gd29ybGQ=
- 让我们使用加密器令牌加密该字符串。
> curl --header "X-Vault-Token: s.O1sI3QhVvSmbG1lyfKSMXXFk" --request POST --data '{"plaintext": "SGVsbG8gd29ybGQ="}' -k https://127.0.0.1:8200/v1/transit/encrypt/my-encryption-key
- 您将收到以下回复。加密数据在字段密文中。
{ "request_id":"c345db50-2517-90de-cc8c-f66812b27d6b", "lease_id":"", "renewable":false, "lease_duration":0, "data":{ "ciphertext":"vault:v1:+VZG+5sZA0AQworFh5+o/kTyri6I+ooKWjfwbVOtB+lY/AWRurhO", "key_version":1 }, "wrap_info":null, "warnings":null, }
- 现在,让我们尝试使用解密器令牌进行解密。请注意,字段密文包含上面的加密数据
curl --header "X-Vault-Token: s.7xdIhRPcJXFw2B1s6fKasHXv" --request POST --data '{"ciphertext":"vault:v1:+VZG+5sZA0AQworFh5+o/kTyri6I+ooKWjfwbVOtB+lY/AWRurhO"}' -k https://127.0.0.1:8200/v1/transit/decrypt/my-encryption-key
- 然后你会得到下面的回复。请注意,我们取回了我们之前加密的 base 64 字符串。
{ "request_id":"7d00a316-90ff-9c94-3e6f-d8e60b0560e3", "lease_id":"", "renewable":false, "lease_duration":0, "data":{ "plaintext":"SGVsbG8gd29ybGQ=" }, "wrap_info":null, "warnings":null, "auth":null }
- 现在,让我们尝试一个否定的测试用例。让我们尝试加密数据,但改用解密器令牌
> curl --header "X-Vault-Token: s.7xdIhRPcJXFw2B1s6fKasHXv" --request POST --data '{"plaintext": "SGVsbG8gd29ybGQ="}' -k https://127.0.0.1:8200/v1/transit/encrypt/my-encryption-key
- 您最终会遇到错误
{"errors":["1 error occurred:\n\t* permission denied\n\n"]}
- 您可以尝试相反,尝试使用加密令牌进行解密
> curl --header "X-Vault-Token: s.O1sI3QhVvSmbG1lyfKSMXXFk" --request POST --data '{"ciphertext":"vault:v1:+VZG+5sZA0AQworFh5+o/kTyri6I+ooKWjfwbVOtB+lY/AWRurhO"}' -k https://127.0.0.1:8200/v1/transit/decrypt/my-encryption-key
您最终将遇到与上述相同的错误
{"errors":["1 error occurred:\n\t* permission denied\n\n"]}
为加密和解密创建令牌
- 回想一下我们的架构,其中 Transaction Services 和 DepositAccount Service 都需要消费和发送消息。这意味着他们需要加密和解密功能。要创建具有这两个功能的令牌,只需指定两个策略:
curl --header "X-Vault-Token: s.WuTNTDpBqsspinc6dlDN0cbz" --request POST --data '{"policies": ["my-decrypt-policy", "my-encrypt-policy"]}' -k https://localhost:8200/v1/auth/token/create
- 您将收到以下回复。您可以使用“client_token”安全地登录到 Vault 以获得加密和解密功能:
{ "request_id": "a1f0fc87-5c27-94e3-b043-29adc5c87557", "lease_id": "", "renewable": false, "lease_duration": 0, "data": null, "wrap_info": null, "warnings": null, "auth": { "client_token": "s.WuTNTDpBqsspinc6dlDN0cbz", "accessor": "Qc...dU", "policies": [ "default", "my-decrypt-policy", "my-encrypt-policy" ], "token_policies": [ "default", "my-decrypt-policy", "my-encrypt-policy" ], "metadata": null, "lease_duration": 2764800, "renewable": true, "entity_id": "", "token_type": "service", "orphan": false } }
Vault的结论
我们终于在我们的 Kubernetes 集群中安装和设置了 Vault(独立设置)。我们已经激活了加密即服务引擎,并创建了两个具有两种不同功能的不同令牌。加密器令牌用于加密数据,解密器令牌用于解密数据。我们还使用令牌测试了引擎的 API,我们看到正面和负面的测试用例都通过了。最后,我们创建了一个用于加密和解密功能的令牌。
- 84 次浏览
SEO Title
Secure inter-micro-service communication with Spring Boot, Kafka, Vault and Kubernetes -- Part 3 : Setting up Vault