前回 作った環境をSSL化したいと思ったので、調査がてらに構築してみました。
最近はかなり安くなったとはいえ、検証のためにお金を出してSSL証明書を発行するのも嫌だし、
オレオレ証明書もなんだかなぁ〜 と思ったので、最近話題の Let's Encrypt(Certbot)
で証明書発行をしてSSL化したいと思います。
Let's Encrypt とは
Let's Encrypt 総合ポータル
Let's Encrypt は、クライアントソフトウェア「Certbot」を使用することで、SSL/TLS サーバ証明書の取得・更新作業を自動化できる仕組みになっています。
独自ドメインがあれば、簡単なコマンド操作で SSL/TLS 証明書(無料)を取得できます。
構築手順や各ツールのバージョンは 前回 と変わりません。
なので、説明は追加した箇所以外は省略します。
前提
- 作業はすべて ConoHaVPS のUbuntu16.04上でやってます。
- OS上にすでに Docker と docker-compose がインストール済みという前提で話を進めます。
- この環境では フロント に Nginx を配置して、バックエンドの Puma にプロキシしています。
バージョン
- ホストOS(Ubuntu16.04 Xenial Xerus)
- Docker(17.06.1-ce)
- docker-compose(1.11.2)
- Nginx(1.15.8)
- Ruby(2.5.1)
- Ruby on Rails(5.2.0)
- MySQL(5.7)
ディレクトリ構成
/var/webapp
├── containers
│ └── nginx
│ ├── Dockerfile
│ └── nginx.conf
├── docker-compose.yml
├── Dockerfile
├── environments
│ └── db.env
├── Gemfile
└── Gemfile.lock
1. 各ディレクトリの作成
アプリケーションルート
$ sudo mkdir -p /var/webapp
$ sudo chown -R $USER:$USER /var/webapp
Nginxコンテナディレクトリ
$ mkdir -p /var/webapp/containers/nginx
環境変数用ディレクトリ
$ mkdir /var/webapp/environments
##2. コンテナ生成のための各ファイルを作成
※ 以降はアプリケーションルート内で行う
Rails用Dockerfile
$ vim Dockerfile
FROM ruby:2.5.1
RUN apt-get update -qq && \
apt-get install -y build-essential \
nodejs
RUN mkdir /webapp
WORKDIR /webapp
ADD Gemfile /webapp/Gemfile
ADD Gemfile.lock /webapp/Gemfile.lock
RUN bundle install
ADD . /webapp
RUN mkdir -p tmp/sockets
Gemfile
$ vim Gemfile
source 'https://rubygems.org'
gem 'rails', '5.2.0'
Gemfile.lock
$ touch Gemfile.lock
Nginx用Dockerfile
$ vim containers/nginx/Dockerfile
FROM nginx:1.15.8
RUN rm -f /etc/nginx/conf.d/*
ADD nginx.conf /etc/nginx/conf.d/webapp.conf
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
Nginx設定ファイル
$ vim containers/nginx/nginx.conf
upstream webapp {
server unix:///webapp/tmp/sockets/puma.sock;
}
# httpでのアクセスはhttpsにリダイレクトさせる
server {
listen 80;
server_name _;
return 301 https://$host$request_uri;
}
server {
# ポート443をリスン
listen 443 ssl;
server_name example.com;
# SSL証明書の場所を指定
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
root /webapp/public;
client_max_body_size 100m;
error_page 404 /404.html;
error_page 505 502 503 504 /500.html;
try_files $uri/index.html $uri @webapp;
keepalive_timeout 5;
# Certbotが指定したドメインにアクセス可能かチェックするためのディレクティブ
location ^~ /.well-known/ {
root /usr/share/nginx/html;
allow all;
}
location @webapp {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass http://webapp;
}
}
DB接続用の情報ファイル
$ vim environments/db.env
MYSQL_ROOT_PASSWORD=db_root_password
MYSQL_USER=user_name
MYSQL_PASSWORD=user_password
docker-compose.yml
$ vim docker-compose.yml
version: '3'
services:
app:
build:
context: .
env_file:
- ./environments/db.env
command: bundle exec puma -C config/puma.rb
volumes:
- .:/webapp
- public-data:/webapp/public
- tmp-data:/webapp/tmp
- log-data:/webapp/log
depends_on:
- db
db:
image: mysql:5.7
env_file:
- ./environments/db.env
volumes:
- db-data:/var/lib/mysql
web:
build:
context: containers/nginx
volumes:
- certs:/etc/letsencrypt # 追加
- certs-data:/var/lib/letsencrypt # 追加
- public-data:/webapp/public
- tmp-data:/webapp/tmp
ports:
- 80:80
- 443:443
depends_on:
- app
volumes:
certs: # 追加
certs-data: # 追加
public-data:
tmp-data:
log-data:
db-data:
Nginxコンテナから証明書を参照できるように Volume を追加します。
3. Certbotコンテナを作成&起動しSSL証明書を作成する
$ docker run --rm \
-p 443:443 -p 80:80 --name letsencrypt \
-v "webapp_certs:/etc/letsencrypt" \
-v "webapp_certs-data:/var/lib/letsencrypt" \
certbot/certbot certonly -n \
-m "test@example.com" \
-d example.com \
--standalone --agree-tos
このコンテナは一時的にSSL証明書を発行するためだけに作成するので、
オプション --rm
でコンテナ実行後は削除しておきます。
docker-compose.yml の名前付きボリュームに指定したキーは、
ビルドすると アプリケーションルート名_ボリューム名
となるため、
Certbotコンテナのボリューム名も同様の名前にしておきます。
- certs -> webapp_certs
- certs-data -> webapp_certs-data
##4. Railsプリケーションの生成と編集
Railsの生成
$ docker-compose run --rm app rails new . --force --database=mysql --skip-bundle
権限の変更
$ sudo chown -R $USER:$USER .
puma.rbの編集
$ cp /dev/null config/puma.rb
$ vim config/puma.rb
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
threads threads_count, threads_count
port ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { "development" }
plugin :tmp_restart
app_root = File.expand_path("../..", __FILE__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"
stdout_redirect "#{app_root}/log/puma.stdout.log", "#{app_root}/log/puma.stderr.log", true
database.ymlの編集
$ cp /dev/null config/database.yml
$ vim config/database.yml
default: &default
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: <%= ENV.fetch('MYSQL_USER') { 'root' } %>
password: <%= ENV.fetch('MYSQL_PASSWORD') { 'password' } %>
host: db
development:
<<: *default
database: webapp_development
test:
<<: *default
database: webapp_test
##5. イメージのビルドとコンテナの起動
イメージのビルド
$ docker-compose build
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
webapp_web latest 6c3ae1c68ff0 18 hours ago 107MB
webapp_app latest fe241efeb525 18 hours ago 820MB
<none> <none> 36b3d89bfec7 18 hours ago 781MB
ruby 2.4.1 15b96d1e91df 2 days ago 679MB
nginx 1.13.3 b8efb18f159b 4 weeks ago 107MB
mysql 5.7 c73c7527c03a 4 weeks ago 412MB
コンテナの起動
$ docker-compose up -d
$ docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------
webapp_app_1 bundle exec puma -C config ... Up
webapp_db_1 docker-entrypoint.sh mysqld Up 3306/tcp
webapp_web_1 /bin/sh -c /usr/sbin/nginx ... Up 0.0.0.0:80->80/tcp
##5. DB設定
権限の付与
$ vim db/grant_user.sql
GRANT ALL PRIVILEGES ON *.* TO 'user_name'@'%';
FLUSH PRIVILEGES;
$ docker-compose exec db mysql -u root -p -e"$(cat db/grant_user.sql)"
$ docker-compose exec db mysql -u user_name -p -e"show grants;"
+------------------------------------------------+
| Grants for user_name@% |
+------------------------------------------------+
| GRANT ALL PRIVILEGES ON *.* TO 'user_name'@'%' |
+------------------------------------------------+
DBの作成
$ docker-compose exec app rails db:create
ここまでで一旦環境構築は完了です。
ブラウザにアクセスしてRailsのWelcomeページが表示できるか確認しておきましょう。
また、 http でアクセスした場合、ちゃんと https にリダイレクトされるかも確認しておきましょう。
##6. scaffoldでアプリのベースを構築
scaffoldの実行
$ docker-compose exec app rails g scaffold User name:string email:string
マイグレートの実行
$ docker-compose exec app rails db:migrate
確認
ブラウザを開いて https://example.com/users にアクセスしてみてください。
下記の通りに表示されればOKです。
CRUD操作が問題なく実行できるか試してみてください。
まとめ
Certbot コマンドで証明書を吐き出すことさえできれば、あとはWebサーバー側でそれを読むだけなので、
かなり手軽にSSL化することができました。
前回 と比べても大きく変更した箇所は nginx.conf くらいかな?
昨今はWebサイトの常時SSL化が推奨され、iOSアプリでもHTTPS義務化(ATS)となっているため、
これを機に Let's Encrypt してみてはどうでしょうか?
その他
Let's Encrypt で発行した証明書の有効期限は3ヶ月です。
なので、3ヶ月に一回は証明書の更新を行なわないといけません。
その更新手順はまたあとで追記しようと思います。