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

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

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

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

# credstashについて
credstashですが日本語情報が少なく、配布元GitHubからの説明になりますが認証情報の管理及び共有システムといえます。(https://github.com/fugue/credstash)
使い方としては、認証情報をAWSのKMS(Key Management Service)を用いて暗号化し、鍵と認証情報の組み合わせをDynamoDBテーブルに保存するといったことが可能になります。
特に推奨バージョンは書かれていませんでしたが、Python2系ですとpipのインストールができなかったので少なくとも3系でインストールする必要があります。
# 事前準備
credstashをインストールする前に暗号化に使用するKMSのキーを作成します。
KMSのメニュー画面からカスタマー管理型のキーを選択し、キーの作成を選択します。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/648568/34fbc3f7-b1b5-e3eb-0db3-ded5d267ded1.png)
設定についてはデフォルトのままで問題ありませんが、エイリアスの部分は**credstash**とします。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/648568/09142960-727d-8a3b-b07d-8a4af3ea0128.png)
キーの作成が完了しましたらcredstashのインストールへ移ります。
# インストール方法と初期設定
pipを用いてインストールします。
`pip install credstash`
初めてcredstashを使用する場合、`credstash setup`でDynamoDBにcredential-storeというテーブルが作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/648568/4fa43c1a-1976-fe9c-6613-c5b389d41080.png)

次に鍵と認証情報をDBへ登録するためにIAMの権限設定を行います。
GitHubに書かれていますが、以下のIAMポリシーを作成してIAMロールにアタッチします。

```json: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"
    }
  ]
}
```

```json: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"
    }
  ]
}
```

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

```json: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に保存されます。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/648568/f618a2fa-26b4-7be6-5639-cda8071d0129.png)
## パスフレーズの復号
復号はgetコマンドを使用します。
`credstash -r ap-northeast-1 get "任意の認証情報名"`
# apache側で読み込み
冒頭の`passphrase.sh`を以下のように書き換えます。

```bash:passphrase.sh
#!/bin/sh
Apache_Pass=`credstash -r ap-northeast-1 get apache_passphrase`

echo "$Apache_Pass"
```

apacheの再起動を行い、パスフレーズを聞かれることなく起動できましたら成功です。

# 引っかかりポイント
実は弊社の環境で、上記のスクリプトでapacheを再起動してもcredstashが機能せず、再起動に失敗しました。
apacheを動かしているサーバのPythonが2.6系だったのですが、2.6系ですとcredstashのインストールができなかったため、pyenvを用いてPythonを3.6系に変更しました。
その時に環境変数回りで問題があったのか、起動できないという問題がありました。
回避方法として、`passphrase.sh`の中身を以下のように修正したらcredstashが上手く動き、apacheの再起動に成功しました。

```bash: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](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_password_file)
少しだけSSLPassPhraseDialogとは記法が異なりますので注意が必要です。

```bash: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;
    }
}
```

```bash:passphrase.sh
#!/bin/bash

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

echo "$Nginx_Pass"
```

![Screenshot 2020-07-24 16:44:30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/648568/62820e17-55d1-a047-2159-1fda0367245d.png)
NginxでもSSL通信ができることを確認できました。