静态的数据加密和传输中的数据加密,对于数据安全来说同等重要。目前最流行的用于传输中数据加密的方式就是 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的方式。
$ mkdir vault && cd vault
$ vi vault.hcl
disable_mlock = true
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = 1
}
backend "file" {
path = "/home/benr/vault/secrets"
}
$ vault server -config=vault.hcl
==> Vault server configuration:
Backend: file
Listener 1: tcp (addr: "0.0.0.0:8200", tls: "disabled")
Log Level: info
Mlock: supported: true, enabled: false
Version: Vault v0.6.0
==> Vault server started! Log data will stream in below:
在另一个终端中初始化并解封Vault:
$ export VAULT_ADDR='http://127.0.0.1:8200'
$ vault init
Unseal Key 1: 811538b33c90d6f558b0296e12dc0023fc4086f5cbc424a2a3766d52dd52d7cf01
Unseal Key 2: 3eb6e073249168bdef779cf0e47f5c02baf7a2260e3d531073ae40862916029302
Unseal Key 3: b99b0b9a16f2f88ab83f87ddab4e6f483b15f288889bc9d1b9b2d154ad14ac8f03
Unseal Key 4: c24260a579914e78f78123b7a83fc96ebd16434980fc5a003f24bd5e2ecf7fa804
Unseal Key 5: 456f8b4c4bf2de4fa0c9389ae70efa243cf413e7065ac0c1f5382c8caacdd1b405
Initial Root Token: d194e2e3-6483-aa23-9bf2-f1bb31b0edbb
...
$ vault unseal 811538b33c90d6f558b0296e12dc0023fc4086f5cbc424a2a3766d52dd52d7cf01
$ vault unseal 3eb6e073249168bdef779cf0e47f5c02baf7a2260e3d531073ae40862916029302
$ vault unseal b99b0b9a16f2f88ab83f87ddab4e6f483b15f288889bc9d1b9b2d154ad14ac8f03
Sealed: false
Key Shares: 5
Key Threshold: 3
Unseal Progress: 0
$ vault auth
Token (will be hidden): d194e2e3-6483-aa23-9bf2-f1bb31b0edbb
Successfully authenticated! You are now logged in.
token: d194e2e3-6483-aa23-9bf2-f1bb31b0edbb
token_duration: 0
token_policies: [root]
好了!到了这一步Vault已经启动并解封,可以使用了。
在Vault中,秘密信息是通过"backend"(后端)管理的,在使用之前必须被挂载。这在刚开始使用Vault时看起来非常奇怪,但这确实是合理的设计,后端可以多次挂载在不同的路径下。这个特性在我们搭建PKI时十分重要,因为Vault的一个pki后端只能代表一个CA,因此我们需要挂载两个pki后端,一个作为根CA,一个作为中间CA。这个特性使一个Vault服务可以支持多个CA,它们之间又彼此独立。
因此,我们先挂载一个pki后端,作为根CA,路径为“cuddltech”。我们在挂载时需要提供路径("path",用于访问这个特定后端的路径),描述("description"),最长有效期("maximum lease TTL"):
$ vault mount -path=cuddletech -description="Cuddletech Root CA" -max-lease-ttl=87600h pki Successfully mounted 'pki' at 'cuddletech'! $ vault mounts Path Type Default TTL Max TTL Description cubbyhole/ cubbyhole n/a n/a per-token private secret storage cuddletech/ pki system 315360000 Cuddletech Root CA secret/ generic system system generic secret storage sys/ system n/a n/a system endpoints used for control, policy and debugging现在我们就可以创建我们的根CA证书和秘钥了:
$ vault write cuddletech/root/generate/internal \
> common_name="Cuddletech Root CA" \
> ttl=87600h \
> key_bits=4096 \
> exclude_cn_from_sans=true
Key Value
--- -----
certificate -----BEGIN CERTIFICATE-----
MIIFKzCCAxOgAwIBAgIUDXiI3GDzP2IbQ9IatFSCv9Pq/lgwDQYJKoZIhvcNAQEL
BQAwHTEbMBkGA1UEAxMSQ3VkZGxldGVjaCBSb290IENBMB4XDTE2MDcwOTA4MTIz
..
axscmLdVE2HTB87W1H77iKKN8n9Xne//LUidxVX0Kg==
-----END CERTIFICATE-----
expiration 1783411981
issuing_ca -----BEGIN CERTIFICATE-----
MIIFKzCCAxOgAwIBAgIUDXiI3GDzP2IbQ9IatFSCv9Pq/lgwDQYJKoZIhvcNAQEL
BQAwHTEbMBkGA1UEAxMSQ3VkZGxldGVjaCBSb290IENBMB4XDTE2MDcwOTA4MTIz
...
axscmLdVE2HTB87W1H77iKKN8n9Xne//LUidxVX0Kg==
-----END CERTIFICATE-----
serial_number 0d:78:88:dc:60:f3:3f:62:1b:43:d2:1a:b4:54:82:bf:d3:ea:fe:58
最后需要做的就是配置URL用于获取CA证书和CRL:
$ vault write cuddletech/config/urls issuing_certificates="http://10.0.0.22:8200/v1/cuddletech
Success! Data written to: cuddletech/config/urls
到此,根CA就已经准备好了,接下来需要生成中间CA。
创建中间CA的过程和创建根CA的过程类似,最大的不同是不能用一步一次生成证书和私钥。我们需要再挂载一个pki后端,在此先生成CSR和私钥,然后将CSR给根CA所在后端签名,将得到的证书导入后创建的pki后端作为中间CA的证书。
说起来有点绕,看步骤就清晰明了了。首先为中间CA再挂载一个pki后端,将它命名为Ops 中间CA:
$ vault mount -path=cuddletech_ops -description="Cuddletech Ops Intermediate CA" -max-lease-ttl=26280h pki
Successfully mounted 'pki' at 'cuddletech_ops'!
$ vault mounts
Path Type Default TTL Max TTL Description
cubbyhole/ cubbyhole n/a n/a per-token private secret storage
cuddletech/ pki system 315360000 Cuddletech Root CA
cuddletech_ops/ pki system 94608000 Cuddletech Ops Intermediate CA
下一步生成一个中签CA的CSR:
$ vault write cuddletech_ops/intermediate/generate/internal \
> common_name="Cuddletech Operations Intermediate CA"
> ttl=26280h \
> key_bits=4096 \
> exclude_cn_from_sans=true
Key Value
--- -----
csr -----BEGIN CERTIFICATE REQUEST-----
MIICuDCCAaACAQAwMDEuMCwGA1UEAxMlQ3VkZGxldGVjaCBPcGVyYXRpb25zIElu
dGVybWVkaWF0ZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALt8
...
hD8cpHTXqjKExYWKc/rQDgjw9+RNDdb45xszDagrgFgNPqI9i0fNh9jViMmjUiTc
PQTZS4XxIoRrx1/xVHJ4Qm++ntLPVCvzjMZafg==
-----END CERTIFICATE REQUEST-----
复制CSR的值到一个新建的文件:
cuddletech_ops.csr 这样做的原因是下一步需要将它作为参数传给根CA所在后端进行签名。
$ vault write cuddletech/root/sign-intermediate \
> csr=@cuddletech_ops.csr \
> common_name="Cuddletech Ops Intermediate CA" \
> ttl=8760h
Key Value
--- -----
certificate -----BEGIN CERTIFICATE-----
MIIEZDCCAkygAwIBAgIUHuIhRF3tYtfoZiAFdjcCtQpMR+cwDQYJKoZIhvcNAQEL
BQAwHTEbMBkGA1UEAxMSQ3VkZGxldGVjaCBSb290IENBMB4XDTE2MDcwOTA4Mjkz
...
UtI2b/AamAqf340eRKmSdEh4WypB4JR+t259YA45w2j4mS+rxREycEk4YosR/vUs
jekMiq57yNq7h8eOTrnOulJxazbVrYGb
-----END CERTIFICATE-----
expiration 1470645002
issuing_ca -----BEGIN CERTIFICATE-----
MIIFKzCCAxOgAwIBAgIUDXiI3GDzP2IbQ9IatFSCv9Pq/lgwDQYJKoZIhvcNAQEL
BQAwHTEbMBkGA1UEAxMSQ3VkZGxldGVjaCBSb290IENBMB4XDTE2MDcwOTA4MTIz
..
1FRGlwHUg+6IIZBVIapzivLc6pAvLFPxQlQvT5CNHPk91zwyNQ9ZX2PzatdajUnd
axscmLdVE2HTB87W1H77iKKN8n9Xne//LUidxVX0Kg==
-----END CERTIFICATE-----
serial_number 1e:e2:21:44:5d:ed:62:d7:e8:66:20:05:76:37:02:b5:0a:4c:47:e7
现在我们就有了一个已经被根CA签名过了证书了,我们将证书内从复制粘贴到一个新建文件:
cuddletech_ops.crt 我们将它导入中间CA的后端:
$ vault write cuddletech_ops/intermediate/set-signed \
> certificate=@cuddletech_ops.crt
Success! Data written to: cuddletech_ops/intermediate/set-signed
我们来验证一下:
$ curl -s http://localhost:8200/v1/cuddletech_ops/ca/pem | openssl x509 -text | head -20
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
76:12:53:41:be:18:98:2c:a1:51:4a:f8:f0:bd:b4:a3:44:7e:74:59
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=Cuddletech Root CA
Validity
Not Before: Jul 9 09:23:39 2016 GMT
Not After : Jul 9 09:24:09 2017 GMT
Subject: CN=Cuddletech Ops Intermediate CA
...
最后要做的就是配置获取CA和CRL的url:
$ vault write cuddletech_ops/config/urls \
> issuing_certificates="http://10.0.0.22:8200/v1/cuddletech_ops/ca" \
> crl_distribution_points="http://10.0.0.22:8200/v1/cuddletech_ops/crl"
Success! Data written to: cuddletech_ops/config/urls
到这里为止,我们的CA就配置好了,接下来看看如何颁发证书。颁发证书需要两个步骤,第一步是创建一个角色,定义和限制生成证书的一些参数,例如秘钥类型和秘钥长度,证书类型等等。第二步就是颁发一个该角色的证书。
在中间CA后端中创建一个名为“web_server”类型的角色,该角色可生成的秘钥长度为2048 bits,最长有效期为1年,允许任意CN。
$ vault write cuddletech_ops/roles/web_server \
> key_bits=2048 \
> max_ttl=8760h \
> allow_any_name=true
Success! Data written to: cuddletech_ops/roles/web_server
接下来就可以使用这个角色去颁发证书了。
$ vault write cuddletech_ops/issue/web_server \
> common_name="ssl_test.cuddletech.com" \
> ip_sans="172.17.0.2" \
> ttl=720h
> format=pem
Key Value
--- -----
lease_id cuddletech_ops/issue/web_server/e03318f2-d005-8196-4ed5-a42f9cd55238
lease_duration 2591999
lease_renewable false
certificate -----BEGIN CERTIFICATE-----
MIIE7jCCAtagAwIBAgIUN+vXFuIf42v1SW+mDROUVAm+lUMwDQYJKoZIhvcNAQEL
BQAwKTEnMCUGA1UEAxMeQ3VkZGxldGVjaCBPcHMgSW50ZXJtZWRpYXRlIENBMB4X
DTE2MDcwOTA5MzE1N1oXDTE2MDgwODA5MzIyN1owIjEgMB4GA1UEAwwXc3NsX3Rl
...
issuing_ca -----BEGIN CERTIFICATE-----
MIIF5DCCA8ygAwIBAgIUdhJTQb4YmCyhUUr48L20o0R+dFkwDQYJKoZIhvcNAQEL
...
private_key -----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEApBabDpPZIloRQUpro3tQEls0FEFvsvfraQzQJLD2dicSPZ2s
CqYyT8OXMclrapG7KKTYp79AaTW8LgNg3WvCzoMGDfhLL9m0QomzrMDzoW8Q7iQO
1MV4f6JXjGMbOMMXatKQlO32fLZln8m+/yJ3pOW0S6uatFzZ/N3+ed+gDuUc7eAO
...
private_key_type rsa
serial_number 37:eb:d7:16:e2:1f:e3:6b:f5:49:6f:a6:0d:13:94:54:09:be:95:43
我们需要将证书(certificate)和签发CA证书也就是中间CA的证书(issuing_ca)一起写入新建文件
ssl_test.cuddletech.com.crt文件中(颁发的证书在前,CA证书在后),将私钥(private_key)写入文件
ssl_test.cuddletech.com.key 中。
接下来可以配置NGINX了!
server {
listen 443 ssl;
server_name ssl_test.cuddletech.com;
ssl_certificate ssl_test.cuddletech.com.crt;
ssl_certificate_key ssl_test.cuddletech.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
在NGINX启动后,用浏览器访问,这时你会看到熟悉的“Your connection is not secure”的警告,我们需要导出根CA的证书,并添加到浏览器的受信证书列表里。导出根CA证书:
$ curl -s http://localhost:8200/v1/cuddletech/ca/pem > cuddletech_ca.pem
当你将根CA导入浏览器受信列表里后,你就会体会到使用Vault来颁发内部TLS证书是如此好用,好用到飞起来:)