30歳からのプログラミング

30歳無職から独学でプログラミングを開始した人間の記録。

Let's Encrypt と Route 53 でローカル開発環境を HTTPS 化する

ブラウザの機能のなかには、HTTPS でないと利用できなかったり、HTTPS か HTTP かで挙動が変わったりする機能がある。
そのため、ローカル開発環境を HTTPS で構築したいことがある。

一番簡単なのは自己署名証明書を作成し利用することだと思うが、その場合ブラウザが警告を出すため、利便性の点で難がある。

以下の記事では、Let's Encrypt で取得した正規の証明書を使って、ローカル開発環境を HTTPS 化している。
これなら、ブラウザが警告を出すことはない。

blog.jxck.io

勉強がてら、この内容を実践してみた。

この方法を試すためには、自由に使えるドメインを所有している必要がある。
既にnumb86.netというドメインを所有していたので、これを利用してlocalhost.numb86.netというドメインで開発環境を作っていく。

A レコードの値として 127.0.0.1 を設定する

127.0.0.1はループバックアドレスという特殊な IP アドレスで、自分自身を指す。

例えば、以下のコードを Deno で実行してhttp://127.0.0.1:8080/にアクセスすると、Hello Deno.と表示される。
なお、Deno のバージョンは1.2.2

import {
  listenAndServe,
} from "https://deno.land/std@0.63.0/http/mod.ts";

listenAndServe({ port: 8080 }, (req) => {
  req.respond({
    status: 200,
    headers: new Headers({
      "content-type": "text/plain",
    }),
    body: "Hello Deno.\n",
  });
});

console.log("Server running on localhost:8080");

この127.0.0.1localhost.numb86.netと紐付けることで、localhost.numb86.netへのアクセスがローカル開発環境に対するアクセスとなるようにする。
元々ネームサーバとして Route 53 を利用していたので、その管理画面で設定を行う。

f:id:numb_86:20200804222729p:plain

digコマンドで、正しく設定されていることを確認する。

$ dig localhost.numb86.net a +short
127.0.0.1

これで、http://localhost.numb86.net:8080/にアクセスしてもHello Deno.が表示されるようになった。

次は、localhost.numb86.netの証明書を Let's Encrypt で取得する。

DNS 認証による証明書の取得

Let's Encrypt は ACME(Automatic Certificate Management Environment)というプロトコルを利用しており、そのプロトコルで定義されている「チャレンジ」によって、申請の正当性をチェックしている。
今回の例で言えば、証明書を取得しようとしている私が本当にlocalhost.numb86.netの管理者であるかを確認するために、チャレンジが行われる。

チャレンジにはいくつか種類があるが、今回のようなケースでは「DNS-01」というチャレンジを使う。

まず、certbotというツールをインストールする。

$ brew install certbot
$ certbot --version
certbot 1.6.0

certbotは ACME のクライアントのひとつ。

以下のコマンドで、チャレンジを行う。

$ sudo certbot certonly --manual -d localhost.numb86.net --preferred-challenges dns-01

いくつか質問に答えていくと、以下のように表示される。

Please deploy a DNS TXT record under the name
_acme-challenge.localhost.numb86.net with the following value:

XXX

Before continuing, verify the record is deployed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

XXXの部分にトークンが表示される。
それを、_acme-challenge.localhost.numb86.netの TXT レコードの値として設定する。
表示されている通り、TXT レコードの設定が終わるまではエンターキーは押さない。

これも Route 53 で設定する。

f:id:numb_86:20200804222743p:plain

設定が反映されているか、digコマンドで確認する。トークンが表示されれば、設定は完了している。

$ dig _acme-challenge.localhost.numb86.net txt +short
"XXX"

設定が完了した状態でエンターキーを押すと、チャレンジが完了し、証明書が手に入る。

/etc/letsencrypt/live/localhost.numb86.net/にインストールされている。
cert.pemが証明書で、privkey.pemが秘密鍵。
このディレクトリにアクセスするには権限が必要だったので、sudo surootに切り替えて操作した。

また、ファイルのパーミッションを変えておかないと取り扱いづらいので、以下のコマンドも実行した。

$ sudo chmod 664 privkey.pem

Deno で HTTPS サーバを立てる

証明書が手に入ったので、それを使って HTTPS サーバを立てる。

以下のコードを Deno で実行してhttps://localhost.numb86.net:8443/にアクセスすると、Hello Secure Deno!と表示される。

import {
  listenAndServeTLS,
} from "https://deno.land/std@0.63.0/http/mod.ts";

listenAndServeTLS(
  { port: 8443, certFile: "./cert.pem", keyFile: "./privkey.pem" },
  (req) => {
    req.respond({
      status: 200,
      headers: new Headers({
        "content-type": "text/plain",
      }),
      body: "Hello Secure Deno!\n",
    });
  },
);

console.log("Server running on localhost:8443");

自己署名証明書ではないので、警告も出ない。

f:id:numb_86:20200804222654p:plain

webpack-dev-server で HTTPS サーバを立てる

この環境を使って、webpack-dev-server で HTTPS サーバを立てることもできる。

以下のバージョンで動作確認をした。

  • webpack@4.44.1
  • webpack-cli@3.3.12
  • webpack-dev-server@3.11.0

以下のファイルと、上記の秘密鍵、証明書を用意した上で$ yarn run webpack-dev-server --mode=developmentを実行すると、https://localhost.numb86.net:8443/Hello webpack!と表示される。

<!-- ./dist/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>webpack</title>
</head>
<body>
<div id="app"></div>
<script src="./index.js"></script>
</body>
</html>
// ./src/index.js
const elem = document.querySelector('#app');
elem.textContent = 'Hello webpack!';
// ./webpack.config.js
const path = require('path');
const fs = require('fs');

module.exports = () => {
  return {
    entry: {
      index: './src/index.js',
    },
    output: {
      path: path.resolve(__dirname, 'dist'),
    },
    devServer: {
      contentBase: path.resolve(__dirname, 'dist'),
      host: 'localhost.numb86.net',
      port: 8443,
      https: true,
      key: fs.readFileSync('./privkey.pem'),
      cert: fs.readFileSync('./cert.pem')
    },
  }
};

『DNSをはじめよう ~基礎からトラブルシューティングまで~ 改訂第2版』を読んだ

DNS の入門書。かなり初歩的な内容で説明も丁寧なので、初心者でも問題なく読み進められる。

mochikoastech.booth.pm

必要な前提知識はほとんどなく、ウェブ開発に関する仕事をしたことがある人なら、誰でも読めると思う。
図や事例が豊富で、理解しやすい。

本書では、実際にドメイン名を取得し、それを Route 53 で運用するハンズオン形式になっている。
そのため、「お名前.com」や AWS の具体的な操作手順に、かなり紙幅を割いている。
私も既にドメインを持っており、Route 53 も利用していたので、本書の内容と自分の設定を見比べることで、理解が深まった。

以下、自分用のメモ。

第 1 章 ドメイン名と Whois

ドメインを買う時は、レジストリと契約をしている。
レジストラ、リセラは、単なる仲介業者。

ドメイン名の一番右を、TLD(トップレベルドメイン)と呼ぶ。
例えば、example.co.jpjpや、yahoo.comcom
ひとつの TLD は、ひとつのレジストリによって管理している。 例えば、jpを管理しているのは、株式会社日本レジストリサービス。
ひとつの TLD を複数のレジストリによって管理している、ということはない。

ICANNという組織が、レジストリを決めている。そういう非営利組織がある。
TLD を作る決定権があるのも、ICANN。プロトコルのポート番号を決めているのも、ICANN。

Whois 検索は、レジストリが提供している機能。レジストリが、自身が管理している TLD の Whois 情報を管理、公開している。
レジストリ毎に管理しているため、フォーマットが統一されていない。

第 2 章 DNS の仕組み

ドメイン名とサーバを紐付けるのが、DNS。
DNS サーバには、ネームサーバフルリゾルバの2種類がある。
ネームサーバは、別名「DNS コンテンツサーバ」あるいは「権威 DNS サーバ」。ドメイン名と、それに紐付く IP アドレスが登録されている。
フルリゾルバは、別名「DNS キャッシュサーバ」あるいは「フルサービスリゾルバ」。ネームサーバに問い合わせて、指定されたドメイン名に対応した IP アドレスを調べる。一度調べた結果は、一定期間キャッシュする。

ドメイン名と IP アドレスの紐付けひとつひとつのことを、リソースレコードと呼ぶ。
リソースレコードには、「A レコード」や「NS レコード」など、いくつか種類がある。

フルリゾルバに問い合わせる前にまず、hosts ファイルを使って、指定したドメイン名に対応した IP アドレスを探す。それで見つからなかった場合に、フルリゾルバに問い合わせる。
Mac の場合、/private/etc/hostsが hosts ファイル。

第 3 章 AWS のネームサーバ(Route53)を使ってみよう

Route 53 は、AWS が提供する DNS サービス。

Route 53 というネームサーバのなかに、自分のドメイン名のゾーンを作る。

TTL とはキャッシュの保存期間のことで、これが切れるまでは、キャッシュがフルリゾルバに残る。
TTL は NS レコードに設定してある。

第 4 章 dig と whois を叩いて学ぶ DNS

dig ドメイン名 +shortで、そのドメイン名に紐付いた IP アドレスを教えてくれる。

whois ドメイン名で、Whois 情報を得られる。

A レコードは、ドメイン名と IP アドレスを紐付けている。ドメイン名から IP アドレスを探す。

MX レコードは、メールアドレスのドメイン名とメールサーバを紐付けている。
設定したメールサーバが、メールを受信する。

NS レコードは、ドメイン名のネームサーバを指定している。

PTR レコードは、A レコードとは逆で、IP アドレスからドメイン名を探す。
dig -x IP アドレス +shortで調べることができる。

CNAME レコードは、エイリアス。ドメイン名に対して、別のドメイン名を設定する。
フルリゾルバは、A レコードを調べた際に CNAME が返ってきた場合、その CNAME の値のドメイン名の A レコードを、調べる。それを繰り返して、最終的に IP アドレスを返す。
CNAME レコードを設定したら、他のリソースレコードは設定できない。なぜなら、上述の A レコードだけでなく、他のリソースレコードでも、CNAME に設定してあるドメインの当該リソースレコードを、調べるから。

サブドメインを含まないドメイン名を ZONE APEX と呼ぶ。
ZONE APEX には、SOA レコードと NS レコードが必ず存在する。そのため、他のリソースレコードと共存できない CNAME は、ZONE APEX では使えない。

ネームサーバは、自分で用意することも出来る。Linux に Apache や Nginx を入れればウェブサーバになるように、BIND を入れればネームサーバになる。

第 5 章 トラブルシューティング

CAA レコードには、「そのドメイン名の証明書を発行できる認証局」を書く。
そうすると、それ以外の認証局に対して発行依頼が行われても、発行しない。