HashiCorp VaultでNGINXのSSL秘密鍵を保護

NGINX | 2019年4月16日

このシリーズの最初の記事では、SSL秘密鍵のセキュリティを強化するための複数のアプローチを紹介しました。記事の最後では、暗号化パスワードをNGINXインスタンスと安全に共有するために使用するリモートパスワード配布ポイント(PDP)のデモを行いました。

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

  • HTTPSまたは別のAPIを使用してアクセスする、一元化された(または高可用性を備えた分散型の)シークレットサービスを使用します
  • クライアントは、認証トークンまたはその他の手段によって認証されます
  • シークレットへのアクセスを制御するため、必要に応じてトークンを失効させることができます

この記事では、SSLパスワードを配布するためにHashiCorp Vaultを設定する方法を紹介します。セキュリティをさらに強化するために、外部ハードウェアセキュリティモジュールを設定する(HSM)こともできます。

SSL証明書と鍵のペアをディスクに一切保存しないようにする方法については、このシリーズの第3回の記事「NGINX Plus Key-Value Storeを使用してHashiCorp Vaultの一時SSL鍵を保護する」をご覧ください。この記事では、HashiCorp Vaultから一時SSL鍵を生成し、NGINX PlusのKey-Value Storeを使用してメモリ内に保存する方法を説明します。

この投稿は、NGINX Open SourceとNGINX Plusの両方に適用されます。読みやすくするため、以降は総称して「NGINX」と表記します。

HashiCorp Vaultを使用したSSL秘密鍵の保護

このセクションの手順では、Vaultを使用してSSLパスワードを配布する中央PDPサーバーをセットアップします。この手順は、DigitalOceanの手順に基づいています。お使いのVaultポリシーに準拠するよう、必要に応じて変更してください。

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

トークンを保護する方法と、必要に応じて個々の認証トークンを失効させる方法を見ていきます。

PDPサーバーでのHashiCorp Vaultのダウンロード、インストール、初期化

  1. Follow the DigitalOceanの手順に従って、PDPサーバーにVaultをダウンロードし、展開します。ここでは、Vaultにリモートからアクセスできるようにし、テストを容易にするためにTLSを無効化する、次のサンプル/etc/vault.hclファイルを使用します:
backend "file" {    path = "/var/lib/vault"
}

listener "tcp" {
    address = "0.0.0.0:8200"
    tls_disable = 1
}
  1. スタートアップスクリプト(作成済みの場合)を使用するか、手動でVaultを起動します:
user@pdp:~$ sudo /usr/local/bin/vault server -config=/etc/vault.hcl
  1. Vaultを初期化し、Initial Root Tokenを取得します:
user@pdp:~$ export VAULT_ADDR=http://localhost:8200
user@pdp:~$ vault init -key-shares=3 -key-threshold=2
user@pdp:~$ vault operator unseal
Initial Root Token: 86c5c2a4-8ab2-24dd-1816-48449c83114e
  1. 以降の多くのコマンドでは、Initial Root Tokenを指定する必要があります。便宜上、これをroot_tokenシェル変数に割り当てます。
user@pdp:~$ root_token=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
...
  1. セキュリティ確保のため、/tmp/ssl_passwords.txtを削除します。
  2. 次の内容で、web.hclというファイルにポリシー仕様を作成します。
path "secret/webservers/*" {
    capabilities = ["read"]
}
  1. ポリシーをVaultに読み込み、名前を「web」にします:
user@pdp:~$ VAULT_TOKEN=$root_token vault policy write web web.hcl
  1. 新しい認証トークンを作成し、webポリシーに関連付けます。必要に応じて、わかりやすい名前を付けるためのdisplay-nameパラメーターも指定します。後続のコマンドで使用するため、tokenとtoken_accessorの値を控えておいてください:
user@pdp:~$ VAULT_TOKEN=$root_token vault token create -policy=web -display-name=webserver1
Key                  Value
---                  -----
token                dcf75ffd-a245-860f-6960-dc9e834d3385
token_accessor       0c1d6181-7adf-7b42-27be-b70cfa264048
  1. 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:8200user@web1:~$ VAULT_TOKEN=dcf75ffd-a245-860f-6960-dc9e834d3385 vault kv get -field=value secret/webservers/ssl_passwords
password1
password2
...

WebサーバーでNGINX Vault Connectorを設定する

  1. 「最初の記事」でサンプルPDPをセットアップした際に、NGINXホスト(Webサーバーマシン)上にconnector.shというシェルスクリプトを作成しました。ここでは、Vaultを使用するようにこのスクリプトを変更します。
#!/bin/sh
# Usage: 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 # race condition, ensures EOF
done
  1. 次のようにスクリプトを呼び出し、バックグラウンドプロセスとして実行します:
root@web1:~# ./connector_v.sh /var/run/nginx/ssl_passwords \dcf75ffd-a245-860f-6960-dc9e834d3385 &
  1. コネクターパスから読み取り、コネクターをテストします:
root@web1:~$ cat /var/run/nginx/ssl_passwordspassword1
password2
...
  1. 起動時にssl_passwordsファイルを読み込み、その内容を暗号化された秘密鍵の復号用パスワードとして使用するようにNGINXを設定します。ssl_password_fileディレクティブは、serverブロック(最初の記事の標準構成で作成したものなど)またはhttpコンテキストに含めることができます。httpコンテキストに含めると、複数の仮想サーバーに適用できます。
ssl_password_file /var/run/nginx/ssl_passwords;
  1. NGINXがパスワードを読み取り、SSLキーを復号できることを確認します:
root@web1:~# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Webサーバーの認証トークンの失効

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

user@pdp:~$ VAULT_TOKEN=$root_token vault token revoke dcf75ffd-a245-860f-6960-dc9e834d3385

Vaultトークンは機密性の高いデータであり、多くのVaultワークフローでは、認証済みクライアントに発行したトークンのコピーを保存しません。トークンのコピーが漏えいすると、攻撃者がそのクライアントになりすます可能性があります。

代わりに、アクティブなトークンは通常、その「アクセサー」を使用して管理します。アクセサーに付与されるトークンに対する権限は限定的であり、トークン値の取得には使用できません。トークンの発行時には、トークン自体ではなく、対応するアクセサーを保存してください。

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

user@pdp:~$ VAULT_TOKEN=$root_token vault list /auth/token/accessors
Keys
----
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
Key                 Value
---                 -----
...
display-name        webserver1       
...
policy              web
...

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

user@pdp:~$ VAULT_TOKEN=$root_token vault token revoke -accessor 0c1d6181-7adf-7b42-27be-b70cfa264048

セキュリティへの影響

Vaultの使用は、最初の投稿で説明したサンプルPDPと同様のセキュリティ特性を備えています。SSL秘密鍵を取得するには、対応するパスワードを入手する必要があり、そのためには攻撃者が現在有効な認証トークンの値を把握している必要があります。

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

外部HSMを使用した秘密鍵の管理

このシリーズでこれまで取り上げたソリューションはいずれも、攻撃者がNGINXサーバーへのrootアクセス権を取得した場合、秘密鍵を保護できません。攻撃者がNGINXの実行時メモリにアクセスしたり、コアダンプを生成したりできる場合、プロセスのメモリをスキャンして秘密鍵データを特定する既知の手法があります。

外部ハードウェアセキュリティモジュール(HSM)は、SSL秘密鍵を外部の耐タンパー性を備えたハードウェアに保存することで、この問題に対処します。HSMは復号機能をサービスとして提供し、NGINXは秘密鍵を必要とするSSL処理を実行するたびに、そのサービスにアクセスします。

NGINXサーバーがSSL秘密鍵データにアクセスすることはありません。サーバー上でrootアクセス権を取得した攻撃者は、SSL秘密鍵を入手することはできませんが、NGINXの認証情報を使用してHSM復号サービスにアクセスし、必要に応じてデータを復号できます。

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

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

NGINXの設定は各ベンダーのHSMに固有ですが、通常は次のようなシンプルな手順で行います。

  • デフォルトのソフトウェアエンジンではなく、ベンダーのOpenSSLエンジンを使用するようにNGINXを設定します:
ssl_certificate_key engine:vendor-hsm-engine:...;
  • 実際の秘密鍵を使用する代わりに、ベンダー提供の「偽の」鍵を使用するようにNGINXを設定します。この鍵には、HSMデバイス上の実際の鍵を識別するハンドルが含まれています:
ssl_certificate_key ssl/vendor.private.key;
  • キーにHSMデバイスへアクセスするための認証情報が含まれている場合もあります。また、ベンダー固有の追加設定を使用して認証情報を指定することもできます。
  • (オプション)NGINXとHSMのパフォーマンスを最大限に引き出すため、NGINXワーカープロセス数を増やすなど、必要に応じてチューニングを行います。

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

セキュリティへの影響

外部HSMは、SSL秘密鍵を高い安全性で保管する方法です。NGINXサーバーへのrootアクセス権を持つ攻撃者は、NGINXの認証情報を悪用し、HSMを使用して任意のデータを復号できますが、暗号化されていない秘密鍵を取得することはできません。HSMを使用することで、攻撃者がWebサイトになりすましたり、任意のデータをオフラインで復号したりすることが大幅に困難になります。

まとめ

SSL秘密鍵などの機密データが漏えいした場合、極めて深刻な結果を招くため、確実に保護することが不可欠です。

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

NGINXの設定を頻繁にデプロイする必要がある組織では、この記事と最初の記事で紹介した対策を使用することで、秘密鍵データを閲覧できるユーザーやエンティティを制限できます。

本シリーズの第3回では、「NGINX PlusのKey-Value Storeを使用してHashiCorp Vaultの一時SSLキーを保護する」と題し、NGINX Plus APIを使用して、VaultからNGINX Plusの「Key-Value Store」へのキーと証明書のプロビジョニングを自動化する方法について説明します。

ここで改めて注意が必要なのは、いずれの方法を使用しても、稼働中のNGINXインスタンスをリモートアクセスや設定の改ざんから十分に保護する必要性は変わらないという点です。

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


共有

著者について

Owen Garrett
Owen Garrettプロダクトマネジメント担当シニアディレクター

Owen Garrettのその他のブログ記事

関連するブログ記事

チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する
NGINX | 2024/01/18

チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する

OpenTelemetryを使用してNGINXサーバーの状態と健全性をより明確に把握することで、問題のトラブルシューティングを行い、Webアプリケーションのパフォーマンスを向上できます。

AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正
NGINX | 2023/05/31

AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正

Akita SoftwareのAPIオブザーバビリティ向け新しいNGINX Plus Certified Moduleを活用して、APIエンドポイントの問題をより迅速に特定し、修正できます。

JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法
NGINX | 2023/02/09

JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法

Red HatのシニアプリンシパルデベロッパーアドボケイトであるDaniel Oh氏が、Javaプロジェクトをゼロから構築するためのひな形を作成し、Kubernetesネイティブアプリケーションとしてビルドする方法を解説します。

F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング
NGINX | 2022/05/24

F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング

NGINXaaS for Azureは、NGINX PlusをAzureネイティブのSaaSとして利用できるサービスです。NGINXの設定を容易にリフト&シフトできるため、クラウド導入とモダンアプリの展開を加速できます。

SELinuxでNGINXおよびNGINX Plusを使用する
NGINX | 2018/08/17

SELinuxでNGINXおよびNGINX Plusを使用する

Red Hat Enterprise Linux(RHEL)および関連ディストリビューションでSecurity-Enhanced Linux(SELinux)を有効にすると、デフォルト設定によりNGINXとNGINX Plusの一部の操作が制限されます。この記事では、すべての機能を利用できるようにSELinuxの設定を変更する方法について説明します。

NGINX PlusによるAWS Auto Scalingグループの負荷分散
NGINX | 2017/03/06

NGINX PlusによるAWS Auto Scalingグループの負荷分散

新しいnginx-asg-sync統合ソフトウェアにより、NGINX PlusはAWS Auto Scalingグループの負荷を直接分散できるため、クラウドでの負荷分散がさらに柔軟になります