ConoHaでCaddy

caddy-dns moduleの開発と解説

ConoHaでCaddy
Page content

はじめに

この記事はワイルドカード証明書の取得を推奨するものではありません。 また、ワイルドカード証明書はむやみにつかって良いものでもありません。 ワイルドカードの不適切な運用はALPACAのような攻撃を可能にし、単一のサーバーの侵害によって、その証明書に紐づけられた他のすべてのサーバーが危険にさらされる可能性があります。 利用する際は、その利便性と危険性を理解し、上記のリスクが適切に軽減されたスコープと組み合わせで利用する必要があります。

Caddy使ってますか?

Caddyは、Golangで書かれた軽量かつ高機能なWebサーバーです。 Webサーバーと聞くと、NginxやApacheを思い浮かべる方が多いかもしれません。

w3techsによると、2025年8月11日現在、Webサイトの約33%がNginxを、約25%がApacheを利用しています。1 一方でCaddyのシェアは0.3%ほど。NginxやApacheに比べると、知名度はそこまで高くないように感じます。

Caddyの初版は2015年と比較的新しく、NginxはCaddyより約10年先輩にあたります。 ですが、この若さこそがCaddyの魅力の一つで、メモリ安全性、高速なコンパイル、シンプルな設定ファイルなど、メジャーなWebサーバーにはない多くの利点があると思います。

CaddyとHTTPS

Caddyの大きな特徴の一つはHTTPSの自動化だと思います。 これはCertMagicによって実現され、デフォルトでHTTPSを有効化し、証明書の取得・更新も自動で行ってくれます。詳しくはAutomatic HTTPSを見ると、その手軽さがわかります。

ただし、ワイルドカード証明書を取得する場合は少し手間がかかります。 これは以前の記事でも書きましたが、ワイルドカード証明書を取得する場合や、何らかの理由でHTTP ポートが使えない、対象サーバーが外部からアクセスできない場合にはdns-01-challengeが必要であり、この方式では検証中にDNSへ特定のレコードを追加しなければなりません。 そのため、自動化するにはDNSプロバイダーのAPIを使う必要があり、プロバイダーごとに仕様が異なるため追加作業が必要になるのです。

CaddyでConoHaのDNSにdns-01-challengeができるようにした

今回はConoHa用のCaddyモジュールを作成したので、それを使ってワイルドカード証明書を取得してみます。

他のDNSプロバイダーでのやり方はCaddyのWikiにまとまっていますが、作業の流れはほぼ同じだと思います。

作業は大きく分けて次の2ステップです。

  1. xcaddyをつかってConoHaのモジュールと一緒にをcaddyをビルド
  2. Caddyfileの記述

今回はdockerをつかって作業していきます。 今回はdockerで進めますが、podmanなど他のコンテナでも同じ要領でできます。

1. xcaddyでbuild

まずは以下のようなContainerfileを書きます。(各versionはそれぞれ最新のものに更新してください)

FROM  caddy:2.10.0-builder-alpine AS builder

RUN xcaddy build --with github.com/caddy-dns/conoha@v0.1.0

FROM caddy:2.10.0

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

2. Caddyfileの記述

次にCaddyfileを書きます。

Caddyfileでは、ConohaのAPIをCaddy側から操作できるようにAPIクレデンシャルを入力する必要があります。 必要な情報はAPIテナントIDAPIユーザーIDAPIパスワードで、いずれもConoHa VPSのAPIページから取得できます。 Caddyfileへの記述の仕方は、githubに書いて置いたので、こちらを参考にしてください。

以下のような感じになると思います。

mssht.net, *.mssht.net {
	tls {
		dns conoha {
			api_tenant_id {env.API_TENANT_ID}
			api_user_id {env.API_USER_ID}
			api_password {env.API_PASSWORD}
		}
		propagation_timeout 10m
		propagation_delay 5m
    }
...

今回はAPIクレデンシャルを環境変数から値を読んでいますが、Caddyfileに直接記述しても良いと思います。

あとはBasic Usage, docker.comを参考にdockerなり、podmanなりで起動するだけです。 docker-compose.ymlは以下のような感じに書けるかと思います。

services:
  caddy:
    build:
      context: ./caddy/
      dockerfile: Containerfile
    container_name: caddy
    restart: always
    ports:
      - "80:80"
      - "443:443"
    env_file:
      - ".env_caddy"
    volumes:
      - ./caddy/conf:/etc/caddy:ro
      - ./caddy/contents/public:/usr/share/caddy:ro

最後に

今回の開発によって以下2モジュールのメンテナになりました。なにかしらの提案だったり、要望等あれば、issue等に書き込んでいただけると助かります。

そういえば、先日ConoHaのAPIが更新されましたね。2 具体的には「サブユーザー」と「ロール」という概念が追加され、APIユーザーが自分のクレデンシャルを使ってロールを作成し、そのロールをサブユーザーにアタッチする形で利用できるようになりました。

これにより、実際にトークンを取得して操作するユーザーの権限を、より細かく設定できるようになりました。 以前のポストでも書いたように、最小権限でないとAPIクレデンシャルが流出した際の被害が大きくなってしまうため、これは非常に良い変更だと思います。

ただし、残念ながらロールにアタッチできるパーミッションはVolumeやImageなど一部のAPIに限られ、DNSのAPIはまだ含まれていませんでした。 同機能が今後DNS APIにも適応されるのかどうかわかりませんが、追加されしだい、上記のモジュールは修正しようと思います。