LAC WATCH

セキュリティとITの最新情報

RSS

サービス・製品 | 

複雑なポリシーを適切に管理する、HashiCorp Vault SSH CA動的シークレットエンジンとSentinel

さらに詳しく知るにはこちら

HashiCorp Vault

機密情報の暗号化や証明書の管理、動的シークレットの発行など、Vaultには様々な機能があります。今回は、動的にSSHキーを発行する手順やEnterprise版で利用できるSentinel機能との連携方法についてご紹介します。

さらに詳しく知るにはこちら

HashiCorp Vault

また、VaultのSSH CAがどのように機能するかを、複数のVagrant仮想マシンを使用した例でご覧いただきます。さらに、VaultとSentinelを組み合わせて、エンドユーザーの一時的なSSHキーがユーザーとLDAPユーザー名などで一致させる方法や、SSH CAワークフローを監査する方法もご紹介します。

なお、この記事はHashiCorpのソリューションエンジニア、Andrew Klaas氏の「HashiCorp Vault SSH CA and Sentinel」を、同社の許可を得て日本語訳したものです。


SSHキー管理における現状の課題

離れた場所にあるサーバへ安全に接続するためとはいえ、数百から数千におよぶユーザーのSSHキー管理は、システム運用者にとって負担の大きい作業です。SSHキーは仮想マシン全体で保持され、ユーザーごとに最新の状態に保たれる必要があります。特定のユーザーが特定のマシンにアクセスできないようにする必要もあるので、SSHキーの管理はさらに複雑になります。また、あるユーザーがプロジェクトを離れた場合には、すぐにそのユーザーのアクセス権を取り消す必要があります。

これらの作業はbashスクリプト、構成管理ツールやその他のソリューションを介して手動で実行されるため、エラーなど問題発生につながる可能性があります。

そこで、VaultのSSHシークレットエンジンを用いると、SSHキーに対して動的あるいはオンデマンドなアプローチを採用できます。

例えば、あるエンジニアが会社で仕事を始めたとします。そのユーザーに対して特定のマシンへのアクセスを許可する場合、管理者は有効期間の長いSSHキーを管理する必要はありません。ユーザーごとに異なる秘密鍵を管理するのではなく、ユーザーが目的のホストにSSH接続するためだけに使用できる、署名済み証明書を作成するSSH CAとしてVaultをセットアップできます。これにより、ホスト上の信頼された証明書の管理を簡素化することができます。ユーザーごとのSSHキーを管理する代わりに、対象ホスト上のロール毎(この場合は請負業者)に1つのSSH CA公開鍵を管理すれば良いことになります。この概念については、図でさらに説明します。

VaultのSSH CA動的シークレットエンジンでは一時的なSSHキーを作成することもできます。この機能はOpenSSHに組み込まれており、リソースへのアクセスを指定した時間内に制限できます。つまり、有効期限がないSSHキーを管理する代わりに、例えば1時間だけ(またはそれ以下でも)有効なSSHキーを提供できます。ユーザーが有効期限後に対象サーバに再ログインする場合、最初にVaultに再認証し、新しいSSHキーの生成を要求する必要があります(SSHキーの生成を要求したことはVaultに記録され、監査されます)。

Vaultはクライアントの公開鍵や秘密鍵への署名だけでなく、ホストキーの生成にも利用できます。ホストキーは先ほどとは逆に機能し、エンドユーザーがSSH経由でアクセスしようとしているターゲットマシンのIDを確認できます。したがって、ホストメッセージの信頼性を承認する代わりに、ホストキーをセットアップして、宛先サーバが信頼されていることを確認できます。これは、WebブラウザがWebサイトのTLS証明書を確認するのと同じように機能します。ホストキーのチェックについてこのブログでは触れませんので、詳細はこちらをご覧ください。

The authenticity of host '192.168.0.100 (192.168.0.100)' can't be established.

RSA key fingerprint is 3f:1b:f4:bd:c5:aa:c1:1f:bf:4e:2e:cf:53:fa:d8:59.

Are you sure you want to continue connecting (yes/no)?

この記事では、Uber、Facebook、Netflixなどの企業が、SSH CAを活用する同様のアプローチについて紹介しています。

SSH CA動的シークレットエンジンの使い方

SSH CAがどのように機能するかは、以下のブログ記事とビデオをご覧ください。

SSHは、非対称の公開鍵暗号化方式(公開鍵/秘密鍵)を活用して、ホストに対するクライアントの認証、およびその逆を行います。

VaultはSSH証明書の認証局として機能するように設定することができます。SSHは非対称公開キー暗号化(公開鍵/秘密鍵)を利用して、クライアントをホストに対して認証します。逆も同様です。

クライアント署名の場合、構成が完了したらユーザーがSSHで接続するホストに対して、CA公開鍵を配布します。その公開鍵は、接続先ホストのsshd設定に信頼できるユーザーキーとして追加されます。ユーザーがログインすると、sshdはユーザーキーのキー署名を信頼されたCA公開鍵と照合して、アクセスを確認します。

接続先ホストへのアクセスを制限するために、Vaultに複数の「ロール」を作成することができます。例えば、web prodロールとdevelopment prodロールを設定します。各ロールには、個別のCA公開鍵と秘密鍵があります。 次に公開鍵を宛先となるマシンに適切に配布します。これにより、ユーザー毎の個別のキーではなくCA公開鍵を管理するだけで済むため、鍵の管理が大幅に簡素化できます。

次の図は構成を示しています。この投稿の後半で、SentinelとLDAPについて説明します。

VaultでCA公開鍵を管理する流れ

ロールは、マシンのサブセットへのアクセスを制限するために使用されます。

複数のロールを設定してアクセス制限をする場合の構成例

では、SSH CA動的シークレットエンジンはどのように機能するのでしょうか?エンドユーザーが接続先ホストにSSHで接続する場合、最初にVaultへ認証します。認証後にSSH CA動的シークレットエンジンが使用できるロールへの権限を与えられていれば、署名のために公開鍵をVaultに送信することができます。Vaultで承認されたら、新しい署名付き公開鍵(証明書)が発行されます。ユーザーはこの公開鍵を使用して、接続先ホストにSSHで接続できます。また、Vaultが発行した署名付き公開鍵に対して事前に「TTL」設定することで、SSH接続に対して有効期限を付与し制限することができます。この制限はOpenSSHによって実装されます。

エンドユーザーが接続先ホストにSSHで接続する流れ

デモ

これまではCAを介してSSHを動的に管理する利点について紹介しましたので、次はデモを見て監査証跡を調べてみましょう。

Vault SSH CA ドキュメント

デモコード(共有サービスアカウントの例)

まず、Vault-guidesリポジトリからコードをコピーします。

以下GitHubのサブディレクトリにあるサンプルを利用します。

リンク先に書かれている手順に沿って進めてください。

一通り手順を踏むと、VagrantユーザーとしてクライアントサーバからVaultサーバに正常にログインできるようになります。

SSH CA動的シークレットエンジンはどのように機能するのか (クライアント検証)

まずVaultの構成では、最初にSSH-CAをセットアップします(ホストの検証は変更されます)。

# Vault server
# Mount a backend's instance for signing client keys
vault secrets enable -path ssh-client-signer ssh

# Configure the client CA certificate
vault write -f -format=json ssh-client-signer/config/ca | jq -r '.data.public_key' >> /home/vagrant/trusted-user-ca-keys.pem

# Add the SSH CA public key to our trusted keys
sudo mv /home/vagrant/trusted-user-ca-keys.pem /etc/ssh/trusted-user-ca-keys.pem
echo "TrustedUserCAKeys /etc/ssh/trusted-user-ca-keys.pem" | sudo tee --append /etc/ssh/sshd_config

# Allow host certificate to have longer TTLs
vault secrets tune -max-lease-ttl=87600h ssh-host-signer

# Create a role to sign host keys, note the default_user
# is a shared service account
# We will show this example for individual accounts in the next section
vault write ssh-host-signer/roles/hostrole ttl=87600h \
  allow_host_certificates=true \
  key_type=ca \
  allowed_domains="localdomain,example.com" \
  allow_subdomains=true

echo '
  {
    "allow_user_certificates": true,
    "allowed_users": "*",
    "default_extensions": [
      {
        "permit-pty": ""
      }
    ],
    "key_type": "ca",
    "key_id_format": "vault-{{role_name}}-{{token_display_name}}-{{public_key_hash}}",
    "default_user": "vagrant",
    "ttl": "30m0s"
  }' >> /home/vagrant/clientrole.json

# Create a role to sign client keys
vault write ssh-client-signer/roles/clientrole @/home/vagrant/clientrole.json

# Restart sshd
sudo systemctl restart sshd

エンドユーザーは、共有サービスアカウントのVaultから証明書を要求します。

# Client
# Authenticate to Vault
vault login -method=userpass username=johnsmith password=test

cat /home/vagrant/.ssh/id_rsa.pub | \
  vault write -format=json ssh-client-signer/sign/clientrole public_key=- \
  | jq -r '.data.signed_key' > /home/vagrant/.ssh/id_rsa-cert.pub

chmod 0400 /home/vagrant/.ssh/id_rsa-cert.pub

# Connect to Vault host as vagrant user
ssh -i /home/vagrant/.ssh/id_rsa -i /home/vagrant/.ssh/id_rsa-cert.pub vagrant@vault

次のセクションでは、共有サービスアカウントの代わりにユーザー固有の証明書を作成するためにSSH CAを活用する方法について説明します。

さらに一歩進んでSentinelを構成

共有サービスアカウントではなく、実際の「user」という名前のアカウントの機能を活用したい場合はどうすれば良いでしょうか?

Vault Enterprise Sentinelを活用して、強制的にLDAPユーザー名とSSHユーザー名を一致するようにできます。以下の例では、簡潔にするためにLDAPではなく「user pass」認証方法を使用しています。ただし、VaultとLDAPを使用した簡単なセットアップを確認したい場合は、Vinnie Ramirezによるブログを参照してください。

先ほどのデモを開始しSentinelを有効化します。また、前提としてVault Enterprise版のバイナリを使用して、さらに環境変数にEnterprise版として有効化する設定が必要です。このSentinelの例を示すために、分岐レポを作成しました。また、コードを簡素化するためにホストキーの検証を削除しました。

Sentinelの検証のため フォークもしくはクローンしコピーを作成します。

次のステップです。

これまでと同じ手順に従いますが、VaultサーバのセットアップスクリプトにSentinelが追加されていることに注意してください。

#Sentinel
cat <<EOF>> ssh-username-restrict.sentinel
import "strings"
username_match = func() {
 # Make sure there is request data
 if length(request.data else 0) is 0 {
     return false
 }
 # Make sure request data includes username
 if length(request.data.valid_principals else 0) is 0 {
     return false
 }
 # Make sure the supplied username matches the user's name
 if request.data.valid_principals != identity.entity.aliases[0].name {
     return false
 }
 return true
}
main = rule {
 strings.has_prefix(request.path, "ssh-client-signer/sign/clientrole") and username_match()
}
EOF

POLICY=$(base64 ssh-username-restrict.sentinel); vault write sys/policies/egp/ssh-username-restrict \
     policy="${POLICY}" \
     paths="ssh-client-signer/sign/clientrole" \
     enforcement_level="hard-mandatory"

上記のコードスニペットでSentinelを構成することで、エンドユーザーが自分のユーザーパスワード(またはLDAP)と一致しないユーザー名の証明書をVaultへリクエストすると、VaultはそのSSH証明書のリクエストを拒否します。

次に、エンドユーザーは以下の方法でVaultを使用します。

vault login -method=userpass username=johnsmith password=test

sudo rm /home/vagrant/.ssh/id_rsa-cert.pub
cat /home/vagrant/.ssh/id_rsa.pub | \
  vault write -format=json ssh-client-signer/sign/clientrole valid_principals=johnsmith public_key=- \
  | jq -r '.data.signed_key' > /home/vagrant/.ssh/id_rsa-cert.pub

sudo chmod 0400 /home/vagrant/.ssh/id_rsa-cert.pub

ssh -i /home/vagrant/.ssh/id_rsa -i /home/vagrant/.ssh/id_rsa-cert.pub johnsmith@vault
Last login: Tue Sep 24 15:35:07 2019 from 192.168.50.101

[johnsmith@vault ~]$ pwd
/home/johnsmith

「valid_principal」には、ユーザー名(LDAP/user pass/etc)が正しく含まれている必要があります。それらが一致しない場合、Sentinelは証明書のリクエストを拒否します。

監査ログ

監査ログには、SSHを実行したユーザーが反映されます。また、おまけとしてSSHキーの管理から開放されます。さらにセキュリティを強化するために、SSHキーの有効期間を30分間に設定することもできます。

監査ログはどのように見えるのでしょうか?

1. Vaultの監査ログには「johnsmith」がVaultからSSHキーをリクエストしたことが記録される。

VaultのAudit Log:

{
  "time": "2019-09-24T15:14:40.67742241Z",
  "type": "request",
  "auth": {
 "client_token": "hmac-sha256:d480cf84fb524bda7bf7017703959034af2bf3338087c7c6cab5a70f78a9403e",
 "accessor": "hmac-sha256:45838cf7b568d9085362d216431e7deb9bb521423f972fb852f908d466685ea1",
 "display_name": "userpass-johnsmith",
 "policies": [
   "default",
   "user"
 ],

 "token_policies": [
   "default",
   "user"
 ],
 "metadata": {
   "username": "johnsmith"
 },
 "entity_id": "ab60db90-9ec9-3c27-bb8f-70eb034718cc",
 "token_type": "service"
  },
  "request": {
 "id": "f5b9b4ba-6a0e-c549-ca43-cfdd8d8efa14",
 "operation": "update",
 "client_token": "hmac-sha256:d480cf84fb524bda7bf7017703959034af2bf3338087c7c6cab5a70f78a9403e",
 "client_token_accessor": "hmac-sha256:45838cf7b568d9085362d216431e7deb9bb521423f972fb852f908d466685ea1",
 "namespace": {
   "id": "root"
 },
"path": "ssh-client-signer/sign/clientrole",
 "data": {
   "public_key": "hmac-sha256:ea8df87d5df3de10ed5f13fbe8e9d3b544e5d3127744b986ca5122c3b1f27a8e",
   "username": "hmac-sha256:6a35732c1cbd35f5718c2b31d182dd5744ad59b1ffbb77ce64be92aacb793e0f",
   "valid_principals": "hmac-sha256:6a35732c1cbd35f5718c2b31d182dd5744ad59b1ffbb77ce64be92aacb793e0f"
 },
 "remote_address": "192.168.50.101"
  }
}
For a shared service account/default user

2. Vaultから発行された署名済みのSSHキーを検査:

$ ssh-keygen -Lf /home/vagrant/.ssh/id_rsa-cert.pub
/home/vagrant/.ssh/id_rsa-cert.pub:
     Type: ssh-rsa-cert-v01@openssh.com user certificate
     Public key: RSA-CERT SHA256:R00FTWX0aVSbSAXL+2ZN2NIKtjyRpWBk6cc89Yu7qTU
     Signing CA: RSA SHA256:7fXfp/Aj6Wr18P1VbtHNslbMOUrjSLQpkgnfcUdxk4Q
     Key ID: "johnsmith"
     Serial: 14304612407374961173
     Valid: from 2019-09-22T17:51:25 to 2019-09-22T18:21:55
     Principals:
             vagrant
     Critical Options: (none)
     Extensions:
             permit-pty

一意のユーザーアカウントの場合(Sentinelがプリンシパルに有効なLDAPユーザー名であるか確認):

$ ssh-keygen -Lf /home/vagrant/.ssh/id_rsa-cert.pub
/home/vagrant/.ssh/id_rsa-cert.pub:
     Type: ssh-rsa-cert-v01@openssh.com user certificate
     Public key: RSA-CERT SHA256:AKrecFXwXNjGiT5ttxTf0KrNJtZ+LNOe78hTq38fpq4
     Signing CA: RSA SHA256:+LBCed4Xyu2WiPCVvlfirHOdmXCsB+iie8rTmGJ7faI
     Key ID: "vault-clientrole-userpass-johnsmith-00aade7055f05cd8c6893e6db714dfd0aacd26d67e2cd39eefc853ab7f1fa6ae"
     Serial: 13310695863379851714
     Valid: from 2019-09-24T15:14:10 to 2019-09-24T15:44:40
     Principals:
             johnsmith
     Critical Options: (none)
     Extensions:
             permit-pty

3. 接続先ホストの監査。

共有サービスアカウント/デフォルトユーザーの場合:

$ journalctl -xn -u sshd | less
Sep 22 17:47:39 vault sshd[7280]: Accepted publickey for vagrant from 192.168.50.101 port 34704 ssh2: RSA-CERT ID vault-clientrole-userpass-johnsmith-474d054d65f469549b4805cbfb664dd8d20ab63c91a56064e9c73cf58bbba935 (serial 10118457451169283482) CA RSA SHA256:7fXfp/Aj6Wr18P1VbtHNslbMOUrjSLQpkgnfcUdxk4Q

実際の人間のユーザーアカウントの場合(Sentinelがプリンシパルに有効なLDAPユーザー名であるか確認):

Sep 22 18:58:45 vault sshd[7359]: Accepted publickey for johnsmith from 192.168.50.101 port 55490 ssh2: RSA-CERT ID vault-clientrole-userpass-johnsmith-f4daddc21c6b73792bb354e508bc64881dc9922c69aefe5f3265d3f6ff78cad1 (serial 14588164476024091953) CA RSA SHA256:foz3A23PrgswbQ7R+Yz5verFcOvvICvAzH7WX0twv7U

まとめ

VaultのSSH CA動的シークレットエンジンの目的とメリットをご説明しました。このエンジンを使用して、数百または数千ユーザーのキーを管理する代わりに、信頼できるCAキーでSSHキー管理することで簡素化します。また、強制的に有効期限が短いSSHキー作成をすることで、ユーザーがサーバにアクセスできる時間を制限できます。

さらに、SentinelとVaultを組み合わせると、SSHシークレットエンジンの使用するパターンを強制できます。その一例では、ユーザーがプリンシパルに有効なLDAPユーザー名である場合のみSSHキーを作成することができました。

その他の参考資料

図の作成はBrian Greenです。(HashiCorpのインプリメンテーションサービスディレクター)


Vaultはオープンソースのソフトウエアですが、本番環境への導入やご利用にあたり商用サポートが必要な場合にはお問い合わせください。

お問合せ

HashiCorp Vaultに関する
お問い合わせ

この記事は役に立ちましたか?

はい いいえ

関連記事

LAC WATCH

関連記事をご紹介します

  • 本番環境でも使えるVaultクラスタをAWS上に構築する(5分以内で!)

  • HashiCorp Vaultの最新バージョン1.4がリリース、ラックが注目する2つの新機能とは?

  • データ暗号化は機密情報保護の最後の砦"Encryption as a Service"としてのVaultの使い方