credstashを用いてApacheのSSL秘密鍵のパスフレーズ問い合わせ無視できるようにしてみた
背景
SSL証明書を入れているApacheを再起動した場合、秘密鍵のパスフレーズの入力が求められます。
手動再起動なら人の手でパスフレーズを入力すればよいのですが、バッチ処理などでApacheの再起動を自動で行なう場合は途中で失敗してしまいます。
解決方法としては、パスフレーズを解除した秘密鍵を配置するか、SSLPassPhraseDialogというApacheのディレクティブ内にexec:/etc/httpd/conf.d/passphrase.sh
と書き、パスフレーズを表示させるシェルスクリプトを読み込ませるというものがあります。
#!/bin/sh
echo 'パスフレーズ'
とは言えセキュリティ的に不安な面がありますので、credstashとAWSリソースのKMSを使うことでパスフレーズ問題をクリアするようにしました。
credstashについて
credstashとは、認証情報の管理および共有システムとGitHubのREADMEに記載されています。
使い方としては、認証情報をAWSのKMS(Key Management Service)を用いて暗号化し、鍵と認証情報の組み合わせをDynamoDBテーブルに保存するといったことが可能になります。事前準備
credstashをインストールする前に暗号化に使用するKMSのキーを作成します。
KMSのメニュー画面からカスタマー管理型のキーを選択し、キーの作成を選択します。
設定についてはデフォルトのままで問題ありませんが、エイリアスの部分はcredstashとします。
キーの作成が完了しましたらcredstashのインストールへ移ります。
インストール方法と初期設定
pipを用いてインストールします。
pip install credstash
はじめてcredstashを使用する場合、credstash setup
でDynamoDBにcredential-storeというテーブルが作成されます。
次に鍵と認証情報をDBへ登録するためにIAMの権限を設定します。
GitHubに書かれていますが、以下のIAMポリシーを作成してIAMロールにアタッチします。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"kms:GenerateDataKey"
],
"Effect": "Allow",
"Resource": "arn:aws:kms:ap-northeast-1:AWSACCOUNTID:key/KEY-GUID"
},
{
"Action": [
"dynamodb:PutItem"
],
"Effect": "Allow",
"Resource": "arn:aws:dynamodb:ap-northeast:AWSACCOUNTID:table/credential-store"
}
]
}
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"kms:Decrypt"
],
"Effect": "Allow",
"Resource": "arn:aws:kms:ap-northeast-1:AWSACCOUNTID:key/KEY-GUID"
},
{
"Action": [
"dynamodb:GetItem",
"dynamodb:Query",
"dynamodb:Scan"
],
"Effect": "Allow",
"Resource": "arn:aws:dynamodb:ap-northeast-1:AWSACCOUNTID:table/credential-store"
}
]
}
AWSACCOUNTID
とKEY-GUID
の箇所は各々の数値を記載してください。(KEY-GUIDはKMSに記載されているキーIDのことです)
またcredstash setup
でDynamoDBのテーブル作成に失敗する場合は、以下のIAMポリシーもIAMロールにアタッチしてください。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"dynamodb:CreateTable",
"dynamodb:DescribeTable"
],
"Effect": "Allow",
"Resource": "arn:aws:dynamodb:ap-northeast-1:<ACCOUNT NUMBER>:table/credential-store"
},
{
"Action": [
"dynamodb:ListTables"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
パスフレーズの暗号化
putコマンドを使用して、パスフレーズを暗号化してDynamoDBに保存します。
credstash -r ap-northeast-1 put --key alias/credstash "任意の認証情報名" "パスフレーズ"
テーブル名をapache_passphraseと設定しましたら、このようにDynamoDBに保存されます。
パスフレーズの復号
復号はgetコマンドを使用します。
credstash -r ap-northeast-1 get "任意の認証情報名"
Apache側で読み込み
冒頭のpassphrase.sh
を以下のように書き換えます。
#!/bin/sh
Apache_Pass=`credstash -r ap-northeast-1 get apache_passphrase`
echo "$Apache_Pass"
Apacheを再起動し、パスフレーズを聞かれることなく起動できましたら成功です。
引っかかりポイント
実は弊社の環境で、上記のスクリプトでApacheを再起動してもcredstashが機能せず、再起動に失敗しました。
Apacheを動かしているサーバーのPythonが2.6系でした。
READMEに記載はありませんでしたが、2系ですとインストールできない可能性もありましたので、pyenvを用いてPythonを3.6系に変更しました。
その時に環境変数回りで問題があったのか、起動できないという問題がありました。
回避方法として、passphrase.sh
の中身を以下のように修正したらcredstashが動き、Apacheの再起動に成功しました。
#!/bin/sh
Apache_Pass=`/root/.pyenv/shims/credstash -r ap-northeast-1 get apache_passphrase`
echo "$Apache_Pass"
所感
ApacheでのSSL秘密鍵によるパスフレーズ問題はネット上でも、パスフレーズそのものを解除するか直接パスフレーズを埋め込んで読み込むしかなかったので、これを使えばよりセキュアなApache構築が可能になりますので、皆さんもぜひ使ってみてください。
credstashはPython製のコマンドラインツールですが、GoやRubyなどで作られたものもありますので、自身の好みの言語を選択するのもの良いのかもしれません。
おまけ 2020/7/24追記
nginxではどのように行なえばよいのか類似モジュールがないか調べましたが以下のディレクティブを記載すれば同じようにパスフレーズを無視してできることを確認できました。
ssl_password_file
少しだけSSLPassPhraseDialogとは記法が異なりますので注意が必要です。
http {
ssl_password_file /etc/nginx/conf.d/passphrase.sh;
server {
server_name www1.example.com;
ssl_certificate_key /etc/keys/first.key;
}
server {
server_name www2.example.com;
# named pipe can also be used instead of a file
ssl_password_file /etc/keys/fifo;
ssl_certificate_key /etc/keys/second.key;
}
}
#!/bin/bash
Nginx_Pass=`/root/.pyenv/shims/credstash get nginx_pass`
echo "$Nginx_Pass"
nginxでもSSL通信ができることを確認できました。
LT資料
この記事を基にしたLTを行ないましたので、資料を置いておきます。
Discussion