背景
AWSを運用しているとEC2のsshのキーペア管理が難しいです。
GCPであればメタデータにsshキーを登録すれば自動で各VMにsshできる仕組みがありますが、AWSは各インスタンスにsshのキーペアを1つだけ登録するようになっているため、複数人で運用するにはぱっと以下の方法が浮かびます。
しかしそれぞれ問題があります。秘密鍵の共有はセキュリティ的に大きな問題がありますし、後の2つは起動時に設定するのが非常に手間です。
仮にLambdaなどで新規サーバに対して設定する処理を自動化しても、あとからジョインしたメンバーは別途対応しなくてはいけません。
そこでsshを公開鍵認証でなく、CA認証を使うことで複数のメンバーでも管理しやすくします。
CA認証は以下の記事で非常に分かりやすく説明されています。
今回このCA認証を、Vaultを使って簡単&セキュアに構築します。
環境
- Vault 0.10.3
設定
Vault Server
Secret Engineをマウントします。ドキュメントに合わせてssh-client-signer
とします。これは任意です。
$ vault secrets enable -path=ssh-client-signer ssh Success! Enabled the ssh secrets engine at: ssh-client-signer/
CA(認証局)の用意
次にCA(認証局)を用意します。CA用の秘密鍵と公開鍵が生成されます。
$ vault write ssh-client-signer/config/ca generate_signing_key=true Key Value --- ----- public_key ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAA...
roleの設定
sshユーザ与えるroleの設定です。my-role
の部分は任意の値です。
$ vault write ssh-client-signer/roles/my-role -<<"EOH" { "allow_user_certificates": true, "allowed_users": "*", "default_extensions": [ { "permit-pty": "" } ], "key_type": "ca", "default_user": "ubuntu", "ttl": "30m0s" } EOH Success! Data written to: ssh-client-signer/roles/my-role
今回はサンプルのため署名後30分間のみ有効にするという設定にしています。
permit-pty
というのはpty(=sshターミナル)を許可するという設定です。
他にもいろんなextensionsがあるので、必要であれば設定してください。
ホスト側
各ホストはvault clientをインストールしない前提で進めます。
各ホストにCAの公開鍵を配置
CA認証(あとで発行するデジタル証明書の検証)用のCA公開鍵を各ホストに置くことになるので、curlで取得します。
# curl -o /etc/ssh/trusted-user-ca-keys.pem http://{vault_addr}:8200/v1/ssh-client-signer/public_key
ちなみにvault clientでも取得できますが、その場合はvault client自体が必要ですし、HTTP APIと違ってトークンも必要なので各サーバに置くケースには向いてません。
なのでcurlをお勧めします。
/etc/ssh/sshd_config
取得した公開鍵を/etc/ssh/sshd_config
のTrustedUserCAKeys
に指定します。
TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem
設定したらsshd
を再起動してください。
クライアント側
クライアント側はvault clientをインストール済みと想定します。
クライアントの公開鍵に対しvaultのCAに署名してもらう
先程設定したroleを指定して署名をしてデジタル証明書(署名された公開鍵)を発行します。
vault write ssh-client-signer/sign/my-role \ public_key=@$HOME/.ssh/id_rsa.pub Key Value --- ----- serial_number 3bf4efaf2cc7fd6d signed_key ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2Vyd...
signed_key
をコピペしてデジタル証明書を保存してもいいですし、以下のように直接保存するのもOKです。
vault write -field=signed_key ssh-client-signer/sign/my-role \ public_key=@$HOME/.ssh/id_rsa.pub > ~/.ssh/signed-cert.pub
ちなみにこの証明書の発行がwriteコマンドしかないのは、おそらくはvaultがデータを保存せずに単に署名結果をstdoutに出しているだけだからです。
Transit - Secrets Engines | Vault by HashiCorp も同じようにデータは保持せず、暗号化・復号をstdoutに出力します。
証明書の確認
証明書の有効期限などを確認します。
$ ssh-keygen -Lf ~/.ssh/signed-cert.pub /Users/jun06t/.ssh/signed-cert.pub: Type: ssh-rsa-cert-v01@openssh.com user certificate Public key: RSA-CERT SHA256:cx2J6jOv6YKT0Wu3rJkyRiKjqZcBng3t7bLT9bNhczc Signing CA: RSA SHA256:VmdOfyA6D01mzdkhEZVBhQgrjzhOFMADmpFgj7ToteY Key ID: "vault-token-731d89ea33afe98293d16bb7ac99324622a3a997019e0dededb2d3f5b3617337" Serial: 1152433828803108559 Valid: from 2018-07-10T17:13:20 to 2018-07-10T17:43:50 Principals: ubuntu Critical Options: (none) Extensions: permit-pty
roleで設定した通り30分間のみ有効ですね。
動作確認
ローカルの秘密鍵と、先程のデジタル証明書(vaultのCAで署名済みな公開鍵)を使ってsshします。
$ ssh -i ~/.ssh/signed-cert.pub -i ~/.ssh/id_rsa ubuntu@{vaultのCAの公開鍵を置いたホスト} Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.4.0-1062-aws x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage Get cloud support with Ubuntu Advantage Cloud Guest: http://www.ubuntu.com/business/services/cloud 8 packages can be updated. 0 updates are security updates.
ログインできました!!
両方指定がちょっと手間かも
ちなみにsigned-cert.pub
を秘密鍵と同じ名前+-cert.pub
にすれば(id_rsa-cert.pub
)指定は省けます。
$ ssh -i ~/.ssh/id_rsa ubuntu@{vaultのCAの公開鍵を置いたホスト}
有効期限過ぎたら?
デジタル証明書の有効期限が過ぎるとsshできなくなります。
$ ssh -i ~/.ssh/signed-cert.pub -i ~/.ssh/id_rsa ubuntu@{vaultのCAの公開鍵を置いたホスト} Permission denied (publickey).
メリット
以下のようなチーム開発であるあるなケースに対応できます。
サーバが増えた
CA公開鍵を登録するだけでOK。
AWSなら登録済みのAMIを作ればそれ以降は対応不要。
メンバーが増えた
新規メンバーの公開鍵をCAで署名(デジタル証明書の発行)すればOK
メンバーが退職した
デジタル証明書には有効期限があり、そのうち使えなくなるのでOK。
既存メンバーの有効期限はvaultコマンドでサクッと更新可能。
まとめ
vaultを使ったsshのCA認証方法を説明しました。
CA認証を使えば、サーバへの設定は最小かつメンバーは自分の鍵を使えるのでチーム開発でのsshがよりセキュアになります。