Let's EncryptでSSL証明書を取得してHTTP/2な世界にGopherくんを召喚してみた
独自ドメインを持ってるなら、オレオレ証明書なんか使わずに
VALIDなHTTPSでヤッテイコーという試み。
Let's Encrypt で証明書を取得する
事前知識
Let's Encrypt で取得できる証明書は ドメイン認証SSL証明書です。
(SSL証明書には企業認証SSL証明書や、EVSSL証明書などがあります。)
そのため、Let's Encrypt で証明書を取得するには、自分で好き勝手できる独自ドメインを用意し、
Automated Certificate Management Environment (ACME)
というドメインの所有を確認する
認証プロセスを通過する必要があります。
ついては以下のいずれかの組み合わせが必要です。
基本的には独自ドメインを持ってさえすればあとはなんとかなるはず。。。
- 独自ドメイン と WEBサーバー (外部からの参照が可能なもの)
- HTTP-01 という認証方式で必要となります。Webサーバーに指定のトークンを記述したファイルを 配置することができるかどうかで認証を行います。
- 独自ドメイン と DNSサーバー
というわけで今回は、「独自ドメイン と DNSサーバー」の組み合わせの認証方法 「DNS-01」 ですすめていきます。
今回はWindowsじゃなくてArchlinuxが個人的にアツいのでVagrantのArchlinux上で処理を進めていきます。
下記のような Vagrant 環境を整えているのでよければどうぞ。
(zplug でプラグインをインストールするか聞かれると思うので y でインストールすると良いです)
dehydrated の用意
過去 letsencrypt.sh
として知られていたシェルスクリプトツール。
いまは dehyrated
(デハイドレテッド) という名前に変わっています。
ちなみに Let's Encrypt 公式の certbot (旧 letsencrypt-cli) というツールもありますが
pythonに依存することと、ちょっと扱いづらいので今回はシンプルな dehydrated を使います。
下記からおもむろに git clone
もしくは ghq get
しておきます。
# git clone git clone https://github.com/lukas2511/dehydrated.git cd dehydrated
ここから先の手順は下記を参考にさせていただきました。
ではすすめていきましょう。まずは元になるファイルをコピーします
cp -ip docs/examples/hook.sh ./
続いてコピーしたファイルを開き deploy_challenge
を下記の様に差し替えます。
内容としてはDNSレコードにTXTレコードとしてTOKENの登録を促すようにし、
digを引いてみてTOKENが一致するかチェックするようにしています。
deploy_challenge() { local DOMAIN="${1}" TOKEN_FILENAME="${2}" TOKEN_VALUE="${3}" local RECORD="_acme-challenge.${DOMAIN}" cat <<EOS Set TXT record of ${RECORD} to ${TOKEN_VALUE} Press Enter to continue... EOS read while :; do local result="$(dig +short txt ${RECORD} | tr -d '"')" [ "${result}" == "${TOKEN_VALUE}" ] && break cat <<EOS Token does not match. Set TXT record of ${RECORD} to ${TOKEN_VALUE} Press Enter to retry... EOS read done }
これで準備ができました。
証明書の取得
と、いうわけで実際にDNS-01による認証を経て証明書の取得を行いましょう。
dehydrated
を下記のように実行します。ドメインのところは任意の値を設定します。
./dehydrated -c -t dns-01 -k ./hook.sh -d <ドメイン> # -c cronモードの指定 # -t 認証方式の指定 # -k hook.shの指定 # -d 証明書のCNに対応するドメイン
試しに実行した結果は下記のとおりです。
$ ./dehydrated -c -t dns-01 -k ./hook.sh -d example.jp # INFO: Using main config file /home/vagrant/works/src/github.com/lukas2511/dehydrated/config Processing example.jp + Signing domains... + Creating new directory /home/vagrant/works/src/github.com/lukas2511/dehydrated/certs/example.jp ... + Generating private key... + Generating signing request... + Requesting challenge for example.jp... Set TXT record of _acme-challenge.example.jp to ******************************** Press Enter to continue...
・・・と、ここで hook.sh で追加した処理の通りTXTレコードの登録を求められるのでその通りに設定してあげます。 設定が終わったらターミナルに戻ってきてEnterを押します。
もし設定が反映されてなかったり間違っていたら再度設定するように求められます。
(ここでネガティブキャッシュを持ってしまったら nscd -i hosts
とかでキャッシュを消して上げると良いかも)
+ Responding to challenge for example.jp... + Challenge is valid! + Requesting certificate... + Checking certificate... + Done! + Creating fullchain.pem... + Done!
無事に証明書が発行できました。
取得結果は ./certs/<ドメイン>
ディレクトリ以下に出力されています。
$ find ./certs/example.jp/*.pem ./certs/example.jp/cert.pem # 証明書 ./certs/example.jp/chain.pem # 中間証明書 ./certs/example.jp/fullchain.pem # フルチェイン証明書 (証明書 + 中間証明書) ./certs/example.jp/privkey.pem # 証明書に対応するRSA秘密鍵
更新などで複数回発行した場合は、上記は最新のファイルへのシンボリックリンクになっています。 実際に見てみましょう。
$ openssl x509 -text -in ./certs/example.jp/cert.pem | grep -E "(Issuer|Subject):|Not\ " Issuer: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3 Not Before: Jan 2 06:57:00 2017 GMT Not After : Apr 2 06:57:00 2017 GMT Subject: CN=example.jp
指定のドメインで使える証明書ができていますね。期限が3ヶ月なのはLet's Encryptの仕様です。
実際に本番運用する場合には、cronなどで dehydrated を定期実行して証明書を更新してあげるとよいでしょう。
また、初回に dehydrated を実行した場合には、証明書発行とは別に秘密鍵を自動で作成して、
Let's Encrypt に登録するようになっています。
登録周りのファイルは account
ディレクトリ配下に保存されます。
証明書の更新などで必要となるため削除しないようにしておきましょう。
HTTP/2 なWebサーバーを立てる
せっかく証明書を取得したので、試しに使ってみましょう。
GoのEchoというパッケージを使って軽いHTTPサーバーを立ててみます。
Echo のインストール
sandbox には go を入れてないのでまずインストールします。
pacman -S go
次は echo を go get
します。
go get -u github.com/labstack/echo
作業用ディレクトリを用意して証明書と鍵をコピーしておきましょう。
mkdir ~/test cp -ip ./certs/example.jp/{fullchain,privkey}.pem ~/test
最後に実行するスクリプトを用意しておきましょう。
cd ~/test vim main.go
package main import "github.com/labstack/echo" func main() { e := echo.New() e.File("/", "index.html") e.Logger.Fatal(e.StartTLS(":443", "fullchain.pem", "privkey.pem")) }
コンテンツも index.html
として用意しておきます
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Echo</title> </head> <body> <div style="text-align: center;"> <img src="https://raw.githubusercontent.com/golang-samples/gopher-vector/master/gopher-front.png" alt="gopher-front"> <div style="font-size:8px;"> <p>by <a href="https://github.com/golang-samples/gopher-vector">Takuya Ueda</a></p> <p>The Gopher character is based on the Go mascot designed by <a href="http://reneefrench.blogspot.jp/">Renée French</a>.</p> </div> </div> </body> </html>
あとは、Aレコードを設定するかhostsをいじるかして、
CNに設定したドメインでHTTPサーバーにアクセスできるようにしておきます。
sandboxを使う場合には 192.168.33.10
を対応付けるよう hosts に記載しておきましょう。
実行&動作確認
ではではサクッと実行してみましょう。
ポートのListenするので実行にはRoot権限が必要です。
sudo go run main.go
満を持してブラウザで https で建てたサーバーにアクセスしてみます。
見事正常なHTTPSでGopherくんを召喚できました!
しかもデフォルトでHTTP/2が有効になっています!!ステキ!!!
ちなみに この青いイナヅママークを出しているのは「HTTP/2 and SPDY indicator」というChrome拡張です。
まとめ
今回は Let's Encryptで証明書を取得して、実際に証明書を使ってアクセスするとこまでやってみました。
SSL証明書といえばお金がそこそこかかるのであちこち手軽に導入するというわけには行きませんでしたが、
Let's Encrypt の登場で手軽に導入できることがわかりました。
自分のドメインを持っているなら使わない手はないのではないかと思います。
ちなみに今回は dehydrated
を使ってCLIベースで証明書の取得を行いましたが、
「SSLなう!」というサービスも有り、これはブラウザのみでSSL証明書の発行まで行うことができます。
単発でテスト用の証明書を発行するにはとても便利な手段だと思いました。