/etc/hosts で wildcard や CNAME 対応させたい

macOS での話です。(macOS Ventura でも機能することを確認しました)

/etc/hosts203.0.113.2 *.example.com みたいに wildcard に対応させたいことが稀にあります。 また、AWS の Application Load Balancer のように IP アドレスの固定されないものに独自ドメインを割り当てて、実際の切り替え前に手元からアクセスしたいこととか。

残念ながら hosts ではできないのですが、mac には /etc/resolver というディレクトリに配置するファイルでドメイン単位で DNS サーバーを指定可能な機能があります。(telepresence を調べていて知った機能です) (man 5 resolver でマニュアルが確認できます)

ということで /etc/resolver/example.com というファイルに次の2行を書いておくと example.com の場合は 127.0.0.1 の 8053/udp に転送してくれます。(tcp にする方法は見つけられなかった)

# /etc/resolver/example.com
nameserver 127.0.0.1
port 8053

8053/udp port で listen する DNS サーバーとして dnsmasq を使います。 Docker で起動させたいところですが、Docker on Lima では UDP の転送ができなかったので Homebrew で dnsmasq をインストールします。

brew install dnsmasq

dnsmasq を foreground で起動させる。設定ファイルを用意しなくてもコマンドラインオプションでレコードも指定できます。

$(brew --prefix)/opt/dnsmasq/sbin/dnsmasq \
  --port=8053 --keep-in-foreground --no-daemon --no-hosts --log-queries \
  --address=/example.com/203.0.113.2 \
  --address=/example.com/203.0.113.129 \
  --host-record=example.com,198.51.100.111 \
  --host-record=host.example.com,198.51.100.222 \
  --cname=alias.example.com,host.example.com \
  --cname=alias2.example.com,www.google.com \

これで ping hogehoge.example.com とすると 203.0.113.129 に ping を送ろうとします。(dig とか host コマンドでは /etc/resolver は参照されません)

curl -k https://alias2.example.com/ すると google の IP アドレスに alias2.example.com としてアクセスに行きます。

--address はサブドメインも含めて一致したら A レコードを返します。同じドメインで複数回指定すれば複数のアドレスを返すことができますが、毎回同じ順序で返します。ラウンドロビンになったりはしない。

--host-record はドメインの完全一致で A レコードを返します。--address よりも優先されます。

--cname は CNAME を返します。

上には書きませんでしたが、 --dns-rr=<name>,<RR-number>,[<data>] というものもあり、任意の DNS レスポンスを返すことができます。HTTPS レコードや CAA などにも使えます。

が、設定はちょっと面倒です。

import dns.rdata
import binascii

name = "cname.example.com"
rclass = dns.rdataclass.IN
rtype = dns.rdatatype.CAA
rdata = 'www.google.com.'

rd = dns.rdata.from_text(rclass, rtype, rdata)
print("--dns-rr={},{},{}".format(name, dns.rdatatype.to_text(rtype),
                                 bytes.decode(binascii.hexlify(rd.to_wire()))))

これで出力される次のような値を引数で指定します。

--dns-rr=cname.example.com,5,0377777706676f6f676c6503636f6d00
Built with Hugo
テーマ StackJimmy によって設計されています。