最近、子ども(男の子、6才)がスーパーマリオメーカー2をやっています。
スーパーマリオブラザーズを全然遊んだことないのに、いきなりスーパーマリオメーカー2を遊ぶ今の子供の感覚には違和感があります。ですが、そういう子も含めてちゃんと遊べるように設計している任天堂はすごいな、と思います。
はじめに
Wordpress のインフラ改善シリーズ第3弾です。
今回は Wordpress の中のシークレット情報を AWS Secrets Manager で管理するようにした話です。
Wordpress の中のシークレット情報
Wordpress では設定情報が wp-config.php の中で管理されています。
その中で、下記のような設定が書かれています。
/**#@+ * 認証用ユニークキー * * それぞれを異なるユニーク (一意) な文字列に変更してください。 * {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org の秘密鍵サービス} で自動生成することもできます。 * 後でいつでも変更して、既存のすべての cookie を無効にできます。これにより、すべてのユーザーを強制的に再ログインさせることになります。 * * @since 2.6.0 */ define('AUTH_KEY', 'put your unique phrase here'); define('SECURE_AUTH_KEY', 'put your unique phrase here'); define('LOGGED_IN_KEY', 'put your unique phrase here'); define('NONCE_KEY', 'put your unique phrase here'); define('AUTH_SALT', 'put your unique phrase here'); define('SECURE_AUTH_SALT', 'put your unique phrase here'); define('LOGGED_IN_SALT', 'put your unique phrase here'); define('NONCE_SALT', 'put your unique phrase here'); /**#@-*/
この認証用のユニークキーは環境ごとに異なる値に設定する必要があります。
複数の方法の比較検討
環境ごとに異なる設定値を設定する方法として、下記の3つの案を比較検討しました。
- 事前に EB 環境の環境プロパティを設定する
- 動的に .ebextensions 以下にファイルを生成して、デプロイ時に環境プロパティを設定する
- 別の場所(例: AWS Secrets Manager )に格納したユニークキーの情報をデプロイ時に取得、利用する
事前に EB 環境の環境プロパティを設定する
Elastic Beanstalk では環境プロパティという機能を使って、環境ごとに異なる値を設定することがとてもカンタンにできます。これは標準的な方法で多くの場合で推奨される方法なのですが、今回は採用しませんでした。
理由は、3つあります。
認証キーのような文字列はできる限り、どこにも表示されず漏洩しにくくしたい、と考えました。AWS のマネジメントコンソールの Elastic Beanstalk の設定画面に表示されない、Git のリポジトリにも保存しないようにしたかったのですす。環境プロパティを使う場合は、設定画面に表示されますし、 ENECHANGE では Terraform を使ってインフラの設定を管理している都合上 Git のリポジトリにも保存することになってしまいます。
インフラとアプリケーションの責任範囲の問題もあります。Terraform の設定を書くのはインフラエンジニアで、Wordpress の Git repo の内容を編集するのはアプリケーションエンジニアです。今回設定したい認証キーのような設定値はアプリケーションエンジニアの責任範囲であるべきです。Terraform で管理されるべき範囲ではありません。
ENECHANGE では Elastic Beanstalk 構築に関する設定などはすべて Terraform で管理しているのですが、その Terraform の環境プロパティに1つでも違いがあれば、ほかのさまざまな設定値も含めて膨大な diff が表示されるというのが Terraform の動作仕様になっています。環境プロパティの数が増えると、些細な差異に対して diff が正しいか目視確認する必要があるため、運用上とてもつらくストレスフルな作業をインフラエンジニアが実施することになってしまいます。
デプロイ時の事前処理で .ebextensions 以下にファイルを生成し、環境プロパティを設定する
Elastic Beanstalk では通常 eb deploy
というコマンドを使ってデプロイを実施します。
環境プロパティを設定する config ファイルを生成する事前処理をしてから、デプロイするようにシェルスクリプトや Ruby スクリプトを書く案を考えました。
しかし、実装まではしたもののこの方法は最終的には採用しませんでした。
理由は
- config ファイルがアプリケーション資源に含まれるため、ステージング環境と本番環境で異なるアプリケーションバージョンとなる
- ステージング用のアプリケーションバージョンを本番にデプロイしてはいけないという運用上の制約が生じる
からです。
注意していたら、問題ないとも言えますが、インフラ上の理由で運用上の制約があるべきではありません。 仮に作業ミスがあればユーザ影響が生じてしまいます。インフラは最初からそういうことが起きないように設計するのが望ましいです。
別の場所(例: AWS Secrets Manager )に格納したユニークキーの情報をデプロイ時に取得、利用する
今回は、この方式を採用しました。 次節以降で詳しく説明します。
AWS Secret Manager によるユニークキーの情報の管理
下記の要件を満たす方法を考える必要がありました。
- ステージング、本番で個別に設定可能とすること
- ソースコード等はまったく同じで EB 上の同じアプリケーションバージョンでステージングでも本番環境でもデプロイ可能にすること
- ステージング、本番などの設定値、集中一元管理し、必要であれば容易に更新できること
この要件を満たすために次の方法を採用しました。
- AWS Secrets Manager で管理する
- どの環境でどのキーを使うかは環境プロパティTIER_ENV で切り替える
- Secrets Manager の設定値を環境変数として、 Wordpress から参照可能にする
- Wordpress から環境変数として利用可能とするため、systemd の設定ファイルをデプロイ時に書き出す
Elastic Beanstalk 環境用の wp-config.php
環境変数から設定情報を取得するため、Elastic Beanstalk 用の wp-config.php をファイルを用意しました。
wp-config-eb.php
<?php define('DB_NAME', $_SERVER['MYSQL_DATABASE']); define('DB_USER', $_SERVER['MYSQL_USERNAME']); define('DB_PASSWORD', $_SERVER['MYSQL_PWD']); define('DB_HOST', $_SERVER['MYSQL_HOST']); define('DB_CHARSET', 'utf8'); define('DB_COLLATE', ''); define('AUTH_KEY', $_SERVER['AUTH_KEY']); define('SECURE_AUTH_KEY', $_SERVER['SECURE_AUTH_KEY']); define('LOGGED_IN_KEY', $_SERVER['LOGGED_IN_KEY']); define('NONCE_KEY', $_SERVER['NONCE_KEY']); define('AUTH_SALT', $_SERVER['AUTH_SALT']); define('SECURE_AUTH_SALT', $_SERVER['SECURE_AUTH_SALT']); define('LOGGED_IN_SALT', $_SERVER['LOGGED_IN_SALT']); define('NONCE_SALT', $_SERVER['NONCE_SALT']); $table_prefix = 'wp_'; define('WP_DEBUG', false); if ( !defined('ABSPATH') ) define('ABSPATH', dirname(__FILE__) . '/'); require_once(ABSPATH . 'wp-settings.php'); define('WP_SITEURL', 'http://' . $_SERVER['HTTP_HOST'] . '/articles'); define('WP_HOME', 'http://' . $_SERVER['HTTP_HOST'] . '/articles'); define('CDN_IMAGE_URL', $_SERVER['CDN_IMAGE_URL']);
wp-config.php ファイルの上書き
Amazon Linux 2 環境で使えるプラットフォームフックという機能を使って、 Elastic Beanstalk にデプロイ時に wp-config-eb.php を wp-config.php に上書きコピーします。
.platform/hooks/prebuild/90_copy_wp-config.sh
#!/bin/bash cp wp-config-eb.php wp-config.php
デプロイ時に、必要な環境変数を有効化
Amazon Linux 2 の Elastic Beanstalk 環境では Wordpress は PHP-FPM 上で動いており、PHP-FPM は systemd 経由で起動します。 そのため、デプロイ時に必要な環境変数を systemd 経由で有効化するようにしました。
環境変数を systemd 経由で有効化するには
- 環境変数ファイルの生成
- systemd でその環境変数ファイルを読み込むように設定
の2つを実施します。
環境変数ファイルの生成
環境変数ファイルを生成するとき、
- EB の環境プロパティ
TIER_ENV
の値に従って、利用する secretsmanager の ID を切り替える - secretsmanager の設定値を読み込んで、
/opt/elasticbeanstalk/deployment/secret
に systemd で扱える形式で保存する
必要があります。
これらに留意して、Elastic Beanstalk でデプロイ時に実行される .ebextensions 以下のファイルで下記の通り設定しました。
.ebextensions/02_environment.config
commands: 01_create_secret_env: command: | case ${TIER_ENV:-foo} in "production") secret_id="enechange-jp-wordpress-production" ;; "staging") secret_id="enechange-jp-wordpress-staging" ;; *) exit 1 esac env=/opt/elasticbeanstalk/deployment/env mkdir -p $( dirname $env ) /opt/elasticbeanstalk/bin/get-config environment | \ ruby -rjson -e 'JSON.load(ARGF).each { |k, v| puts %(#{k}=#{v}) }' > $env secret=/opt/elasticbeanstalk/deployment/secret aws --region=ap-northeast-1 --output=text secretsmanager \ get-secret-value \ --secret-id $secret_id \ --query "SecretString" | \ ruby -rjson -e 'JSON.load(ARGF).each { |k,v| puts %(#{k}=#{v}) }' > $secret env: TIER_ENV: "Fn::GetOptionSetting": Namespace: "aws:elasticbeanstalk:application:environment" OptionName: "TIER_ENV" AWS_REGION: ap-northeast-1 AWS_DEFAULT_REGION: ap-northeast-1
.ebextensions 以下のファイルの設定内容に関する詳細は下記を参考にしてください。
生成した環境変数ファイルを systemd で読み込むための設定
.ebextensions で生成した環境変数ファイルを systemd で読み込めるようにするために、 /etc/systemd/system/php-fpm.d
ディレクトリ以下に 10-environment.conf
というファイルを生成しています。
Elastic Beanstalk の Amazon Linux 2 の PHP 環境では /etc/systemd/system/php-fpm.d
ディレクトリ以下の .conf
拡張子のファイルは自動的に systemd で読み込まれます。
systemd で環境変数ファイルを有効化するには、Service セクション以下で EnvironmentFile
という設定値を使います。
.platform/hooks/predeploy/02-environment.sh
#!/bin/bash UNIT='php-fpm.service' DIR="/etc/systemd/system/${UNIT}.d" mkdir -p $DIR CONFFILE=${DIR}/10-environment.conf echo "[Service]" > $CONFFILE echo EnvironmentFile=/opt/elasticbeanstalk/deployment/secret >> $CONFFILE
おわりに
今回は Wordpress の wp-config.php 内の設定値を例に、Elastic Beanstalk のアプリケーション内で使うシークレット情報を secrets manager で管理するように移行した例について説明しました。
これらの設定によって、環境ごとに一部の設定値を切り替えたいが、その具体的な値は秘匿したい(AWS のマネジメントコンソール上も表示したくない)という要件を満たすことができました。