はじめに
Using Access Control Lists (ACLs) with both addresses and keysを参考にBIND9 ACLの解説をします。
DNSコンテンツサーバーを自前で運用しなくなったせいか、4年も経過した割には日本語のドキュメントが見当たらず、いい機会なので元記事を参考に詳細を説明したいと思います。
BIND9 ACLとは
アクセスコントロールリスト(Access Control Lists)とあるように、BIND9が提供するサービスに対して、アクセスを許可・不許可するための機能となります。ACLそのものの概念は色々なサービス等で登場するわけですが、実装への落とし込みに関するルールについては、それぞれサービス固有の方針に依存するため、そのルールを把握して設定する必要があります。
単純なケースであればテンプレ化されたものを参照すれば済むわけですが、ちょっと凝った設定をしようとすると途端に怪しくなる、典型的な設定の一つではあります。ここではよくあるパターンを参照しつつ、BIND9 ACLについて解説します。
BIND9 ACLについての要素としては下記の物があります。
- IPv4アドレス:
XXX.XXX.XXX.XXX/nnn
表現 - IPv6アドレス:
XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/nnn
表現 - ビルトインACL名:
any
、none
、localhost
、localnets
- ACL名:
acl ACL名 { ・・・; };
句によって定義される名前 - TSIGキー:
key キー名
表現 - SIG(0)キー:
key キー名
表現
ご覧の通りTSIGキーとSIG(0)キーの設定は同等になります(要注意、後述)。
BIND9 ACLを解釈するルール
と言うわけで、よくある典型的な例を参考にルールを確認します。
allow-update { !{ !10/8; any; }; key example; };
BIND9 ACLの処理はリストの左から右へ評価されます。これはAND結合であるということを意味しません。またネストがあれば、ネスト内の解釈が優先されます。
いわゆるファイアウォールの類いと同じく、ファーストマッチ・ファーストアウトで解釈する必要があります。
この評価において、接続元IPアドレスに対して、以下の3つが判断されます。
- match and accept(一致かつ受諾)
- match and reject(一致かつ拒絶)
- no match(一致しない、なので次のリストに委ねる)
この意味をよくよく実感すると「
」(accept、「!
」が指定されてない)と「!
」(reject)とのニュアンスが直感とは違うことが理解できます。
例えば !10/8
という表現が「10.0.0.0/8
に不一致」という風に見えませんでしたか? 実際には「10.0.0.0/8
に一致したら拒絶」という驚愕の事実におののくことになります(自分はそうでした)。
またネスト({
・・・}
)されていた場合、ネスト内の結果について下記の2つの評価を行い(ここでは一致・不一致の直感と一致する)、その結果に対して上記3つの判断が実施されます。ネストの中と外でも解釈が違うという罠が用意されていることが分かります。
- match(一致)
- no match(不一致)
この例における評価の順番は !10/8
→ { !10/8; any; }
→ !{ !10/8; any; }
→ !{ !10/8; any; }; key example;
となります。
-
!10/8
:10.0.0.0/8
に一致する → match and reject -
!10/8; any;
:10.0.0.0/8
に一致しない → no match なのでany
を解釈(常に一致) → match and accept -
{ !10/8; any; }
: ネスト内で match and reject または match and accept が評価されたので、ネストの結果としては no match または match という評価になる。 -
!{ !10/8; any; }
: no match の評価に対して no match という結論が、match の評価に対して match and reject の結論となる。 - この時点で
10.0.0.0/8
以外のアクセスが拒絶、10.0.0.0/8
からのアクセスは次の評価に委ねられることになる。 - 最後に
key example
でTSIGまたはSIG(0)の評価(署名されているかどうか)が実施される。 - 署名が成功すれば match and accept で、失敗すれば match and reject となる。
このように丁寧に読み解くと !{ !10/8; any; }
と key example
の関係は、単純なANDの関係に無いことがわかります。が、実際の所、イディオムとしてみるならANDの関係にあると(結果的に)解釈しても良いです(後述)。
なおネストの中で更にネストすることが可能なので、その場合は上記2評価(match または no match)を繰り返し評価します。
また !10/8; any;
は !10/8;
と省略できます。10.0.0.0/8
に一致しない場合は常に match and accept と解釈されるからです。
しかしその書き方はオススメしません。any
が無い場合の(人間側の)解釈ミスによる事故を引き起こす可能性があります。
機械に優しくても人に優しくない表現となります。とは言え、当たり前に存在する表現ではあるので「こういう意味だ」というのは意識しておいた方が安全です。
BIND9 ACLの典型的評価パターン
Using Access Control Lists (ACLs) with both addresses and keysで紹介されている、接続元アドレスAに対する以下の表現と効果についてまとめます。
{ A; B; } == Aは許可、直ちに受諾(match and accept)
{ { A; }; B; } == Aは許可、直ちに受諾(上記と等価表現)
{ !A; B; } == Aは不許可、直ちに拒絶(match and reject)
{ !{ A; }; B; } == Aは不許可、直ちに拒絶(上記と等価表現)
{ { !A; }; B; } == Aは一致したが不許可、Bの条件次第 ※後述
{ !{ !A; }; B; } == Aは一致したが不許可、Bの条件次第 ※後述
最後の二行が肝で、単純なケースでは両者は一致します。が、Aがネストにより他の接続元IPアドレスを評価した場合に振る舞いが変わります。
{ { !A; any; }; B; } == A以外のアドレスは許可。ただしAがBの条件を満たす場合は許可。
ブール代数表現: ((not A) or (A and B))
{ !{ !A; any; }; B; } == A以外のアドレスは不許可。ただしAがBも条件を満たす場合は許可。
ブール代数表現: (A and B)
この結果を見る限り、前者のパターンを使うケースは無いかと思います。
TSIGキーとSIG(0)キー
TSIGキーとSIG(0)キーは、クエリーを保護するための署名の仕組みが共有鍵型(HMAC)か公開鍵型(HASH)かの違いになります。
もちろんそのための内部の仕組みは全然違いますが、key キー名
という指定において、両者の表現に違いは無いです。
TSIGキーの設定
下記のようにしてTSIGキーを発行します。発行者はサーバー側でもクライアント側でもかまいません。いずれにせよ同一のモノとして両者で共有する必要があります。
またどこに置くのかとか、TSIGキー名の命名規則は…については今回は省略します。
$ sudo tsig-keygen -a hmac-sha256 TSIGキー名 > TSIGキー.key
$ sudo chmod 0400 TSIGキー.key
実際の利用の場合は、下記のようにして参照/設定することで機能します。もちろんサーバー側では更に、ACLでTSIGキーを指定してやる必要があります。
- クライアントサイド:
nsupdate -k TSIGキー.key
として実行する - サーバーサイド(設定):
named.conf
中にinclude "TSIGキー.key";
を追加する - サーバーサイド(サーバーユース):
named.conf
中にkey TSIGキー名
という指定を加える - サーバーサイド(クライアントユース):
named.conf
中にkey TSIGキー名
という指定を加える
ファイルの内容は下記のように、KEY句そのままの内容が記録されています。
key "TSIGキー名" {
algorithm hmac-sha256;
secret "loWM+WsnKvXItLz6dT+Gz5m/VOTX6McEnzdIUrx/JlI=";
};
SIG(0)キーの設定
下記のようにしてSIG(0)キーを発行します。どこに置くのか…については省略するとして、SIG(0)キー名の命名規則は対象となるドメイン名(最後に .
が必須)を指定します。なおキー名の最後に .
が無い場合は自動的に補完されます。
$ sudo dnssec-keygen -a ED25519 -n HOST -T KEY SIG(0)キー名
この時発行される2つのファイルを確認します。
KSIG(0)キー名.+アルゴリズム名(数値表現)+キーID.key
KSIG(0)キー名.+アルゴリズム名(数値表現)+キーID.private
これではわかりにくいこともあり example.jp
ドメインを例に、上記内容を具体的に説明します。
-
dnssec-keygen -a ED25519 -n HOST -T KEY example.jp
コマンドを実行Kexample.jp.+015+04398.key
Kexample.jp.+015+04398.private
このうち .key ファイルを exmaple.jp
ゾーンに登録します。その際TTLは0を設定します。
exmaple.jp. 0 IN KEY 512 3 15 kln+meCE/bhlB9OPbtFbKoNJZkocnaFNyT5EcFu97t0=
実際の利用の場合は、下記のようにして参照/設定することで機能します。
- クライアントサイド:
nsupdate -k Kexample.jp.+015+04398.private
として実行する - サーバーサイド(設定): 指定無し(指定されたキー名のKEYリソースレコードを参照解決するため)
- サーバーサイド(サーバーユース):
named.conf
中にkey example.jp.
という指定を加える - サーバーサイド(クライアントユース): 指定不可能
ファイルの内容は下記のように、公開鍵側はDNSのKEY句そのままの内容が記録されています(TTLは未記載)。
exmaple.jp. IN KEY 512 3 15 kln+meCE/bhlB9OPbtFbKoNJZkocnaFNyT5EcFu97t0=
秘密鍵側は下記の通りとなります。
Private-key-format: v1.3
Algorithm: 15 (ED25519)
PrivateKey: Tcx7O7aHjl+jyraSmP4rB9W1EP0n32Vb9O6JiNgpZVY=
Created: 20230401000000
Publish: 20230401000000
Activate: 20230401000000
残念なことにセカンダリDNSとしての用途で本機能は使えません。
その代わり nsupdate
を使うケース(レッツエンクリプトなど)では、サーバー側との鍵ファイルの共有が不要な上、設定ファイルの変更を行わなくて済むメリットがあります。サーバー側にはレコード追加して欲しいという依頼になるため、クライアント側主体で設定できる点で、TSIGキー発行には無いアドバンテージがあります。
繰り返しになりますが、セカンダリDNSとしての用途(クライアントユース)では使用不可能なのであって、サーバーとしての用途での使用(allow-transfer
などの設定)については問題はありません。
よくある質問とその答え
Q.この説明って重要なんですか?
A.大抵の場合、テンプレ範疇内で収まるのであんまり重要では無いです。テンプレ通りなら知らんでもいいくらい。
元資料が作成された当時は、誤解された設定している人が居たので発表されたように記憶してるのですが、何か特殊な運用している人向けだった記憶があるので、あまり自分には関係無いな、と思った記憶があります。
記憶、記憶でいいのか?については、自分は困ってないので、気になる人は調べてください。フィードバックがあれば修正します。
Q.じゃあなんで書いたの?
A.久しぶりに設定見直したら「??」となりまして、数年も経つと忘れるもんだな…と。ついでにSIG(0)の知見も得たので書いておこうかと。SIG(0)非常に便利。
SIG(0)便利なんだけど、運用例を見ないため、どう設定すればいいのか調べるのに苦労したです。
Q.!{ !10/8; any; }; key example;
なんてめんどくさいから !{ !10/8; }; key example;
でいいじゃん。
A.その上更に、ネストを外して !!10/8; key example;
からの 10/8; key example;
と、なんちゃって最適化しない自信があればどうぞ。
こうなってしまった恐怖(署名による保護が無い)については上記説明を読めば分かるよね?
Q.1IP=1キー設定したい場合、どうすればいいの?
Q.本例のように !{ !10/8; any; }; key example;
と書いた場合、この時点で処理が確定されるので、この後に設定の追加ができません(解釈されません)。
よってネストで保護してやれば(accept or no match になって)追加できます(下記例参照)。
allow-update {
{ !{ !10/8; any; }; key example1; };
{ !{ !192.168/16; any; }; key example2; };
};
Q.キーの設定に対して色々とパラメーター(アルゴリズム)あるけど、知っておかないといけないのでは?
A.互換性のために、古いアルゴリズムを使用したいユースケース以外では不要です。今時使うなら使用するパターンは一択になっています。
hmac-sha256
については、次はSHA3世代がやってくるので、無理にビット数(hmac-sha384
、hmac-sha512
)上げなくても良し。
ed25519
の次世代はまだですが ed448
にすれば自動的にビット数が上がるので……。
Q.SIG(0)が便利なのは分かったけど、複数台でSIG(0)設定した場合のレコードの登録方法はどうなってるの?
A.できた分だけレコードを追加します。TXTレコードのように、マージして1レコードに収める必要はありません。というか、そのような想定はしてないです。
また区別できなくても問題ありません。どのレコードか適当に探してくれます。なのでクライアント間でSIG(0)キーを共有する必要はありません。むしろ共有してはいけません。
Q.うちのDNSサーバーでKEYレコードが追加できません!
A.そういう実装もありますね。KEYレコードに対応していることとSIG(0)に対応していることは別な点も注意。
そもそもSIG(0)サポートしてるのかどうかとか確認しないといけないことが…。TSIGよりもサポートされてないことは確かで、そもそも nsupdate
コマンドが通用するかも怪しいです。別にAPIが用意されてるケースもあるので、そちらを調べてもらう必要があるかと思います。
いずれにせよBIND9の範囲外の話となるため、ここでは取り扱いません。
Q.今ひとつKEYレコードというヤツがピンときません。設定例を教えてください。
A.example.jp
ゾーンに対して設定した例を示します。
$TTL 3600
@ IN SOA ns0.example.jp. hostmaster.example.jp. 2022123101 86400 3600 86400 3600
IN NS ns1.example.jp.
IN NS ns2.example.jp.
0 IN KEY 512 3 15 qPMwWuKG2+bgSJa26d17NbrLYJCs9Z5et76SxN5SCMo=
0 IN KEY 512 3 15 4zm7RenuvxysxYa2wlNFImgNPzW1jjsw8KbU5uQctHs=
:
厳密には example.jp
ゾーンのための設定だから、example.jp
でないと行けないということは無いです(後述)。
Q.SIG(0)で指定するキー名はゾーンでないといけないのですか?またゾーンをまたいでもいいですか?
A.ゾーンでないといけないことないです。またゾーンをまたいでも問題ありません。
たとえば、example.jp
ゾーンに対して allow-update
で許可したいケースで、key.example.jp
や key.example.com
レコードで、KEYレコードを参照するといった指定が可能です。
アクセスがあった瞬間に指定されたキーが解決できることが必要になります。
allow-update { !{ !10/8; any; }; key key.example.com.; };
参考文献
検索して出てくる資料も悪くないですが、本家ドキュメント(英語ではありますが)を参照の上で設定しましょう。
参考資料では想定が甘かったり、抜けていたり、変っていたり、間違っているケースが多々見られます。
正しい運用は正しい設定から始まります。
だが本家ドキュメントのSIG(0)、お前はダメだ。「あります」程度しか書いてないじゃないか! orz