用Hashicorp Vault搭建自己的CA(五)——cert-Manager 与AppRole

上一篇文章我们使用cert-manager的ACME功能从自己的Vault生成了数字证书,其实cert-manager也能直接与Vault的非ACME API交互。

cert-manager与AppRole

过程很简单,cert-manager在Vault那里验证身份,通过后就可以提交证书请求,有权限的话就拿到证书。

身份验证与权限

Vault支持多种身份验证方式

先改下安全凭据的默认有效期:

1
2
vault login # 用root的token登录,照我第一篇文章那样搞的日常管理员用户没权限读下面的路径
vault read sys/auth/approle/tune

可以看到当前的也是默认的设置:

1
2
3
4
5
6
7
Key                  Value
---                  -----
default_lease_ttl    768h
description          n/a
force_no_cache       false
max_lease_ttl        768h
token_type           default-service

喜欢的话可以把它改长点:

1
vault auth tune -default-lease-ttl=8760h -max-lease-ttl=8760h approle/

㊟ 确保/etc/vault.d/这里没有文件定义了上面的时间。

⚠️ 本系列文章都不适合于生产环境,这些只是过家家的。

验证方式默认都是关闭的:我们手动打开AppRole类型的验证方式

1
vault auth enable approle

然后可以创建一个服务账号:

1
2
3
4
5
6
7
vault write auth/approle/role/certmanager-approle \
    token_type=batch \
    # secret_id_ttl=720h \ # 从这行开始我都注释掉的,过家家无所谓什么过期时间,默认是0——永不过期
    token_ttl=720h \
    token_max_ttl=720h \
    secret_id_num_uses=20 \ #使用次数
    secret_id_bound_cidrs="" # 使用反向代理服务器或负载均衡器的填写对端地址,比如“127.0.0.1/32”

可以查看它的信息:

1
vault read auth/approle/role/certmanager-approle

可以改,比如:

1
vault write auth/approle/role/certmanager-approle secret_id_bound_cidrs="192.168.0.0/16, 127.0.0.1/32"

获取role ID,四舍五入约等于用户名:

1
2
3
4
vault read auth/approle/role/certmanager-approle/role-id
Key        Value
---        -----
role_id    24d0b1fc-de60-cd1e-b69c-e53be481c864

创建一条ACL,这决定了关联的服务账号能不能搞证书,能在哪里搞:

1
2
3
4
5
6
vault policy write cert-manager - <<EOF
path "ca*" { capabilities = ["read", "list"] }
path "ca-int-x1/roles/cert-test"   { capabilities = ["create", "update"] }
path "ca-int-x1/sign/cert-test"    { capabilities = ["create", "update"] }
path "ca-int-x1/issue/cert-test"   { capabilities = ["create"] }
EOF

将策略与服务账号关联:

1
vault write auth/approle/role/certmanager-approle policies=cert-manager

给它生成secure ID,四舍五入相当于密码:

1
2
3
4
5
vault write -f auth/approle/role/certmanager-approle/secret-id
Key                   Value
---                   -----
secret_id             51434556-11ed-1447-157f-5ad363a38727
secret_id_accessor    8c666f20-a127-7464-a08b-783e1bd3574e

㊟ 只显示一次,坏掉了忘记了等等情况就要再生成一个。

看看密码过期时间:

1
vault write  auth/approle/role/certmanager-approle/secret-id/lookup secret_id='<secret_id>'

编个码:

1
2
echo -n '<secret-id>' | openssl base64
ZTRkY2IzMzYtMzQzYy0wYmZiLThlYzMtY2ViMmE4ZDk0NjVl # 记下来

我们那个发证书的角色我手贱乱改过,先看看信息:

1
vault read ca-int-x1/roles/cert-test

有什么地方不满意的话可以改,比如:

1
2
3
4
vault write ca-int-x1/roles/cert-test \
    key_type=any \
    allowed_domains=miyunda.com,.local \
    allow_subdomains=true

Kubernetes 这边

这回与上次不同,这次实验不再需要搞什么DNS劫持什么IP转发了,因为不再有ACME挑战,而是有权限就能给。让我们到minikube机器上:

1
2
3
minikube stop
minikube delete
minikube start

Secret

“密码”存这里:

1
2
3
4
mkdir ~/hellonerd-certmanager-approle && cd ~/hellonerd-certmanager-approle
nano secret.yaml
kubectl create -f secret.yaml
secret/certmanager-vault-approle created
1
2
3
4
5
6
7
8
9
# secret.yaml
piVersion: v1
kind: Secret
type: Opaque
metadata:
  name: certmanager-vault-approle
  namespace: default # 这行的内容和楼下的文件要一致
data:
  secretId: "<secret_id>"

Issuer

还得把Vault的信息给它,让它知道去哪能得到证书:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# issuer.yaml
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: vault-dev-approle
  namespace: default
spec:
  vault:
    path: ca-int-x1/sign/cert-test
    server: https://vault-dev.miyunda.com
#    caBundle: <base64 encoded caBundle PEM file> # Vault使用自签名CA提供服务的话得把CA证书放在这里
    auth:
      appRole:
        path: approle
        roleId: "<rold_id>"
        secretRef:
          name: certmanager-vault-approle
          key: secretId
1
2
3
nano issuer.yaml
kubectl create -f issuer.yaml
certificate.cert-manager.io/foo-miyunda-com created

建好之后可以看看状态:

1
2
3
kubectl get issuer
NAME                      READY   AGE
vault-dev-approle   True    3s

不行的话可以看看:

1
kubectl describe issuer <issuer名字> # -n <namespace>

打印的信息会报告哪里有问题。

证书

从Vault获取证书:

1
2
nano mycert.yaml
kubectl create -f mycert.yaml

这文件很多地方不用填写的,我就是为了举例子弄成这样:

 1
 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# mycert.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: foo-miyunda-com
  namespace: default
  annotations:
      cert-manager.io/issuer: "vault-dev-approle"
spec:
  secretName: foo-miyunda-com
  subject:
    organizations:
      - "Miyunda & Co Inc"
    countries:
      - Haidian # ???
      - Xuanwu # ???
    localities:
      - Beijing
    organizationalUnits:
      - "Marketing"
    provinces:
      - Asia
  commonName: foo.miyunda.com
  dnsNames:
    - bar.miyunda.com
    - foo.bar.miyunda.com
  issuerRef:
    name: vault-dev-approle
    kind: Issuer
    group: cert-manager.io
  
  privateKey:
    # Vault那边叫“EC”
    algorithm: ECDSA

  duration: 2160h
  # 默认是三个月有效期,提前一个月续期
  renewBefore: 360h

  isCA: false
  usages:
    - server auth
    - client auth

看看状态:

1
2
3
kubectl get certificate foo-miyunda-com
NAME              READY   SECRET            AGE
foo-miyunda-com   True    foo-miyunda-com   2s
  1
  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
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
kubectl describe certificate foo-miyunda-com # -n <namespace>
Name:         foo-miyunda-com
Namespace:    default
Labels:       <none>
Annotations:  cert-manager.io/issuer: vault-dev-approle
API Version:  cert-manager.io/v1
Kind:         Certificate
Metadata:
  Creation Timestamp:  2024-09-15T14:13:51Z
  Generation:          1
  Resource Version:    187301
  UID:                 c74f8a18-bdde-4c90-ba55-fa89c98d3351
Spec:
  Common Name:  foo.miyunda.com
  Dns Names:
    bar.miyunda.com
    foo.bar.miyunda.com
  Duration:  2160h
  Is CA:     false
  Issuer Ref:
    Group:  cert-manager.io
    Kind:   Issuer
    Name:   vault-dev-approle
  Private Key:
    Algorithm:   ECDSA
  Renew Before:  360h
  Secret Name:   foo-miyunda-com
  Subject:
    Countries:
      Haidian
      Xuanwu
    Localities:
      Beijing
    Organizational Units:
      Marketing
    Organizations:
      Miyunda & Co Inc
    Provinces:
      Asia
  Usages:
    server auth
    client auth
Status:
  Conditions:
    Last Transition Time:  2024-09-15T14:13:51Z
    Message:               Certificate is up to date and has not expired
    Observed Generation:   1
    Reason:                Ready
    Status:                True
    Type:                  Ready
  Not After:               2024-12-12T14:27:12Z
  Not Before:              2024-09-13T14:26:42Z
  Renewal Time:            2024-11-27T14:27:12Z
Events:                    <none>
kubectl describe certificate hellonerd-example-tls-approle
Name:         hellonerd-miyunda-com-tls-approle
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  cert-manager.io/v1
Kind:         Certificate
Metadata:
  Creation Timestamp:  2024-09-15T14:12:37Z
  Generation:          1
  Owner References:
    API Version:           networking.k8s.io/v1
    Block Owner Deletion:  true
    Controller:            true
    Kind:                  Ingress
    Name:                  kuard
    UID:                   c273e1c4-68ca-48bf-bb86-775ed5e86d40
  Resource Version:        187229
  UID:                     85bc79a6-bff5-469b-bee6-18568e45e14a
Spec:
  Common Name:  hellonerd.miyunda.com
  Dns Names:
    hellonerd.miyunda.com
  Duration:  2160h0m0s
  Issuer Ref:
    Group:  cert-manager.io
    Kind:   Issuer
    Name:   vault-dev-approle
  Private Key:
    Algorithm:        ECDSA
    Rotation Policy:  Always
    Size:             384
  Renew Before:       720h0m0s
  Secret Name:        hellonerd-miyunda-com-tls-approle
  Subject:
    Countries:
      CN
    Localities:
      Beijing
    Organizational Units:
      Marketing
    Organizations:
      Miyunda & Co Inc
    Provinces:
      Haidian
      Xuanwu
  Usages:
    key agreement
    digital signature
    server auth
Status:
  Conditions:
    Last Transition Time:  2024-09-15T14:12:37Z
    Message:               Certificate is up to date and has not expired
    Observed Generation:   1
    Reason:                Ready
    Status:                True
    Type:                  Ready
  Not After:               2024-12-14T13:31:50Z
  Not Before:              2024-09-15T13:31:20Z
  Renewal Time:            2024-11-14T13:31:50Z
Events:                    <none>

然后在随便什么资源里面引用这个证书就好了。

Ingress

也可以搞成纯自动的,自动申请安装续期等等。

先把上一篇文章那个DNS私有解析去掉——用不上了:

1
2
sudo sed -i '/hellonerd/d' /etc/hosts
nslookup hellonerd.miyunda.com

准备一个文件,同上面的例子一样

1
nano ingress.yaml
 1
 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
27
28
29
30
31
32
33
34
35
36
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: kuard
  annotations:
    cert-manager.io/issuer: "vault-dev-approle" #必填
    cert-manager.io/common-name: "hellonerd.miyunda.com" #必填
    cert-manager.io/subject-organizations: "Miyunda & Co Inc"
    cert-manager.io/subject-organizationalunits: "Marketing"
    cert-manager.io/subject-countries: "CN"
    cert-manager.io/subject-provinces: "Haidian,Xuanwu"
    cert-manager.io/subject-localities: "Beijing"
    cert-manager.io/private-key-algorithm: "ECDSA" #默认是RSA
    cert-manager.io/private-key-size: "384" # 选了ECDSA那么默认是256
    cert-manager.io/duration: "2160h" # 默认就是这个
    cert-manager.io/renew-before: "720h" # 同上
    cert-manager.io/usages: "key agreement, digital signature, server auth" # 同上
    cert-manager.io/private-key-rotation-policy: "Always" # 默认是永不改
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - hellonerd.miyunda.com
    secretName: hellonerd-miyunda-com-tls-approle
  rules:
  - host: hellonerd.miyunda.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: kuard
            port:
              number: 80

创建:

1
kubectl create -f ingress.yaml

可以看看它的信息

 1
 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
kubectl describe certificate hellonerd-miyunda-com-tls-approle -n <namespace>
Name:         hellonerd-miyunda-com-tls-approle
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  cert-manager.io/v1
Kind:         Certificate
Metadata:
  Creation Timestamp:  2024-09-15T14:12:37Z
  Generation:          1
  Owner References:
    API Version:           networking.k8s.io/v1
    Block Owner Deletion:  true
    Controller:            true
    Kind:                  Ingress
    Name:                  kuard
    UID:                   c273e1c4-68ca-48bf-bb86-775ed5e86d40
  Resource Version:        187229
  UID:                     85bc79a6-bff5-469b-bee6-18568e45e14a
Spec:
  Common Name:  hellonerd.miyunda.com
  Dns Names:
    hellonerd.miyunda.com
  Duration:  2160h0m0s
  Issuer Ref:
    Group:  cert-manager.io
    Kind:   Issuer
    Name:   vault-dev-approle
  Private Key:
    Algorithm:        ECDSA
    Rotation Policy:  Always
    Size:             384
  Renew Before:       720h0m0s
  Secret Name:        hellonerd-miyunda-com-tls-approle
  Subject:
    Countries:
      CN
    Localities:
      Beijing
    Organizational Units:
      Marketing
    Organizations:
      Miyunda & Co Inc
    Provinces:
      Haidian
      Xuanwu
  Usages:
    key agreement
    digital signature
    server auth
Status:
  Conditions:
    Last Transition Time:  2024-09-15T14:12:37Z
    Message:               Certificate is up to date and has not expired
    Observed Generation:   1
    Reason:                Ready
    Status:                True
    Type:                  Ready
  Not After:               2024-12-14T13:31:50Z
  Not Before:              2024-09-15T13:31:20Z
  Renewal Time:            2024-11-14T13:31:50Z
Events:                    <none>

minikube机器上去访问网站,注意观察打印出的服务器端证书信息:

1
2
3
4
5
curl -o my-ca.crt $VAULT_ADDR/v1/ca-root-x1/ca/pem
curl -ivL \
    --cacert my-ca.crt \
    --resolve hellonerd.miyunda.com:443:192.168.49.2 \
    https://hellonerd.miyunda.com

503 Service Temporarily Unavailable 错误是正常的,因为后面根本就没有服务,这里只看证书,别的不管。

好了,Kubernetes用AppRole验证获取Vault证书的介绍就这么多了。我们可以看到它配置起来很简单,但是它有个明显的缺点,就是secret id会过期,生产环境中很少有人使用永不过期的密码。一旦过期了,还得有人更新。

下一片文章我们将验证方式改为Kubernetes,看看能不能有完全不用人工操作的自动化证书管理。

看过了就等于学会了。 下次见~ 📅

0%