在我之前的一篇文章中,我探讨过如何使用 GPG 来保护数据库的访问凭据。这一方式依赖于 MySQL 客户端配置的一份本地拷贝。但是如果你想要让这些证书安全地同其它非常机密的信息存储在一起,又该如何去做呢?诚然, 仍就可以利用 GPG,但还有另一种轻松的方式来做这件事情。
本文我们来看看一种使用 Vault 的
方法,它能将你的证书存储在一个中心位置并用它们来访问数据库。对于那些还没有碰到过 Vault
的人,我必须要告诉你们,它是一种很好的管理秘密的方法 —
确保安全,提供储存并且对访问进行严格控制。它还有一些额外的好处,那就是能对凭据租赁、关键撤销、关键滚动以及审计进行处理。
在阅读这篇博文的过程中我们将会完成如下一些任务:
-
下载必要的软件 -
获取一个免费的 SAN 证书来用于 Vault 的API以及自动证书更新 -
对Vault进行配置,使其运行在一个受限和用户下面,并且对其文件和API访问受到安全防护 -
为Vault创建一个策略以提供访问控制 -
启用 Vault 的 TLS 认证并使用 OpenSSL 创建一个自签名的客户端证书,用于我们的客户端 -
为 Vault 添加一个新的密钥,并是 TLS 认证从客户端获取访问权限 -
启用自动化的过期时 MySQL 授权
在继续下面的内容之前,我先快速地说明一下,接下来是一个快捷的示例,向你展示的是如何才能够让 Valut 跑起来并应用于 MySQL,这并不是一份用于生产环境的指南,而且并没有涵盖诸如高可用性(HA)实现这方面的内容。
下载时间
为了使用 Vault,我们还需要一些工具,Let’s Encrypt, OpenSSL 和 json_pp (JSON::PP 的命令行工具)。我们使用 Ubuntu 16.04 LTS 进行演示,而且假定这些都还没有安装。 1 | $ sudo apt-get install letsencrypt openssl libjson-pp-perl
|
如果你听说过 Let’s Encrypt,那你就知道它是免费、自动化并且是开放认证授权(CA)的,它为你的网站或其它服务提供安全防护,而你不需要为 SSL 证书花钱;你甚至可以创建SAN(Subject Alternative Name)证书,它能简化操作,因为它可以将一个证书用于多个域名。在此,我推荐使用 EFF(Electronic Frontier Foundation)提供的 Certbot 工具来管理证书,这是 letsencrypt 软件的新名字。如果你还没有安装 letsencrypt/certbot,那么你应该 快速安装。我们会使用 json_pp 来格式化从 Vault API 输出的 JSON,并用 openssl 来创建客户端证书。 另外,我们还需要下载 Vault,选择适合你操作系统和结构的二进制包。在写这篇文章的时候,Vault 最新的版本是 0.6.2。如果你使用的是不同的版本,下面的步骤能帮你进行一些调整。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # Download Vault (Linux x86_64), SHA256SUMS and signature
$ wget https:
https:
https:
# Import the GPG key
$ gpg --keyserver pgp.mit.edu --recv-keys 51852D87348FFC4C
# Verify the checksums
$ gpg --verify vault_0. 6 .2_SHA256SUMS.sig
gpg: assuming signed data in `vault_0. 6 .2_SHA256SUMS'
gpg: Signature made Thu 06 Oct 2016 02 : 08 : 16 BST using RSA key ID 348FFC4C
gpg: Good signature from "HashiCorp Security <security@hashicorp.com>"
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 91A6 E7F8 5D05 C656 30BE F189 5185 2D87 348F FC4C
# Verify the download
$ sha256sum --check <(fgrep vault_0. 6 .2_linux_amd64.zip vault_0. 6 .2_SHA256SUMS)
vault_0. 6 .2_linux_amd64.zip: OK
# Extract the binary
$ sudo unzip -j vault_0. 6 .2_linux_amd64.zip -d /usr/local/bin
Archive: vault_0. 6 .2_linux_amd64.zip
inflating: /usr/local/bin/vault
|
Let’s Encrypt… 为什么不?
[译者注:Let's Encrypt 是一个 CA 的名称,这里也表示让我们来加密,一语双关] 我们希望,不管通过什么渠道,都能访问 Vault,我们可以在这些地方添加一些安全防护来阻止未授权的访问,所以我们需要通过加密来保护自己。下面的示例演示了在公共服务器上安装 Vault,需要通过 CA 来验证你的请求。从 Certbot 文档中可以找到更多不同方法的信息。 1 2 3 4 5 6 7 8 9 10 11 12 13 | $ sudo letsencrypt --webroot -w /home/www/vhosts/ default / public -d myfirstdomain.com -d myseconddomain.com
#IMPORTANT NOTES:
# - Congratulations! Your certificate and chain have been saved at
# /etc/letsencrypt/live/myfirstdomain.com/fullchain.pem. Your cert will
# expire on 2017 - 01 - 29 . To obtain a new or tweaked version of this
# certificate in the future, simply run certbot again. To
# non-interactively renew *all* of your certificates, run "certbot
# renew"
# - If you like Certbot, please consider supporting our work by:
#
# Donating to ISRG / Let's Encrypt: https:
# Donating to EFF: https:
#
|
所有这些事情都需要 SAN SSL 证书!执行的服务器提供了公共的 WebService,服务在证书请求的域名上。请求处理的过程中,指定放在 webroot 的一个文件会被用来验证请求的域名。实质上命令的内容是: myfirstdomain.com and myseconddomain.com use /home/www/vhosts/default/public for the document root, so place your files there
Let’s Encrypt CA 提供的证书很短暂(90天),所以需要保持适时的更新。不过不用担心,这就跟最开始创建他们一样容易!你可以像下面这样测试更新工作是否能正常进行(如果没有 --dry-run 参数,它会更新所有证书): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | $ sudo letsencrypt renew --dry-run
#
#-------------------------------------------------------------------------------
#Processing /etc/letsencrypt/renewal/myfirstdomain.com.conf
#-------------------------------------------------------------------------------
#** DRY RUN: simulating 'letsencrypt renew' close to cert expiry
#** (The test certificates below have not been saved.)
#
#Congratulations, all renewals succeeded. The following certs have been renewed:
# /etc/letsencrypt/live/myfirstdomain.com/fullchain.pem (success)
#** DRY RUN: simulating 'letsencrypt renew' close to cert expiry
#** (The test certificates above have not been saved.)
#
#IMPORTANT NOTES:
# - Your account credentials have been saved in your Certbot
# configuration directory at /etc/letsencrypt. You should make a
# secure backup of this folder now. This configuration directory will
# also contain certificates and private keys obtained by Certbot so
# making regular backups of this folder is ideal.
|
自动更新
更新的测试结果正常,所以我们现在可以把它做成自动计划。我在使用 systemd,所以下面的例子会使用计时器,你也可以使用 cron 或类似的工具。这里展示的是如何在 systemd 中计划更新,在06:00的时候,更新进程会自动处理之前获得并将在30天内到期的证书。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | $ sudo cat <<EOF > /etc/systemd/system/cert-renewal.service
[Unit]
Description=SSL renewal
[Service]
Type=simple
ExecStart=/usr/bin/letsencrypt renew --quiet
User=root
Group=root
EOF
$ sudo cat <<EOF > /etc/systemd/system/cert-renewal.timer
[Unit]
Description=Automatic SSL renewal
[Timer]
OnCalendar=*-*-* 06 : 00 : 00
Persistent= true
[Install]
WantedBy=timers.target
EOF
$ sudo systemctl enable cert-renewal.timer
Created symlink from /etc/systemd/system/timers.target.wants/cert-renewal.timer to /etc/systemd/system/cert-renewal.timer.
$ sudo systemctl start cert-renewal.timer
$ sudo systemctl list-timers
NEXT LEFT LAST PASSED UNIT ACTIVATES
Tue 2016 - 11 - 01 06 : 00 : 00 UTC 6h left n/a n/a cert-renewal.t
|
Vault 入门
首先要说明一下的是,本文并不对 Vault 做深入的探查,Vault 的使用方法以及 Vault 安装必要的一些最佳实践,这些都超出了本文的范围。文章的目的是为了让你可以尝试使用 Vault。因此,如果你想认真学习的话,请进一步去阅读 Vault 的文档。 尽管这里已经有了一个可以用 vault server -dev 命令来启动的开发环境服务器,我们仍需要花一点时间来进行配置,并将数据持久化。Vault 支持许多数据存储后台,包括 Zookeeper, Amazon S3 以及 MySQL,而三个主要由 HashiCorp 维护的则是 consul,file 以及 inmem。内存库存储后台并不提供持久化数据,因此可能用到这个的实际只会是开发和测试环境 – 对于 server 命令 来说, -dev 选项是会被用到的存储后台。在本文中我们不对 Consul 的安装以及配置做处理,而是使用文件存储。 在启动服务器之前我们要创建一个配置,它可以使用 –HCL (HashiCorp 配置语言) 或者 JSON (JavaScript 对象标记) 两种格式中的一种来编写。我们将使用 HCL,因为它更清晰一点,并且可以少敲几行代码! 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 | # Create a system user
$ sudo useradd -r -g daemon -d /usr/local/vault -m -s /sbin/nologin -c "Vault user" vault
$ id vault
uid= 998 (vault) gid= 1 (daemon) groups= 1 (daemon)
# Create a config directory remove global access
$ sudo mkdir /etc/vault /etc/ssl/vault
$ sudo chown vault.root /etc/vault /etc/ssl/vault
$ sudo chmod 750 /etc/vault /etc/ssl/vault
$ sudo chmod 700 /usr/local/vault
# Copy the certficates and key
$ sudo cp -v /etc/letsencrypt/live/myfirstdomain.com/*pem /etc/ssl/vault
/etc/letsencrypt/live/myfirstdomain.com/cert.pem -> /etc/ssl/vault/cert.pem
/etc/letsencrypt/live/myfirstdomain.com/chain.pem -> /etc/ssl/vault/chain.pem
/etc/letsencrypt/live/myfirstdomain.com/fullchain.pem -> /etc/ssl/vault/fullchain.pem
/etc/letsencrypt/live/myfirstdomain.com/privkey.pem -> /etc/ssl/vault/privkey.pem
# Create a combined PEM certificate
$ sudo cat /etc/ssl/vault/{cert,fullchain}.pem /etc/ssl/vault/fullcert.pem
# Write the config to file
$ cat <<EOF | sudo tee /etc/vault/demo.hcl
listener "tcp" {
address = "10.0.1.10:8200"
tls_disable = 0
tls_cert_file = "/etc/ssl/vault/fullcert.pem"
tls_key_file = "/etc/ssl/vault/privkey.pem"
}
backend "file" {
path = "/usr/local/vault/data"
}
disable_mlock = true
EOF
|
接着我们要设置一个用户以及一些路径来存储配置、SSL 证书以及密钥, 还有就是数据,然后再限制对数据访问。 请注意一定要保持 Vault 数据目录的安全,因为它包含了所有的密钥以及机密信息。在示例中,我们已经通过将其放到 vault 用户的主目录中并且只允许 vault 用户来访问,来确保其安全。你还可以进一步对本地(通过登录)访问进行限制,并提供访问控制清单。 启动 Vault
现在是时候启动服务器来看看一切是否能正常运行了! 1 2 3 4 5 6 7 8 | $ sudo -su vault vault server -config=/etc/vault/demo.hcl >/tmp/vault-debug.log 2 >& 1 &
$ jobs
[ 1 ] + running sudo -su vault vault server -config=/etc/vault/demo.hcl > /tmp/vault-debug.lo
$ VAULT_ADDR=https:
Error checking seal status: Error making API request.
URL: GET https:
Code: 400 . Errors:
* server is not yet initialized
|
虽然看起来像是某些地方出了差错(我们需要对服务器进行初始化),但这都是我们预期的安排。因此,下一步我们要对 Vault 进行初始化,这是一项相当简单的任务,不过服务器在初始化期间会给你提供一些信息,你需要将他们记录下来 — 启动令牌以及初始的根秘钥。你应该将这些放到某个安全的地方,不过现在,我们还是将它们同配置放到一起。 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 | # Change to vault user
$ sudo su -l vault -s /bin/bash
(vault)$ export VAULT_ADDR=https:
# Initialize Vault and save the token and keys
(vault)$ vault init 2 >& 1 | egrep '^Unseal Key|Initial Root Token' >/etc/vault/keys.txt
(vault)$ chmod 600 /etc/vault/keys.txt
# Unseal Vault
(vault)$ egrep -m3 '^Unseal Key' /etc/vault/keys.txt | cut -f2- -d: | tr -d ' ' |
while read key
do
vault unseal
-ca-cert=${VAULT_SSL}/fullchain.pem
-client-cert=${VAULT_SSL}/client.pem
-client-key=${VAULT_SSL}/privkey.pem ${key}
done
Sealed: true
Key Shares: 5
Key Threshold: 3
Unseal Progress: 1
Sealed: true
Key Shares: 5
Key Threshold: 3
Unseal Progress: 2
Sealed: false
Key Shares: 5
Key Threshold: 3
Unseal Progress: 0
# Check Vault status
(vault)$ vault status
Sealed: false
Key Shares: 5
Key Threshold: 3
Unseal Progress: 0
Version: 0.6 . 2
Cluster Name: vault-cluster-ebbd5ec7
Cluster ID: 61ae8f54-f420-09c1-90bb-60c9fbfa18a2
High-Availability Enabled: false
|
好了,Vault 已经被初始化并且 status 命令现在会返回其已经可以运行的详情和确认信息。这里值得我们注意的是,每次当我们要启动 Vault 时,它都会是被密封起来的,意思是 Vault 只有在密封解除时,3个秘钥都被使用了,才能被解除密封 – 出于额外的安全防护,你应该确保一个人不能同时持有 3 份秘钥,因此,(重新)启动服务总是需要多个人进行。 设置一个策略
策略让你可以对访问控制限制进行设置,来决定被认证用户可以访问哪些数据。这里再强调一次,用来编写策略的格式也可以是 HCL 或者 JSON 两者之一。用它们可以轻松地进行编写和引用,但唯一需要注意的是,一旦令牌被发布了,策略同令牌的关联就不可以被修改(添加/移除);你需要撤销令牌并应用新的策略。不过,如果你想要修改策略的规则,那么就可以立即进行,而修改会引用到接下来对 Vault 的调用。 当我们初始化了服务器,会得到初始的根秘钥,而现在就需要用它来开启配置服务器。 1 | (vault)$ export VAULT_TOKEN=$(egrep '^Initial Root Token:' /etc/vault/keys.txt | cut -f2- -d: | tr -d
|
我们将创建一个简单的策略来让我们读取 MySQL 上的机密信息, 而阻止对系统信息和命令的访问操作。 1 2 3 4 5 6 7 8 9 10 11 | (vault)$ cat <<EOF > /etc/vault/demo-policy.hcl
path "sys/*" {
policy = "deny"
}
path "secret/mysql/*" {
policy = "read"
capabilities = [ "list" , "sudo" ]
}
EOF
(vault)$ vault policy-write demo /etc/vault/demo-policy.hcl
Policy 'demo' written.
|
这里我们只添加了一个策略, 而你可以根据使用该服务的人群及应用程序的使用情况,创建任意数量的策略,来进行适当的访问控制。对不同类型的数据存储而言,用合适的方式存储你的数据是很重要的,这有助于你根据所需的力度级别来编写更加严谨策略。如果要你把所有东西都写到 /secrets 目录顶层,很可能会让你愈加头疼,或给你带来冗长的策略定义! 针对 MySQL 机密信息的 TLS 认证
我们即将为 Vault 添加第一个机密信息, 但首先需要有一种方式对访问进行认证。Vault 针对你所存储的机密信息,提供了一套 API 来控制访问,还有很多像刚才那样能直接使用到 vault 二进制程序的命令。现在我们将开启证书身份验证后台, 它会允许使用 SSL/TLS 客户端证书来进行认证。 1 2 | (vault)$ vault auth-enable cert
Successfully enabled 'cert' at 'cert' !
|
使用 OpenSSL 来生成一个客户端证书TLS 认证后台既能接收由 CA 签名的证书,也能接受自签名的证书,,因此,我们就使用 openssl 来快速创建一个用于认证的自签名证书。 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 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | # Create working directory for SSL managment and copy in the config
$ mkdir ~/.ssl && cd $_
$ cp /usr/lib/ssl/openssl.cnf .
# Create a 4096 -bit CA
$ openssl genrsa -des3 -out ca.key 4096
Generating RSA private key, 4096 bit long modulus
...........++
..........................................................................++
e is 65537 ( 0x10001 )
Enter pass phrase for ca.key:
Verifying - Enter pass phrase for ca.key:
$ openssl req -config ./openssl.cnf - new -x509 -days 365 -key ca.key -out ca.crt
Enter pass phrase for ca.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.' , the field will be left blank.
-----
Country Name ( 2 letter code) [GB]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) [Some-Place]:
Organization Name (eg, company) [Percona]:
Organizational Unit Name (eg, section) [Demo]:
Comon Name (e.g. server FQDN or YOUR name) [ceri]:
Email Address [thisisnotme @myfirstdomain .com]:
# Create a 4096 -bit Client Key and CSR
$ openssl genrsa -des3 -out client.key 4096
Generating RSA private key, 4096 bit long modulus
......................++
..................................++
e is 65537 ( 0x10001 )
Enter pass phrase for client.key:
Verifying - Enter pass phrase for client.key:
$ openssl req -config ./openssl.cnf - new -key client.key -out client.csr
Enter pass phrase for client.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.' , the field will be left blank.
-----
Country Name ( 2 letter code) [GB]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) [Some-Place]:
Organization Name (eg, company) [Percona]:
Organizational Unit Name (eg, section) [Demo]:
Comon Name (e.g. server FQDN or YOUR name) [ceri]:
Email Address [thisisnotme @myfirstdomain .com]:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
# Self-sign
$ openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt
Signature ok
subject=/C=GB/ST=Some-State/L=Some-Place/O=Percona/OU=Demo/CN=ceri/emailAddress=thisisnotme @myfirstdomain .com
Getting CA Private Key
Enter pass phrase for ca.key:
# Create an unencrypted copy of the client key
$ openssl rsa -in client.key -out privkey.pem
Enter pass phrase for client.key:
writing RSA key
# Copy the certificate for Vault access
$ sudo cp client.crt /etc/ssl/vault/user.pem
|
好了,这里的信息可有不少。你可以对 openssl.cnf 进行编辑,然后按照自己的需要对默认的配置进行修改,这能节省时间。简单来讲,我们已经创建了我们自己的 CA,并用它来创建了一个自签名的证书,而后又创建了一个带有解密秘钥的 PEM 证书 (这样避免了指定使用它时需要提供密码的问题 – 假如你的客户端应用程序能请求密码, 你可能希望保留密码以增强安全性。
先为 Vault 添加一个认证证书
现在我们已经创建了一个证书和一个策略,当前需要做的是,允许使用证书进行认证。我们会给令牌一个小时的超时时间,并且允许用前一个步骤中创建的示例策略来对 MySQL 的机密信息进行访问。 1 2 3 4 5 6 7 8 | (vault)$ vault write auth/cert/certs/demo
display_name=demo
policies=demo
certificate=@${VAULT_SSL}/user.pem
ttl= 3600
Success! Data written to: auth/cert/certs/demo
$ curl --cert user.pem --key privkey.pem ${VAULT_ADDR}/v1/auth/cert/login -X POST
{ "request_id" : "d5715ce1-2c6c-20c8-83ef-ce6259ad9110" , "lease_id" : "" , "renewable" : false , "lease_duration" : 0 , "data" : null , "wrap_info" : null , "warnings" : null , "auth" :{ "client_token" : "e3b98fac-2676-9f44-fdc2-41114360d2fd" , "accessor" : "4c5b4eb5-4faf-0b01-b732-39d309afd216" , "policies" :[ "default" , "demo" ], "metadata" :{ "authority_key_id" : "" , "cert_name" : "demo" , "common_name" : "thisisnotme@myfirstdomain.com" , "subject_key_id" : "" }, "lease_duration" : 600 , "renewable" : true }}
|
好极了! 我们通过使用 SSL 客户端证书请求了第一个客户端令牌。我们只要登录进去就能从响应消息中得到一个访问令牌 (client_token),它提供给我们 1 小时(lease_duration)访问时间,好让让客户端发起请求时无需重复认证, 不过现在 vault 里面还什么也没有。
嘘! 这是一个秘密!
“时机已经成熟,” Vault 的主要开发者说, “我们加密的东西包括:钥匙和密码,绝密笔记,MySQL 的 DSN 和 字符串。”
使用 Vault 最简单的方式是,当应用程序首先需要它时,在配置文件中配置应用的存储信息。例如,这些信息是 MySQL 连接的 Data Source Name (DSN),或者动态生成所需信息的 my.cnf。像这样在 MySQL 中使用 Vault ,我们需要明确存储用户名,密码和连接方法,以此作为我们的第一个加密,然后使用命令行工具来检查,查看它是否如预期那样运作。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | (vault)$ $ vault write secret/mysql/test password= "mysupersecretpassword" user= "percona" socket= "/var/run/mysqld/mysqld.sock"
Success! Data written to: secret/mysql/test
(vault)$ vault read secret/mysql/test
Key Value
--- -----
refresh_interval 768h0m0s
password mysupersecretpassword
socket /var/run/mysqld/mysqld.sock
user percona
|
稍等片刻(希望等待时间小于一小时!)我们使用 cURL 验证,并获得 token,因此,现在我们可以试试读取加密。炫耀和呐喊已经在准备中了…… 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | $ curl --cert user.pem --key privkey.pem -H 'Content-type: application/json' -H 'X-Vault-Token: 2f1fb630-cbe9-a8c9-5931-515a12d79291' ${VAULT_ADDR}/v1/secret/mysql/test -X GET 2 >/dev/ null | json_pp
{
"wrap_info" : null ,
"lease_id" : "" ,
"request_id" : "c79033b1-f8f7-be89-4208-44d721a55804" ,
"auth" : null ,
"data" : {
"password" : "mysupersecretpassword" ,
"socket" : "/var/run/mysqld/mysqld.sock" ,
"user" : "percona"
},
"lease_duration" : 2764800 ,
"renewable" : false ,
"warnings" : null
}
|
如上操作完成!现在则不需要在你的代码或配置文件中存储密码,当你需要他们的时候,在 Vault 中即能立刻获取。例如,当你的应用程序启动并驻留在内存中时,您的应用程序需要容忍额外的延迟等。你需要采取进一步措施来确保您的应用程序在 Vault 性能下降正常运行,以及提供一个 HA 设置来最小化加密不可用的风险。
按需定制的 MySQL 授权机制
Vault 的机制像是一个虚拟文件系统,而且默认情况下使用的是通用的存储后端,以 /secret 进行安装,而因为其强大的抽象能力,所以要使用许多诸如 SQL 数据库、 AWS IAM、 HSM 以及其它更多的后台来充当安装点也是可能的。这里我们为了保持简单,目前还是使用通用的后端。你可以使用 mounts 命令来查看可(安装)使用的后端: 1 2 3 4 | (vault)$ vault mounts
Path Type Default TTL Max TTL Description
secret/ generic system system generic secret storage
sys/ system n/a n/a system endpoints used for control, policy and debugging
|
现在我们要启用 MySQL 后端,添加管理连接(这个要用到 auth_socket 插件) 而且之后会请求一个新的将可以自动过期的 MySQL 用户! 1 2 3 4 5 6 7 8 9 10 11 12 13 | # Create a dedicated MySQL user account
$ mysql -Bsse "CREATE USER vault@localhost IDENTIFIED WITH auth_socket; GRANT CREATE USER, SELECT, INSERT, UPDATE ON *.* TO vault@localhost WITH GRANT OPTION;"
# Enable the MySQL backend and set the connection details
(vault)$ vault mount mysql
(vault)$ vault write mysql/config/connection connection_url= "vault:vault@unix(/var/run/mysqld/mysqld.sock)/"
Read access to this endpoint should be controlled via ACLs as it will return the connection URL as it is, including passwords, if any.
# Write the template for the readonly role
(vault)$ vault write mysql/roles/readonly
sql= "CREATE USER '{{name}}'@'%' IDENTIFIED WITH mysql_native_password BY '{{password}}' PASSWORD EXPIRE INTERVAL 1 DAY; GRANT SELECT ON *.* TO '{{name}}'@'%';"
Success! Data written to: mysql/roles/readonly
# Set the lease on MySQL grants
(vault)$ vault write mysql/config/lease lease=1h lease_max=12h
Success! Data written to: mysql/config/lease
|
在此,你可以看到一个模板已经创建好,你可以对每个角色的授权进行自定义。我们创建了一个只读角色,因而它只能进行 SELECT 访问。我们已经在账户上做了一个过期设置,那样 MySQL 就会自动将密码标识为过期的,并且阻止其访问。这个并不是绝对必要的设置,因为 Vault 在让令牌过期时会删除其创建的用户账户, 不过通过在 MySQL 中额外再加上这么一层,就能让你利用 MySQL 的密码过期设置来将 Vault (这个看起来是全局的)设置得比需要的更加长一点点,并且按角色而有所不同的。你也可以利用它来作为跟踪方式,追踪那些由 Vault 生成的很快就会过期 MySQL 账户的。更重要的是,你确保了应用程序能接收重新认证,不管它是在做这件事情的时候停止了操作、接受新增的延时,还是会在处理的过程中发生终止和重启。 现在我们将对要连接到数据库的用户进行认证和请求。 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 | $ curl --cert user.pem --key privkey.pem -H 'Content-type: application/json' ${VAULT_ADDR}/v1/auth/cert/login -X POST 2 >/dev/ null | json_pp
{
"auth" : {
"policies" : [
"default" ,
"demo"
],
"accessor" : "2e6d4b95-3bf5-f459-cd27-f9e35b9bed16" ,
"renewable" : true ,
"lease_duration" : 3600 ,
"metadata" : {
"common_name" : "thisisnotme@myfirstdomain.com" ,
"cert_name" : "demo" ,
"authority_key_id" : "" ,
"subject_key_id" : ""
},
"client_token" : "018e6feb-65c4-49f2-ae30-e4fbba81e687"
},
"lease_id" : "" ,
"wrap_info" : null ,
"renewable" : false ,
"data" : null ,
"request_id" : "f00fe669-4382-3f33-23ae-73cec0d02f39" ,
"warnings" : null ,
"lease_duration" : 0
}
$ curl --cert user.pem --key privkey.pem -H 'Content-type: application/json' -H 'X-Vault-Token: 018e6feb-65c4-49f2-ae30-e4fbba81e687' ${VAULT_ADDR}/v1/mysql/creds/readonly -X GET 2 >/dev/ null | json_pp
{
"errors" : [
"permission denied"
]
}
|
这是怎么回事呢? 好吧,还记得我们之前创建的策略吗? 我们还没有允许对 MySQL 角色生成器的访问,因此需要对策略进行修改,然后再应用这个策略。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | (vault)$ cat <<EOF | vault policy-write demo /dev/stdin
path "sys/*" {
policy = "deny"
}
path "secret/mysql/*" {
policy = "read"
capabilities = [ "list" , "sudo" ]
}
path "mysql/creds/readonly" {
policy = "read"
capabilities = [ "list" , "sudo" ]
}
EOF
Policy 'demo' written.
|
现在我们已经对策略进行了修改,允许对只读角色访问(在请求访问是请求走的是 mysql/creds),我们可以对策略进行一下检查,看看其是否已经被应用,再看看我们是否可以得到一个 MySQL 的用户账户。 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 | # Request a user account
$ curl --cert user.pem --key privkey.pem -H 'Content-type: application/json' -H 'X-Vault-Token: 018e6feb-65c4-49f2-ae30-e4fbba81e687' ${VAULT_ADDR}/v1/mysql/creds/readonly -X GET 2 >/dev/ null | json_pp
{
"request_id" : "7b45c9a1-bc46-f410-7af2-18c8e91f43de" ,
"lease_id" : "mysql/creds/readonly/c661426c-c739-5bdb-cb7a-f51f74e16634" ,
"warnings" : null ,
"lease_duration" : 3600 ,
"data" : {
"password" : "099c8f2e-588d-80be-1e4c-3c2e20756ab4" ,
"username" : "read-cert-401f2c"
},
"wrap_info" : null ,
"renewable" : true ,
"auth" : null
}
# Test MySQL access
$ mysql -h localhost -u read-cert-401f2c -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or g.
Your MySQL connection id is 17
Server version: 5.7 . 14 - 8 -log Percona Server (GPL), Release '8' , Revision '1f84ccd'
Copyright (c) 2009 - 2016 Percona LLC and/or its affiliates
Copyright (c) 2000 , 2016 , Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.
mysql> show grants;
+-----------------------------------------------+
| Grants for read-cert-401f2c@% |
+-----------------------------------------------+
| GRANT SELECT ON *.* TO 'read-cert-401f2c' @ '%' |
+-----------------------------------------------+
1 row in set ( 0.00 sec)
# Display the full account information
$ pt-show-grants --only= 'read-cert-401f2c' @ '%'
-- Grants dumped by pt-show-grants
-- Dumped from server Localhost via UNIX socket, MySQL 5.7 . 14 - 8 -log at 2016 - 11 - 08 23 : 28 : 37
-- Grants for 'read-cert-401f2c' @ '%'
CREATE USER IF NOT EXISTS 'read-cert-401f2c' @ '%' ;
ALTER USER 'read-cert-401f2c' @ '%' IDENTIFIED WITH 'mysql_native_password' AS '*FF157E33408E1FBE707B5FF89C87A2D14E8430C2' REQUIRE NONE PASSWORD EXPIRE INTERVAL 1 DAY ACCOUNT UNLOCK;
GRANT SELECT ON *.* TO 'read-cert-401f2c' @ '%' ;
|
好了!现在我们不需要直接去创建一个用户了,当需要的时候,应用程序能自动获取一个。我们已经让账户可以自动过期,因此不管 Vault 是否会过期,证书都只会在一天之内有效,而我们也减少了令牌的有效时间量,这样做非常好,它能减少发生任何流氓行为的时间窗口。
本文中我们就主题做了许多介绍,一些细节之所以被省略是为了突出重点。OpenSSL, Let’s Encrypt 以及 Vault 的在线文档写得相当好,因此,如果你感兴趣的话,也可以继续深入了解下去。希望本文的提供的内容能让你满意,并且能引起你试用 Vault 的欲望。同时,我们也希望本文能让你对 Let's Encrypt 这个很棒的服务进行关注,并使你意识到,现在是时候为读者、客户以及其它服务对象提供一种安全的在线体验了。 |