Amazon LinuxでLet's Encrypt
Amazon LinuxでLet's Encryptを使ってSSLサーバー証明書を取得してnginxに設定する方法を紹介します。
TL;DR(とりあえず結論)
コマンド一発でちょー簡単に証明書発行できてしまいます。ただし、Amazon Linux上ではLet's Encryptクライアントアプリがまだ実験段階のようです。とりあえず正常に発行できましたが、ご利用は自己責任でお願いいたします。
システム構成
せっかくなんでリリースされたばかりのAmazon Linux AMI 2016.03を利用してみます。
EC2のセットアップ
以下、Ansibleを使ってEC2インスタンスとnginxをセットアップしていきますが、そのへんはあまり本質ではないのでびゃーっと記事中段あたりまで読み飛ばして頂いても構いません。
事前作業
Ansibleが利用するAWSのアクセスキー&シークレットキーなどplaybook内には直書きしたくない情報を環境変数としてセットしておきます。 私の場合は普段direnvを利用しているので、ローカルの.envrcに登録して環境変数に反映されるようにしました。
$ cd REPO_ROOT $ cat .envrc export AWS_ACCESS_KEY_ID=XXXXXXXXXXXX export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXX export AWS_REGION=ap-northeast-1 $ direnv allow
インベントリ
AWSリソースを用いますのでDynamic Inventoryを利用します。Dynamic Inventoryの解説は下記サイトなどを参照ください。
- 【Ansible】EC2 External Inventory Scriptを使った動的ホスト一覧生成 | Developers.IO
- Ansible Dynamic Inventoryを使ってAWSのEC2インスタンスに付与されたタグでプロビジョニング対象を決める - Qiita
ec2.iniとec2.pyを取得します。
$ cd REPO_ROOT/provision/inventories $ wget https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/ec2.ini $ wget https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/ec2.py $ chmod u+x ec2.py
site.yml
AWSリソースのセットアップタスクで、elasticacheロールとec2ロールを呼び出します。
# PLAYBOOK_ROOT/site.yml --- - name: AWSリソースのセットアップ hosts: localhost connection: local roles: - ec2 vars: service_name: nslides01 group_name: application region: ap-northeast-1 - name: アプリケーションサーバーのセットアップ hosts: tag_Name_application remote_user: ec2-user become: yes roles: - nginx vars: private_key: ~/.ssh/nslides01.pem
ec2ロール
ec2ロールではセキュリティグループとEC2インスタンスを作成します。 EC2用セキュリティグループではSSH接続とHTTP/HTTPS接続のみを許可しておきます。
# PLAYBOOK_ROOT/roles/ec2/tasks/main.yml --- - name: EC2用セキュリティグループを作成 ec2_group: name: "{{ group_name }}" description: "{{ group_name }}" region: ap-northeast-1 rules: - proto: tcp from_port: 22 to_port: 22 cidr_ip: 0.0.0.0/0 - proto: tcp from_port: 80 to_port: 80 cidr_ip: 0.0.0.0/0 - proto: tcp from_port: 443 to_port: 443 cidr_ip: 0.0.0.0/0 rules_egress: - proto: all from_port: 0 to_port: 65535 cidr_ip: 0.0.0.0/0 - name: EC2インスタンスを作成 ec2: image: "{{ ami_image }}" instance_type: "{{ instance_type }}" region: "{{ region }}" key_name: "{{ service_name }}" group: "{{ group_name }}" instance_tags: Name: "{{ group_name }}" exact_count: 1 count_tag: Name: "{{ group_name }}" wait: yes wait_timeout: 300 volumes: - device_name: "{{ device_name }}" volume_type: "{{ volume_type }}" volume_size: "{{ volume_size }}" delete_on_termination: yes instance_profile_name: ap_servers register: ec2 - name: SSHで接続できるようになるまで待機 wait_for: port=22 host="{{ item.public_ip }}" timeout=300 state=started with_items: ec2.instances - name: 作成したEC2インスタンスをインベントリに追加 add_host: hostname="{{ item.public_ip }}" groupname="tag_Name_{{ group_name }}" with_items: ec2.instances
上記で参照している変数にはAWSの無料枠で収まるように最低限のインスタンスサイズを下記のように指定しています。(節約♪節約♪)
# PLAYBOOK_ROOT/roles/ec2/defaults/main.yml --- ami_image: ami-f80e0596 # ← Amazon Linux AMI 2016.03.0 instance_type: t2.micro device_name: /dev/xvda volume_type: gp2 volume_size: 30
nginxのセットアップ
# PLAYBOOK_ROOT/roles/nginx/tasks/main.yml --- - yum: name={{item}} state=latest with_items: - nginx - git - name: change log_dir permission become: yes file: path: /var/log/nginx state: directory owner: nginx group: nginx mode: 0755 - name: service登録 service: name: nginx state: started enabled: yes
ここまでで、EC2インスタンスを立ち上げてnginxをHTTPのみで起動した状態となりました。
Let's Encrypt
ここからはLet's EncryptをセットアップしてSSLサーバー証明書を発行してみます。
クライアントアプリのインストール
Let's Encryptクライアントアプリをインストールします。今回は/opt
以下に配置するようにしました。
Gitリポジトリをクローンします。
$ cd /opt $ sudo git clone https://github.com/letsencrypt/letsencrypt Cloning into 'letsencrypt'... remote: Counting objects: 33135, done. remote: Compressing objects: 100% (82/82), done. remote: Total 33135 (delta 48), reused 0 (delta 0), pack-reused 33053 Receiving objects: 100% (33135/33135), 8.74 MiB | 4.66 MiB/s, done. Resolving deltas: 100% (23496/23496), done. Checking connectivity... done.
クライアントアプリを実行してみます。
$ ./letsencrypt-auto --help WARNING: Amazon Linux support is very experimental at present... if you would like to work on improving it, please ensure you have backups and then run this script again with the --debug flag!
おや? なんかAmazon Linuxは実験的なサポートしかしてないよって警告でましたね...
--debug
オプションをつけると先に進めるようなので、自己責任の元、すすめてみましょう。
$ ./letsencrypt-auto --help --debug Bootstrapping dependencies via Amazon Linux... yum は /usr/bin/yum です 読み込んだプラグイン:priorities, update-motd, upgrade-helper パッケージ python-tools は利用できません。 依存性の解決をしています --> トランザクションの確認を実行しています。 (中略)
上記のメッセージがでたあと、ずらずらーっと依存パッケージのインストールが実行されます。インストールされるパッケージは下記とそれらが依存しているパッケージのようです。
- python26
- python26-devel
- python26-pip
- python26-virtualenv
- augeas-libs
- dialog
- gcc
- libffi-devel
- openssl-devel
- system-rpm-config
上記のインストールが完了したところで画面には下記のようなメッセージが出力されました。
(中略) Checking for new version... Creating virtual environment... Installing Python packages... Installation succeeded. Requesting root privileges to run letsencrypt... sudo /home/ec2-user/.local/share/letsencrypt/bin/letsencrypt --help --debug letsencrypt-auto [SUBCOMMAND] [options] [-d domain] [-d domain] ... The Let's Encrypt agent can obtain and install HTTPS/TLS/SSL certificates. By default, it will attempt to use a webserver both for obtaining and installing the cert. Major SUBCOMMANDS are: (default) run Obtain & install a cert in your current webserver certonly Obtain cert, but do not install it (aka "auth") install Install a previously obtained cert in a server renew Renew previously obtained certs that are near expiry revoke Revoke a previously obtained certificate rollback Rollback server configuration changes made during install config_changes Show changes made to server config during installation plugins Display information about installed plugins Choice of server plugins for obtaining and installing cert: --apache Use the Apache plugin for authentication & installation --standalone Run a standalone webserver for authentication (nginx support is experimental, buggy, and not installed by default) --webroot Place files in a server's webroot folder for authentication OR use different plugins to obtain (authenticate) the cert and then install it: --authenticator standalone --installer apache More detailed help: -h, --help [topic] print this message, or detailed help on a topic; the available topics are: all, automation, paths, security, testing, or any of the subcommands or plugins (certonly, install, nginx, apache, standalone, webroot, etc)
とりあえず実行準備は整ったようです。
SSLサーバー証明書の発行
下記のコマンドを実行します。
$ ./letsencrypt-auto certonly --webroot -w /usr/share/nginx/html -d nslides.devchick.link --agree-tos -m xxx@example.com
オプションの意味はそれぞれ下記のとおりです。
--webroot
: HTTPサーバーが既に稼働している場合に指定します。今回はnginxが既に稼働している状態なのでこのオプションを利用します。HTTPサーバーが稼働していないマシンの場合は--standalone
オプションを指定すれば、Let's EncryptクライアントアプリがHTTPサーバーを一時的に立ち上げてくれるようです。-w
: HTTPサーバーのドキュメントルートのパスを指定します。このパスの下に.well-known/acme-challenge/xxxxxxxxxxxxxxxxxxxx
というようなファイルを一時的に作成しLet's Encryptサーバー側から読み取ってもらうことでドメイン保有者であることを証明しているようです。認証完了後にはこのファイルは削除されます。-d
: 取得したいSSLサーバー証明書のドメインを指定します。--agree-tos
: Let's Encrypt の利用規約に同意します。事前に利用規約に目を通しておきましょう。https://letsencrypt.org/repository/-m
: 自分の連絡先メールアドレスを指定します。緊急の通知や鍵を紛失したときの復旧に使われます。
$ ./letsencrypt-auto certonly --webroot -w /usr/share/nginx/html -d nslides.devchick.link --agree-tos -m xxx@example.com Checking for new version... Requesting root privileges to run letsencrypt... sudo /home/ec2-user/.local/share/letsencrypt/bin/letsencrypt certonly --webroot -w /usr/share/nginx/html -d nslides.devchick.link --agree-tos -m xxx@example.com Version: 1.1-20080819 Version: 1.1-20080819 IMPORTANT NOTES: - If you lose your account credentials, you can recover through e-mails sent to xxx@example.com. - Congratulations! Your certificate and chain have been saved at /etc/letsencrypt/live/nslides.devchick.link/fullchain.pem. Your cert # ← ここ! will expire on 2016-06-21. To obtain a new version of the certificate in the future, simply run Let's Encrypt again. - Your account credentials have been saved in your Let's Encrypt configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Let's Encrypt so making regular backups of this folder is ideal. - If you like Let's Encrypt, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le
実行すると上記のようなメッセージが出力されます。「ここ!」と記載した箇所に発行されたSSLサーバー証明書が保存されました。 フォルダの中を確認してみると下記のようにシンボリックリンクが張られた状態になります。
$ sudo ls -al /etc/letsencrypt/live/nslides.devchick.link 合計 8 drwxr-xr-x 2 root root 4096 3月 23 13:35 . drwx------ 3 root root 4096 3月 23 13:35 .. lrwxrwxrwx 1 root root 41 3月 23 13:35 cert.pem -> ../../archive/nslides.devchick.link/cert1.pem lrwxrwxrwx 1 root root 42 3月 23 13:35 chain.pem -> ../../archive/nslides.devchick.link/chain1.pem lrwxrwxrwx 1 root root 46 3月 23 13:35 fullchain.pem -> ../../archive/nslides.devchick.link/fullchain1.pem lrwxrwxrwx 1 root root 44 3月 23 13:35 privkey.pem -> ../../archive/nslides.devchick.link/privkey1.pem
シンボリックリンク先のcert1.pem
の1
の部分は証明書を発行した回数が連番で記載されます。今後再発行をしていくとカウントアップされていきます。
ちなみに4つのファイルはそれぞれ下記を意味しています。
- cert.pem : 今回発行したSSLサーバー証明書単体 (Subject: CN=nslides.devchick.link)
- chain.pem : 上記のSSLサーバー証明書を発行した認証局の証明書 (Subject: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X1)
- fullchain.pem : 上記の2つの証明書を1つにまとめたもの
- privkey.pem : cert.pemに対応する秘密鍵
nginxのセットアップ その2
nginxの設定を修正してHTTPSで接続できるようにします。デフォルトの設定ファイルの中にサンプルがあるので、コメントアウトを解除します。
# /etc/nginx/nginx.conf server { listen 443 ssl; listen [::]:443 ssl; server_name localhost; root /usr/share/nginx/html; ssl_certificate "/etc/letsencrypt/live/nslides.devchick.link/fullchain.pem"; # ← ここ変更 ssl_certificate_key "/etc/letsencrypt/live/nslides.devchick.link/privkey.pem"; # ← ここ変更 # It is *strongly* recommended to generate unique DH parameters # Generate them with: openssl dhparam -out /etc/pki/nginx/dhparams.pem 2048 #ssl_dhparam "/etc/pki/nginx/dhparams.pem"; ssl_session_cache shared:SSL:1m; ssl_session_timeout 10m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP; ssl_prefer_server_ciphers on; # Load configuration files for the default server block. include /etc/nginx/default.d/*.conf; location / { } error_page 404 /404.html; location = /40x.html { } error_page 500 502 503 504 /50x.html; location = /50x.html { } }
「ここ変更」とマークした箇所を修正して先ほど発行されたSSLサーバー証明書のfullchainと秘密鍵を指定します。
動作確認
Webブラウザでサイトにアクセスします。
やった! 無事にSSLサーバー証明書が認識されました。
更新処理
Let's Encryptで発行される証明書は有効期限が90日間と非常に短いものとなっています。なので、定期的に更新してあげないといけません。
cronで1ヶ月に1回更新させるように、/etc/cron.monthly
ディレクトリ以下にスクリプトをひとつセットしておきます。
$ sudo vi /etc/cron.monthly/letsencrypt /opt/letsencrypt/letsencrypt-auto renew --webroot -w /usr/share/nginx/html --force-renew --debug /sbin/service nginx reload $ sudo chmod a+x /etc/cron.monthly/letsencrypt
--debug
オプションを付けていないとまたAmazon Linuxは実験的なサポートしかしてないよって警告がでて処理失敗してしまうので付けています。
おわりに
非常に簡単に、かつ無料でSSLサーバー証明書を発行&設定できてしまいました。
HTTP/2の恩恵を受けるためにはHTTPS通信が必要ですが、Let's Encryptのおかげでいろいろなサイトで利用できるようになりそうです。