この記事の内容
- WordPressで作成したコンテンツを静的にホスティングする設計まとめ
- ハンズオンを兼ねた事例報告(+自分の所感)の流れで執筆
背景
そもそもなぜタイトルのような設計を考えたのかというと、先日アサインされたプロジェクトの要件が以下のとおりだったからです。
- 誰でも簡単にコンテンツを作成できる
- セキュアな設計
ここで、WordPressには以下の長所があります。
- 技術に明るくない人であっても簡単にコンテンツを作成できる
- 裏はPHPで動いているので拡張性が非常に高い
- 初期導入に必要となるコストが少ないこと
ただし、ご存知の通りWordPressの欠点として大きなものが以下です。
- 脆弱性が多く十分な対策を要求される
上記を鑑みて、WordPressの長所を余すことなく享受し欠点を最大限カバーすることができるようになる「WordPressを静的コンテンツで配信」を目標に構築を行いました。
対応策
今回はタイトルにも書いている通り、「WordPress」+「S3」を採用しましたが他にも上記の項目を満たす候補はありましたので、それらの否決理由を簡単に紹介しておきます。
方法
- microCMS
- メリット
- コンテンツの配信が簡単にできる
- 拡張性が無限
- デメリット
- 外側のフロント部分やコンテンツ配信以外のロジック部分をコーディングする必要あり、初期導入コストと運用コストが大きい
- メリット
- 他社CMSサービス
- メリット
- コンテンツの配信が簡単にできる
- サーバーの運用などが必要なくなる
- デメリット
- 拡張性が少ないものが多い
- 月々のコストが高い
- メリット
それぞれのメリデメを評価した結果敢えなく採用には至りませんでした。
手順
AWSサイド
1. VPC
- 検索窓から「VPC」を検索
- リージョンが「東京(ap-northeast-1)」であることを確認
- ダッシュボードの「VPCを作成」を押下
- 設定項目は以下の通り
項目 | VPC |
---|---|
作成するリソース | VPCなど |
名前タグの自動生成 | True static-wordpress(好きな名前タグを入力) (以降は、名前タグのプレフィックスとして static-wordpressを使用します) |
IPv4 CIDR ブロック | 10.0.0.0/16 |
IPv6 CIDR ブロック | Ipv6 CIDRブロックなし |
テナンシー | デフォルト |
アベイラリティゾーン(AZ)の数 | 2 |
プライベートサブネットの数 | 2 |
1aパブリックサブネット CIDRブロック | 10.0.1.0/24 |
1cパブリックサブネット CIDRブロック | 10.0.2.0/24 |
1aプライベートサブネット CIDRブロック | 10.0.3.0/24 |
1cプライベートサブネット CIDRブロック | 10.0.4.0/24 |
NATゲートウェイ | なし |
VPCエンドポイント | なし |
DNSホスト名を有効化 | True |
DNS解決を有効化 | True |
ポイント
- 今回はALBにIP制限を設けることで安全性を担保します。
ALBを使用するにはサブネットが複数必要となりますので、空のサブネットとして1cを作成しておきます。
2. セキュリティグループ
- 検索窓から「EC2」を検索して押下
- 画面左ペインから「ネットワーク&セキュリティ」の「セキュリティグループ」を押下
- 「セキュリティグループ」を押下
- 以下の通りに3つ作成
項目 | ALB用 | WordPress用 | RDS用 |
---|---|---|---|
セキュリティグループ名 | static-wordpress-sg-for-alb | static-wordpress-sg-for-wp | static-wordpress-sg-for-rds |
VPC | static-wordpress-vpc | static-wordpress-vpc | static-wordpress-vpc |
インバウンドルール | https カスタム (自分の使用するIPアドレス) http カスタム (自分の使用するIPアドレス) (httpは初期設定後に削除する) |
http カスタム static-wordpress-sg-for-alb | カスタムTCP 3306 カスタム static-wordpress-sg-for-wp |
アウトバウンドルール | http カスタム static-wordpress-sg-for-wp | htttp カスタム 0.0.0.0/0 https カスタム 0.0.0.0/0 |
- |
タグ | static-wordpress | static-wordpress | static-wordpress |
ポイント
- 大まかな流れは admin <- HTTPS -> ALB(IP制限) <- HTTP -> EC2 <- TCP -> RDS
- EC2のアウトバウンドの HTTP, HTTPS はプラグインのインストール時に使用することを考慮
- admin <-> ALB のHTTP通信は最初だけ許可する
- WordPressはデフォルトではSSL通信に対応していない
- WordpressがSSL通信に対応するまでは、HTTP通信でやり取りを行うので設定が終わるまで許可しておく
- 外部のネットワークに晒されていない ALB <-> EC2 はHTTPS通信する必要はない
現状の構成
VPCから変更はないので省略
3. EC2
-
検索窓から「EC2」を検索して押下
-
リージョンが「東京」であることを確認
-
画面中部の「インスタンスを起動」を押下
-
以下の通りにインスタンスを起動
項目 インスタンス 名前 static-wordpress-instance-pub AMI Amazon Linux AMI (HVM) Kernel 5.10 インスタンスタイプ t3.micro キーペア 新規作成 VPC static-wordpress-vpc サブネット static-wordpress-subnet-public-ap-northeast-1a パブリックIPの割り当て 有効化 ファイアウォール(セキュリティグループ) 既存のセキュリティグループを選択
static-wordpress-sg-for-wp -
画面左ペインの「Elastic IP」を押下
-
「Elastic IPアドレスを割り当てる」を押下
-
ネットワークボーダーグループが「ap-northeast-1」であることを確認して「割り当て」を押下
-
「アクション」「Elastic IPアドレスの関連付け」を押下
-
次の通りに設定
項目 関連付け リソースタイプ インスタンス インスタンス static-wordpress-instance-pub プライベートIPアドレス (出てくるやつ) このElastic IPアドレスの際関連付けを許可する False
ポイント
- ボリュームサイズは後でも変更することが可能なので、まずは一番安いので問題なし
- しかもこのインスタンスはWordPressの編集・出力のみにしか使用しないのでスループットが250MbhB/sに張り付かないと予想
- Elastic IPはパブリックIPが変動しないように設定する
- AMIはAMIMOTOのようにWordPress用にカリカリにチューニングされたものも存在するが、今回は編集・出力にしか使用しないのでAmazon Linuxで十分と判断した
- ユーザーデータはこけたときにログが出力しにくいので立ち上がったのちに、SSMで手動で実行するようにした
現在の構成
4. Route53
- まず自分の独自ドメインを取得する(私はお名前ドットコムで無料で取得しました。)
- 「Route53」に移動
- リージョンが「グローバル」であることを確認
- 「ホストゾーン」>「ホストゾーンの作成」を押下
- 以下の通りに ホストゾーンを作成
項目 ドメイン名 (0. で取得したドメイン。サブドメインは指定しないほうがいい)
static-wordpress.ioタイプ パブリックホストゾーン - 「static-wordpress.io」のダッシュボードで「NSレコード」の「値/トラフィックのルーティング先」のURLの末尾の「.」を除外したものを、それぞれのドメイン会社に従ってネームサーバー登録する。
- お名前.comはリンク先の「他社レンタルサーバー・お客様独自のネームサーバーにて運用する場合」に則って作業してください。
ポイント
- 特になし
5. ACM
- 「ACM」>「Certificate Manager」を押下
- リージョンが「バージニア北部」であることを確認
- 「証明書をリクエスト」を押下
- 以下の設定通りに証明書をリクエスト
項目 証明書 - S3 証明書タイプ パブリック証明書をリクエスト 完全修飾ドメイン user.static-wordpress.io 検証方法 DNS検証 - 推奨 キーアルゴリズム RSA 2048 - 作成された証明書 >「Route53でレコードを作成」を押下
- ステータスが「成功」になるまで待つ
- リージョンを「東京」に移動する
- 以下の通りにもう一度証明書をリクエストして「成功」になるまで待つ
項目 証明書 - EC2 証明書タイプ パブリック証明書をリクエスト 完全修飾ドメイン admin.static-wordpress.io 検証方法 DNS検証 - 推奨 キーアルゴリズム RSA 2048
ポイント
- 証明書は使用する対象のサービスによって異なる
- Route53:グローバル(リージョンは関係ない)
- Cloud Front:グローバル(バージニア北部のみ)
- ALB:リージョナル
- 今回のケースでは、管理者用のEC2が東京リージョン、パブリック公開するS3がグローバルになるので、それぞれのリージョンを変えた
6. ALB
- 「ALB」>「Certificate Manager」を押下
- リージョンが東京であることをかくにん
- 「ターゲットグループ」>「ターゲットグループの作成」を押下
- 以下の通りにターゲットグループを設定
項目 ターゲットグループ ターゲットタイプ インスタンス ターゲットグループ名 static-wordpress-tg プロトコル:ポート名 HTTP: 80 IPアドレスタイプ名 IPv4 VPC static-wordpress-vpc プロトコルバージョン HTTP1 ヘルスチェック HTTP / ヘルスチェックポート トラフィックポート - 使用可能なインスタンスに「static-wordpress-instannce-pub」を選択
- 「ロードバランサー」>「ロードバランサーを作成」>「Application Load Balancer」を押下
- 以下の通りにALBを作成
項目 ALB ロードバランサー名 static-wordpress-alb スキーム インターネット向け IPアドレスタイプ IPv4 VPC static-wordpress-vpc マッピング ap-northeast-1a,
ap-northeast-1c1aサブネット static-wordpress-subnet-public1-ap-northeast-1a 1cサブネット static-wordpress-subnet-public2-ap-northeast-1c セキュリティグループ static-wordpress-sg-for-alb リスナー HTTPS 443 static-wordpress-tg,
HTTP 80 static0wordpress-tgセキュリティカテゴリ 全てのセキュリティポリシー ポリシー名 ELBsecurityPolicy-TLS13-1-2-2021-06(推奨) デフォルトSSL / TLSサーバー証明書 ACMから 証明書 admin.static-wordpress.io 相互認証 False
ポイント
- 再掲だが、admin(リスナー) <- HTTPS, HTTP -> ALB <- HTTP -> EC2(ターゲットグループ) なので、リスナーにはHTTPSとHTTPを、ターゲットグループにはHTTPのみを設定
- 先ほどACMで作成した証明書を付与することでSSL通信を実現
- ここのセキュリティグループでIP制御することでセキュアな管理者画面を実現
7. RDS
- 検索窓から「RDS」を検索して押下
- リージョンが「東京」であることを確認
- 画面左ペインの「サブネットグループ」を押下
- 「DBサブネットグループを作成」を押下
- 以下の通りにサブネットグループを設定
項目 サブネットグループ 名前 static-wordpres-db-subnet-group VPC static-wordpress-vpc アベイラリティゾーン ap-northeast-1a,
ap-northeast-1cサブネット 10.0.3.0/24,
10.0.4.0/24 - 画面中部にある「データベースの作成」を押下
- 以下の通りにDBを作成
項目 RDS データベース作成方法 標準作成 エンジンのタイプ MySQL エンジンバージョン MySQL 8.0.35 テンプレート 無料利用枠(必要に応じて「本番稼働用」とかに変更) 可用性と耐久性 単一のDBインスタンス DBクラスター識別子 static-wordpress-rds マスターユーザー名 (DBのadmin名) マスターパスワード (DBのパスワード) DBインスタンスクラス バースト可能クラス(tクラスを含む)
db.t4g.microストレージタイプ 汎用SSD(gp3) ストレージ割り当て 20 コンピューティングリソース C2コンピューティングリソースに接続しない Virtuak Private Cloud (VPC) static-wordpress-vpc DBサブネットグループ static-wordpress-db-subnet-group VPCセキュリティグループ 既存の選択
static-wordpress-sg-for-rdsアベイラリティゾーン ap-northeast-1a 認証機関 - 任意 rds-ca-rsa2048-g1 データベースポート 3306 最初のデータベース名 wordpress バックアップウィンドウ ウィンドウを選択 開始時間 / 機関 17:00 UTC (2:00 JST) ログのエクスポート 全てTrue メンテナンスウィンドウ ウィンドウを選択 開始日 / 開始時間 / 期間 日曜日 / 17:00 UTC / 0.5時間 - 「マスターユーザー名」「マスターパスワード」「データベースポート」「最初のデータベース名」は後ほどWordPressの初期設定に使用するためメモする
- ステータスが「利用可能」になってから表示される「エンドポイント」も後ほどWordPressの初期設定に使用するためメモする
ポイント
- DBインスタンスクラスは後で変更ができるので、最初は一番安いもの(2024/01時点では「db.t4g.micro」)を選択
- 認証機関には有効期限が長い「rds-ca-rsa2048-g1」を選択
- バックアップウィンドウ・メンテナンスウィンドウにはメンテナンスをする可能性が低い時間帯を選択する(セオリー通りなら週末の夜とか)
8. S3
- 検索窓から「S3」を検索して押下
- 「バケット作成」を押下
- 以下のように設定
項目 S3 AWSリージョン アジアパシフィック(東京)ap-northeast-1 バケットタイプ 汎用 バケット名 static-wordpress-s3 オブジェクト所有者 ACL無効 ブロックパブリックアクセス 全てブロック バケットのバージョニング 有効にする デフォルトの暗号化 SSE-S3 バケットキー 有効にする
ポイント
- CloudFrontからのみアクセス可能にするので、「ブロックパブリックアクセス」は全てブロックでOK
9. IAM
-
検索窓から「IAM」を検索して押下
-
「ポリシー」>「ポリシーの作成」を押下
-
「JSON」を選択し、以下を作成
項目 WordPress用 ポリシー ポリシー参照 ポリシー名 DevelopS3FromWordPress ポリシー①:
S3が一般的操作を許可する{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation" ], "Resource": "arn:aws:s3:::[static-wordpress-s3のarn]" }, { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject" ], "Resource": "arn:aws:s3:::[static-wordpress-s3のarn]/*" } ] }
-
「ロール」>「ロールを作成」を押下
-
以下の通りにロールを作成
項目 エンティティ 信頼されたエンティティタイプ AWSのサービス ユースケース EC2 / EC2 許可ポリシー AmazonSSMManagedInstanceCore(AWS管理) ロール名 ManageSSMForStaticWordPressRole -
「ユーザー」>「ユーザーの作成」を押下
-
以下の通りにユーザーを作成
項目 エンティティ ユーザー名 S3DeveloperFromWordPress 許可のオプション ポリシーを直接アタッチする 許可ポリシー DevelopS3FromWordPress -
検索窓から「EC2」を検索して押下
-
static-wordpress-pub-instance を選択し「アクション」>「セキュリティ」>「IAMロールを変更」を押下
-
IAMロールを ManageSSMForStaticWordPressRole に変更
-
検索窓から「S3」を検索して押下
-
「static-wordpress-s3」>「アクセス許可」>「バケットポリシー」の「編集」を押下
-
以下のようにバケットポリシーを設定
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::[DevelopS3FromWordPress]:user/DevelopS3FromWordPress" }, "Action": [ "s3:ListBucket", "s3:GetBucketLocation" ], "Resource": "arn:aws:s3:::static-wordpress-s3" }, { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::[DevelopS3FromWordPress]:user/DevelopS3FromWordPress" }, "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject" ], "Resource": "arn:aws:s3:::static-wordpress-s3/*" } ] }
ポイント
- EC2に ManageSSMForStaticWordPressRole を付与することで、SSMを使うことが出るようになる(セッションを開始する必要はある)
- S3に DevelopS3FromWordPress からの操作権限を付与することで、後ほど WordPress にインストールするプラグインからS3に直接吐き出せるようになる
10. CloudFront
- 「CloudFront」>「ディストリビューション」>「作成」を押下
- 以下の通りにディストリビューションを作成
項目 オリジンドメイン static-wordpress-s3.s3.ap-northeast-1.amazonaws.com オリジンパス / 名前 static-wordpress-s3.s3.ap-northeast-1.amazonaws.com オリジンアクセス Origin access control settings (recommended) Origin access control (「コントロール作成」を押下して自動作成したもの) ウェブアプリケーションファイアウォール (WAF) セキュリティ保護を有効にしないでください カスタム証明書 user.static-wordpress.io デフォルトルートオブジェクト - オプション /index.html - 作成したディストリビューションの「一般」>「ARN」をコピー
- 「S3」>「static-wordpress-s3」>「アクセス許可」>バケットポリシーの「編集」を押下
- 以下のようにバケットポリシーを追記
{ ~~~~~ "Statment": { { ~~~~~~~~~ }, { "Sid": "AllowCloudFrontServicePrincipal", "Effect": "Allow", "Principal": { "Service": "cloudfront.amazonaws.com" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3::static-wordpress-s3/*", "Condition": { "StringEquals": { "AWS:SourceArn": "arn:aws:cloudfront::[CloudFrontのARN]" } } } } }
ポイント
- 静的なS3を攻撃するとしてもDOS攻撃程度(筆者の知識の範囲内では...)であり、損害は少ないと判断しWAFは設定しない
現在の構成
ついにこれで基盤部分の構築が完了となります。
次にWordPressの中身を設定していきます。
WordPressサイド
1. WordPressの構築
- AWSのコンソール画面 > 「EC2」のダッシュボードへ移動 > 「static-wordpress-instance-pub」>「接続」>「セッションマネージャー」で「接続」を押下
- 以下の通りにコマンドを実行
sudo dnf update -y sudo dnf install -y httpd wget php-fpm php-mysqli php-json php php-devel php-gd sudo dnf -y localinstall https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm sudo dnf -y install mysql mysql-community-client sudo dnf -y install mysql mysql-devel sudo wget http://ja.wordpress.org/latest-ja.tar.gz ~/ sudo tar zxvf latest-ja.tar.gz sudo rm latest-ja.tar.gz sudo cp -r ~/wordpress/* /var/www/html/ sudo rm -rf ~/wordpress sudo chown apache:apache -R /var/www/html/ sudo systemctl start httpd.service sudo systemctl status httpd.service
ポイント
- sshを開けずにSSMからのみの接続にすることで、よりセキュアな管理が可能
2. WordPressのSSL化
参考:https://tanojinfrom30.com/2021/03/13/wordpress-https/
- 「http://admin.static-wordpress.io(自分で設定したドメイン)/wp-login」 で管理者画面に入る
- 画面の指示に従い必要な情報を入力する
- 管理者画面に入ったのちに「プラグイン」>「新規のプラグインをインストール」>「Really Simple SSL」を検索しインストール・有効化
- AWSのコンソール画面 > 「EC2」のダッシュボードへ移動 > 「static-wordpress-instance-pub」>「接続」>「セッションマネージャー」で「接続」を押下
- 以下の通りにコマンドを実行
/var/www/html/wp-config.phpに以下の記述を追記$ sudo su - $ vi /var/www/html/wp-config.php
- 以下の位置に
wp-config.php
に書き込み~~~~~~~~~~~~~~ define( ~~~~~); >> ここを書き込む $_SERVER['HTTPS']='on'; define('FORCE_SSL_LOGIN', true); define('FORCE_SSL_ADMIN', true); << ここを書き込む ~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~ require_once(ABSPATH . 'wp-settings.php’);
- 「EC2」>「セキュリティグループ」>「static-wordpress-sg-for-alb」>「インバウンドルール」の「編集」を押下
- インバウンドルールから「http」のルールを削除
- 「EC2」>「ロードバランサー」> 最後にALBのHTTPのリスナーのターゲットをHTTPSへのリダイレクトに変える
3. S3吐き出しのプラグインをインストール
- パーマリンクを記事の名前にする
- プラグインの導入
- 無料で試す場合はstatic-html-output-pluginがいいのかと思います。(WordPressの非公式のプラグインハブです。脆弱性なども含まれる可能性があるので商用利用には向かないかもです。)
- 有償で実現するにはsimply staticを使ってみてください。自分調べでは最も理想的な挙動を示してくれました。
どちらのプラグインにおいても使い方は直感的にわかると思うので省略します。
以上でS3に吐き出すことが可能となりました。
FAQ
Start static site export でS3にあげるときに「AccessControlListNotSupported」が出た時
- 原因
- おそらくACLの問題
- 対応策
パーマリンク変更したら「JSONが未対応」的なメッセージが出てくる時
- 原因
- .htaccess が反映されてないのが原因っぽい
- 対応策
- (これしなくてもいいかも)
vi /etc/httpd/conf/httpd.conf
2. apachサーバーの再起動
sudo systemctl status httpd