【AlmaLinux 9.5 + BIND】VirtualBox仮想環境で2ドメイン対応のDNSサーバ構築（正引き・逆引き）

## はじめに
本記事では、VirtualBox上の仮想環境でAlmaLinux 9.5を使用し、2つのドメイン（example.local、example.sub.local）に対応したDNSサーバ（正引き・逆引き対応）を構築する手順をまとめます。
インフラエンジニア研修で実際に構築した内容をベースにしており、以下のような方を対象としています。
- DNSの正引き／逆引きを自分で設定してみたい方
- BINDの基本的な使い方を知りたい方

※本記事の設定内容はすべて研修用の仮想環境上のものであり、実際の業務環境とは異なります。

## 仮想環境の構成
| 仮想マシン名     | ホスト名       | FQDN（完全修飾ドメイン名）          | 役割          | ドメイン名             | IPアドレス         |
| ---------- | ---------- | ------------------------ | ----------- | ----------------- | -------------- |
| dns-server | bindserver | bindserver.example.local | DNSサーバ      | example.local     | 192.168.56.101 |
| SV01       | AAA        | AAA.example.local        | クライアント（検証用） | example.local     | 192.168.56.102 |
| SV02       | BBB        | BBB.example.sub.local    | クライアント（検証用） | example.sub.local | 192.168.56.103 |
| SV03       | CCC        | CCC.example.sub.local    | クライアント（検証用） | example.sub.local | 192.168.56.104 |

- VirtualBox: 7.1.8-168469-Win
- OS: AlmaLinux 9.5 (x86_64)
- DNSソフト: BIND

## BINDのインストール
```bash
[root@vbox ~]# dnf install bind bind-chroot
```

## /etc/named.conf の設定
```bash
[root@vbox ~]# vi /etc/named.conf
```

```conf
options {
        listen-on port 53 { 127.0.0.1; 192.168.56.101; }; ←DNSサーバのIPアドレスを追加
        listen-on-v6 port 53 { ::1; };
        directory       "/var/named";
        dump-file       "/var/named/data/cache_dump.db";
        statistics-file "/var/named/data/named_stats.txt";
        memstatistics-file "/var/named/data/named_mem_stats.txt";
        secroots-file   "/var/named/data/named.secroots";
        recursing-file  "/var/named/data/named.recursing";
        allow-query     { any; }; ←localhostをanyに変更

        recursion no; ← yesをnoに変更

        dnssec-validation yes;

        managed-keys-directory "/var/named/dynamic";
        geoip-directory "/usr/share/GeoIP";

        pid-file "/run/named/named.pid";
        session-keyfile "/run/named/session.key";

        include "/etc/crypto-policies/back-ends/bind.config";
};

logging {
        channel default_debug {
                file "data/named.run";
                severity dynamic;
        };
};

zone "." IN {
        type hint;
        file "named.ca";
};

↓example.laocalゾーンを指定
zone "example.local" IN { 
        type master;
        file "example.local.zone"; ←example.local用のゾーン定義ファイル名を指定
        allow-update { none; };
};

zone "example.sub.local" IN {
        type master;
        file "example.sub.local.zone";
        allow-update { none; };
};

zone "56.168.192.in-addr.arpa" IN {
        type master;
        file "example.local.rev";
        allow-update { none; };
};
```
### allow-query { any; }; に変更する理由
デフォルトではlocalhostとなっているため、127.0.0.1からの問い合わせ（ローカルホスト）以外は無視されます。
つまり、他のクライアント（SV01~SV02）からDNS問い合わせが来てもDNSサーバが応答しません。
そこで、ネットワーク内のすべてのホストからのDNS問い合わせを許可するためにanyに変更します。
セキュリティ的には、特定のIPアドレスやサブネットに制限（allow-query { 192.168.56.0/24; };）するのがより安全だと思います。

### recursion no;にする理由
再帰問い合わせ（recursion）とは、DNSサーバが自分の知らない名前について他のDNSサーバに問い合わせて結果を返す動作のことです。
再帰問い合わせを有効にすると、外部のDNSキャッシュサーバのように振る舞ってしまい、DDoS攻撃などに利用されるリスクがあります。
構築しているDNSは「権威DNSサーバ（Authoritative DNS）」として、自分のゾーン（example.local など）だけを返せばよいので、再帰問い合わせは不要となります。

## ゾーンファイルの作成
```bash
[root@vbox ~]# cp -p /var/named.empty /var/named/example.local.zone
[root@vbox ~]# cp -p /var/named.empty /var/named/example.local.sub.zone
[root@vbox ~]# cp -p /var/named.empty /var/named/example.local.rev
```

### /var/named/example.local.zone
```conf
$TTL 3H
@       IN SOA  bindserver.example.local. root.example.local. (
                                        0       ; serial
                                        1D      ; refresh
                                        1H      ; retry
                                        1W      ; expire
                                        3H )    ; minimum

        NS      bindserver.example.local.

bindserver      A       192.168.56.101
AAA             A       192.168.56.102
```

### /var/named/example.sub.local.zone
```conf
$TTL 3H
@       IN SOA  bindserver.example.sub.local. root.example.sub.local. (
                                        0       ; serial
                                        1D      ; refresh
                                        1H      ; retry
                                        1W      ; expire
                                        3H )    ; minimum

        NS      bindserver.example.sub.local.

bindserver      A       192.168.56.101
BBB             A       192.168.56.103
CCC             A       192.168.56.104
```

### /var/named/example.local.rev
```conf
$TTL 3H
@       IN SOA  bindserver.example.local. root.example.local. (
                                        0       ; serial
                                        1D      ; refresh
                                        1H      ; retry
                                        1W      ; expire
                                        3H )    ; minimum

        IN NS   bindserver.example.local.

101     IN PTR  bindserver.example.local.
102     IN PTR  AAA.example.local.
103     IN PTR  BBB.example.sub.local.
104     IN PTR  CCC.example.sub.local.
```

## 設定ファイルの書式確認
```bash
[root@vbox ~]# named-checkconf
```

## ゾーンファイルの書式確認
```bash
[root@vbox ~]# named-checkzone example.local. /var/named/ example.local.zone
zone example.local/IN: loaded serial 0
OK
```
## BINDの起動
```bash
[root@vbox ~]# systemctl start named-chroot
```

## 自動起動の設定
```bash
[root@vbox ~]# systemctl enable named-chroot
```

## ファイアウォールの設定
```bash
[root@vbox ~]#  firewall-cmd --add-service=dns --zone=public --permanent
success
[root@vbox ~]# firewall-cmd --reload
success
```

## 参照するDNSサーバの設定
参照するDNSサーバは/etc/resolv.confに記述されています。
/etc/resolv.confにDNSサーバのIPアドレス（今回は192.168.56.101)が設定されていることを確認します。
```bash
[root@vbox ~]# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 192.168.56.101
```

## NATネットワークの停止
NATネットワークでは、ホストマシンがゲートウェイとなって外部と通信ができるようになりますが、ほかの仮想マシンと直接通信しづらい構成です。
DNSなど仮想マシン間の通信（SV01～SV03 ⇔ bindserver）が必要な構成では、内部ネットワーク（Internal Network)やホストオンリーアダプタ（Host-Onry）を使った方が適しています。

```bash
[root@vbox ~]# nmcli connection down enp0s3
```

## 管理対象サーバ(AAA,BBB,CCC)の設定
エディタで/etc/resolv.confを修正すると、設定は即時有効となります。
/etc/resolv.confはNetworkManagerが自動的に生成しており、システムの再起動などによって再生成されるとエディタでの変更は破棄されます。
DNS設定を恒久化させたい場合はNetworkManagerファイルの修正をする必要がある。
/etc/NetworkManager/system-connections/イーサネットファイルにあり、ここにDNS設定を書き加えればよい。
```bash
[root@vbox ~]# vi /etc/NetworkManager/system-connections/enp0s3.nmconnection
```

```conf
[connection]
id=enp0s3
uuid=d9f0a0bb-1e76-367f-a7f4-ff761c0019f3
type=ethernet
autoconnect-priority=-999
interface-name=enp0s3
timestamp=1747318746

[ethernet]

[ipv4]
method=auto
dns=192.168.56.101 ←　参照するDNSサーバのIPアドレスを記載

[ipv6]
addr-gen-mode=eui64
method=auto

[proxy]
```

## 動作確認
検証用サーバ（SV01～SV03）からDNSサーバに問い合わせて結果が返ってくれば正常に動作していることが確認できます。
### 正引き確認
```bash
[user@vbox ~]$ dig AAA.example.local

; <<>> DiG 9.16.23-RH <<>> AAA.example.local
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39081
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: 99f0f8ead211963d010000006828327aefffb40e8f1c79a9 (good)
;; QUESTION SECTION:
;AAA.example.local.             IN      A

;; ANSWER SECTION:
AAA.example.local.      10800   IN      A       192.168.56.102

;; Query time: 1 msec
;; SERVER: 192.168.56.101#53(192.168.56.101) ←　参照したDNSサーバのIPアドレス
;; WHEN: Sat May 17 15:53:47 JST 2025
;; MSG SIZE  rcvd: 90
```
### 逆引き確認
```bash
[user@vbox ~]$ dig -x 192.168.56.102

; <<>> DiG 9.16.23-RH <<>> -x 192.168.56.102
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 17674
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: ffdd59031ffde21001000000682841c2195c22b482b5d47d (good)
;; QUESTION SECTION:
;102.56.168.192.in-addr.arpa.   IN      PTR

;; ANSWER SECTION:
102.56.168.192.in-addr.arpa. 10800 IN   PTR     AAA.example.local.

;; Query time: 2 msec
;; SERVER: 192.168.56.101#53(192.168.56.101)
;; WHEN: Sat May 17 16:58:58 JST 2025
;; MSG SIZE  rcvd: 115
```

## トラブルシューティング
- namedの起動確認
```bash
systemctl status named
```
- 構文チェック
```bash
named-checkconf
nemed-checkzone example.local /var/named/example.local.zone
```

- SELinuxで失敗する場合
```bash
setenforce 0
```

## まとめ
この構築を通してまなべたこと
- BINDの基本設定とゾーンファイルの作成方法
- ドメイン名の階層構造（example.local →　example.sub.local)
- 正引き・逆引きの理解と構築手順
- ネットワーク構成のトラブル対応

## 今後の応用
- DHCPと連携した動的DNSの構築
- 内部DNS＋キャッシュDNSの構成
- Active Directoryとの連携に発展

## 参考
- [Linuxサーバ構築標準教科書（LinuCレベル2対応） - LPI-Japan公式](https://linuc.org/textbooks/server/)
　LinuCレベル2の学習教材として公開されているPDF形式の教科書です。DNSサーバ（BIND）の構築手順を含め、Web・メールなど主要なサーバ構築方法が実習形式で解説されています。
　本記事のDNSサーバ構築はこの教科書を参考に実施しました。

## ご意見・ご指摘お待ちしています！
本記事はインフラエンジニア研修の一環として初めてDNSサーバ構築に取り組んだ際の記録です。
初心者のため、構成や記述に誤り・非効率な点などがあるかもしれません。

「ここはこうした方がいい」「この部分の理解が違ってるかも」などのご指摘やアドバイスをいただけると嬉しいです！
コメント欄でお気軽に教えていただけると助かります🙏