🐁

credstashを用いてApacheのSSL秘密鍵のパスフレーズ問い合わせ無視できるようにしてみた

2020/09/19に公開

背景

SSL証明書を入れているApacheを再起動した場合、秘密鍵のパスフレーズの入力が求められます。
手動再起動なら人の手でパスフレーズを入力すればよいのですが、バッチ処理などでApacheの再起動を自動で行なう場合は途中で失敗してしまいます。
解決方法としては、パスフレーズを解除した秘密鍵を配置するか、SSLPassPhraseDialogというApacheのディレクティブ内にexec:/etc/httpd/conf.d/passphrase.shと書き、パスフレーズを表示させるシェルスクリプトを読み込ませるというものがあります。

passphrase.sh
#!/bin/sh
echo 'パスフレーズ'

とは言えセキュリティ的に不安な面がありますので、credstashとAWSリソースのKMSを使うことでパスフレーズ問題をクリアするようにしました。

credstashについて

credstashとは、認証情報の管理および共有システムとGitHubのREADMEに記載されています。
https://github.com/fugue/credstash
使い方としては、認証情報を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ロールにアタッチします。

secret-writer
{
  "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"
    }
  ]
}
secret-reader
{
  "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"
    }
  ]
}

AWSACCOUNTIDKEY-GUIDの箇所は各々の数値を記載してください。(KEY-GUIDはKMSに記載されているキーIDのことです)
またcredstash setupでDynamoDBのテーブル作成に失敗する場合は、以下のIAMポリシーもIAMロールにアタッチしてください。

Setup
{
    "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を以下のように書き換えます。

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の再起動に成功しました。

passphrase.sh
#!/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とは記法が異なりますので注意が必要です。

nginx.conf
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;
    }
}
passphrase.sh
#!/bin/bash

Nginx_Pass=`/root/.pyenv/shims/credstash get nginx_pass`

echo "$Nginx_Pass"


nginxでもSSL通信ができることを確認できました。

LT資料

この記事を基にしたLTを行ないましたので、資料を置いておきます。

GitHubで編集を提案

Discussion