ＢＩＮＤ９のＡＣＬ設定詳細

# はじめに

[Using Access Control Lists (ACLs) with both addresses and keys](https://kb.isc.org/docs/aa-00723)を参考にＢＩＮＤ９ ＡＣＬの解説をします。
ＤＮＳコンテンツサーバーを自前で運用しなくなったせいか、４年も経過した割には日本語のドキュメントが見当たらず、いい機会なので元記事を参考に詳細を説明したいと思います。

# ＢＩＮＤ９ ＡＣＬとは

アクセスコントロールリスト（Access Control Lists）とあるように、ＢＩＮＤ９が提供するサービスに対して、アクセスを許可・不許可するための機能となります。ＡＣＬそのものの概念は色々なサービス等で登場するわけですが、実装への落とし込みに関するルールについては、それぞれサービス固有の方針に依存するため、そのルールを把握して設定する必要があります。

単純なケースであればテンプレ化されたものを参照すれば済むわけですが、ちょっと凝った設定をしようとすると途端に怪しくなる、典型的な設定の一つではあります。ここではよくあるパターンを参照しつつ、ＢＩＮＤ９ ＡＣＬについて解説します。

ＢＩＮＤ９ ＡＣＬについての要素としては下記の物があります。

- ＩＰｖ４アドレス： `XXX.XXX.XXX.XXX/nnn` 表現
- ＩＰｖ６アドレス： `XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/nnn` 表現
- ビルトインＡＣＬ名： `any`、`none`、`localhost`、`localnets`
- ＡＣＬ名： `acl ＡＣＬ名 { ・・・; };` 句によって定義される名前
- ＴＳＩＧキー： `key キー名` 表現
- ＳＩＧ（０）キー： `key キー名` 表現

ご覧の通りＴＳＩＧキーとＳＩＧ（０）キーの設定は同等になります（要注意、後述）。

## ＢＩＮＤ９ ＡＣＬを解釈するルール

と言うわけで、よくある典型的な例を参考にルールを確認します。

```
allow-update { !{ !10/8; any; }; key example; };
```

ＢＩＮＤ９ ＡＣＬの処理はリストの左から右へ評価されます。これはＡＮＤ結合であるということを意味しません。またネストがあれば、ネスト内の解釈が優先されます。
いわゆるファイアウォールの類いと同じく、ファーストマッチ・ファーストアウトで解釈する必要があります。

この評価において、接続元ＩＰアドレスに対して、以下の３つが判断されます。

- match and accept（一致かつ受諾）
- match and reject（一致かつ拒絶）
- no match（一致しない、なので次のリストに委ねる） 

この意味をよくよく実感すると「` `」（accept、「`!`」が指定されてない）と「`!`」（reject）とのニュアンスが直感とは違うことが理解できます。
例えば `!10/8` という表現が「`10.0.0.0/8` に不一致」という風に見えませんでしたか？ 実際には「`10.0.0.0/8` に一致したら拒絶」という驚愕の事実におののくことになります（自分はそうでした）。

またネスト（`{`･･･`}`）されていた場合、ネスト内の結果について下記の２つの評価を行い（ここでは一致・不一致の直感と一致する）、その結果に対して上記３つの判断が実施されます。ネストの中と外でも解釈が違うという罠が用意されていることが分かります。

- match（一致）
- no match（不一致） 

この例における評価の順番は `!10/8` → `{ !10/8; any; }` → `!{ !10/8; any; }` → `!{ !10/8; any; }; key example;` となります。

1. `!10/8`： `10.0.0.0/8` に一致する → match and reject
1. `!10/8; any;`： `10.0.0.0/8` に一致しない → no match なので `any` を解釈（常に一致） → match and accept
1. `{ !10/8; any; }`： ネスト内で match and reject または match and accept が評価されたので、ネストの結果としては no match または match という評価になる。
1. `!{ !10/8; any; }`： no match の評価に対して no match という結論が、match の評価に対して match and reject の結論となる。
1. この時点で `10.0.0.0/8` 以外のアクセスが拒絶、`10.0.0.0/8` からのアクセスは次の評価に委ねられることになる。
1. 最後に `key example` でＴＳＩＧまたはＳＩＧ（０）の評価（署名されているかどうか）が実施される。
1. 署名が成功すれば match and accept で、失敗すれば match and reject となる。

このように丁寧に読み解くと `!{ !10/8; any; }` と `key example` の関係は、単純なＡＮＤの関係に無いことがわかります。が、実際の所、イディオムとしてみるならＡＮＤの関係にあると（結果的に）解釈しても良いです（後述）。

なおネストの中で更にネストすることが可能なので、その場合は上記２評価（match または no match）を繰り返し評価します。 
また `!10/8; any;` は `!10/8;` と省略できます。`10.0.0.0/8` に一致しない場合は常に match and accept と解釈されるからです。
しかしその書き方はオススメしません。`any` が無い場合の（人間側の）解釈ミスによる事故を引き起こす可能性があります。
機械に優しくても人に優しくない表現となります。とは言え、当たり前に存在する表現ではあるので「こういう意味だ」というのは意識しておいた方が安全です。

## ＢＩＮＤ９ ＡＣＬの典型的評価パターン

[Using Access Control Lists (ACLs) with both addresses and keys](https://kb.isc.org/docs/aa-00723)で紹介されている、接続元アドレスＡに対する以下の表現と効果についてまとめます。

```
{     Ａ;    Ｂ; }   == Ａは許可、直ちに受諾（match and accept）
{  {  Ａ; }; Ｂ; }   == Ａは許可、直ちに受諾（上記と等価表現）
{    !Ａ;    Ｂ; }   == Ａは不許可、直ちに拒絶（match and reject）
{ !{  Ａ; }; Ｂ; }   == Ａは不許可、直ちに拒絶（上記と等価表現）
{  { !Ａ; }; Ｂ; }   == Ａは一致したが不許可、Ｂの条件次第 ※後述
{ !{ !Ａ; }; Ｂ; }   == Ａは一致したが不許可、Ｂの条件次第 ※後述
```

最後の二行が肝で、単純なケースでは両者は一致します。が、Ａがネストにより他の接続元ＩＰアドレスを評価した場合に振る舞いが変わります。

```
{  { !Ａ; any; }; Ｂ; }  == Ａ以外のアドレスは許可。ただしＡがＢの条件を満たす場合は許可。
                            ブール代数表現: ((not Ａ) or (Ａ and Ｂ))
{ !{ !Ａ; any; }; Ｂ; }  == Ａ以外のアドレスは不許可。ただしＡがＢも条件を満たす場合は許可。
                            ブール代数表現: (Ａ and Ｂ)
```

この結果を見る限り、前者のパターンを使うケースは無いかと思います。

# ＴＳＩＧキーとＳＩＧ（０）キー

ＴＳＩＧキーとＳＩＧ（０）キーは、クエリーを保護するための署名の仕組みが共有鍵型（ＨＭＡＣ）か公開鍵型（ＨＡＳＨ）かの違いになります。
もちろんそのための内部の仕組みは全然違いますが、`key キー名` という指定において、両者の表現に違いは無いです。

## ＴＳＩＧキーの設定

下記のようにしてＴＳＩＧキーを発行します。発行者はサーバー側でもクライアント側でもかまいません。いずれにせよ同一のモノとして両者で共有する必要があります。
またどこに置くのかとか、ＴＳＩＧキー名の命名規則は…については今回は省略します。

```console
$ sudo tsig-keygen -a hmac-sha256 ＴＳＩＧキー名 > ＴＳＩＧキー.key
$ sudo chmod 0400 ＴＳＩＧキー.key
```

実際の利用の場合は、下記のようにして参照／設定することで機能します。もちろんサーバー側では更に、ＡＣＬでＴＳＩＧキーを指定してやる必要があります。

- クライアントサイド： `nsupdate -k ＴＳＩＧキー.key` として実行する
- サーバーサイド（設定）： `named.conf` 中に `include "ＴＳＩＧキー.key";` を追加する
- サーバーサイド（サーバーユース）： `named.conf` 中に `key ＴＳＩＧキー名` という指定を加える
- サーバーサイド（クライアントユース）： `named.conf` 中に `key ＴＳＩＧキー名` という指定を加える

ファイルの内容は下記のように、ＫＥＹ句そのままの内容が記録されています。

```
key "ＴＳＩＧキー名" {
        algorithm hmac-sha256;
        secret "loWM+WsnKvXItLz6dT+Gz5m/VOTX6McEnzdIUrx/JlI=";
};
```

## ＳＩＧ（０）キーの設定

下記のようにしてＳＩＧ（０）キーを発行します。どこに置くのか…については省略するとして、ＳＩＧ（０）キー名の命名規則は対象となるドメイン名（最後に `.` が必須）を指定します。なおキー名の最後に `.` が無い場合は自動的に補完されます。

```console
$ sudo dnssec-keygen -a ED25519 -n HOST -T KEY ＳＩＧ（０）キー名
```

この時発行される２つのファイルを確認します。
- `KＳＩＧ（０）キー名.+アルゴリズム名（数値表現）+キーＩＤ.key`
- `KＳＩＧ（０）キー名.+アルゴリズム名（数値表現）+キーＩＤ.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` ゾーンに登録します。その際ＴＴＬは０を設定します。

```
exmaple.jp. 0 IN KEY 512 3 15 kln+meCE/bhlB9OPbtFbKoNJZkocnaFNyT5EcFu97t0=
```

実際の利用の場合は、下記のようにして参照／設定することで機能します。

- クライアントサイド： `nsupdate -k Kexample.jp.+015+04398.private` として実行する
- サーバーサイド（設定）： 指定無し（指定されたキー名のＫＥＹリソースレコードを参照解決するため）
- サーバーサイド（サーバーユース）： `named.conf` 中に `key example.jp.` という指定を加える
- サーバーサイド（クライアントユース）： 指定不可能

ファイルの内容は下記のように、公開鍵側はＤＮＳのＫＥＹ句そのままの内容が記録されています（ＴＴＬは未記載）。

```Kexample.jp.+015+04398.key: 公開鍵
exmaple.jp. IN KEY 512 3 15 kln+meCE/bhlB9OPbtFbKoNJZkocnaFNyT5EcFu97t0=
```

秘密鍵側は下記の通りとなります。

```Kexmaple.jp.+015+30364.private: 秘密鍵
Private-key-format: v1.3
Algorithm: 15 (ED25519)
PrivateKey: Tcx7O7aHjl+jyraSmP4rB9W1EP0n32Vb9O6JiNgpZVY=
Created: 20230401000000
Publish: 20230401000000
Activate: 20230401000000
```

残念なことにセカンダリＤＮＳとしての用途で本機能は使えません。
その代わり `nsupdate` を使うケース（レッツエンクリプトなど）では、サーバー側との鍵ファイルの共有が不要な上、設定ファイルの変更を行わなくて済むメリットがあります。サーバー側にはレコード追加して欲しいという依頼になるため、クライアント側主体で設定できる点で、ＴＳＩＧキー発行には無いアドバンテージがあります。 

繰り返しになりますが、セカンダリＤＮＳとしての用途（クライアントユース）では使用不可能なのであって、サーバーとしての用途での使用（`allow-transfer` などの設定）については問題はありません。

# よくある質問とその答え

## Ｑ．この説明って重要なんですか？
Ａ．大抵の場合、テンプレ範疇内で収まるのであんまり重要では無いです。テンプレ通りなら知らんでもいいくらい。
元資料が作成された当時は、誤解された設定している人が居たので発表されたように記憶してるのですが、何か特殊な運用している人向けだった記憶があるので、あまり自分には関係無いな、と思った記憶があります。

記憶、記憶でいいのか？については、自分は困ってないので、気になる人は調べてください。フィードバックがあれば修正します。

## Ｑ．じゃあなんで書いたの？
Ａ．久しぶりに設定見直したら「？？」となりまして、数年も経つと忘れるもんだな…と。ついでにＳＩＧ（０）の知見も得たので書いておこうかと。ＳＩＧ（０）非常に便利。
ＳＩＧ（０）便利なんだけど、運用例を見ないため、どう設定すればいいのか調べるのに苦労したです。

## Ｑ．`!{ !10/8; any; }; key example;` なんてめんどくさいから `!{ !10/8; }; key example;` でいいじゃん。
Ａ．その上更に、ネストを外して `!!10/8; key example;` からの `10/8; key example;` と、なんちゃって最適化しない自信があればどうぞ。
こうなってしまった恐怖（署名による保護が無い）については上記説明を読めば分かるよね？

## Ｑ．１ＩＰ＝１キー設定したい場合、どうすればいいの？
Ｑ．本例のように `!{ !10/8; any; }; key example;` と書いた場合、この時点で処理が確定されるので、この後に設定の追加ができません（解釈されません）。
よってネストで保護してやれば（accept or no match になって）追加できます（下記例参照）。

```
allow-update {
    { !{ !10/8;       any; }; key example1; };
    { !{ !192.168/16; any; }; key example2; };
};
```

## Ｑ．キーの設定に対して色々とパラメーター（アルゴリズム）あるけど、知っておかないといけないのでは？
Ａ．互換性のために、古いアルゴリズムを使用したいユースケース以外では不要です。今時使うなら使用するパターンは一択になっています。
`hmac-sha256` については、次はＳＨＡ３世代がやってくるので、無理にビット数（`hmac-sha384`、`hmac-sha512`）上げなくても良し。
`ed25519` の次世代はまだですが `ed448` にすれば自動的にビット数が上がるので……。

## Ｑ．ＳＩＧ（０）が便利なのは分かったけど、複数台でＳＩＧ（０）設定した場合のレコードの登録方法はどうなってるの？
Ａ．できた分だけレコードを追加します。ＴＸＴレコードのように、マージして１レコードに収める必要はありません。というか、そのような想定はしてないです。
また区別できなくても問題ありません。どのレコードか適当に探してくれます。なのでクライアント間でＳＩＧ（０）キーを共有する必要はありません。むしろ共有してはいけません。

## Ｑ．うちのＤＮＳサーバーでＫＥＹレコードが追加できません！
Ａ．そういう実装もありますね。ＫＥＹレコードに対応していることとＳＩＧ（０）に対応していることは別な点も注意。
そもそもＳＩＧ（０）サポートしてるのかどうかとか確認しないといけないことが…。ＴＳＩＧよりもサポートされてないことは確かで、そもそも `nsupdate` コマンドが通用するかも怪しいです。別にＡＰＩが用意されてるケースもあるので、そちらを調べてもらう必要があるかと思います。
いずれにせよＢＩＮＤ９の範囲外の話となるため、ここでは取り扱いません。

## Ｑ．今ひとつＫＥＹレコードというヤツがピンときません。設定例を教えてください。
Ａ．`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` でないと行けないということは無いです（後述）。

## Ｑ．ＳＩＧ（０）で指定するキー名はゾーンでないといけないのですか？またゾーンをまたいでもいいですか？
Ａ．ゾーンでないといけないことないです。またゾーンをまたいでも問題ありません。
たとえば、`example.jp` ゾーンに対して `allow-update` で許可したいケースで、`key.example.jp` や `key.example.com` レコードで、ＫＥＹレコードを参照するといった指定が可能です。
アクセスがあった瞬間に指定されたキーが解決できることが必要になります。

```
allow-update { !{ !10/8; any; }; key key.example.com.; };
```

# 参考文献

検索して出てくる資料も悪くないですが、本家ドキュメント（英語ではありますが）を参照の上で設定しましょう。
参考資料では想定が甘かったり、抜けていたり、変っていたり、間違っているケースが多々見られます。
正しい運用は正しい設定から始まります。

だが本家ドキュメントのＳＩＧ（０）、お前はダメだ。「あります」程度しか書いてないじゃないか！ orz

- [Using Access Control Lists (ACLs) with both addresses and keys](https://kb.isc.org/docs/aa-00723)
- [7.1. Access Control Lists](https://bind9.readthedocs.io/en/latest/chapter7.html#access-control-lists)
- [Securing dynamic DNS updates (DDNS) with SIG(0)](https://jpmens.net/2010/12/01/securing-dynamic-dns-updates-ddns-with-sig0/)
- [ＤＮＳのレプリケーション設定と運用](https://wiki.ninth-nine.com/bind9/ＤＮＳのレプリケーション設定と運用)
- [DNS BIND acl clause](https://www.zytrax.com/books/dns/ch7/acl.html)
- [BINDの大切な設定の話](https://tech.synapse.jp/entry/2020/07/15/130000)