使用 K8s/Istio/Cert-manager 和 Vault 保障应用的 Tls 安全

作者:人工智能 来源:数据库 浏览: 【】 发布时间:2025-11-27 00:49:27 评论数:
前言

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: 512

m

memory: 512

Mi

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 21

h

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 Not

Found

< date: Thu, 28 Jul 2022 21:44:50

GMT

< 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 create

ns web

kubectl label namespace web istio-injection=enabled --overwrite1.2. 安装 Vault

如果这是一个生产环境,我们会按照严格标准去调整以及安装 vault,但当前只是一个一次性的测试环境,所以我们不做任何优化调整。高防服务器

使用 helm 安装 Vault Chart

复制kubectl create

namespace 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 6

m28s

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-2674

b6befe23

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 进行所有测试。

复制# create

a 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=43800

h 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:4

d

Signature Algorithm:

sha256WithRSAEncryption

Issuer: CN=somecompany.com

Intermediate Authority

Validity

Not Before: Jul 29 20:32:09 2022

GMT

Not After : Jul 30 20:32:39 2022

GMT

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 上看到我们的证书 。

Cert manager

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.yaml

helm 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:11

Z

certificates.cert-manager.io 2022-07-29T20:59:11

Z

challenges.acme.cert-manager.io 2022-07-29T20:59:11

Z

clusterissuers.cert-manager.io 2022-07-29T20:59:11

Z

issuers.cert-manager.io 2022-07-29T20:59:11

Z

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 5

m34s

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 应用 。