使用Vault搭建企业PKI系统

静态的数据加密和传输中的数据加密,对于数据安全来说同等重要。目前最流行的用于传输中数据加密的方式就是 SSL/TLS。 对于大多数企业内部网络管理员,如果不是非用不可,一般都不会主动使用SSL/TLS,没有一个方便高效和安全的创建CA,签发证书的解决方案。

Hashicorp公司的Vault工具出现,将秘钥管理提升到了一个新水平,其中的PKI后端可以方便的帮助企业搭建一套私有的PKI体系。Vault的使用文档从功能的角度对pki后端做了详细介绍,我在此将提供一个搭建企业自建PKI的完成过程。

这篇介绍将包括以下几个部分:

  1. 启动vault服务

  2. 为企业创建一个根CA

  3. 为企业创建一个中间CA

  4. 为一个web服务颁发TLS秘钥和证书

  5. 使用NGINX测试签发的证书

需要澄清的是这篇介绍只是一个对于概念完整介绍,并不是一个最好的实践方案,在此的目的是介绍pki后端的功能。 真实实践时,Vault需要配置为HA模式,由TLS保护,不使用root token和策略,与CA和证书关联的有效期也因谨慎的根据真实的应用情况而定。

Vault作为企业内部自建CA最方便的一点就是,应用可以随时请求证书和秘钥。这就意味着可以通过基本的cron任务来更新证书,因此证书的有效期可以尽可能的短(这部分内容我会在我的另一篇博客中介绍)。但这部分功能也是Vault与我们之前习惯使用的EasyRSA或者是CFSSL的区别所在,它改变了我们管理TLS的方式。

启动 Vault

下载Vault后,创建一个目录存储相关加密数据和配置等。接下来创建一个配置文件,并启动服务:

 

1$ mkdir vault && cd vault 2$ vi vault.hcl 3disable_mlock = true 4 5listener "tcp" { 6 address = "0.0.0.0:8200" 7 tls_disable = 1 8} 9 10backend "file" { 11 path = "/home/benr/vault/secrets" 12} 13$ vault server -config=vault.hcl 14==> Vault server configuration: 15 16 Backend: file 17 Listener 1: tcp (addr: "0.0.0.0:8200", tls: "disabled") 18 Log Level: info 19 Mlock: supported: true, enabled: false 20 Version: Vault v0.6.0 21 22==> Vault server started! Log data will stream in below: 23
1 在另一个终端中初始化并解封Vault: 2

 

 

1$ export VAULT_ADDR='http://127.0.0.1:8200' 2$ vault init 3Unseal Key 1: 811538b33c90d6f558b0296e12dc0023fc4086f5cbc424a2a3766d52dd52d7cf01 4Unseal Key 2: 3eb6e073249168bdef779cf0e47f5c02baf7a2260e3d531073ae40862916029302 5Unseal Key 3: b99b0b9a16f2f88ab83f87ddab4e6f483b15f288889bc9d1b9b2d154ad14ac8f03 6Unseal Key 4: c24260a579914e78f78123b7a83fc96ebd16434980fc5a003f24bd5e2ecf7fa804 7Unseal Key 5: 456f8b4c4bf2de4fa0c9389ae70efa243cf413e7065ac0c1f5382c8caacdd1b405 8Initial Root Token: d194e2e3-6483-aa23-9bf2-f1bb31b0edbb 9... 10 11$ vault unseal 811538b33c90d6f558b0296e12dc0023fc4086f5cbc424a2a3766d52dd52d7cf01 12$ vault unseal 3eb6e073249168bdef779cf0e47f5c02baf7a2260e3d531073ae40862916029302 13$ vault unseal b99b0b9a16f2f88ab83f87ddab4e6f483b15f288889bc9d1b9b2d154ad14ac8f03 14Sealed: false 15Key Shares: 5 16Key Threshold: 3 17Unseal Progress: 0 18$ vault auth 19Token (will be hidden): d194e2e3-6483-aa23-9bf2-f1bb31b0edbb 20Successfully authenticated! You are now logged in. 21token: d194e2e3-6483-aa23-9bf2-f1bb31b0edbb 22token_duration: 0 23token_policies: [root] 24
1 好了!到了这一步Vault已经启动并解封,可以使用了。 2

 

创建一个根CA

在Vault中,秘密信息是通过"backend"(后端)管理的,在使用之前必须被挂载。这在刚开始使用Vault时看起来非常奇怪,但这确实是合理的设计,后端可以多次挂载在不同的路径下。这个特性在我们搭建PKI时十分重要,因为Vault的一个pki后端只能代表一个CA,因此我们需要挂载两个pki后端,一个作为根CA,一个作为中间CA。这个特性使一个Vault服务可以支持多个CA,它们之间又彼此独立。

因此,我们先挂载一个pki后端,作为根CA,路径为“cuddltech”。我们在挂载时需要提供路径("path",用于访问这个特定后端的路径),描述("description"),最长有效期("maximum lease TTL"):

 

1$ vault mount -path=cuddletech -description="Cuddletech Root CA" -max-lease-ttl=87600h pki 2Successfully mounted 'pki' at 'cuddletech'! 3$ vault mounts 4Path Type Default TTL Max TTL Description 5cubbyhole/ cubbyhole n/a n/a per-token private secret storage 6cuddletech/ pki system 315360000 Cuddletech Root CA 7secret/ generic system system generic secret storage 8sys/ system n/a n/a system endpoints used for control, policy and debugging 9
1 现在我们就可以创建我们的根CA证书和秘钥了: 2

 

 

1$ vault write cuddletech/root/generate/internal \ 2> common_name="Cuddletech Root CA" \ 3> ttl=87600h \ 4> key_bits=4096 \ 5> exclude_cn_from_sans=true 6Key Value 7--- ----- 8certificate -----BEGIN CERTIFICATE----- 9MIIFKzCCAxOgAwIBAgIUDXiI3GDzP2IbQ9IatFSCv9Pq/lgwDQYJKoZIhvcNAQEL 10BQAwHTEbMBkGA1UEAxMSQ3VkZGxldGVjaCBSb290IENBMB4XDTE2MDcwOTA4MTIz 11.. 12axscmLdVE2HTB87W1H77iKKN8n9Xne//LUidxVX0Kg== 13-----END CERTIFICATE----- 14expiration 1783411981 15issuing_ca -----BEGIN CERTIFICATE----- 16MIIFKzCCAxOgAwIBAgIUDXiI3GDzP2IbQ9IatFSCv9Pq/lgwDQYJKoZIhvcNAQEL 17BQAwHTEbMBkGA1UEAxMSQ3VkZGxldGVjaCBSb290IENBMB4XDTE2MDcwOTA4MTIz 18... 19axscmLdVE2HTB87W1H77iKKN8n9Xne//LUidxVX0Kg== 20-----END CERTIFICATE----- 21serial_number 0d:78:88:dc:60:f3:3f:62:1b:43:d2:1a:b4:54:82:bf:d3:ea:fe:58 22
1 最后需要做的就是配置URL用于获取CA证书和CRL: 2

 

 

1$ vault write cuddletech/config/urls issuing_certificates="http://10.0.0.22:8200/v1/cuddletech 2Success! Data written to: cuddletech/config/urls 3
1 到此,根CA就已经准备好了,接下来需要生成中间CA。 2

 

创建一个中间CA

创建中间CA的过程和创建根CA的过程类似,最大的不同是不能用一步一次生成证书和私钥。我们需要再挂载一个pki后端,在此先生成CSR和私钥,然后将CSR给根CA所在后端签名,将得到的证书导入后创建的pki后端作为中间CA的证书。

说起来有点绕,看步骤就清晰明了了。首先为中间CA再挂载一个pki后端,将它命名为Ops 中间CA:

 

1$ vault mount -path=cuddletech_ops -description="Cuddletech Ops Intermediate CA" -max-lease-ttl=26280h pki 2Successfully mounted 'pki' at 'cuddletech_ops'! 3 4$ vault mounts 5Path Type Default TTL Max TTL Description 6cubbyhole/ cubbyhole n/a n/a per-token private secret storage 7cuddletech/ pki system 315360000 Cuddletech Root CA 8cuddletech_ops/ pki system 94608000 Cuddletech Ops Intermediate CA 9
1 下一步生成一个中签CA的CSR: 2

 

 

1$ vault write cuddletech_ops/intermediate/generate/internal \ 2> common_name="Cuddletech Operations Intermediate CA" 3> ttl=26280h \ 4> key_bits=4096 \ 5> exclude_cn_from_sans=true 6Key Value 7--- ----- 8csr -----BEGIN CERTIFICATE REQUEST----- 9MIICuDCCAaACAQAwMDEuMCwGA1UEAxMlQ3VkZGxldGVjaCBPcGVyYXRpb25zIElu 10dGVybWVkaWF0ZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALt8 11... 12hD8cpHTXqjKExYWKc/rQDgjw9+RNDdb45xszDagrgFgNPqI9i0fNh9jViMmjUiTc 13PQTZS4XxIoRrx1/xVHJ4Qm++ntLPVCvzjMZafg== 14-----END CERTIFICATE REQUEST----- 15
1 复制CSR的值到一个新建的文件:**cuddletech_ops.csr **这样做的原因是下一步需要将它作为参数传给根CA所在后端进行签名。 2

 

 

1$ vault write cuddletech/root/sign-intermediate \ 2> csr=@cuddletech_ops.csr \ 3> common_name="Cuddletech Ops Intermediate CA" \ 4> ttl=8760h 5Key Value 6--- ----- 7certificate -----BEGIN CERTIFICATE----- 8MIIEZDCCAkygAwIBAgIUHuIhRF3tYtfoZiAFdjcCtQpMR+cwDQYJKoZIhvcNAQEL 9BQAwHTEbMBkGA1UEAxMSQ3VkZGxldGVjaCBSb290IENBMB4XDTE2MDcwOTA4Mjkz 10... 11UtI2b/AamAqf340eRKmSdEh4WypB4JR+t259YA45w2j4mS+rxREycEk4YosR/vUs 12jekMiq57yNq7h8eOTrnOulJxazbVrYGb 13-----END CERTIFICATE----- 14expiration 1470645002 15issuing_ca -----BEGIN CERTIFICATE----- 16MIIFKzCCAxOgAwIBAgIUDXiI3GDzP2IbQ9IatFSCv9Pq/lgwDQYJKoZIhvcNAQEL 17BQAwHTEbMBkGA1UEAxMSQ3VkZGxldGVjaCBSb290IENBMB4XDTE2MDcwOTA4MTIz 18.. 191FRGlwHUg+6IIZBVIapzivLc6pAvLFPxQlQvT5CNHPk91zwyNQ9ZX2PzatdajUnd 20axscmLdVE2HTB87W1H77iKKN8n9Xne//LUidxVX0Kg== 21-----END CERTIFICATE----- 22serial_number 1e:e2:21:44:5d:ed:62:d7:e8:66:20:05:76:37:02:b5:0a:4c:47:e7 23
1 现在我们就有了一个已经被根CA签名过了证书了,我们将证书内从复制粘贴到一个新建文件:**cuddletech_ops.crt **我们将它导入中间CA的后端: 2

 

 

1$ vault write cuddletech_ops/intermediate/set-signed \ 2> certificate=@cuddletech_ops.crt 3Success! Data written to: cuddletech_ops/intermediate/set-signed 4
1 我们来验证一下: 2

 

 

1$ curl -s http://localhost:8200/v1/cuddletech_ops/ca/pem | openssl x509 -text | head -20 2Certificate: 3 Data: 4 Version: 3 (0x2) 5 Serial Number: 6 76:12:53:41:be:18:98:2c:a1:51:4a:f8:f0:bd:b4:a3:44:7e:74:59 7 Signature Algorithm: sha256WithRSAEncryption 8 Issuer: CN=Cuddletech Root CA 9 Validity 10 Not Before: Jul 9 09:23:39 2016 GMT 11 Not After : Jul 9 09:24:09 2017 GMT 12 Subject: CN=Cuddletech Ops Intermediate CA 13 ... 14
1 最后要做的就是配置获取CA和CRL的url: 2

 

 

1$ vault write cuddletech_ops/config/urls \ 2> issuing_certificates="http://10.0.0.22:8200/v1/cuddletech_ops/ca" \ 3> crl_distribution_points="http://10.0.0.22:8200/v1/cuddletech_ops/crl" 4Success! Data written to: cuddletech_ops/config/urls 5

 

为一个Web服务申请一个证书

到这里为止,我们的CA就配置好了,接下来看看如何颁发证书。颁发证书需要两个步骤,第一步是创建一个角色,定义和限制生成证书的一些参数,例如秘钥类型和秘钥长度,证书类型等等。第二步就是颁发一个该角色的证书。

在中间CA后端中创建一个名为“web_server”类型的角色,该角色可生成的秘钥长度为2048 bits,最长有效期为1年,允许任意CN。

 

1$ vault write cuddletech_ops/roles/web_server \ 2> key_bits=2048 \ 3> max_ttl=8760h \ 4> allow_any_name=true 5Success! Data written to: cuddletech_ops/roles/web_server 6
1 接下来就可以使用这个角色去颁发证书了。 2

 

 

1$ vault write cuddletech_ops/issue/web_server \ 2> common_name="ssl_test.cuddletech.com" \ 3> ip_sans="172.17.0.2" \ 4> ttl=720h 5> format=pem 6Key Value 7--- ----- 8lease_id cuddletech_ops/issue/web_server/e03318f2-d005-8196-4ed5-a42f9cd55238 9lease_duration 2591999 10lease_renewable false 11certificate -----BEGIN CERTIFICATE----- 12MIIE7jCCAtagAwIBAgIUN+vXFuIf42v1SW+mDROUVAm+lUMwDQYJKoZIhvcNAQEL 13BQAwKTEnMCUGA1UEAxMeQ3VkZGxldGVjaCBPcHMgSW50ZXJtZWRpYXRlIENBMB4X 14DTE2MDcwOTA5MzE1N1oXDTE2MDgwODA5MzIyN1owIjEgMB4GA1UEAwwXc3NsX3Rl 15... 16issuing_ca -----BEGIN CERTIFICATE----- 17MIIF5DCCA8ygAwIBAgIUdhJTQb4YmCyhUUr48L20o0R+dFkwDQYJKoZIhvcNAQEL 18... 19private_key -----BEGIN RSA PRIVATE KEY----- 20MIIEowIBAAKCAQEApBabDpPZIloRQUpro3tQEls0FEFvsvfraQzQJLD2dicSPZ2s 21CqYyT8OXMclrapG7KKTYp79AaTW8LgNg3WvCzoMGDfhLL9m0QomzrMDzoW8Q7iQO 221MV4f6JXjGMbOMMXatKQlO32fLZln8m+/yJ3pOW0S6uatFzZ/N3+ed+gDuUc7eAO 23... 24private_key_type rsa 25serial_number 37:eb:d7:16:e2:1f:e3:6b:f5:49:6f:a6:0d:13:94:54:09:be:95:43 26
1 因为使用NGINX,我们将证书(certificate)和CA证书(issuing_ca)一起写入新建文件**ssl_test.cuddletech.com.crt**文件中(颁发的证书在前,CA证书在后),将私钥(private_key)写入文件**ssl_test.cuddletech.com.key** 中。 2

 

接下来可以配置NGINX了!

在NGINX中测试证书

NGINX需要将CA证书加在服务证书里,服务证书在前,CA证书在后。将服务证书和私钥路径写入NGINX配置文件,然后启动。

1server { 2 listen 443 ssl; 3 server_name ssl_test.cuddletech.com; 4 ssl_certificate ssl_test.cuddletech.com.crt; 5 ssl_certificate_key ssl_test.cuddletech.com.key; 6 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 7 ssl_ciphers HIGH:!aNULL:!MD5; 8 9 10 location / { 11 root /usr/share/nginx/html; 12 index index.html index.htm; 13 } 14} 15
1 在NGINX启动后,用浏览器访问,这时你会看到熟悉的“Your connection is not secure”的警告,我们需要导出根CA的证书,并导入浏览器的受信证书里。导入跟CA证书: 2
1$ curl -s http://localhost:8200/v1/cuddletech/ca/pem > cuddletech_ca.pem 2
1 当你将根CA导入浏览器受信列表里后,你就会感觉到Vault用来颁发内部TLS证书是如此好用,好用到飞起来。 2

代码交流 2021