LoginSignup
39
30

WordPressをS3で静的ホスティングしてセキュアに配信する

Last updated at Posted at 2024-01-21

この記事の内容

  • WordPressで作成したコンテンツを静的にホスティングする設計まとめ
  • ハンズオンを兼ねた事例報告(+自分の所感)の流れで執筆

背景

そもそもなぜタイトルのような設計を考えたのかというと、先日アサインされたプロジェクトの要件が以下のとおりだったからです。

  • 誰でも簡単にコンテンツを作成できる
  • セキュアな設計

ここで、WordPressには以下の長所があります。

  • 技術に明るくない人であっても簡単にコンテンツを作成できる
  • 裏はPHPで動いているので拡張性が非常に高い
  • 初期導入に必要となるコストが少ないこと

ただし、ご存知の通りWordPressの欠点として大きなものが以下です。

  • 脆弱性が多く十分な対策を要求される

上記を鑑みて、WordPressの長所を余すことなく享受し欠点を最大限カバーすることができるようになる「WordPressを静的コンテンツで配信」を目標に構築を行いました。

対応策

今回はタイトルにも書いている通り、「WordPress」+「S3」を採用しましたが他にも上記の項目を満たす候補はありましたので、それらの否決理由を簡単に紹介しておきます。

方法

  • microCMS
    • メリット
      • コンテンツの配信が簡単にできる
      • 拡張性が無限
    • デメリット
      • 外側のフロント部分やコンテンツ配信以外のロジック部分をコーディングする必要あり、初期導入コストと運用コストが大きい
  • 他社CMSサービス
    • メリット
      • コンテンツの配信が簡単にできる
      • サーバーの運用などが必要なくなる
    • デメリット
      • 拡張性が少ないものが多い
      • 月々のコストが高い

それぞれのメリデメを評価した結果敢えなく採用には至りませんでした。

手順

AWSサイド

00_all.png

1. VPC

  1. 検索窓から「VPC」を検索
  2. リージョンが「東京(ap-northeast-1)」であることを確認
  3. ダッシュボードの「VPCを作成」を押下
  4. 設定項目は以下の通り
項目 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を作成しておきます。

現状の構成
01_vpc.png

2. セキュリティグループ

  1. 検索窓から「EC2」を検索して押下
  2. 画面左ペインから「ネットワーク&セキュリティ」の「セキュリティグループ」を押下
  3. 「セキュリティグループ」を押下
  4. 以下の通りに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 はプラグインのインストール時に使用することを考慮
    01-1_sg.png
  • admin <-> ALB のHTTP通信は最初だけ許可する
    • WordPressはデフォルトではSSL通信に対応していない
    • WordpressがSSL通信に対応するまでは、HTTP通信でやり取りを行うので設定が終わるまで許可しておく
  • 外部のネットワークに晒されていない ALB <-> EC2 はHTTPS通信する必要はない

現状の構成
VPCから変更はないので省略

3. EC2

  1. 検索窓から「EC2」を検索して押下

  2. リージョンが「東京」であることを確認

  3. 画面中部の「インスタンスを起動」を押下

  4. 以下の通りにインスタンスを起動

    項目 インスタンス
    名前 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
  5. 画面左ペインの「Elastic IP」を押下

  6. 「Elastic IPアドレスを割り当てる」を押下

  7. ネットワークボーダーグループが「ap-northeast-1」であることを確認して「割り当て」を押下

  8. 「アクション」「Elastic IPアドレスの関連付け」を押下

  9. 次の通りに設定

    項目 関連付け
    リソースタイプ インスタンス
    インスタンス static-wordpress-instance-pub
    プライベートIPアドレス (出てくるやつ)
    このElastic IPアドレスの際関連付けを許可する False

ポイント

  • ボリュームサイズは後でも変更することが可能なので、まずは一番安いので問題なし
    • しかもこのインスタンスはWordPressの編集・出力のみにしか使用しないのでスループットが250MbhB/sに張り付かないと予想
  • Elastic IPはパブリックIPが変動しないように設定する
  • AMIはAMIMOTOのようにWordPress用にカリカリにチューニングされたものも存在するが、今回は編集・出力にしか使用しないのでAmazon Linuxで十分と判断した
  • ユーザーデータはこけたときにログが出力しにくいので立ち上がったのちに、SSMで手動で実行するようにした

現在の構成

02_EC2.png

4. Route53

  1. まず自分の独自ドメインを取得する(私はお名前ドットコムで無料で取得しました。)
  2. 「Route53」に移動
  3. リージョンが「グローバル」であることを確認
  4. 「ホストゾーン」>「ホストゾーンの作成」を押下
  5. 以下の通りに ホストゾーンを作成
    項目
    ドメイン名 (0. で取得したドメイン。サブドメインは指定しないほうがいい)
    static-wordpress.io
    タイプ パブリックホストゾーン
  6. 「static-wordpress.io」のダッシュボードで「NSレコード」の「値/トラフィックのルーティング先」のURLの末尾の「.」を除外したものを、それぞれのドメイン会社に従ってネームサーバー登録する。
    • お名前.comはリンク先の「他社レンタルサーバー・お客様独自のネームサーバーにて運用する場合」に則って作業してください。

ポイント

  • 特になし

現在の構成
04_Route53.png

5. ACM

  1. 「ACM」>「Certificate Manager」を押下
  2. リージョンが「バージニア北部」であることを確認
  3. 「証明書をリクエスト」を押下
  4. 以下の設定通りに証明書をリクエスト
    項目 証明書 - S3
    証明書タイプ パブリック証明書をリクエスト
    完全修飾ドメイン user.static-wordpress.io
    検証方法 DNS検証 - 推奨
    キーアルゴリズム RSA 2048
  5. 作成された証明書 >「Route53でレコードを作成」を押下
  6. ステータスが「成功」になるまで待つ
  7. リージョンを「東京」に移動する
  8. 以下の通りにもう一度証明書をリクエストして「成功」になるまで待つ
    項目 証明書 - EC2
    証明書タイプ パブリック証明書をリクエスト
    完全修飾ドメイン admin.static-wordpress.io
    検証方法 DNS検証 - 推奨
    キーアルゴリズム RSA 2048

ポイント

  • 証明書は使用する対象のサービスによって異なる
    • Route53:グローバル(リージョンは関係ない)
    • Cloud Front:グローバル(バージニア北部のみ)
    • ALB:リージョナル
  • 今回のケースでは、管理者用のEC2が東京リージョン、パブリック公開するS3がグローバルになるので、それぞれのリージョンを変えた

現在の構成
05_ACM.png

6. ALB

  1. 「ALB」>「Certificate Manager」を押下
  2. リージョンが東京であることをかくにん
  3. 「ターゲットグループ」>「ターゲットグループの作成」を押下
  4. 以下の通りにターゲットグループを設定
    項目 ターゲットグループ
    ターゲットタイプ インスタンス
    ターゲットグループ名 static-wordpress-tg
    プロトコル:ポート名 HTTP: 80
    IPアドレスタイプ名 IPv4
    VPC static-wordpress-vpc
    プロトコルバージョン HTTP1
    ヘルスチェック HTTP /
    ヘルスチェックポート トラフィックポート
    1. 使用可能なインスタンスに「static-wordpress-instannce-pub」を選択
  5. 「ロードバランサー」>「ロードバランサーを作成」>「Application Load Balancer」を押下
  6. 以下の通りにALBを作成
    項目 ALB
    ロードバランサー名 static-wordpress-alb
    スキーム インターネット向け
    IPアドレスタイプ IPv4
    VPC static-wordpress-vpc
    マッピング ap-northeast-1a,
    ap-northeast-1c
    1aサブネット 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のみを設定
    01-1_sg.png
  • 先ほどACMで作成した証明書を付与することでSSL通信を実現
  • ここのセキュリティグループでIP制御することでセキュアな管理者画面を実現

現在の構成
05_ALB.png

7. RDS

  1. 検索窓から「RDS」を検索して押下
  2. リージョンが「東京」であることを確認
  3. 画面左ペインの「サブネットグループ」を押下
  4. 「DBサブネットグループを作成」を押下
  5. 以下の通りにサブネットグループを設定
    項目 サブネットグループ
    名前 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
  6. 画面中部にある「データベースの作成」を押下
  7. 以下の通りに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時間
  8. 「マスターユーザー名」「マスターパスワード」「データベースポート」「最初のデータベース名」は後ほどWordPressの初期設定に使用するためメモする
  9. ステータスが「利用可能」になってから表示される「エンドポイント」も後ほどWordPressの初期設定に使用するためメモする

ポイント

  • DBインスタンスクラスは後で変更ができるので、最初は一番安いもの(2024/01時点では「db.t4g.micro」)を選択
  • 認証機関には有効期限が長い「rds-ca-rsa2048-g1」を選択
  • バックアップウィンドウ・メンテナンスウィンドウにはメンテナンスをする可能性が低い時間帯を選択する(セオリー通りなら週末の夜とか)

現在の構成
06_RDS.png

8. S3

  1. 検索窓から「S3」を検索して押下
  2. 「バケット作成」を押下
  3. 以下のように設定
    項目 S3
    AWSリージョン アジアパシフィック(東京)ap-northeast-1
    バケットタイプ 汎用
    バケット名 static-wordpress-s3
    オブジェクト所有者 ACL無効
    ブロックパブリックアクセス 全てブロック
    バケットのバージョニング 有効にする
    デフォルトの暗号化 SSE-S3
    バケットキー 有効にする

ポイント

  • CloudFrontからのみアクセス可能にするので、「ブロックパブリックアクセス」は全てブロックでOK

現在の構成
07_S3.png

9. IAM

  1. 検索窓から「IAM」を検索して押下

  2. 「ポリシー」>「ポリシーの作成」を押下

  3. 「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]/*"
        }
      ]
    }
    
  4. 「ロール」>「ロールを作成」を押下

  5. 以下の通りにロールを作成

    項目 エンティティ
    信頼されたエンティティタイプ AWSのサービス
    ユースケース EC2 / EC2
    許可ポリシー AmazonSSMManagedInstanceCore(AWS管理)
    ロール名 ManageSSMForStaticWordPressRole
  6. 「ユーザー」>「ユーザーの作成」を押下

  7. 以下の通りにユーザーを作成

    項目 エンティティ
    ユーザー名 S3DeveloperFromWordPress
    許可のオプション ポリシーを直接アタッチする
    許可ポリシー DevelopS3FromWordPress
  8. 検索窓から「EC2」を検索して押下

  9. static-wordpress-pub-instance を選択し「アクション」>「セキュリティ」>「IAMロールを変更」を押下

  10. IAMロールを ManageSSMForStaticWordPressRole に変更

  11. 検索窓から「S3」を検索して押下

  12. 「static-wordpress-s3」>「アクセス許可」>「バケットポリシー」の「編集」を押下

  13. 以下のようにバケットポリシーを設定

    {
      "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に直接吐き出せるようになる

現状の構成
08_IAM.png

10. CloudFront

  1. 「CloudFront」>「ディストリビューション」>「作成」を押下
  2. 以下の通りにディストリビューションを作成
    項目
    オリジンドメイン 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
  3. 作成したディストリビューションの「一般」>「ARN」をコピー
  4. 「S3」>「static-wordpress-s3」>「アクセス許可」>バケットポリシーの「編集」を押下
  5. 以下のようにバケットポリシーを追記
    {
      ~~~~~
      "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は設定しない

現在の構成
09_CloudFront.png
ついにこれで基盤部分の構築が完了となります。
次にWordPressの中身を設定していきます。

WordPressサイド

1. WordPressの構築

  1. AWSのコンソール画面 > 「EC2」のダッシュボードへ移動 > 「static-wordpress-instance-pub」>「接続」>「セッションマネージャー」で「接続」を押下
  2. 以下の通りにコマンドを実行
    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からのみの接続にすることで、よりセキュアな管理が可能

現在の構成
00_all.png

2. WordPressのSSL化

参考:https://tanojinfrom30.com/2021/03/13/wordpress-https/

  1. http://admin.static-wordpress.io(自分で設定したドメイン)/wp-login」 で管理者画面に入る
  2. 画面の指示に従い必要な情報を入力する
  3. 管理者画面に入ったのちに「プラグイン」>「新規のプラグインをインストール」>「Really Simple SSL」を検索しインストール・有効化
  4. AWSのコンソール画面 > 「EC2」のダッシュボードへ移動 > 「static-wordpress-instance-pub」>「接続」>「セッションマネージャー」で「接続」を押下
  5. 以下の通りにコマンドを実行
    /var/www/html/wp-config.phpに以下の記述を追記
    $ sudo su -
    $ vi /var/www/html/wp-config.php
    
  6. 以下の位置に wp-config.php に書き込み
    ~~~~~~~~~~~~~~
    define( ~~~~~);
    
    >> ここを書き込む
    $_SERVER['HTTPS']='on';
    define('FORCE_SSL_LOGIN', true);
    define('FORCE_SSL_ADMIN', true);
    << ここを書き込む
    
    ~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~
    require_once(ABSPATH . 'wp-settings.php);
    
  7. 「EC2」>「セキュリティグループ」>「static-wordpress-sg-for-alb」>「インバウンドルール」の「編集」を押下
  8. インバウンドルールから「http」のルールを削除
  9. 「EC2」>「ロードバランサー」> 最後にALBのHTTPのリスナーのターゲットをHTTPSへのリダイレクトに変える

3. S3吐き出しのプラグインをインストール

  1. パーマリンクを記事の名前にする
  2. プラグインの導入
    1. 無料で試す場合はstatic-html-output-pluginがいいのかと思います。(WordPressの非公式のプラグインハブです。脆弱性なども含まれる可能性があるので商用利用には向かないかもです。)
    2. 有償で実現するにはsimply staticを使ってみてください。自分調べでは最も理想的な挙動を示してくれました。

どちらのプラグインにおいても使い方は直感的にわかると思うので省略します。

以上でS3に吐き出すことが可能となりました。

FAQ

Start static site export でS3にあげるときに「AccessControlListNotSupported」が出た時

パーマリンク変更したら「JSONが未対応」的なメッセージが出てくる時

  • 原因
    • .htaccess が反映されてないのが原因っぽい
  • 対応策
  1. (これしなくてもいいかも)
vi /etc/httpd/conf/httpd.conf

 
2. apachサーバーの再起動

sudo systemctl status httpd
39
30
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
30