AWS Lightsail(AmazonLinux2)とApacheとLet's EncryptでSSLする2020年最新版


PONOS Advent Calendar 2020の25日目の記事です。

昨日は@kenta-sanさんでした。

。。。なんとかギリギリ滑り込みで全枠埋まりました。皆様ありがとうございました!
そして宜しければこれまでの記事もお読みいただけますと幸いです。

それでは本題

やること

AWS LightsailでAmazonLinux2インスタンスを立て、ApaheでLet’s EncryptでのSSL接続が可能な環境を構築します。
(なんでLet's Encryptかというと、ACMを使用したりなんだりするためにはプラスアルファの料金がかかるためです。
ACM使ったらええやんっていうと前提が壊れるので今回はご勘弁)

上記のポイントを主眼におき、今回は最小限の紹介に留めておいて、2020年12月現在の最新の状態でのステップと、注意すべきポイントを書ければと思います。

やらないこと

DNSの設定やドメインの取得、Apacheのセキュリティ云々という部分は手順上スキップします。

実は結論をいうと。。。。

Amazonの公式チュートリアルに、[Amazon Linux 2 に SSL/TLS を設定する] というのがあり、
そこでLet's Encryptの手順の最新手順が紹介されています。

合わせて参照されると良いのですが、注意点もありましたため、実際やってみたことを踏まえた手順を記します。

Lightsailとは

簡単にいうとAWSが提供するVPSで、EC2と異なりインスタンスからネットワーク、DNSやCDN、ロードバランサー などを一箇所で簡単に構築可能なパッケージングになっています。
柔軟性がないところはありますが、ちょっとしたことにはとっても便利。

構築手順

Lightsailのインスタンスをたてる

他にも記事がたくさんありますので、ここでは文字だけで簡単に書きます。

[AWSコンソール]からLightsailを検索し、Lightsailのホームに移動します。

インスタンスの作成

[インスタンス] > [インスタンスの作成]をクリック。
[プラットフォームの選択] は [Linux/Unix]
[設計図の選択] は [OSのみ] > [AmazonLinux2]
[インスタンスプランの選択] は今回[$3.5]を選びます。なんでもいいです。

最後に好きなインスタンス名を入力し、[インスタンスの作成]をクリックします。

グローバルIPを紐付ける

[ホーム] > [ネットワーキング] > [静的IPの作成]をクリックします。
アタッチするインスタンスに、先ほどのインスタンスを選択して、作成をクリックします。

これでできた!簡単

ファイアーウォール設定

[インスタンスを選択] > [ネットワーキング] > [IPv4 Firewall]で簡単に設定できます。
固定IPがある環境の場合、最低限SSHのポートは設定しておきましょう。

ここで注意が必要なのは、80や443ポートです。
Let's Encryptを使用する場合は所定のURLに外部からアクセスできる必要があるため、丸ごとはじいてしまうとSSL証明書が発行できません。

また、この後SSL接続できるようにするので、今回この段階で443ポートもアクセス可能にします。
今回のLightsailではデフォルトだと80しか開いていないと思いますので、443を追加してください。

Apacheのインストールと設定

SSH接続する

とりあえずアップデートしとく

sudo yum update

Apacheとmod_sslをインストールする

sudo yum install httpd mod_ssl

※ この段階でセキュリティ関連について諸々良い感じにしてください。Apacheのセキュリティについてはここでは言及しません。

VirtualHostを設定する

なぜVirtualHostで設定するかというと、後述のLet’s Encryptのコマンドで自動設定するためです。

設定ファイルを開きます。

sudo vi /etc/httpd/conf/httpd.conf

Listen 80のしたの行あたりに設定します。
ServerNameには今回SSL接続するドメインを書きます。
ここでは"example.com"を所有しているものとして、サブドメインの"xxx.example.com"でWEBサーバをたてるものと想定します。

<VirtualHost *:80>
  ServerName xxx.example.com
  DocumentRoot "****"

  <Directory "****">
    AllowOverride All
    ~(いい感じ)~
  </Directory>
</VirtualHost>

もしIPでのアクセス制限をかけたい場合、Let's Encryptが使用するURLだけはアクセス可能にしておく必要があります。
例えば(Apache2.4なので)このような風に。

<VirtualHost *:80>
  ServerName xxx.example.com
  DocumentRoot "****"

  <Directory "****">
    AllowOverride All
    <RequireAny>
      Require all denied
      Require ip xxx.xxx.xxx.xxx
    </RequireAny>
    <RequireAny>
      Require expr %{REQUEST_URI} =~ m#^/\.well-known/*#
    </RequireAny>
  </Directory>
</VirtualHost>

Apacheを再起動しておきましょう。

sudo systemctl restart httpd

ドメインとDNSの設定

すでにドメインを取得してある場合、Lightsailの[ホーム] > [ネットワーキング] > [DNSゾーンの作成]から、DNSを設定できます。
ゾーンを作成し、LightsailのグローバルIPを紐づけてください。

SSL接続できるようにする

ほとんど公式チュートリアルと同じなので一気にいきます。

EPEL7をダウンロードする

cd ~
sudo wget -r --no-parent -A 'epel-release-*.rpm' http://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/

リポジトリパッケージのインストール

sudo rpm -Uvh dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/epel-release-*.rpm

EPELの有効化

sudo yum-config-manager --enable epel*

Certbotのインストール

sudo yum install -y certbot python2-certbot-apache

Certbotの実行

sudo certbot

"Enter email address (used for urgent renewal and security notices)"が表示されたら連絡先メールアドレスを入力してEnterします。
他にも、メーリングリストへの登録してよいか?みたいなのが聞かれますが、好きな選択肢を選んでください。

VirtualHostを設定していると、ここで証明書を取得するドメインを確認されます。

Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: xxx.example.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel):

大人しく[Enter]します。

さて、これでうまくいくと設定が完了します。あら簡単。
あとは公式にあるようにcertbotの自動更新をするようにすればOKです。

最後の仕上げと注意点

certbotのバージョンによるのか、途中の手順によるものなのかどうか確認はしていませんが、実はこの段階ではちょっと注意が必要なポイントがあります。

certbotで自動的に設定し、HTTPSへのリダイレクトも設定してもらうと、Apacheの設定が下記のようになることがあります。

  1. httpd.confのVirtualHostでHTTPSへのリダイレクトが行われるように設定が書き込まれている。
  2. httpd.confの最終行に"Include /etc/httpd/conf/httpd-le-ssl.conf"が記入されてインポートされるようになっている。
  3. httpd-le-ssl.confで443のVirtualHostが設定され、その中でさらに"Include /etc/letsencrypt/options-ssl-apache.conf"がインポートされている

こういう風になっていない場合は問題ないかもしれません。しかしこうなっていると、このままだと危ないポイントがあります。

元々存在しているssl.confにも443のVirtualHostが残ったままになっているため、ドメインでアクセスするとhttpd-le-ssl.conf + options-ssl-apache.confのほうが適用されるが、グローバルIPでアクセスした場合にはssl.confのVirtualHostの設定が使われるという現象が発生します。

そのため思ったものと違うSSLプロトコルになったり、IP制限が意図しない状態になったりすることが考えられます。
潔くssl.confのデフォルトのVirtualHost設定はコメントアウトしたほうがよさそうです。

また、httpd-le-ssl.conf + options-ssl-apache.confによる設定は、ssl.confの設定と比較して、SSLプロトコル以外にもログ出力の設定も異なります。
ssl.confが行っているssl_access_logなどの出力を行うためには、options-ssl-apache.confに下記を追記すればOKです。

ErrorLog logs/ssl_error_log
CustomLog logs/ssl_access_log combined

CustomLog logs/ssl_request_log \
          "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"

※ この辺りはお好みですが

付加情報

Let's Encryptといえば2021年9月にルート証明書が変更されることで、Android7.1以前の端末が多くのWEBサービスを利用できなくなるという懸念点がありました。
こちらについて現時点では次のようなニュースがありましたので載せておきます。
Android 7.1以前の証明書問題が解決。向こう3年間はひきつづき安全なウェブ閲覧が可能に

まとめ

ということで、実は公式リファレンスを見ると簡単にLet's Encryptが使えちゃうんですが、それによって生成された設定がどうなっているのか、ssl.confとの関係はどうなのか、というところには注意が必要です。

誰かが書いた情報というのは、その時の環境によったりもしますし、『前提は何なのか』、『書かれていないことは何か』ということがわからなかったりもします。
書き手の想定と読み手の想定が異なることもあります。
『デフォルトを信じない、書かれていることを鵜呑みにしない』姿勢も大事ですね。