あけましておめでとうございます。本年度もよろしくお願い致します。

年末年始にLINE BOT AWARDの作品を作ろうとして、callbackが自鯖のオレオレ自己証明書だとVerifyで弾かれてしまい、年始早々打ちのめされたので、この際だからアレしようと思って、ちゃんとSSL化した時のメモ

この記事に書いてある事一覧

LetsEncryptで証明書の発行(certbotクライアントのインストール)

Debianではjessie-backportsでパッケージが提供されているようですが、backportsは使用せず、certbot-autoスクリプトを使用しました

なお、certbotのDockerイメージも公開されているため、Dockerコンテナでcertbotを使いたい場合はこちらを利用する方法もあります。

// Dockerfile
RUN wget "https://dl.eff.org/certbot-auto" -P /usr/local/bin/
RUN chmod a+x /usr/local/bin/certbot-auto
RUN /usr/local/bin/certbot-auto --os-packages-only --non-interactive

--os-packages-onlyオプションを指定すると、実際に証明書の発行はせずに、必要なパッケージのインストールのみ行い、exitしてくれます。逆にこれを指定しないと、心の準備が伴っていない場合もどんどん処理が進みます。

--non-interactiveオプションは、対話式でなく自動でcertbotの処理が進むようになるオプション。今回は、--os-packages-onlyを同時に指定しているので不要のように見えますが、このオプションが無いとapt-getの[Y/n]で停止したので、これを指定するとそこが自動になります(地味)

Letsencryptで証明書の発行(認証方式になやむ)

認証方式がたくさんあって、apacheとかnginxとか指定して勝手に設定ファイルいじられたり再起動されたりするのはなんか嫌だし、じゃあstandalonewebroot?これって何が違うのよ?というので調べ回ってたのですが、以下の記事に図解があって大変わかりやすかったです

そして、ACMEプロトコルについては以下の記事がとても参考になりました

結論として自分の環境では、webrootプラグインを使う事にしました

Letsencryptで証明書の発行(認証する)

webrootプラグインは、認証の過程で[DocumentRoot]/.well-known/acme-challenge/に一時的にトークンを出力し、そのトークンに対してアクセスしにくるので、

  • certbot-auto実行ユーザが[DocumentRoot]/.well-known/acme-challenge/へのファイル書き込み権限がある事
  • 対象のドメインでHTTPアクセス(80)が出来る事。

このあたりはクリアにする必要があるようです。(この流れが分かってなかったので結構右往左往しました)

// 証明書発行コマンド
$ docker exec [webのコンテナ名] certbot-auto certonly --webroot --non-interactive --agree-tos -w [DocumentRootのPATH] --email [メールアドレス] -d [発行するドメイン]

長いですね。このままッターンってやってもかっこいいと思いますが僕は嫌です

設定ファイルにしてしまいましょう。設定ファイルの雛形はここにあります

// つくったiniファイル(以下、余分な行は省略してます)
$ cat 7me.ini

# This is an example of the kind of things you can do in a configuration file.
# All flags used by the client can be configured here. Run Certbot with
# "--help" to learn more about the available options.

# Use a 4096 bit RSA key instead of 2048
rsa-key-size = 4096

# Uncomment and update to register with the specified e-mail address
email = [メールアドレス]

# Uncomment and update to generate certificates for the specified
# domains.
domains = [発行するドメイン(※カンマ区切りで複数指定可)]

# Uncomment to use a text interface instead of ncurses
# text = True

# Uncomment to use the standalone authenticator on port 443
# authenticator = standalone
# standalone-supported-challenges = tls-sni-01

# Uncomment to use the webroot authenticator. Replace webroot-path with the
# path to the public_html / webroot folder being served by your web server.
authenticator = webroot
webroot-path = [DocumentRootのPATH]

このiniファイルを元に実行します。

$ docker exec [webのコンテナ名] certbot-auto certonly --config /path/to/7me.ini --non-interactive --agree-tos

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Starting new HTTPS connection (1): acme-v01.api.letsencrypt.org
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for [ドメイン名]
Using the webroot path [DocumentRootのPATH] for all unmatched domains.
Waiting for verification...
Cleaning up challenges
Generating key (4096 bits): /etc/letsencrypt/keys/0002_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0002_csr-certbot.pem
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/[ドメイン名]/fullchain.pem. Your cert will
   expire on 2017-04-03. To obtain a new or tweaked version of this
   certificate in the future, simply run certbot-auto again. To
   non-interactively renew *all* of your certificates, run
   "certbot-auto renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

うまく行けばこんな感じに表示されます。そして、/etc/letsencrypt/live/[ドメイン名]/ あたりに、証明書が出力されていると思います

$ docker exec [webのコンテナ名] ls -l /etc/letsencrypt/live/[ドメイン名]
合計 0
lrwxrwxrwx 1 root root 38  1月  3 15:23 cert.pem -> ../../archive/[ドメイン名]/cert1.pem
lrwxrwxrwx 1 root root 39  1月  3 15:23 chain.pem -> ../../archive/[ドメイン名]/chain1.pem
lrwxrwxrwx 1 root root 43  1月  3 15:23 fullchain.pem -> ../../archive/[ドメイン名]/fullchain1.pem
lrwxrwxrwx 1 root root 41  1月  3 15:23 privkey.pem -> ../../archive/[ドメイン名]/privkey1.pem

Webサーバに設定する

Nginx or Apacheに以下のように設定

// Nginx
ssl_certificate          /path/to/fullchain.pem;
ssl_certificate_key      /path/to/privkey.pem;

// Apache2
SSLCertificateFile       /path/to/fullchain.pem
SSLCertificateKeyFile    /path/to/privkey.pem

HTTPからHTTPSへ301リダイレクトさせてHSTSを仕込む

Nginxの場合

server {
    listen 80;
    server_name             [ドメイン名];

    location / {
        return 301 https://$host$request_uri;        # ← 301リダイレクトさせる
        # proxy_pass http://172.19.0.xx/;            # ← プロキシさせてた箇所(跡地)
        break;
    }
}

server {
    listen 443 ssl;
    server_name             [ドメイン名];

    ssl_certificate          /path/to/fullchain.pem;
    ssl_certificate_key      /path/to/privkey.pem;

    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains;preload';        # ← HSTSの値をセット

    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout  5m;

    ssl_protocols  SSLv2 SSLv3 TLSv1;
    ssl_ciphers  HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers   on;

    location / {
        proxy_pass      https://172.19.0.xx/;
        break;
    }
}

Apacheの場合

// 必要に応じてモジュールを有効にする
$ sudo a2enmod rewrite ssl headers
$ sudo systemctl restart apache2

$ cat vhost.conf
<VirtualHost *:80>
    RewriteEngine on
    RewriteCond %{HTTP_HOST} ^example\.com
    RewriteRule ^/(.*)$ https://example.com/$1 [R=301,L]
</VirtualHost>

<VirtualHost *:443>
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

    Header set Strict-Transport-Security 'max-age=31536000; includeSubDomains;preload'

    ServerAdmin hoge@example.local
    DocumentRoot "/var/virtualdomains/example.com/"
    ServerName example.com
    CustomLog "/var/log/apache2/vhost/example.com-access-ssl.log" combined
    ErrorLog "/var/log/apache2/vhost/example.com-error-ssl.log"

    <Directory "/var/virtualdomains/example.com/">
            Allowoverride All
            Require all granted
    </Directory>
</VirtualHost>