Hashicorp VaultのPKI Secerts Engineを試す
HashicorpのVaultはPKI(Public Key Infrastructure、公開鍵基盤)として利用できるらしい。今回はRoot CAの作成、サーバー証明書の作成、クライアント証明書の作成と失効を試した。間違ってたらゴメンなさい。Vaultについての概要はWEB+DB PRESS Vol.104に記事が書かれているので読むとよさそう。
準備
Vaultのバージョンはv0.10.1。macOS用のバイナリをダウンロードして、PATHの通った場所に置いた。
➤ vault version
Vault v0.10.1 ('756fdc4587350daf1c65b93647b2cc31a6f119cd')
curlはHomwbrewでインストールし、opensslを使うようにしている。
➤ curl --version
curl 7.59.0 (x86_64-apple-darwin16.7.0) libcurl/7.59.0 OpenSSL/1.0.2o zlib/1.2.8
Release-Date: 2018-03-14
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP UnixSockets HTTPS-proxy
Homebrewでcurlをインストールした時のコマンド。
➤ brew install curl --with-openssl
➤ brew link curl --force
nginxもHomebrewでインストールした。
➤ nginx -v
nginx version: nginx/1.13.12
Vault serverサーバーの初期化・ログイン
まずはvault serverを用意する。ファイルで諸々を設定。バックエンドにはファイルを使うことにした。また、vaultのAPIをTLSなしで利用できるようにしておく。
disable_mlock = true
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = 1
}
backend "file" {
path = "./secrets"
}
シュッと起動。
➤ vault server -config=vault.hcl
==> Vault server configuration:
Cgo: disabled
Listener 1: tcp (addr: "0.0.0.0:8200", cluster address: "0.0.0.0:8201", tls: "disabled")
Log Level: info
Mlock: supported: false, enabled: false
Storage: file
Version: Vault v0.10.1
Version Sha: 756fdc4587350daf1c65b93647b2cc31a6f119cd
==> Vault server started! Log data will stream in below:
ターミナルの別セッションを開いて、initからのunseal、login。このあたりは長いので実行コマンドだけ。
➤ vault init
➤ vault operator unseal R4ltBqjMeWg1ouKU02YeV4wxxc8qAQdQ2xH1m84z+jT1
➤ vault operator unseal 2MKCTBnYuFlOg1nhdy45dPXrCbQ3zLgQODf7XqGQ5edW
➤ vault operator unseal igJGlnwWqO/S3pH30r6HwylKPSOrA6Ekrw6bQ0YHwtuz
➤ vault login
これで利用準備が整った。
Root CAの作成
まずはRoot CAを作るのだが、vaultでPKIを有効にする必要がある。vault secrets enableコマンドを使う。
➤ vault secrets enable -path=alpaca -description="Alpaca Root CA" -max-lease-ttl=87600h pki
Success! Enabled the pki secrets engine at: alpaca/
これで、PKIタイプのsecretとしてalpacaができた。
➤ vault secrets list
Path Type Description
---- ---- -----------
alpaca/ pki Alpaca Root CA
cubbyhole/ cubbyhole per-token private secret storage
identity/ identity identity store
secret/ kv key/value secret storage
sys/ system system endpoints used for control, policy and debugging
Generate Root APIを使ってRoot CAを作る。
➤ vault write alpaca/root/generate/internal common_name="Alpaca Root CA" ttl=87600h key_bites=4096 exclude_cn_from_sans=true
Key Value
--- -----
certificate -----BEGIN CERTIFICATE-----
MIIDIzCCAgugAwIBAgIUCcDoq0yOld0Y+uzXO14xuMyO/D0wDQYJKoZIhvcNAQEL
[snip]
IdBHItMaNSwMlOnlTDaYN+YSLBJZNRQhTpqglEKoPKZK6Kcp8pm7
-----END CERTIFICATE-----
expiration 1841048532
issuing_ca -----BEGIN CERTIFICATE-----
MIIDIzCCAgugAwIBAgIUCcDoq0yOld0Y+uzXO14xuMyO/D0wDQYJKoZIhvcNAQEL
[snip]
IdBHItMaNSwMlOnlTDaYN+YSLBJZNRQhTpqglEKoPKZK6Kcp8pm7
-----END CERTIFICATE-----
serial_number 09:c0:e8:ab:4c:8e:95:dd:18:fa:ec:d7:3b:5e:31:b8:cc:8e:fc:3d
certificate endpoint / crl distribution points などを設定する。set url apiを使う。
➤ ➤ vault write alpaca/config/urls issuing_certificates="http://127.0.0.1:8200/v1/alpaca/ca" crl_distribution_points="http://127.0.0.1:8200/v1/alpaca/crl"
Success! Data written to: alpaca/config/urls
Root CAまわりはこれでOK。
SSLサーバー証明書を作る
まず、Create Role APでロールを作る。
➤ vault write alpaca/roles/alpacareplinfo key_bites=2048 max_ttl=8760h allow_any_name=true
Success! Data written to: alpaca/roles/alpacareplinfo
Generate Certificate API でCertificateを作る。Private Keyは保存されないので、コマンド実行後に出力されたものを自分で保存しておくこと。certificateとprivate_keyに対応するvalueをファイルに保存する。alpaca.repl.info.crt と alpaca.repl.info.keyとする。
➤ vault write alpaca/issue/alpacareplinfo common_name="alpaca.repl.info" ip_sans="127.0.0.1" ttl=720h foramt=pem
Key Value
--- -----
certificate -----BEGIN CERTIFICATE-----
MIID0DCCArigAwIBAgIUPgktLTjT1Q38xoSajQKaKqPYg2MwDQYJKoZIhvcNAQEL
[snip]
liCiDZ3EW2BFgi63RNATXLqkg0qnPocKh6UN5Q455+RhxlWdDCXfPEnxjCsSQ2Hy
gXd0ExXo4CjQ57pVgNCBU1c9gA0=
-----END CERTIFICATE-----
issuing_ca -----BEGIN CERTIFICATE-----
MIIDIzCCAgugAwIBAgIUCcDoq0yOld0Y+uzXO14xuMyO/D0wDQYJKoZIhvcNAQEL
[snip]
IdBHItMaNSwMlOnlTDaYN+YSLBJZNRQhTpqglEKoPKZK6Kcp8pm7
-----END CERTIFICATE-----
private_key -----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEArxRBksznyg+C0Bd7I/SqIjjo61CT73+3pwQTIb5IWtjiq9D4
[snip]
GbS2BirViEYlPy2obPzbhWoHV9KOu1m1ObRz/xaObhEW8FyewklEgMM=
-----END RSA PRIVATE KEY-----
private_key_type rsa
serial_number 3e:09:2d:2d:38:d3:d5:0d:fc:c6:84:9a:8d:02:9a:2a:a3:d8:83:63
nginxに設定する
nginx.confを用意する。ssl_certificateとssl_certificate_keyに先ほど作成したcertificateとprivate_keyを設定する。
➤ cat nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 443 ssl;
server_name alpaca.repl.info;
ssl_certificate alpaca.repl.info.crt;
ssl_certificate_key alpaca.repl.info.key;
ssl_protocols TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
root /usr/local/var/www;
index index.html index.htm;
}
}
}
nginxを起動して…
➤ sudo nginx -c /path/to/nginx.conf
alpaca.repl.infoでnginxにアクセスできるよう、hostsに追記しておく。
➤ grep alpaca.repl.info /etc/hosts
127.0.0.1 localhost alpaca.repl.info
ここで、curlを使ってアクセスしてみる。nginxで設定したサーバー証明書の検証ができないので、以下のようなエラーとなる。
➤ curl https://alpaca.repl.info
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
insecureオプションをつけて検証しないようにするとアクセスできる。
➤ curl -k https://alpaca.repl.info
<html><body><h1>It works!</h1></body></html>
ここで、Root CAのcertificateをca.pemとして用意し、–cacertオプションで指定する。これにより、サーバー証明書の検証を行うことができ、アクセスできる。
➤ curl --cacert ca.pem https://alpaca.repl.info
<html><body><h1>It works!</h1></body></html>
クライアント証明書で認証する
認証したい。まずは認証に失敗してアクセスできないことを確認する。nginx.confに設定を追加する。ここではca.pemはRoot CAのcerfiticateを使っている。
ssl_verify_client on;
ssl_client_certificate ca.pem;
アクセス不可になることを確認する。
➤ curl --cacert ca.pem https://alpaca.repl.info
<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx/1.13.12</center>
</body>
</html>
クライアント用の証明書と秘密鍵を作る。サーバー証明書を作った時と同様、ロールを作成して証明書を作る。
➤ vault write alpaca/roles/client001 key_bites=2048 max_ttl=8760h allow_any_name=true
Success! Data written to: alpaca/roles/client001
➤ vault write alpaca/issue/client001 common_name="alpaca.repl.info" ip_sans="127.0.0.1" ttl=720h foramt=pem
Key Value
--- -----
certificate -----BEGIN CERTIFICATE-----
MIID0DCCArigAwIBAgIUN9J828bPrc0hAFymYy1bUfHicYcwDQYJKoZIhvcNAQEL
[snip]
yGQuy/YKoC15E5xuk1+y1oHOYsQNPtOXAXGo2qoGKEn78FKzkYgVEB9MkMaica63
L3f5+JatCMlu9PFUazKsAuzJrxg=
-----END CERTIFICATE-----
issuing_ca -----BEGIN CERTIFICATE-----
MIIDIzCCAgugAwIBAgIUCcDoq0yOld0Y+uzXO14xuMyO/D0wDQYJKoZIhvcNAQEL
[snip]
9jVJzSPjoFE3ea+njGR95yxIXT2vD3aznFvShPmlpl7DA/SeeOWe1MtSUb02pcKA
IdBHItMaNSwMlOnlTDaYN+YSLBJZNRQhTpqglEKoPKZK6Kcp8pm7
-----END CERTIFICATE-----
private_key -----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAw8mDJNdEgf9j8rkcN3mwXYt4hSYDw2P54jBmiuOL4WmbLm1x
[snip]
A3fqBRhroUMkLXPDfJbhi+YRfcYd1OrhodBTNJreDCZ87eW+tV8kWw==
-----END RSA PRIVATE KEY-----
private_key_type rsa
serial_number 37:d2:7c:db:c6:cf:ad:cd:21:00:5c:a6:63:2d:5b:51:f1:e2:71:87
certificateとprivate_keyをファイルに保存し、curlの–certと–keyオプションで指定することでアクセスに成功する。
➤ curl -k --cert client001.crt --key client001.key https://alpaca.repl.info
<html><body><h1>It works!</h1></body></html>
クライアント証明書を失効させてみる
認証で使うのだから、何かあったときには失効させたい。revoke certificate APIがあって、これを使えば失効させられる。
➤ vault write alpaca/revoke serial_number=37:d2:7c:db:c6:cf:ad:cd:21:00:5c:a6:63:2d:5b:51:f1:e2:71:87
Key Value
--- -----
revocation_time 1525689460
revocation_time_rfc3339 2018-05-07T10:37:40.219554054Z
失効できたが、これだけだとまだアクセスできる。CRLをnginxが参照していないためである。
➤ curl --cert client001.crt --key client001.key --cacert ca.pem https://alpaca.repl.info
<html><body><h1>It works!</h1></body></html>
API経由でcrl.pemを取得する。
➤ curl http://localhost:8200/v1/alpaca/crl/pem > crl.pem
見てみると、失効したシリアルナンバーである”37:d2:7c:db:c6:cf:ad:cd:21:00:5c:a6:63:2d:5b:51:f1:e2:71:87″がrevoked certificatesに含まれていることがわかる。
➤ openssl crl -in crl.pem -text
Certificate Revocation List (CRL):
Version 2 (0x1)
Signature Algorithm: sha256WithRSAEncryption
Issuer: /CN=Alpaca Root CA
Last Update: May 7 10:37:40 2018 GMT
Next Update: May 10 10:37:40 2018 GMT
CRL extensions:
X509v3 Authority Key Identifier:
keyid:1B:34:31:B1:15:2B:82:E5:48:29:C8:24:D3:D3:FA:7A:26:E5:56:8A
Revoked Certificates:
Serial Number: 37D27CDBC6CFADCD21005CA6632D5B51F1E27187
Revocation Date: May 7 10:37:40 2018 GMT
Signature Algorithm: sha256WithRSAEncryption
b3:09:ec:6d:da:01:c1:e2:c6:53:da:25:69:69:2c:f2:ab:60:
1d:41:f2:cd:44:28:60:b0:43:a5:05:6a:80:58:06:ec:a2:e9:
47:35:28:66:b9:60:4e:69:45:bf:95:0a:87:22:36:2d:9c:0c:
a0:23:45:35:a0:b3:f6:41:32:d0:8b:21:fe:b5:34:37:4d:fd:
84:78:cb:b7:a6:b4:3d:d4:13:23:4f:8d:53:17:3b:e2:88:af:
bf:f5:5e:18:ee:50:97:75:a8:90:29:b6:64:5a:94:f9:eb:a5:
c8:ea:03:af:83:72:03:2c:80:26:55:77:ba:6e:e2:be:f5:db:
c7:a4:68:f5:1c:07:89:45:98:53:0f:90:eb:23:a3:fd:a3:34:
e8:17:7c:58:69:39:1d:86:a0:e1:ee:35:c9:e8:11:63:ed:93:
76:c6:04:98:1d:5c:42:ca:a1:c7:bc:8a:0f:99:18:50:8c:7c:
5f:82:cc:fa:3b:fb:b4:20:ac:5d:92:b7:34:96:38:c9:0a:29:
3d:67:63:a7:f5:f1:f3:5f:61:99:37:53:f8:76:d4:7b:cd:d4:
31:b3:48:6f:1e:68:ff:11:b6:1e:b1:7d:53:b0:c6:b5:d0:e8:
2c:f3:eb:5d:08:49:3c:44:79:b6:17:3a:3f:c5:cb:a3:ed:ba:
57:17:ff:bd
-----BEGIN X509 CRL-----
MIIBsDCBmQIBATANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDEw5BbHBhY2EgUm9v
dCBDQRcNMTgwNTA3MTAzNzQwWhcNMTgwNTEwMTAzNzQwWjAnMCUCFDfSfNvGz63N
IQBcpmMtW1Hx4nGHFw0xODA1MDcxMDM3NDBaoCMwITAfBgNVHSMEGDAWgBQbNDGx
FSuC5UgpyCTT0/p6JuVWijANBgkqhkiG9w0BAQsFAAOCAQEAswnsbdoBweLGU9ol
aWks8qtgHUHyzUQoYLBDpQVqgFgG7KLpRzUoZrlgTmlFv5UKhyI2LZwMoCNFNaCz
9kEy0Ish/rU0N039hHjLt6a0PdQTI0+NUxc74oivv/VeGO5Ql3WokCm2ZFqU+eul
yOoDr4NyAyyAJlV3um7ivvXbx6Ro9RwHiUWYUw+Q6yOj/aM06Bd8WGk5HYag4e41
yegRY+2TdsYEmB1cQsqhx7yKD5kYUIx8X4LM+jv7tCCsXZK3NJY4yQopPWdjp/Xx
819hmTdT+HbUe83UMbNIbx5o/xG2HrF9U7DGtdDoLPPrXQhJPER5thc6P8XLo+26
Vxf/vQ==
-----END X509 CRL-----
nginxに設定を追加して、crlを見るようにする。
ssl_crl crl.pem;
再度アクセスを試みるが、bad requestとなり、繋がらなくなることがわかる。
➤ curl --cert client001.crt --key client001.key --cacert ca.pem https://alpaca.repl.info
<html>
<head><title>400 The SSL certificate error</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<center>The SSL certificate error</center>
<hr><center>nginx/1.13.12</center>
</body>
</html>
ひとまず動いたぞ!という記録だが、結構使えそうだなあという感触を得た。Kubernetesのような、いろんなコンポーネントがバシバシ証明書を必要とする環境で役立ちそうである。今回は証明書の配置を手動で行ったが、Consul Templateと組み合わせて自動配布、なんてこともできるので夢が広がるなあ。また、中間CAを扱うこともできるようなので、そちらも試しておきたい。