Loading...

2023-11-22(æ°´) 11:00

📱 Caddyで独自ドメインのワイルドカードSSL証明書に対応したリバースプロキシを構築する

CaddyTailscale
社内・ホームネットワークやTailscaleやZeroTierを使ったVPN上で動作しているサービスに自分が持つ独自ドメインのサブドメインでアクセスするために、Caddyを使ったリバースプロキシを構築する手順を解説します。

目次

前提と注意事項

  • Ubuntu22.04 での作業を前提としています。
  • Tailscale に参加している Ubuntu を前提としていますが、他の VPN や LAN 環境 でも同様のことができると思います。
  • Google Domains で取得した独自ドメインを使いますが、Caddy が対応していれば他の DNS プロバイダでも同様のことができます。
  • Go 言語の実行環境が必要です。

この記事のゴール

以下のようなネットワークで、自分が持つ独自ドメイン(example.comとします。)とそのサブドメインを使って Tailscale ネットワーク内で動いているサービスにアクセスできるようにするところまでをゴールとします。 この記事では、Tailscale を使ったネットワークとしていますが、ZeroTier などの VPN、他の閉じたネットワークでも同様のことができると思います。

ネットワーク図

上記のネットワークイメージ図のように、複数のサービス(Nextcloud、code-server など)が1つのサーバ上で Docker を使って動作しているとします。
これらのサービスは異なるポート番号を使ってアクセスできるようになっているため、例えばブラウザで Tailscale 上の IP アドレスとポート番号を使ってhttp://100.100.100.1:8080のように他の Tailscale 端末からアクセスできる状態です。
ただこのままだと以下の問題があります。

  • サービスが増える度にどのポート番号がどのサービスに対応しているかを覚えておく必要があり、IP アドレスとポート番号を覚えていないとアクセスできないので使い勝手が大変悪い。
  • httpsでのアクセスが必須のサービスの場合は自己証明書を使う必要がありますが、これも自己証明書の取得、更新の対応が必要になり手間がかかる。

そこで、覚えやすい独自ドメインとサブドメインを割り当て、さらに SSL 証明書の発行と更新を自動化することで、より使い勝手の良い環境を構築します。
例えば、nextcloud.example.comやcode.example.comのようなサブドメインを割り当て、https://nextcloud.example.comやhttps://code.example.comのようにアクセスできるようにします。 もちろん、もっとシンプルなnx.example.comを割り当てても OK です。好きなサブドメインを使用できます。 そしてその実現のために Caddy を使います。


上記のネットワーク図ではフリーアイコンズ 様のアイコンを使用しています。

Caddy について

Web サーバと言えば Apache や Nginx がメジャーですが、Caddy も Web サーバの1つです。 Caddy は Nginx や Apache のように大量のトラフィック捌くことを目的とした Web サーバではなく、小規模な Web サーバを構築することを目的としています。 以下が Caddy の公式サイトとリポジトリになります。

Caddy

THE ULTIMATE SERVER

caddyserver.com

caddyserver/caddy

Caddy is an extensible server platform that uses TLS by default.

github.com

Caddy の最大の特徴は、SSL 証明書の自動管理です。ACME 標準での SSL 証明書の取得方法としてメジャーな http-01 チャレンジも dns-01 チャレンジも少なからず DNS レコードの追加などの手作業が必要です。 これらの作業を Caddy は自動で行うことができます。 参考に Let's Encrypt の公式サイトにも記載されているチャレンジの種類について以下にリンクを載せます。

チャレンジの種類

Let’s Encrypt から証明書を取得するときには、ACME 標準で定義されている「チャレンジ」を使用して、証明書が証明しようとしているドメイン名があなたの制御下にあることを検証します。

letsencrypt.org

作業の流れ

以下が流れになります。

  • Caddy を Ubuntu にインストールする
  • xcaddy を Ubuntu にインストールする
  • xcaddy と caddy-dns 使ってワイルドカード証明書の自動取得に対応した Caddy をビルドする
  • DNS プロバイダでサーバの Tailscale の IP アドレスのレコードを追加する
  • Google Domains のアクセストークンを取得する
  • Caddyfile を作成する
  • 動作確認する

Caddy を Ubuntu にインストールする

以下の公式ドキュメントに従ってインストールを行います。 ここでは、Caddy を Ubuntu22.04 にインストールします。 以下のコマンドを実行します。

ターミナル
$ sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
$ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
$ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
$ sudo apt update
$ sudo apt install caddy

上記を実行後、caddyコマンドでバージョンを確認できれば正常にインストール完了です。

ターミナル
$ caddy -v
v2.7.5 h1:HoysvZkLcN2xJExEepaFHK92Qgs7xAiCFydN5x5Hs6Q=

Caddy を起動する

以下のコマンドで起動します。実行すると以下のように表示されます。

ターミナル
$ caddy run
2023/11/14 10:19:32.041 INFO    admin   admin endpoint started  {"address": "localhost:2019", "enforce_origin": false, "origins": ["//127.0.0.1:2019", "//localhost:2019", "//[::1]:2019"]}
2023/11/14 10:19:32.042 INFO    serving initial configuration

上記のようにrunを使うとバックグラウンドでの実行ではなく、そこにログが表示され続けます。よって動作確認時にrunを使うのが良いです。
もしバックグランドで起動したい場合はstartを使用します。

ターミナル
$ caddy start

停止する時はstopを使用します。

ターミナル
$ caddy stop

リバースプロキシのための設定や実際の動作確認は後述します。

動作確認

Caddy を起動したら Web サーバとしての動作を確認します。試しに Caddy を起動した状態で、ブラウザでhttp://localhost:2015にアクセスしてみます。以下のように表示されれば OK です。

ターミナル
$ curl http://localhost:2015
Hello, World!

xcaddy を Ubuntu にインストールする

Caddy のモジュールを簡単にインストールするためのツールとしてxcaddy があるのでこれもインストールします。 以下の手順に従います。Caddy 公式ドキュメントに従ってxcaddyをインストールします。 なお、xcaddyのインストールと動作には、Go 言語の実行環境が必要です。
まだ Go 環境の実行環境がない場合は、先に Go をインストールしてください。

以下を実行します。

ターミナル
$ sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
$ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/xcaddy/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-xcaddy-archive-keyring.gpg
$ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/xcaddy/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-xcaddy.list
$ sudo apt update
$ sudo apt install xcaddy

以上でxcaddyのインストールは完了です。

xcaddy と caddy-dns 使ってワイルドカード証明書の自動取得に対応した Caddy をビルドする

caddy-dns について

ワイルドカード証明書の取得を自動化するために、Caddy が公式に提供しているcaddy-dnsというモジュールを利用します。

caddy-dns

Caddy modules that automate manipulation of DNS records (built on libdns interfaces)

github.com

caddy-dnsを使うことで、ワイルドカード証明書の取得時に必要となるdns-01チャレンジを自動化できます。 なお、ワイルドカード証明書が不要な場合はdns-01チャレンジの他にhttp-01チャレンジも使えます。Caddy はhttp-01チャレンジも対応しており自動で行うことができます。


ただし、http-01チャレンジを完了させるには、実在する IP アドレスに認証局がアクセスできる必要があります。 この場合、例えば社内ネットワークや、他には Tailscale のような VPN の閉じたネットワーク内にあるサーバの IP アドレスは外部からアクセスすることができないためhttp-01チャレンジを成功させることができません。 そのためそのような場合はdns-01チャレンジを使います。

xcaddy を使って caddy-dns を組み込んだ Caddy をビルドする

xcaddyとcaddy-dns/google-domainsを使って、Google Domains の操作を API 経由で実行してワイルドカード証明書の取得を自動化できる Caddy をビルドします。 Google Domains ではなく他の DNS プロバイダを使っている場合は、caddy-dnsのリポジトリで使っている DNS プロバイダ用のモジュールを確認して以下の--with github.com/caddy-dns/google-domains部分を置き換えてください。 また、ここでは適当なディレクトリとして~/caddy-google-domainsを作成し、そこでビルドしますが、各自の環境に合わせて任意のところで実行してください。

ターミナル
$ mkdir ~/caddy-google-domains
$ cd ~/caddy-google-domains
$ xcaddy build --with github.com/caddy-dns/google-domains                                                                                            2023/11/17 06:24:09 [INFO] Temporary folder: /tmp/buildenv_2023-11-17-0624.2884679400
2023/11/17 06:24:09 [INFO] Writing main module: /tmp/buildenv_2023-11-17-0624.2884679400/main.go
package main
 
(...省略...)
 
go: downloading cloud.google.com/go/iam v1.1.1
go: downloading golang.org/x/oauth2 v0.12.0
go: downloading cloud.google.com/go/compute v1.23.0
go: downloading github.com/google/s2a-go v0.1.7
2023/11/17 06:25:04 [INFO] exec (timeout=0s): /usr/local/go/bin/go build -o /home/hisui/caddy-google-domai
ns/caddy -ldflags -w -s -trimpath
2023/11/17 06:25:24 [INFO] Build complete: ./caddy
2023/11/17 06:25:24 [INFO] Cleaning up temporary folder: /tmp/buildenv_2023-11-17-0624.635568165

上記が成功すると、上記のコマンドを実行したディレクトリの直下にcaddyという実行ファイルが生成されます。
このcaddyがcaddy-dnsを組み込んだ Caddy になり、これを使ってワイルドカード証明書の取得に対応したリバースプロキシを構築します。 念の為以下のようにバージョンを表示できることを確認してみてください。

~/caddy-google-domains
$ ./caddy -v
v2.7.5 h1:HoysvZkLcN2xJExEepaFHK92Qgs7xAiCFydN5x5Hs6Q=

DNS プロバイダで Tailscale の IP アドレスのレコードを追加する

自分の独自ドメインの DNS プロバイダの管理画面にて、サーバの Tailscale の IP アドレス(本記事冒頭のネットワーク図の100.100.100.1)を A レコードとして追加します。
また、同様に AAAA レコード(記事冒頭の画像内に記載していませんが、Tailscale の管理画面で IPv6 のアドレスも確認できます。)も追加します。 この手順は使用している DNS プロバイダによって異なりますが、以下の内容のワイルドカードの A レコードと AAAA レコードを追加します。

ワイルドカードのAレコードとAAAAレコードを追加する
# Aレコード
ホスト名: *.example.com
タイプ: A
TTL: 1時間
値: 100.100.100.1
 
# AAAAレコード
ホスト名: *.example.com
タイプ: AAAA
TTL: 1時間
値: 2001:0ab8:91a3:0000:0000:8a2e:0370:7334

上記を追加することで、同じ Tailscale のネットワークに参加している端末ならば、example.comの名前解決先が Tailscale 内のサーバとなります。


なお、Tailscale の IP アドレスをパブリックな A レコードと AAAA レコードとして登録することについては特に害はないという趣旨の説明は Tailscale の公式ドキュメントにもありますが、もし気になる場合は以下の記事を参考にしてください。

Using a public DNS subdomain

If you’d prefer not to manage DNS settings via the admin console, you can instead publish records on your public-facing DNS server, assuming you have one.

tailscale.com

Google Domains のアクセストークンを取得する

次は Google Domains のアクセストークンを取得します。 ここの作業は各自が取得した独自ドメインの DNS プロバイダによって手順が異なります。 Google Domains で場合は、Google Domains にログインしてサイドメニューにある「セキュリティ」をクリックして開きます。
開いたページの最下部に「ACME DNS API」という項目があり、その中の「トークンを作成」をクリックします。

Google DomainsのACME DNS API

「トークンを作成」をクリックすると、以下のようにトークンが表示されるのでこれをーコピーしておきます。

Google Domainsのアクセストークン

ここでコピーしたアクセストークンを後ほど使用します。

Caddyfile を作成する

Caddy は、サーバとしての設定を行う方法がいくつか用意されています。コマンドでオプションを指定するけでリバースプロキシとして起動もできます。
ここでは、Caddyfileという設定ファイルを使って設定を行います。 リバースプロキシとして動作させてワイルドカード証明書を自動取得させるためにCaddyfileを以下のようにします。

~/caddy-google-domains/Caddyfile
# 組み込んだGoogle Domainsのモジュールを使う
# アクセストークンは前節で取得したものを指定する
{
	acme_dns google_domains Akd9JS129sA9dkASJd9sksjn2A==
}
 
# code-serverへのリバースプロキシ
code.example.com {
	reverse_proxy localhost:8080
}
 
# immichへのリバースプロキシ
immich.example.com {
	reverse_proxy localhost:2283
}
 
# nextcloudへのリバースプロキシ
nextcloud.example.com {
	reverse_proxy localhost:8022
	header {
		# enable HSTS
		Strict-Transport-Security max-age=31536000;
	}
}

上記だけでワイルドカード SSL 証明書を自動取得し、リバースプロキシとして動作するための記述になります。 上記のCaddyfileを反映させるためにadaptコマンドを使います。 adaptコマンドを実行すると、カレントディレクトリにあるCaddyfileを読み込んで設定が反映されます。

~/caddy-google-domains
$ sudo ./caddy adapt

Caddyfileを反映させたら以下で Caddy を起動します。

~/caddy-google-domains
$ sudo ./caddy run
# もしくはバックグラウンドで起動する場合は以下
$ sudo ./caddy start

バックグラウンドで起動すると以下のように表示されます。

~/caddy-google-domains
$ sudo ./caddy start
[sudo] username のパスワード:
2023/11/21 13:55:21.874 INFO    using adjacent Caddyfile
2023/11/21 13:55:21.878 INFO    admin   admin endpoint started  {"address": "localhost:2019", "enforce_origin": false, "origins": ["//127.0.0.1:2019", "//localhost:2019", "//[::1]:2019"]}
2023/11/21 13:55:21.879 INFO    http.auto_https server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS   {"server_name": "srv0", "https_port": 443}
2023/11/21 13:55:21.880 INFO    http.auto_https enabling automatic HTTP->HTTPS redirects        {"server_name": "srv0"}
2023/11/21 13:55:21.879 INFO    tls.cache.maintenance   started background certificate maintenance      {"cache": "0xc000498600"}
2023/11/21 13:55:21.882 INFO    tls     cleaning storage unit   {"description": "FileStorage:/root/.local/share/caddy"}
2023/11/21 13:55:21.884 INFO    http    enabling HTTP/3 listener        {"addr": ":443"}
2023/11/21 13:55:21.885 INFO    tls     finished cleaning storage units
2023/11/21 13:55:21.885 INFO    http.log        server running  {"name": "srv0", "protocols": ["h1", "h2", "h3"]}
2023/11/21 13:55:21.886 INFO    http.log        server running  {"name": "remaining_auto_https_redirects", "protocols": ["h1", "h2", "h3"]}
2023/11/21 13:55:21.886 INFO    http    enabling automatic TLS certificate management   {"domains": ["cloud.example.com", "code.example.com", "immich.example.com"]}
2023/11/21 13:55:21.889 INFO    autosaved config (load with --resume flag)      {"file": "/root/.config/caddy/autosave.json"}
2023/11/21 13:55:21.889 INFO    serving initial configuration
Successfully started Caddy (pid=2710021) - Caddy is running in the background

Caddy is running in the backgroundと表示されていれば問題なく起動できています。
あとはCaddyfileで記述したhttps://code.example.comやhttps://nextcloud.example.comにブラウザからアクセスできれば正常に Caddy が動作していることを確認できます。

まとめ

Caddy を使うことで、独自ドメインのワイルドカード証明書を自動取得してリバースプロキシとして動作させることができました。 Caddy は設定ファイルに記述する内容が少なくて済み、SSL 証明書の自動管理にも対応しているため非常に便利です。 もし同じように Tailscale や ZeroTier などを使って自前のサーバのサービスに独自ドメインでアクセスしたい場合は、ぜひ Caddy を使ってみてください。