【結構詳細】EC2にDNSサーバー立ててみた

## 概要
- FTP、NTPにチャレンジしたので、DNSサーバーにもせっかくなのでチャレンジしたくなった
- DNSの設定事項がいまいち理解できていないので、サーバーを作る過程で理解を深めたいと思う

## 作成手順
### 1．流れの説明
https://qiita.com/mechamogera/items/574de60310fb19e088ca

上記の記事を主に参考に行っていく形で進めていく。
特に、自身が詰まったところを重点的に説明していこうと思うので、詰まったときなどの参考にしてほしい。

### 2. EC2のセットアップ
**今回選択したEC2の初期環境**
1. AmazonLinux2023 64bit
1. 自宅のグローバルIPで22(ssh用)、UDP/TCPの53番、TCPの953、HTTPの80を開放している

この状況が同じ場合は、基本的に問題なく設定が可能であるはずです、、

### 3. 諸々のインストール
```shell
# すべてのパッケージをアップデート,-yはすべての質問にyesで回答する
$ sudo yum update -y
# Apacheのインストール,-y..略
$ sudo yum install httpd -y
# echo | teeで標準出力しながら、ファイルにhogeeを保存、このindex.htmlはApacheのデフォルトページ
$ echo "hogeee" | sudo tee /var/www/html/index.html
# Apacheの起動
$ sudo /etc/init.d/httpd start
# 自動再起動の設定
$ sudo chkconfig httpd on
# httpリクエストを送信して、設定が反映されているか確認する
$ curl http://[EC2インスタンスのPublicIP]
hogeee
```
このタイミングで。Apacheが起動しているかを確認しておきましょう
```sh
$ sudo systemctl status httpd
```
以下の状態であればOKです

    ● httpd.service - The Apache HTTP Server
     Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; preset: di>
     Active: active (running) since Sat 2024-02-10 03:18:15 UTC; 1min 41s ago
     ...


:::note warn
EC2の設定の関係で、最後のcurlを送信しても下のように帰ってきます。なので指定するportの開放が必要です。
:::

```sh
$ curl http://xx.xxx.xxx.xx
curl: (28) Failed to connect to xx.xxx.xxx.xx port 80 after 129585 ms: Couldn't connect to server
```
`port 80`が開いていないので、アクセスできないと怒っています。
EC2のコンソールから、セキュリティグループ＞＞インバウンドルールを探し、httpを選択し、自宅のグローバルIPを設定したのちに、再度curlを送信してください。

:::note info
EC2の再起動やApacheの再起動は必要なく、即座に反映されます
:::

### 4. DNSソフトのインストールと設定
```sh
# インストール
$ sudo yum install bind -y

# originalファイルのコピーと保存（.originalを後ろに着けた状態でコピーする）
$ sudo cp /etc/named.conf{,.original}
# vimでファイルのconfファイルの編集
$ sudo vi /etc/named.conf
```

### 4.1. 設定を行う
:::note warn
調べながら、コメントに書いてあるように設定を行いましたが、初めてDNSサーバの設定をおこなうため、誤っている可能性があるので有識者の方がいらっしゃいましたら設定の妥当性などに言及していただければ幸いです
:::

```conf
//
// named.conf
//
// Provided by Red Hat bind package to configure the ISC BIND named(8) DNS
// server as a caching only nameserver (as a localhost DNS resolver only).
//
// See /usr/share/doc/bind*/sample/ for example named configuration files.
//

options {
        # listen-on port 53 { 127.0.0.1; };
        # DNSサーバが待機するIPアドレスとポートを設定する
        listen-on port 53 { 127.0.0.1; [EC2のPrivateIPアドレスを設定する] }; # 変更した
        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     { localhost; };
        # すべて許可する設定。EC2のセキュリティーグループでアクセスは制限する
        allow-query  { any; }; # 変更した

        /*
         - If you are building an AUTHORITATIVE DNS server, do NOT enable recursion.
         - If you are building a RECURSIVE (caching) DNS server, you need to enable
           recursion.
         - If your recursive DNS server has a public IP address, you MUST enable access
           control to limit queries to your legitimate users. Failing to do so will
           cause your server to become part of large scale DNS amplification
           attacks. Implementing BCP38 within your network would greatly
           reduce such attack surface
        */
        # リクエストされたドメイン名がローカルzoneにない場合に、
        # ほかのDNSサーバに問い合わせることを許可する
        recursion yes;
        
        # 追加した
        # DNSSECが有効なゾーンからの応答に対して、署名の添付や検証する準備を行う
        dnssec-enable yes;
        
        # DNSSEC署名が有効かどうかを検証する機能の有効化
        dnssec-validation yes;

        # 参考サイトでは使用していたが、現在非推奨かつ影響がすくなそうなため、コメントアウト
        # dnssec-lookaside auto;

        # サーバが解決できないクエリをフォワーダー（上流のDNS）に転送する際のIPを指定する
        # ここでは、Googleの公開DNSを指定している
        forwarders { 8.8.8.8; 8.8.4.4; };
        
        # 解決できない場合は転送して、自身は再帰的な問い合わせを行わない
        forward only;

        # 参考サイトでは使用していたが、include "/etc/named.root.key";があることで問題ないようだ
        # bindkeys-file "/etc/named.iscdlv.key";

        managed-keys-directory "/var/named/dynamic";

        # 地域情報などを結びつけることで、応答速度などを早めることに利用するらしい
        # geoip-directory "/usr/share/GeoIP";

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

        /* https://fedoraproject.org/wiki/Changes/CryptoPolicy */
        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.comに対するDNSクエリが来た場合に、原本をもつサーバ（type:master）として、
# master.example.comに基づいて適切な処理を行う
zone "example.com" IN {
  type master;
  file "master.example.com";
};
include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";
```

### 4.2. ゾーンファイルの作成
```sh
$ sudo vi /var/named/master.example.com
```

```/var/named/master.example.com
$TTL 43200
@       IN      SOA     help.example.com. ns01.example.com. (
                            1          ; serial
                            21600      ; refresh (6 hours)
                            7200       ; retry (2 hours)
                            1209600    ; expire (2 weeks)
                            43200 )    ; minimum (12 hours)

        IN      NS      ns01.example.com.

ns01    IN      A       [EC2インスタンスのPrivateIP]
www     IN      A       [EC2インスタンスのPrivateIP]
```
```conf:/var/named/master.example.com　解説
# 43200s=12hを示し、DNSキャッシュの保持時間である
# 時間が短いほど、変更の反映が速いがその分サーバ負荷が高まる
$TTL 43200

# @　$ORIGINで設定された内容か、ゾーンファイルの名前から推測する。
# 今回の場合は@ = example.com. ドメインである

# IN(Internet)
#  - インターネットに関するDNS問い合わせを担当するクラスである

# SOA(Start of Authority) 
#  - DNSゾーンの最初のレコードであること
#  - このゾーンに関する権威ある情報を含んでいることを示す

# help.example.com.
#  - ゾーンの管理者のEメールアドレス
#  - @記号の代わりに.が使用され、末尾にも.が付く

# ns01.example.com.
#  - このDNSゾーンのプライマーサーバを指定する
#  - このサーバが正式なバージョンで、ほかのネームサーバはデータを複製する
@       IN      SOA     help.example.com. ns01.example.com. (

# DNSゾーンのバージョンの識別
#  - DNSゾーンファイルを更新するタイミングで更新が必須
#  - スレーブサーバがserial番号を確認し、更新を行う
                            1          ; serial

# SOAレコード内でのリフレッシュ間隔の指定
#  - スレーブサーバがマスターサーバの更新を確認する頻度を調整する
                            21600      ; refresh (6 hours)

# ゾーン情報の更新に失敗してから、再挑戦するまでの時間
                            7200       ; retry (2 hours)

# 情報の有効期限
#  - 今回の場合最後に更新してから2週間情報が有効である
                            1209600    ; expire (2 weeks)

# キャッシュの保存期限の指定
#  - $TTLとの違い
#    - 影響範囲が、$TTLの場合はゾーンファイル全体である。
#    - 対して、minimumはネガティブレスポンスのキャッシュに影響を与える。またゾーン全体に影響を及ぼす可能性がある。
                            43200 )    ; minimum (12 hours)

# NS(Name Server)
#  - NSレコードは、そのゾーン（ドメイン）のDNSクエリに応答する権威サーバである
#  - nsプレフィックスを利用することは、慣例的である
#  - また、ns01,ns02とすることで、識別しやすくする

# 記述なしでも「＠」と同じように動作するようだ。
# ドメインの指定に関してのみ言えば、下3つは同じ意味である。
        IN      NS      ns01.example.com.
@       IN      NS      ns02.example.com.
example.com.    IN      NS      ns03.example.com.

# ns01
#  - ホスト名 or サブドメイン名
# A(Address)
#  - 特定のホストのIPv4を指定することで、ホスト名をＩＰに解決できる
#  - 冗長性の観点から、同じ設定内容をほかでも同様に設定するなどを行うっぽい
ns01    IN      A       [ns01のEC2インスタンスのPrivateIP]
ns02    IN      A       [ns02のEC2インスタンスのPrivateIP]
ns03    IN      A       [ns03のEC2インスタンスのPrivateIP]
www     IN      A       [EC2インスタンスのPrivateIP]

# AAAAレコード
#  - IPv6をAレコードと同じようにマッピングする

# MX(Mail eXchange)
#  - ドメインに対するメールサービスのホスト名と、優先順位を指定する
#  - また、数値が少ないほど高い優先度となる
#  - MXレコードはメールサーバのアドレスを示す。下記が例である
# ex)
# @       IN      MX      10 mail.example.com.

# CNAME(Canonical Name)
#  - 一つのドメイン名を別のドメイン名にエイリアス（別名）としてマッピングする

# TXT
#  - ドメインに関連付けられた任意のテキスト情報を格納するために利用する
#  - 認証関係で使われるっぽい
```
### 4.3. bindの起動＆自動起動の設定
```sh
$ sudo systemctl start named
$ sudo systemctl enable named
```

:::note warn
自分は起動時に、構文エラーが出たためエラー内容と対処法を共有しておきます。
:::

```sh
$ sudo systemctl start named
Job for named.service failed because the control process exited with error code.
See "systemctl status named.service" and "journalctl -xeu named.service" for details
```
起動時に、正常に起動しなかったことを示しています
>See "systemctl status named.service" and "journalctl -xeu named.service" for details

この部分で、詳細の見方を説明されているのでそれに従いました。
```sh
$ systemctl status named.service
```

```sh
× named.service - Berkeley Internet Name Domain (DNS)
     Loaded: loaded (/usr/lib/systemd/system/named.service; disabled; preset: disabled)
     Active: failed (Result: exit-code) since Sat 2024-02-10 05:02:08 UTC; 2min 13s ago
    Process: 30920 ExecStartPre=/bin/bash -c if [ ! "$DISABLE_ZONE_CHECKING" == "yes" ]; then /usr/sbin/named-checkconf -z "$NAMEDCONF"; else echo "Checking of zone files is disabled"; fi (code=exited, status=1/FAILURE)
        CPU: 18ms

Feb 10 05:02:08 ip-xxx-xx-xxx-xxx.ap-northeast-1.compute.internal bash[30921]: zone example.com/IN: not loaded due to errors.
Feb 10 05:02:08 ip-xxx-xx-xxx-xxx.ap-northeast-1.compute.internal bash[30921]: _default/example.com/IN: unexpected end of input
Feb 10 05:02:08 ip-xxx-xx-xxx-xxx.ap-northeast-1.compute.internal bash[30921]: zone localhost.localdomain/IN: loaded serial 0
Feb 10 05:02:08 ip-xxx-xx-xxx-xxx.ap-northeast-1.compute.internal bash[30921]: zone localhost/IN: loaded serial 0
Feb 10 05:02:08 ip-xxx-xx-xxx-xxx.ap-northeast-1.compute.internal bash[30921]: zone 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa/IN: loaded serial 0
Feb 10 05:02:08 ip-xxx-xx-xxx-xxx.ap-northeast-1.compute.internal bash[30921]: zone 1.0.0.127.in-addr.arpa/IN: loaded serial 0
Feb 10 05:02:08 ip-xxx-xx-xxx-xxx.ap-northeast-1.compute.internal bash[30921]: zone 0.in-addr.arpa/IN: loaded serial 0
Feb 10 05:02:08 ip-xxx-xx-xxx-xxx.ap-northeast-1.compute.internal systemd[1]: named.service: Control process exited, code=exited, status=1/FAILURE
Feb 10 05:02:08 ip-xxx-xx-xxx-xxx.ap-northeast-1.compute.internal systemd[1]: named.service: Failed with result 'exit-code'.
Feb 10 05:02:08 ip-xxx-xx-xxx-xxx.ap-northeast-1.compute.internal systemd[1]: Failed to start named.service - Berkeley Internet Name Domain (DNS).
```
```sh
Feb 10 05:02:08 ip-xxx-xx-xxx-xxx.ap-northeast-1.compute.internal bash[30921]: zone example.com/IN: not loaded due to errors.
Feb 10 05:02:08 ip-xxx-xx-xxx-xxx.ap-northeast-1.compute.internal bash[30921]: _default/example.com/IN: unexpected end of input
```
ChatGPTによると、example.comファイルの問題であると特定されました。
>このエラーは、example.comゾーンファイル内に構文エラーや不完全な記述があることを示しています。特に「unexpected end of input」というメッセージは、ファイルの終わりが予期せず早く来てしまっていること、つまりファイルが不完全であるか、必要な記述が終了していないことを意味しています。

実際に、構文の誤りが存在していたため、構文を修正することで無事に解決と至りました。


### 4.3. resolve.confの設定
:::note warn
参考サイトによると
- このまま再起動すると設定した内容は消える
- このEC2からEC2のPublicDNSへのアクセスはPublicIPごしになる(通常PrivateIP)

とのことなので注意が必要である。
:::
:::note alert
サイトを参考に設定を行ったが、**AmazonLinux2023の場合は下記の設定を行っても再起動すると設定が消えてしまうことが分かった。** 4.3.1.のほうで、2023の方法で書き直すことにする。
:::


参考サイト同様に設定を行う
```sh
# 設定ファイルのコピー
$ sudo cp /etc/resolv.conf{,.original}
# 設定ファイルの編集
$ sudo vi /etc/resolv.conf
```

```conf:/etc/resolv.conf
search ap-northeast-1.compute.internal
nameserver 127.0.0.1
```
nameserverをlocalhostに設定することで、BINDにクエリを転送することが可能になる

次に自動更新によって設定が、更新されないように設定する
```sh
$ sudo vi /etc/sysconfig/network-scripts/ifcfg-eth0
# => PEERDNS=noにする
```
### 4.3.1. AmazonLinux2023の/etc/resolv.confが更新されないようにする方法
↓↓↓↓↓↓↓↓↓超々詳しい記事↓↓↓↓↓↓↓↓↓

https://dev.classmethod.jp/articles/amazon-linux-2023-static-dns/

詳細について学びたい場合は、上記記事を参考にしてください。
どうやら本物の内部の方が書いてくださっている記事のようです、、、
雑に必要な情報を引っこ抜いているので、詳しい情報は本家サイトへどうぞ💁

>Amazon Linux 2の場合、networkサービス再起動後も/etc/resolv.confの設定を維持するためには`/etc/dhcp/dhclient.conf`もしくは`/etc/sysconfig/network-scripts/ifcfg-eth0`の変更が必要でした。
Amazon Linux 2023では新しく`systemd-resolved.service`が起動しています。

要は、これまで`network`サービスの設定を更新していたが、実際に起動しているサービスは`systemd-resolved.service`であったということのようだ？？

:::note info
1. AmazonLinux2023では`/etc/systemd/resolved.conf`を変更する必要がある
1. しかし、デフォルトでは`systemd-resolved.service`を利用していないっぽい
1. `/etc/systemd/resolved.conf`を変更しても、DHCP option setsで設定したDNSサーバーの設定は残る
:::
ということらしい。

>AWS公式ドキュメントを確認すると/etc/systemd/resolved.confは/run/systemd/networkで設定されたデフォルト値を上書きすることができると記載がありました。

つまりは、`/etc/systemd/resolved.conf`で設定すればデフォルト値を更新できるはず、、
```sh
$ sudo vi /etc/systemd/resolved.conf

$ tail -n15 /etc/systemd/resolved.conf
# Google:     8.8.8.8#dns.google 8.8.4.4#dns.google 2001:4860:4860::8888#dns.google 2001:4860:4860::8844#dns.google
# Quad9:      9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net 2620:fe::fe#dns.quad9.net 2620:fe::9#dns.quad9.net
DNS=[EC2のプライベートIP]
#FallbackDNS=
Domains=ap-northeast-1.compute.internal
#DNSSEC=no
#DNSOverTLS=no
#MulticastDNS=no
#LLMNR=no
#Cache=yes
#CacheFromLocalhost=no
#DNSStubListener=yes
#DNSStubListenerExtra=
#ReadEtcHosts=yes
#ResolveUnicastSingleLabel=no

# 設定の反映
$ sudo systemctl restart systemd-resolved.service
```

確かに、`/etc/resolv.conf`を確認すると、設定が反映されている！！
```sh
$ tail /etc/resolv.conf
# Third party programs should typically not access this file directly, but only
# through the symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a
# different way, replace this symlink by a static file or a different symlink.
#
# See man:systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.

nameserver xxx.xxx.xxx.x
nameserver 172.31.0.2
search ap-northeast-1.compute.internal
```

実際にhostを利用して、確認をしても先ほど同様動作します。
```sh
$ host ns01.example.com
ns01.example.com has address xxx.xx.xxx.xx
```

>systemd-resolved を使って名前解決するように変更

に関しては、理解が追い付かなかったため、保留しています、、、orz

**↓↓↓とりあえず、めちゃ詳しいので確認してみると流れの理解が深まるはずです！↓↓↓**

https://dev.classmethod.jp/articles/amazon-linux-2023-static-dns/#toc-1


### 4.4. 再起動して、動作チェック
```sh
# 再起動
$ sudo systemctl restart systemd-networkd
# ドメイン名：host ns01.example.comに関連付けられたIPやレコードを返す
$ host ns01.example.com
>ns01.example.com has address xxx.xxx.xxx.xxx
```
:::note info
これで、DNSサーバ自体の設定は終了です！！
:::
### 4.5. 別のEC2インスタンスから確認する
DNSクライアント側をこのように設定します。
```sh
$ sudo cp /etc/resolv.conf{,.original}
$ sudo vi /etc/resolv.conf
$ cat /etc/resolv.conf
search ap-northeast-1.compute.internal
nameserver [DNSサーバのPrivateIP]
```

:::note warn
自分の場合は、接続にうまくいかず、下のようにエラーが発生しました。
:::

```sh
$ host www.example.com
;; connection timed out; no servers could be reached
```

設定を何度確認しても参考サイトと相違点がなかったため、キャッシュをクリアしたところ成功しました。
```sh
$ sudo systemctl restart systemd-resolved
$ host www.example.com
www.example.com has address xxx.xxx.xxx.xxx
www.example.com has IPv6 address xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx

# 今回のdnsで解決できないものを上位DNSに投げてるかを確認する
$ host google.com
google.com has address 142.250.207.14
google.com has IPv6 address 2404:6800:4004:80c::200e
google.com mail is handled by 10 smtp.google.com.
```

## まとめ
じっくりと理解しながらDNSサーバを立てたので思った以上に時間がかかってしまいました。
とはいえ、各設定についてもそれなりにきちんと学べたので、次回は比較的早いはず！！
レコードに関しての理解があいまいで、Route 53でDNSを設定したときもあいまいなままで行っていたので、今回レコードについてきちんと調べたことで、以前以上に設定がはかどりそうである。

楽しかったです。

# 参考サイト
↓↓↓↓↓↓古い記事なので、信頼し過ぎないほうが良き

https://qiita.com/mechamogera/items/574de60310fb19e088ca#%E5%8F%82%E7%85%A7%E3%82%B5%E3%82%A4%E3%83%88

↓↓↓↓↓↓中の人が書いているだけあって丁寧でよい。一読の価値があるが自分には難しい

https://dev.classmethod.jp/articles/amazon-linux-2023-static-dns/#toc-1