您现在的位置是:亿华灵动 > 网络安全
使用 K8s/Istio/Cert-manager 和 Vault 保障应用的 Tls 安全
亿华灵动2025-11-26 18:17:09【网络安全】7人已围观
简介前言Vault 是安全应用维护人员最喜欢的 hashcorp 产品之一 。Vault 是存储机密、证书、管理策略、加密数据等内容的安全工具。Vault 使用受信任的身份集中密码和控制访问权限,以此减少
Vault 是使用安全应用维护人员最喜欢的 hashcorp 产品之一 。Vault 是保障存储机密、证书、应用管理策略、安全加密数据等内容的使用安全工具。Vault 使用受信任的保障身份集中密码和控制访问权限 ,以此减少对静态、应用硬编码凭证的安全需求 。它使用集中托管和受保护的使用加密密钥来动态和静态加密敏感数据,所有一切均可通过单个工作流和 API 实现。源码下载保障
Istio 非常重要的应用一个功能是能够锁定并且保护网格内的来往流量 。本文的安全目的是基于 TLS 启动一个简单的 Web 应用程序,然后使用 Vault 生成对应的使用安全证书 ,从而保证 Istio 管理的保障服务安全 。
简单点说 ,应用使用 TLS 时候,当您建立连接时,服务器租用服务器会提供一个公钥 ,您可以使用此密钥对传输中的数据进行加密,一旦被目标服务器接收 ,数据将使用私钥解密。为了验证密钥是否有效 ,可以使用 CA(通常 CA 是付费服务) ,但我们可以通过 vault 创建一个自签名 CA)。
现在先让我们忽略 vault,让我们在系统上创建一个测试环境。为此使用 kind (https://kind.sigs.k8s.io/) 来快速创建一个测试环境 。
用 kind 建立一个 kubernetes 集群下载 kind 后 ,我们可以定义一个 kind 配置文件来启动 4 个 worker 。我们还可以配置一些端口映射,香港云服务器以确保我们可以直接访问入口网关。
复制kind:Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:- role: control-plane
extraPortMappings:- containerPort: 30725 hostPort: 8080 listenAddress: "127.0.0.1" protocol:TCP
- containerPort: 32652 hostPort: 8443 listenAddress: "127.0.0.1" protocol:TCP
- role:worker
- role:worker
- role:worker
- role: worker1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.使用 kind 来创建我们的集群
复制kind create cluster --name chris --config ~/kube.cfg1.节点已启动
复制kubectl get nodes
NAME STATUS ROLES AGE VERSION
chris-control-plane Ready control-plane 21h v1.24.0chris-worker Ready <none> 21h v1.24.0chris-worker2 Ready <none> 21h v1.24.0chris-worker3 Ready <none> 21h v1.24.0chris-worker4 Ready <none> 21h v1.24.01.2.3.4.5.6.7.使用istioctl (https://github.com/istio/istio/releases) 命令安装 istio 。首先让我们定义一下配置内容 。因为我们在本地运行 ,所以我们降低了一些资源要求 。
复制--- apiVersion: install.istio.io/v1alpha1
kind:IstioOperator
metadata: namespace: istio-system
name: installed-state
spec: components: ingressGateways: - enabled: name: istio-ingressgateway
namespace: istio-system
pilot: k8s: hpaSpec: maxReplicas: 2 minReplicas: 1 resources: requests: cpu: 512m
memory: 512Mi
profile:default
values: global: istioNamespace: istio-system1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.安装 istio
复制istioctl manifest install -f ~/istio.cfg1.删除默认类型的 istio-ingressgateway 的 service。
复制kubectl delete svc istio-ingressgateway -n istio-system1.创建一个 NodePort 服务 ,从而实现从节点端口访问 istio-ingressgateway 。
复制apiVersion:v1
kind:Service
metadata: labels: app: istio-ingressgateway
istio:ingressgateway
name: istio-ingressgateway
namespace: istio-system
spec: type:NodePort
ports: - name:http
nodePort: 30725 port: 8080 protocol:TCP
targetPort: 8080 - name:https
nodePort: 32652 port: 443 protocol:TCP
targetPort: 8443 selector: app: istio-ingressgateway1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.应用一下
复制kubectl apply -f svc.yml1.此时 ,istio 应该正常运行 。
复制kubectl get pods -n istio-system
NAME READY STATUS RESTARTS AGE
istio-ingressgateway-5f86977657-qfxrs 1/1 Running 0 21h
istiod-67db665bd9-4d8nl 1/1 Running 0 21h1.2.3.4.我们将创建一个虚拟网关进行测试
复制apiVersion: networking.istio.io/v1alpha3
kind:Gateway
metadata: name:gateway
namespace: istio-system
spec: selector: app: istio-ingressgateway
servers: - port: number: 8080 name:http
protocol:HTTP
hosts: - "*"1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.同时应用
复制kubectl apply -f gateway.yml1.使用 curl,我们能够从我们客户端去访问 istio 入口 。注意 :404 是免费模板预期的结果 ,因为我们还没有配置任何应用程序或 istio 虚拟服务 。
复制curl localhost:8080 -vs
* Trying 127.0.0.1...
* TCP_NODELAY set* Connected to localhost (127.0.0.1) port 8080 (#0)> GET / HTTP/1.1> Host: localhost:8080> User-Agent: curl/7.64.1> Accept: */*>< HTTP/1.1 404 NotFound
< date: Thu, 28 Jul 2022 21:44:50GMT
< server: istio-envoy
< content-length: 0<*Connection #0 to host localhost left intact
* Closing connection 01.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.我们不仅连接了(即使我们得到了 404 ) ,而且您可以看到服务器是 istio-envoy 响应,这意味着 envoy 正在处理流量。
最后一步 ,让我们创建一个命名空间并将其标记为 istio 注入以供将来工作。
复制kubectl createns web
kubectl label namespace web istio-injection=enabled --overwrite1.2. 安装 Vault如果这是一个生产环境,我们会按照严格标准去调整以及安装 vault ,但当前只是一个一次性的测试环境,所以我们不做任何优化调整 。源码库
使用 helm 安装 Vault Chart
复制kubectl createnamespace vault
helm repo add hashicorp https://helm.releases.hashicorp.comhelm install vault hashicorp/vault --namespace vault1.2.3.Vault pod 启动了 ,但它还没有就绪,因为它当前处于锁定状态 。在 pod 中执行以下命令以解封 。
复制kubectl -n vault exec vault-0 -- vault operator init -key-shares=1 -key-threshold=1 -format=json > /tmp/token.json1.从 /tmp/token.josn 中取出 unseal_keys_b64 密钥 ,并使用它来解封 vault。
复制kubectl exec -n vault vault-0 -- vault operator unseal <key>1.现在,vault 应该已解封并处于就绪状态 。
复制kubectl get pods -n vault
NAME READY STATUS RESTARTS AGE
vault-0 1/1 Running 0 6m28s
vault-agent-injector-5d4c695bf4-zzgq5 1/1 Running 0 6m29s1.2.3.4.现在 ,我们可以使用 port-forward 来转发访问 vault ,并使用可以在 /tmp/token.json 中找到的根令牌登录 vault 。
复制kubectl port-forward vault-0 8200 -n vault
# Open a browser to localhost:82001.2.现在 vault 已准备就绪 ,让我们创建一个新的 secret engine,启用新 engine -> PKI Certificates,模板下载我使用的是 pki/ 路径。或者,如果启用了 cli,您可以使用以下命令
复制vault secrets enable pki1.将 vault 二进制文件下载到您本地并设置 vault 地址和令牌。
复制brew install vault
export VAULT_ADDR=http://127.0.0.1:8200export VAULT_TOKEN=<key>1.2.3.现在我们可以从 cli 与 Vault 进行交互了
复制vault status
Key Value
--- -----Seal Type shamir
Initialized trueSealed falseTotal Shares 1Threshold 1Version 1.10.3Build Date n/a
Storage Type file
Cluster Name vault-cluster-d4e826eb
Cluster ID 76196d6e-0033-b892-4826-2674b6befe23
HA Enabled false1.2.3.4.5.6.7.8.9.10.11.12.13.14.我们想给 istio 一个证书以供 CA 验证 ,所以让我们在 Vault 中创建一个。首先调整一些租期时间(10 年) 。
复制vault secrets tune -max-lease-ttl=87600h pki1.创建一个名为 allowit 的角色,然后定义要发送到 CA 的 CSR 的路径 。本处将使用域名 somecompany.com 进行所有测试。
复制# createa role
vault write pki/roles/allowit allow_any_name=true# generate
vault write -field=certificate pki/root/generate/internal \
common_name="somecompany.com"\
issuer_name="root"\
ttl=87600h > ca.crt# config
vault write pki/config/urls \
issuing_certificates="$VAULT_ADDR/v1/pki/ca"\
crl_distribution_points="$VAULT_ADDR/v1/pki/crl"1.2.3.4.5.6.7.8.9.10.11.我们当前在做的是为访问路径设置角色以与 CA 一起使用。
现在是时候获得中间证书了 。CA 虽然很不错 ,它确认了证书属于正确的人,但它有很大的权力。中间证书基本上是一种从另一个 CA 创建 CA 的方法 。最佳做法是使用中间证书。根证书将能够撤销任何中间 CA 。
但如下所见 ,步骤类似,我们现在使用的是 pki_int 路径。
复制vault secrets enable -path=pki_int pki
vault secrets tune -max-lease-ttl=43800h pki_int
vault write -format=json pki_int/intermediate/generate/internal \
common_name="somecompany.com Intermediate Authority"\
issuer_name="somecompany-intermediate"\
| jq -r .data.csr > pki_intermediate.csrvault write -format=json pki/root/sign-intermediate \
issuer_ref="root"\
csr=@pki_intermediate.csr\
format=pem_bundle ttl="43800h"\
| jq -r .data.certificate > intermediate.cert.pem1.2.3.4.5.6.7.8.9.10.11.仍然使用 somecompany.com 域名,确保颁发者名称与根 CA 和中间 CA 的颁发者名称相同(在此示例中仅称为 root)。
将签名的证书写回 vault 。
复制vault write pki_int/intermediate/set-signed certificate=@intermediate.cert.pem1.现在我们的 CA 和中间证书已经创建好了 。我们应该为它创建一个角色 。
复制vault write pki_int/roles/example-dot-com \
issuer_ref="$(vault read -field=default pki_int/config/issuers)"\
allowed_domains="somecompany.com"\
allow_subdomains=true\
max_ttl="720h"1.2.3.4.5.目前 ,我们都准备好了 !我们已经创建了一个带有 vault 的 CA 和中间 CA!
让我们测试一下!让我们向 vault 申请 chris.somecompany.com 的 24 小时有效证书 。(这将返回 CA、证书和私钥)
复制vault write pki_int/issue/allowit common_name="chris.somecompany.com" ttl="24h"1.本处只关心证书,所以将上面命令返回的base64复制并粘贴到一个临时文件中,然后使用openssl 进行检查 。
复制openssl x509 -in site -text | head -n 10Certificate: Data: Version: 3 (0x2) Serial Number: 4f:87:f9:6f:eb:68:db:2a:39:f5:06:1e:43:32:98:04:32:2e:df:4d
Signature Algorithm:sha256WithRSAEncryption
Issuer: CN=somecompany.comIntermediate Authority
Validity
Not Before: Jul 29 20:32:09 2022GMT
Not After : Jul 30 20:32:39 2022GMT
Subject: CN=chris.somecompany.com Subject Public Key Info: Public Key Algorithm: rsaEncryption1.2.3.4.5.6.7.8.9.10.11.12.13.14.从上面的输出可以看出 ,证书有效期为 24 小时,对 chris.somecompany.com 有效 ,包含我们要求的所有内容 !
目前的操作都是通过命令行,忽略使用 vault UI ,但我们也可以使用 UI 来执行上述所有步骤 。例如 ,我们可以在 UI 上看到我们的证书 。

kubernetes、istio 和 vault 就这样配置好了。我们的最终目标是启动一个使用 vault 的 TLS 证书的应用程序。
Cert-manager (https://cert-manager.io/) 允许我们向 kubernetes secret api 请求和写入证书。然后 istio 将这些证书与网关 crd 一起使用。
使用 helm 安装Cert-manager 。
复制helm repo add jetstack https://charts.jetstack.iohelm repo updatekubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.crds.yamlhelm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \ --create-namespace \ --version v1.9.11.2.3.4.5.6.7.8.当前可以确认 pod 已在 cert-manager 命名空间中启动,我们也可以看到 cert-manager crds 被创建了 。
复制kubectl get crd |grep cert
certificaterequests.cert-manager.io 2022-07-29T20:59:11Z
certificates.cert-manager.io 2022-07-29T20:59:11Z
challenges.acme.cert-manager.io 2022-07-29T20:59:11Z
clusterissuers.cert-manager.io 2022-07-29T20:59:11Z
issuers.cert-manager.io 2022-07-29T20:59:11Z
orders.acme.cert-manager.io 2022-07-29T20:59:11Z1.2.3.4.5.6.7.当前 cert-manager 已经启动并运行,它如何对 vault 进行身份验证?Authrole 允许我们设置一个密钥来与 vault 对话。
通过如下命令启用审批
复制vault auth enable approle1.创建 policy.hcl 文件,它可以访问 pki/ 和 pki-int 路径
复制#policy.hclpath "pki*" { capabilities = ["create", "read", "update", "delete", "list", "sudo"]}1.2.写入 vault
复制vault policy write pki_policy policy.hcl1.创建一个 certmanager 角色 ,该角色附加到 pki_policy vault策略
复制vault write auth/approle/role/certmanager secret_id_ttl=8760h token_num_uses=0 token_ttl=20m token_max_ttl=30m secret_id_num_uses=0 policies=pki_policy1.我们现在有一个 role_id
复制vault read auth/approle/role/certmanager/role-id
Key Value
--- -----role_id bb3cdc37-9fe0-f99f-99d4-1ff80d26ab1d1.2.3.4.让我们来写入一个 secret
复制vault write -f auth/approle/role/certmanager/secret-id1.这将返回一个 secret_id(你的密钥) ,获取 ,base64 ,然后创建一个 kubernetes secret并上传
复制apiVersion:v1
kind:Secret
type:Opaque
metadata: name: cert-manager
namespace: istio-system
data: secretId: <base64 key>1.2.3.4.5.6.7.8.看起来内容比较多,但我们所做的只是告诉 Vault 创建一个密钥 。我们可以使用此密钥进行身份验证以进行保管 。
配置 cert-manager现在 cert-manager 可以与 vault 通信,让我们配置 cert-manager。
issuer 用于与 vault 对话,传递我们的应用角色密钥和路径 。
本处 ,我们将所有这些东西都放在了 istio-system 命名空间中。
复制---apiVersion: cert-manager.io/v1
kind:Issuer
metadata: name:istio
namespace: istio-system
spec: vault: path: pki_int/sign/allowit
server: http://vault-internal.vault.svc.cluster.local:8200 auth: appRole: path:approle
roleId: "bb3cdc37-9fe0-f99f-99d4-1ff80d26ab1d" secretRef: name: cert-manager
key: secretIda1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.然后我们创建一个证书 CRD 来定义我们的证书信息并告诉它把我们生成的证书放在哪里(在这个例子中是 my-cert )。
复制---apiVersion: cert-manager.io/v1
kind:Certificate
metadata: name:istio
namespace: istio-system
spec: secretName: my-cert
issuerRef: name:istio
commonName: chris.somecompany.com dnsNames: - chris.somecompany.com1.2.3.4.5.6.7.8.9.10.11.12.13.等待 30s ,如果没有意外 ,那么 cert-manager 将去 vault 请求一个证书,并将其保存为一个可以使用的密钥。
复制kubectl -n istio-system get Issuer
NAME READY AGE
istio True 5m34s
kubectl -n istio-system get Certificate
NAME READY SECRET AGE
istio True my-cert 5m57s1.2.3.4.5.6.最重要的是,我们的证书是否已准备好用作 kubernetes secret ?
复制kubectl -n istio-system get secret my-certNAME TYPE DATA AGEmy-cert kubernetes.io/tls 3 6m43s1.在这个过程中 ,可以通过 kubectl describe 命令来查看详细信息。 。
创建示例应用一切准备就绪,让我们在 web 命名空间中启动一个 nginx pod 虚拟服务 。请记住,我们标记了命名空间,因此该命名空间内启动的 pod 也将获得 istio sidecar。
复制kubectl -n web create deploy nginx --image=nginx --port 80kubectl -n web expose deploy nginx --port 801.2.对于 istio,我们需要一个 VirtualService 来进行路由 。
复制apiVersion: networking.istio.io/v1alpha3
kind:VirtualService
metadata: name:nginx
namespace:web
spec: gateways: - istio-system/https
hosts: - "chris.somecompany.com" http: - route: - destination: host: nginx1.2.3.4.5.6.7.8.9.10.11.12.13.14.此外 ,创建一个使用我们来自 vault 的新证书的网关
复制---apiVersion: networking.istio.io/v1alpha3
kind:Gateway
metadata: name:https
namespace: istio-system
spec: selector: app: istio-ingressgateway
servers: - port: number: 8443 name:https
protocol:HTTPS
hosts: - "chris.somecompany.com" tls: mode:SIMPLE
credentialName: my-cer1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.在真实环境中,我们会在 istio ingress 前面添加一个负载均衡器 ,但是因为我们使用的是 kind ,所以本处会将主机添加到 /etc/hosts 并进行端口转发。
复制kubectl -n istio-system port-forward istio-ingressgateway-5f86977657-qfxrs 84431.在我们点击页面之前,让我们从 k8s 中获取公共 CA 并信任它。
复制kubectl -n istio-system get secret my-cert -o json | jq -r .data["ca.crt"] | base64 -d > ca.crtopen ca.crt# mark as always trust1.2.3.在浏览器中打开,我们现在可以使用由 vault 签名的有效 TLS 证书访问我们的 nginx 服务器 (https://chris.somecompany.com:8443) !

通过本文,我们学习了如何使用 vault 来保存证书等密钥信息 ,如何使用 cert-manager 来申请免费的证书以及 istio 如何去使用这些信息 ,最终实现了一个基于 istio 的安全 web 应用。
很赞哦!(46)







