ブログ | NGINX

HashiCorp Vault を使用して NGINX の SSL 秘密鍵を保護する

NGINX-F5 水平黒タイプ RGB の一部
オーウェン・ギャレット サムネイル
オーウェン・ギャレット
2019年4月16日公開

このシリーズの最初の投稿では、SSL 秘密鍵のセキュリティを向上させるためのいくつかの方法について説明します。 この投稿は、NGINX インスタンスと暗号化パスワードを安全に共有するために使用されるリモート パスワード配布ポイント (PDP) のデモで終わりました。

HashiCorp Vaultのようなシークレット管理システムは、サンプル PDP と同様の方法で動作します。

  • HTTPSまたは別のAPIを使用してアクセスされる中央(または高可用性と分散)シークレットサービスを使用します。
  • クライアントは認証トークンまたはその他の手段によって認証される
  • 秘密へのアクセスを制御するために必要に応じてトークンを取り消すことができる

この投稿では、HashiCorp Vault を設定して SSL パスワードを配布する方法を説明します。 セキュリティをさらに強化するには、外部ハードウェア セキュリティ モジュール (HSM) を設定できます。

SSL 証明書とキーのペアのディスク上の保存を完全に排除するには、このシリーズの 3 番目の投稿「NGINX Plus キー値ストアを使用して HashiCorp Vault の一時 SSL キーを保護する」を参照してください。 HashiCorp Vault から一時的な SSL キーを生成し、NGINX Plus キー値ストアのメモリに保存する方法について説明します。

この投稿は、NGINX Open Source と NGINX Plus の両方に適用されます。 読みやすくするために、全体を通じてNGINXを参照します。

HashiCorp Vault を使用して SSL 秘密鍵を保護する

このセクションの手順では、Vault を使用して中央 PDP サーバーを設定し、SSL パスワードを配布します。 これらはDigitalOcean の指示に基づいています。必要に応じて変更し、独自の Vault ポリシーに準拠させてください。

この例では、各リモート Web サーバーには一意の認証トークンがあります。 これらのトークンは、Vault のsecret/webservers/パス内のシークレットにアクセスするために使用でき、SSL パスワードはsecret/webservers/ssl_passwordsに保存されます。

トークンを保護する方法と、必要に応じて個々の認証トークンを取り消す方法について説明します。

PDP サーバーに HashiCorp Vault をダウンロード、インストール、初期化する

  1. DigitalOcean の指示に従って、PDP サーバーに Vault をダウンロードして解凍します。 Vault をリモートからアクセスできるようにし、TLS を無効にするために (テスト時の使いやすさのため)、次のサンプル/etc/vault.hclファイルを使用しています。

    バックエンド "file" { パス = "/var/lib/vault"
    }
    
    リスナー "tcp" {
    アドレス = "0.0.0.0:8200"
    tls_disable = 1
    }
    
  2. 起動スクリプト (作成した場合) を使用して、または手動で Vault を起動します。

    ユーザー@pdp:~$ sudo /usr/local/bin/vault サーバー -config=/etc/vault.hcl
    
  3. Vault を初期化し、初期ルート トークンを取得します。

    user@pdp:~$ export VAULT_ADDR=http://localhost:8200 user@pdp:~$ vault init -key-shares=3 -key-threshold=2 user@pdp:~$ vault operator unseal初期ルートトークン: 86c5c2a4-8ab2-24dd-1816-48449c83114e
    
  4. 以下のコマンドの多くでは、初期ルート トークンを提供する必要があります。 便宜上、これをroot_tokenシェル変数に割り当てます。

    ユーザー@pdp:~$ルートトークン=86c5c2a4-8ab2-24dd-1816-48449c83114e
    

秘密を保存する

  1. PDP サーバーで引き続き作業しながら、パスワードが入った/tmp/ssl_passwords.txtという一時ファイルを作成します。

  2. このファイルを Vault にシークレットとして保存し、取得できることを確認します。

    user@pdp:~$ VAULT_TOKEN=$root_token vault kv put secret/webservers/ssl_passwords value=@/tmp/ssl_passwords.txt user@pdp:~$ VAULT_TOKEN=$root_token vault kv get -field=value secret/webservers/ssl_passwords password1 password2 ...
    
  3. セキュリティのため、 /tmp/ssl_passwords.txtを削除してください。

  4. web.hclというファイルに次の内容のポリシー仕様を作成します。

    パス "secret/webservers/*" {
    機能 = ["read"]
    }
    
  5. ポリシーを Vault に読み込み、 webという名前を付けます。

    user@pdp:~$ VAULT_TOKEN=$root_token ボールトポリシー書き込みウェブ web.hcl
    
  6. 新しい認証トークンを作成し、それをWebポリシーに関連付け、オプションでdisplay-nameパラメータを含めてユーザーフレンドリな名前を付けます。 tokentoken_accessorの値をメモします。後続のコマンドで使用します。

    user@pdp:~$ VAULT_TOKEN=$root_token vault token create -policy=web -display-name=webserver1キー 値 --- ----- token dcf75ffd-a245-860f-6960-dc9e834d3385 token_accessor 0c1d6181-7adf-7b42-27be-b70cfa264048
    

    NGINX Web サーバーはこのトークンを使用して SSL パスワードを取得します。 Webポリシーにより、Web サーバーがsecret/webservers/*パスの外部からシークレットを取得できなくなります。

Webサーバーがトークンを取得できることを確認する

  1. NGINX Web サーバーで作業し、Vault バイナリをインストールします。
  2. リモート Vault サーバーの場所 (ここではhttp://pdp:8200 ) を宣言し、Web サーバー マシンがトークンを使用して SSL パスワードを取得できることを確認します。

    user@web1:~$ export VAULT_ADDR=http://pdp:8200 user@web1:~$ VAULT_TOKEN=dcf75ffd-a245-860f-6960-dc9e834d3385 vault kv get -field=value secret/webservers/ssl_passwords password1 password2 ...
    

Webサーバー上でNGINX Vaultコネクタを構成する

  1. 最初の投稿でサンプル PDP を設定する一環として、NGINX ホスト (Web サーバー マシン) 上にconnector.shというシェル スクリプトを作成しました。 ここで、Vault を使用するように変更します。

    #!/bin/sh
    # 使用方法: connector_v.sh 
    
    CONNECTOR=$1
    CREDS=$2
    
    [ -e $CONNECTOR ] && /bin/rm -f $CONNECTOR
    
    mkfifo $CONNECTOR; chmod 600 $CONNECTOR
    
    export VAULT_ADDR=http://pdp:8200
    export VAULT_TOKEN=$CREDS
    
    while true; do
    vault kv get -field=value secret/webservers/ssl_passwords > $CONNECTOR
    sleep 0.1 # 競合状態、EOF を確実にする
    done
    
  2. 次のように呼び出され、スクリプトをバックグラウンド プロセスとして実行します。

    root@web1:~# ./connector_v.sh /var/run/nginx/ssl_passwords \dcf75ffd-a245-860f-6960-dc9e834d3385 &
    
  3. コネクタ パスから読み取ってコネクタをテストします。

    root@web1:~$ cat /var/run/nginx/ssl_passwordsパスワード1 パスワード2 ...
    
  4. NGINX を起動時にssl_passwordsファイルを読み取り、その内容をパスワードとして使用して暗号化された秘密鍵を復号化するように設定します。 ssl_password_fileディレクティブは、サーバーブロック (最初の投稿の標準構成用に作成されたものなど) に含めることも、 httpコンテキストに含めることもでき、複数の仮想サーバーに適用できます。

    ssl_password_file /var/run/nginx/ssl_passwords;
    
  5. NGINX がパスワードを読み取り、SSL キーを復号化できることを確認します。

    root@web1:~# nginx -t nginx: 設定ファイル /etc/nginx/nginx.conf の構文は正常です nginx: 設定ファイル /etc/nginx/nginx.conf のテストは成功しました
    

Webサーバーの認証トークンの取り消し

Web サーバーが侵害された場合や廃止された場合は、簡単にアクセスを取り消すことができます。 これを行うには、Web サーバーで使用される認証トークンを直接取り消します。

user@pdp:~$ VAULT_TOKEN=$root_token ボールトトークン取り消し dcf75ffd-a245-860f-6960-dc9e834d3385

Vault トークンは機密データ項目であり、多くの Vault ワークフローでは認証されたクライアントに発行されたトークンのコピーは保存されません。 トークンのコピーが漏洩すると、攻撃者がクライアントになりすます可能性があります。

代わりに、アクセサーを使用してアクティブ トークンを管理するのが一般的です。アクセサーはトークンに対して制限された権限を与え、トークンの値を取得するために使用することはできません。 トークンが発行されたときに保存するのではなく、対応するアクセサーを保存します。

Web サーバーの認証トークンのアクセサーを特定する必要がある場合は、 vault listコマンドを実行してアクセサーのリストを取得し、各アクセサーに対してvault token lookupコマンドを実行して、関連する表示名とポリシーを持つアクセサーを見つけます。

user@pdp:~$ VAULT_TOKEN=$root_token vault list /auth/token/accessorsキー ---- 83be5a73-9025-1221-cb70-4b0e8a3ba8df 0c1d6181-7adf-7b42-27be-b70cfa264048 f043b145-7a63-01db-ea85-9f22f413c55e user@pdp:~$ VAULT_TOKEN=$root_token vault token lookup -accessor 0c1d6181-7adf-7b42-27be-b70cfa264048キー値 --- ----- ... display-name webserver1 ... policy web ...

次に、アクセサーを使用してトークンを取り消すことができます。

user@pdp:~$ VAULT_TOKEN=$root_token ボールトトークン取り消し -accessor 0c1d6181-7adf-7b42-27be-b70cfa264048

セキュリティへの影響

Vault を使用すると、最初の投稿で説明したサンプル PDP と同様のセキュリティ プロファイルが実現します。 SSL 秘密鍵は、対応するパスワードが取得された場合にのみ取得できます。そのためには、攻撃者は現在の認証トークンの値を知っている必要があります。

Vault を使用する主な利点は、シークレット ストアを自動化し、拡張できることです。

外部 HSM を使用して秘密鍵を管理する

このシリーズでこれまで取り上げてきたソリューションはどれも、攻撃者が NGINX サーバーへのルートアクセス権を取得した場合に秘密鍵を保護しません。 攻撃者が NGINX のランタイム メモリにアクセスしたり、コア ダンプを生成したりできる場合、プロセスのメモリをスキャンして秘密鍵データを見つけるためのよく知られた手法があります。

外部ハードウェア セキュリティ モジュール (HSM) は、SSL 秘密キーを外部の改ざん防止ハードウェアに保存することでこの問題に対処します。 復号化はサービスとして提供されており、NGINX は kay を必要とする SSL 操作を実行する必要があるときはいつでもそのサービスにアクセスします。

NGINX サーバーは SSL 秘密鍵データを見ることはありません。 サーバーのルートアクセス権を取得した攻撃者は SSL 秘密鍵を取得できませんが、NGINX 資格情報を使用して HSM 復号化サービスにアクセスすることで、オンデマンドでデータを復号化できます。

HSM にアクセスするための NGINX の設定

NGINX は、すべての SSL 秘密鍵操作を OpenSSL と呼ばれる暗号ライブラリに委任します。 HSM ベンダーの OpenSSL エンジンを使用することで、サードパーティの HSM デバイスを NGINX で利用できるようになります。

NGINX の構成は各ベンダーの HSM に固有ですが、一般的には次のような簡単なパスに従います。

  • デフォルトのソフトウェア エンジンではなく、ベンダーの OpenSSL エンジンを使用するように NGINX を構成します。

    ssl_certificate_keyエンジン:ベンダー-hsm-engine:...;
    
  • 実際の秘密鍵を使用するのではなく、ベンダーが提供する「偽の」キーを使用するように NGINX を構成します。 このキーには、HSM デバイス上の実際のキーを識別するハンドルが含まれています。

    ssl_certificate_key ssl/vendor.private.key;
    

    キーには HSM デバイスにアクセスするための資格情報も含まれる場合があります。また、追加のベンダー固有の構成を使用して資格情報が提供される場合もあります。

  • (オプション) NGINX ワーカー プロセスの数を増やすなど、必要なチューニングを適用して、NGINX と HSM のパフォーマンスを最大化します。

HSM の設定例については、 Amazon の CloudHSM ドキュメントを参照してください。

セキュリティへの影響

外部 HSM は、SSL 秘密鍵を保存するための非常に安全な方法です。 NGINX サーバーへのルートアクセス権を持つ攻撃者は、NGINX 資格情報を利用して HSM を使用して任意のデータを復号化できますが、暗号化されていない秘密鍵を取得することはできません。 HSM を使用すると、攻撃者が Web サイトを偽装したり、オフラインで任意のデータを復号化したりすることが非常に困難になります。

結論

漏洩した場合の影響は非常に深刻なため、SSL 秘密鍵などの秘密データが完全に保護されていることを確認することが重要です。

適切なセキュリティ プロセスを導入している多くの組織では、フロントエンド ロード バランサーに秘密キーを保存し、それらのサーバーへのすべてのアクセスを制限して監査するだけで十分です (最初の投稿で説明した標準構成)。

NGINX 構成を頻繁に展開する必要がある組織では、この投稿と最初の投稿の対策を使用して、秘密鍵データを表示できるユーザーまたはエンティティを制限できます。

このシリーズの 3 番目の投稿「NGINX Plus キー値ストアを使用して HashiCorp Vault からの一時 SSL キーを保護する」では、 NGINX Plus API を使用して、Vault から NGINX Plus のキー値ストアへのキーと証明書のプロビジョニングを自動化する方法について説明します。

繰り返しになりますが、これらの方法のいずれも、実行中の NGINX インスタンスをリモート アクセスや構成操作から完全に保護する必要性を軽減するものではありません。

NGINX Plus をぜひお試しください。今すぐ30 日間の無料トライアルを開始するか、弊社にお問い合わせの上、ユースケースについてご相談ください


「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 以前の NGINX.com リンクはすべて、F5.com の同様の NGINX コンテンツにリダイレクトされます。"