WordPressで作られたAWS環境のWEBサイトの常時SSL対応、HTTPS化をやってみた際の作業手順とハマりポイントです。
きっかけは、WordPressで作られたWEBサイトの常時SSL対応、HTTPS化を頼まれた際に、依頼者の環境と割と近い環境で作られたこのブログも恥ずかしながら対応を後回しにしていた事もあり、予行演習がてらやってみたという軽いきっかけだったのですが、予想以上に色々な罠が用意されていて結構ハマりました。。。w
という事で、常時SSL対応、サイトアドレスのHTTPS化です。
去年の7月にリリースされたChromeのバージョン68以降、通信の暗号化がされていないページを表示させると保護されていない通信と怒られるようになったので、慌てて対応を開始した人も多いのではないでしょうか。
常時SSL対応とは の詳細は調べればいくらでも出てくるので省きますが、信頼できるページ所有者が実在し、表示時の通信も暗号化された安全な通信ですよ!という結果を伝えて、安心して閲覧してもらえる状態を作る為の対応になります。
前提: 今回対応を行った環境
まず初めにの前提として、今回対応を行なったこのブログの環境がどんなアーキテクチャ構成だったのかが以下になります。
AWS環境の上で手軽にWordPressを動かす一般的な構成で、DNSの名前解決をEC2に紐づけ、EC2の上でApacheやNginxに代表されるWEBサーバとphp-fpmなどのPHPのFastCGIでWordPressが動いているよく見られる構成です。
細かい差異はありましたが、依頼してくれた方の環境も上記構成と同じだったので予行演習には最適そうです。
常時SSL対応、HTTPS化手順
前提の説明が済んだところで、作業手順に入ります。
1. バックアップの取得
何かの変更を行う前にはいつでも元に戻せるようにする為の準備が必要で、バックアップ取得はとても大事な作業なので必ず行います。
WordPressのバックアップは、
- データベースのレコード内容
- サーバー内に置かれた各種ファイルの内容
の二つを復元すれば基本的に元の状態に戻せるので、最低限この二つはバックアップしておきます。
1-1. データベース(RDS)のバックアップ取得
データベースに保存しているレコード内容のバックアップです。
* DBがEC2から外出し構成の為手順に記載していますが、EC2内に自前のMySQLを立ち上げている場合はEC2のAMIだけで十分なのでこの手順は飛ばします。
RDSのバックアップ取得は、AWSマネジメントコンソール上からスナップショットを取得するだけです。
特に詰まるところもないはずなので、詳細は割合します。
1-2. サーバー(EC2)のバックアップ取得
サーバー内に置かれた各種ファイルの内容などのバックアップです。
EC2内に自前のMySQLを立ち上げている場合は、この手順でデータベースのレコード内容も合わせてバックアップを取得できます。
EC2のバックアップ取得は、AWSマネジメントコンソール上からAMIを取得するだけです。
こちらも同じく特に詰まるところもないはずなので、詳細は割合します。
2. SSL証明書の発行方法を選択
ここからが今回の本題です。
常時SSL対応を行うには、SSL証明書を発行する必要があります。
証明書の発行方法は色々ありますが、AWS環境の証明書発行は大きく分けて二つの方法に分類されます。
- ACMを利用して発行
- 自前で独自のSSL証明書を用意して発行
AWSサービスのACM (Certificate Manager) を利用する方法です。
マネージドサービスで、無料で証明書発行ができたり、面倒な証明書更新を自動で行ってくれたりなど、運用コストを抑えられる利点があります。
ただ、EC2とは連携していないサービスの為、発行した証明書をEC2に対して設定する事はできません。
その為、この方法を選択時はACMが連携しているサービスを加えた形のアーキテクチャ構成に変更する必要があります。
メリット
– 忘れがちな証明書の更新作業を自動でやってくれる
– 証明書の発行自体は無料
– ELBに証明書を発行すれば配下サーバ全ての証明書発行が不要になる(後述)
デメリット
– ACMがEC2と連携していない為、使用する為に構成変更が必要になる
– 上記に関連し、変更後の構成で使用するサービスの利用料金が追加される
ACMが連携しているサービスを加えた形のアーキテクチャを考えると、以下のような構成が考えられます。
ACMが連携しているサービスは今回選択したELB以外にもあるので、EC2の手前にCloudFrontを置いてACMを発行するなど、他の選択肢もあります。
その中でこの構成を選択したのは、証明書の発行がELBに対してのみで済むようになり、ELB配下のサーバーが増えた/変更されたなどの場合にも証明書の再発行をせずに済む運用コスト削減のメリットがあります。
その他にも、
– EC2の一台構成に比べてスケールアウトが可能になり可用性が増す
– プライベートサブネットに移してELB経由のアクセスしか受けないようにする事でのセキュリティ面の向上
など、この構成にするメリットは多々ありますが、今回の常時SSL対応の趣旨とはズレるのでここでは割合します。
尚、EC2の複数台構成を作るには、サーバー内に固有の情報を持たせていないステートレスな状態になっている事が前提な点にご注意ください。
ステートフルな状態で複数台構成にすると、ELBのトラフィック振り分け先によりレスポンス結果が変わってしまいます。
結果、アップロードしたはずの記事や画像が見えたり見えなかったりする状態になってしまいスケールアウトができないので、まずはサーバー内に持たせている情報を外出しする対応から始めましょう。
もちろん、自前で独自のSSL証明書発行を行い設定する事も可能です。
Let’s Encrypt など、無料で証明書発行をしてくれるサービスもいくつかあるので料金の心配はないですし、EC2に証明書を発行する事ができるので構成変更が要らないなど、手軽に対応できるのもメリットです。
ただ、証明書の更新を毎回対応しないといけない(Let’s Encryptを使用した場合は3ヶ月毎に更新が必要)、追加/変更したサーバーそれぞれに対して証明書の再発行が必要になるなど運用コストがかかるのと、スケールアウトしようとすると結局構成変更をしないといけなくなるなどのデメリットもあります。
メリット
– EC2に証明書を発行可能で、構成変更が不要で対応工数が低い
– 無料で証明書を発行してくれるサービスもあり、証明書の発行自体も無料
– 追加で使用するサービスが無い為、金額面のコストが抑えられる
デメリット
– 証明書更新/再発行対応など、運用コストが大きい
– 結局どこかのタイミングでスケールアウトしないといけない時はくるので、問題の先延ばしにしかならない
この方法を選択した場合は、EC2に対して証明書発行がされるだけです。
手軽にできるが運用コストがかかる方法でもあるので、証明書更新など人の対応が必要な内容に対して自動化していく工夫は必要になってきます。
二つ方法を書きましたが、どちらを選択する場合もメリットデメリットはあるので、この対応に何を求めるのか を判断軸に適切な手段を選択しましょう。
尚、今回は前者のACMを利用する方法を選択したので、以降の内容はこの方法前提の内容になる点をご了承ください。
3. ACMを使用する為のアーキテクチャ構成変更
手順2でACMを利用する方法を選択したので、それに伴い発生するアーキテクチャ構成の変更を実施していきます。
これが主題ではないので、今回は行った変更内容を箇条書きで並べるだけです。
(この辺の内容はそれだけで専用エントリ書けそうなので、後日改めて別で書きます)
- ELB新規作成
- DNS(Route 53)のドメイン向き先をEC2からELBに変更
- EC2に設定したセキュリティグループのインバウンド設定変更
- EC2にアタッチしていたElastic Ipをデタッチ&開放
- EC2の属するサブネットのルートテーブルの関連付け変更
- 同じVPC内のパブリックサブネットに踏み台用EC2をもう一台作成
- SSM用のVPCエンドポイントを作ってEC2が属するプライベートサブネットのルートテーブルに追加
種別: ALB(インターネットからのアクセス受付用途の為)
スキーム: インターネット向け
VPC: EC2が属するVPCと同一のVPC
サブネット: パブリックサブネットを複数AZに跨がる形で最低二つ
セキュリティグループ: ポート80と443を開放
(httpからのアクセスもhttpsにリダイレクトさせる為開放する)
ターゲットグループ: EC2を登録
EC2のAMIからクローンしたインスタンスを別AZに配置してターゲットグループに追加すればいつでも冗長構成にできる為、今回登録するのは一台だけにします
ヘルスチェック: /index.php
(/だとWordPressの構成上301が返り通らないので変更する)
ドメインのZone apexのAレコードをELBに変更する。
(ELBのIPは動的に変化する為、ALIASレコードでエンドポイントを指定)
アクセス元を0.0.0.0/0からELBに設定したセキュリティグループIDに変更。
(インターネットからのアクセスを遮断し、ELBからのアクセスのみ許可)
EC2に対してインターネットからのアクセスがなくなる為。
* デタッチだけだと料金が発生する為、必ずElastic IPの開放までを行う
IGWが登録されていないルートテーブルを関連付けてパブリックサブネットからプライベートサブネットに変更する。
* セキュリティ向上目的での変更なのでプライベートサブネットにする事を推奨しますが、変更するとローカルからEC2へのssh接続、およびセッションマネージャーでのEC2へのシェルアクセスが不可になります。
プライベートサブネットにした上でEC2へのssh接続を可能にするには、
のどちらかの対応が必要ですが、どちらも利用料金がかかる方法です。
EC2が属するサブネットに他のリソースを設置しなければ、セキュリティグループで制御する事でセキュリティリスクは軽減できる為、コスト重視の場合はサブネットをパブリックのままにしておく選択肢も有だと思います。
以上でACMでSSL証明書発行を行う為の構成変更は完了です。
4. ACMでSSL証明書発行 & ELBに関連付け
準備が整ったところで証明書の発行を行っていきます。
- マネジメントコンソールからEC2 → ロードバランサーを選択
- 対象のELBを選択 → リスナータブを選択
- リスナーの追加ボタンを選択
- プロトコルにHTTPSを選択後、新しいACM証明書をリクエストのリンク選択
- ドメイン名に自サイトドメイン名を入力後、次へボタンを選択
- 検証方法の選択に「DNSの検証」を選択して、次へボタンを選択
- タグ名に管理用のタグ名を付け、確認ボタンを選択
- 入力内容を確認し、確定とリクエストボタンを選択
- ALBのリスナー追加画面に戻り、必要事項を入力して保存ボタンを選択
どの方法で入力したドメイン名の所有者である事を検証するかというだけなので、Eメールの検証を選択しても大丈夫です。
(AWSに登録してあるEメールアドレスに確認メールが届きます)
スキップ可ですが、Nameタグは設定して管理を楽にする事を推奨します。
これで証明書発行の手続きは終わりです。
数分で発行されるので、ELBに関連付ける為に発行されるまで待ちます。
プロトコル: HTTPS
ポート: 443
デフォルトアクション: アクションの追加 → 転送先 → ターゲットグループにEC2が登録されたターゲットグループ
セキュリティポリシー: ELBSecurityPolicy-TLS-1-2-2017-01
TLS通信のバージョンいくつまで対応するかの選択ですが、古いブラウザバージョンを考慮し続けるのは辛いので、もうTLS1.2のみ対応で良いと思います。
TLS1.0/1.1も対応したい場合は、ELBSecurityPolicy-2016-08を選択します。
デフォルトのSSM証明書: ACMから(推奨) → 先程作った証明書
これでELBに証明書が関連付けられ、AWS側でのhttpsアクセス準備が整いました。
5. WordPress側の登録URLをhttpからhttpsに変更
WordPress側の設定に入り、まずは登録URLをhttpからhttpsに変更します。
登録URLは、管理画面の「設定」→「一般」と遷移して
- WordPressアドレス(URL)
- サイトアドレス(URL)
の二つを変えると変更できます。
変更完了後は、EC2に入り、動いているPHPのプロセスを再起動します。
1 2 3 4 5 6 7 8 9 10 11 |
$ ssh {ec2-host-name} Last login: {last-login-time} from {login-ip} __| __|_ ) _| ( / Amazon Linux AMI ___|\___|___| $ sudo service php-fpm restart [sudo] password for {user-name}: Stopping php-fpm: [ OK ] Starting php-fpm: [ OK ] |
これで、httpsのURLでアクセス可能になった筈です。
- ブラウザのURL欄にhttpsのURLを入れてアクセス
- curlでhttpsのURLを叩いてみる
などの方法で、httpsのURLが認識されるようになった事を確認します。
6. 全URLをhttpsに変更(Mixed Contentの解消)
手順5でhttpsのURLが認識されるようになりましたが、サイト内にはまだhttpのURLが残っており、httpsのURLと混在している状態です。
ページ表示時のアドレスバー横を見ると黄色の注意マークがまだ出ていて、マークを選択すると 「この接続は安全ではありません」、「このページの一部(画像など)は安全ではありません」と表示されていると思います。
これは、過去にアップロードした画像に対する記事内からの表示用URLなど、httpのURLを絶対パスの文字列としてデータベースに登録してしまっている為起こるWordPress固有の現象です。
この問題を解消する為には、データベースに登録されている全レコード内容のhttpをhttpsに置換する必要があるので、今回は以下のプラグインを使用して一括置換を行う対応をとりました。
【Search Regex】
https://ja.wordpress.org/plugins/search-regex/
* プラグインインストール時に、cURL error 77: Problem with the SSL CA certのエラーが出る場合は、opensslのバージョンが古いままになっている可能性が高いので、PHPプロセスの再起動を行ってください。
インストールして有効化すると管理画面にメニューが追加されるようになるので、「ツール」→「Search Regex」と遷移して
- Search patternのフォームにhttpの自サイトドメイン名
- Replace patternのフォームにhttpsの自サイトドメイン名
を入力してReplaceボタンを押します。
*1 Replaceボタンを押しても確認用の置換結果が表示されるだけで置換は行われません。
*2 http→httpsだけだと他サイトURLも全て置換されてしまう為、必ず自サイトドメイン部分までを対象にします
と、ここで問題が発生します。
Replaceボタンを押すとエラーになり結果が表示されてくれません。
Uncaught Error: Call to undefined method SearchRegex::base_url() in ~/wp-content/plugins/search-regex/view/results.php:26
出力されているエラーメッセージには上記のように書かれているので、対象ファイルの該当部分から追っていくと、エラーメッセージの通りでbase_url関数が定義されていないのが原因のようです。
(本当にこの内容が正しいのか、ここを追うのに時間がかかりました…w)
幸い、エラーになっている26行目の内容はimgタグで画像を表示させているだけ & display noneになっていたので消しても問題なさそうだったので、Search Regexのview/result.php の26行目をコメントアウトしてしまいましょう。
(最終更新が4年前のプラグインで更新は望めないので、あまりやりたくないですがプラグインを更新してしまいます)
1 2 3 |
<img src="<?php echo plugins_url( '/images/small.gif', $this->base_url() ); ?>" style="display: none" alt="pre"/> ↓ <?php /*?><img src="<?php echo plugins_url( '/images/small.gif', $this->base_url() ); ?>" style="display: none" alt="pre"/><?php */?> |
コメントアウトするとSearch RegexのReplaceボタン押下時にエラーが起こらなくなるので、置換結果を確認して問題がなければ横のReplace & Saveボタンを押して、置換後の結果をデータベースに更新登録します。
これでページ表示時のアドレスバー横から黄色の注意マークが消え、マーク選択時も「この接続は保護されています」と表示されるようになるはずです。
7. httpでのアクセスをhttpsにリダイレクトさせる
手順6まででhttpsでのアクセス時のSSL対応は完了しましたが、この時点ではまだhttpsとhttp両方のURLがアクセス可能な状態になっています。
httpの方のURLはもう不要なので、httpsにリダイレクトさせる設定を追加します。
リダイレクト設定を挟む箇所の候補はいくつかありますが、今回は最もシンプルなWEBサーバーの設定で行う事にしました。
(Nginxを使用していたので、/etc/nginx/nginx.confが設定ファイルです)
serverセクションの上にもう一つhttp用のserverセクションを追加し、httpアクセス時にレスポンスコード301を返した上でhttpsにリダイレクトするよう設定します。
1 2 3 4 5 |
server { listen 80; server_name {自サイトドメイン名}; return 301 https://$host$request_uri; } |
追記したらNginxをリスタートして、httpのURLでアクセスするとhttpsにリダイレクトされるようになっている事が確認できればOKです。
以上7つの手順を持って、常時SSL対応、HTTPS化の全工程が完了です。
お疲れ様でした!