LoginSignup
2

posted at

updated at

UnboundをIPv6対応させようとしてエラーと戦った話

足りないっていうとNATっていう。
こだまでしょうか、いいえIPv4。

枯渇するといわれ続けて三千年、枯渇しても普及しませんIPv6。
まずは自宅から始めてみましょう。

未来ずらぁ

皆さんはIPv6と聞くとどんなイメージを抱きますか?
私自身IPv6に対しては割り当て数が多い次世代規格程度の認識しか持っておらず、手持ちの環境(ISP)では実現不可だと思っていました。
ですが、先日、自宅回線をauひかりからコミュファ光に乗り換えようとしたところ「IPv6?速いらしいじゃん?PPPoE?IPoE?ポッポー!」みたいな状態だったので、きちんと理解しようと一念発起し勉強しました。
すると、今の環境でもIPv6いけるんじゃねということになり、実際、Wi-FiルーターのIPv6という項目をポチっとするだけでIPv6で接続ができ、じゃあサーバーなんかもIPv6にしてしまえってことで本件が開戦した次第です。

別にIPv6のヘッダフォーマットやらまで理解する必要はありませんが、最低限、書式やリンクローカルアドレス等が分からないと危険ですので、僕と同じでIPv6?ピーポ君?な人はこちらの記事でも読んでみてください。
IPv6に限らずですが、ルーターのセキュリティはきちんと設定してくださいね。

IPv6デュアルスタック化

ネットをあさればゴロゴロと情報がありますが、当方の設定は以下のようにしてあります。

/etc/unbound/unbound.conf.d/unbound.conf
server:
        #ログ
        verbosity: 0 #無効

        #CPUコア数
        num-threads: 4

        #受け入れインターフェース
        interface: ::1 #IPv6ローカルホスト≒127.0.0.1
        interface: :: #IPv6任意IP≒0.0.0.0
        interface: 127.0.0.1 #IPv4ローカルホスト
        interface: 0.0.0.0 #IPv4任意IP

        #受け入れポート
        port: 53 #デフォルト

        #アクセスコントロール
        access-control: 192.168.1.0/24 allow #IPv4ローカル
        access-control: fe80::/10 allow #IPv6ローカル

        #動作設定
        do-ip4: yes #IPv4を解決する
        do-ip6: yes #IPv6を解決する
        do-udp: yes #UDPを解決する
        do-tcp: yes #TCPを解決する

        #情報の秘匿
        hide-identity: yes
        hide-version: yes

        #IPv6優先
        prefer-ip6: yes

        #パフォーマンス設定
        outgoing-range: 200 # 1024/コア数 - 50
        num-queries-per-thread: 100 # outgoing-rangeの半分
        msg-cache-size: 64m #キャッシュメモリサイズ(合計で最大2.5倍になるので余裕をもって)
        rrset-cache-size: 128m #キャッシュメモリサイズ(msg-cache-sizeの2倍)
        msg-cache-slabs: 4 #コア数に最も近い2の累乗
        rrset-cache-slabs: 4 #コア数に最も近い2の累乗
        infra-cache-slabs: 4 #コア数に最も近い2の累乗
        key-cache-slabs: 4 #コア数に最も近い2の累乗

#該当しない問い合わせをDNSに投げる
forward-zone:
        name: "." #すべて
        forward-addr: 2001:4860:4860::8888 #Google public DNS IPv6
        forward-addr: 2001:4860:4860::8844 #Google public DNS IPv6
        forward-addr: 8.8.8.8 #Google public DNS IPv4
        forward-addr: 8.8.4.4 #Google public DNS IPv4

このように設定しておけばIPv4/IPv6デュアルスタックのDNSキャッシュサーバーとして稼働するはずです。

outgoing-range

パフォーマンス設定についてoutgoing-rangenum-queries-per-thread8192のように大きく設定している記事がありますが、ユーザー会様によると、outgoing-rangeには最大で1024の制限があるとのことです。
libeventというプラットフォームをコンパイル時に導入すると、1024の制限を突破できるようですがapt installした場合は不可だと思われます。
当方は家庭内のみでの実装なので制限に甘んじて、推奨の1024/コア数 - 50としています。

ファイアウォール

忘れないようにファイアウォールも開けておきましょう。
当方ではufwを使用していますので、sudo ufw allow from fe80::/10 to any port 53とすればローカルから53ポートへのアクセスが可能になります。

機器側設定

IPv6ではIPアドレスは基本的にDHCPv6かRA(Router Advertisement)を用いた自動割り当てですから任意のIPを設定することはありません。
ですが、ローカル内ではプレフィックスやMACアドレスを用いて生成されたリンクローカルアドレスという固有のアドレスを持っていますので、そいつを使ってDNSサーバーを指定します。
まず、ip aでUnboundを走らせるマシンのリンクローカルアドレスを調べます。

hogehoge@hogehoge:~ $ ip a #リンクローカルアドレスを調べます
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.x/24 brd 192.168.1.255 scope global noprefixroute wlan0
       valid_lft forever preferred_lft forever
    inet6 xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/64 scope global dynamic mngtmpaddr noprefixroute
       valid_lft 297sec preferred_lft 117sec
    inet6 fe80::xxxx:xxxx:xxxx:xxxx/64 scope link #こいつがリンクローカルアドレス
       valid_lft forever preferred_lft forever

inet6というところにあるfe80::xxxx:xxxx:xxxx:xxxxがリンクローカルアドレスです。
ちなみに、リンクローカルアドレスはfe80::から始まります。
このアドレスをPCのDNSサーバーとして設定すれば完了です。

DNSゾーン設定

ここからが本命のローカルでのDNSに関する設定です。

ダメです!

こっちがダメだった設定です。
ゾーンファイルについて少しですが解説も書いておきます。

/etc/unbound/unbound.conf.d/dns.conf
server:
      # local-zone: "ローカルで返すゾーン." type => 以下の表参照
        local-zone: "example.com." transparent

      # 正引きの設定
      # local-zone: ローカルで返すドメイン.(最後尾の.を忘れずに)
      #             |              TTL(パケットの寿命、一個ルーターを通る毎に減るので300回渡り歩く)
      #             |              |   クラス名(INTERNETの略)
      #             |              |   |  Aレコード(エラーの原因でした)
      #             |              |   |  | IPアドレス
        local-data: "example.com. 300 IN A 192.168.1.x" #これをそのままIPv6に書き換えたら…。
        local-data: "www.example.com. 300 IN A 192.168.1.x"

        #IPv6の正引き設定
        local-data: "example.com. 300 IN A fe80::xxxx:xxxx:xxxx:xxxx" #ダメです!
        local-data: "www.example.com. 300 IN A fe80::xxxx:xxxx:xxxx:xxxx" #動きません!

IPv6のlocal-zoneをコメントアウトすると動くので、ここが原因と推測。

local-zoneの主なtype

type
deny local-dataに一致したら問い合わせに回答します。
一致しない場合は無視します。
refuse local-dataに一致したら、問い合わせに回答します。
一致しない場合はrcode REFUSEDを添えてエラーを返します。
static local-dataに一致したら、問い合わせに回答します。
完全不一致なときは、nodataあるいはnxdomainを回答します。
サブドメインなどで部分一致としてlocal-dataに存在するときには、否定回答として回答にSOAが含まれます。
transparent local-dataに一致したら、問い合わせに回答します。
一致しない場合はforward-zoneで設定したDNSサービスに問い合わせます。

原因と解決策

IPv4の時はlocal-data: "example.com. 300 IN A 192.168.1.x"のように設定してたので、IPv6でも"example.com. 300 IN A fe80::xxxx:xxxx:xxxx:xxxx"と設定しましたがエラーになりました。
試行錯誤した結果、IPv6ゾーンはAレコードではなくAAAAレコードであることが分かりました。
ですので、"example.com. 300 IN AAAA fe80::xxxx:xxxx:xxxx:xxxx"とするのが正解です。

正解

IPv6をAAAAレコードにして、逆引き設定を追加して完成形です。

/etc/unbound/unbound.conf.d/dns.conf
server:
        local-zone: "example.com." transparent

        local-data: "example.com. 300 IN AAAA fe80::xxxx:xxxx:xxxx:xxxx"
        local-data: "www.example.com. 300 IN AAAA fe80::xxxx:xxxx:xxxx:xxxx"

        local-data-ptr: "fe80::xxxx:xxxx:xxxx:xxxx 300 example.com."
        local-data-ptr: "fe80::xxxx:xxxx:xxxx:xxxx 300 www.example.com."

        local-data: "example.com. 300 IN A 192.168.1.x"
        local-data: "www.example.com. 300 IN A 192.168.1.x"

        local-data-ptr: "192.168.1.x 300 example.com."
        local-data-ptr: "192.168.1.x 300 www.example.com."

これでexample.comwww.example.comに対してIPv6アドレスとIPv4アドレスの両方を返してくれます。
ディレクトリ/etc/unbound/unbound.conf.d/以下にあって拡張子が.confであればファイル名は任意です。

気分爽快

デュアルスタックにすると処理量が増えます。
もう一度言います。
デュアルスタックにすると、IPv6とIPv4二重に処理をするため処理量が増えます。
見にくいし、長いし、つまりメリットなんてありません。多分。
それでも、「俺、IPv6使ってるぜー!」って爽快感が堪りません。
同じ気分を味わいたい方はぜひ挑戦してみてください。
それと「v6プラス契約してもうたー」って人も参考にしてみてください。

IPv6化のメリットについて追記

自宅IPに動的DNSを設定してサーバーを開設したものの、ローカルからドメインにアクセスするとルーターの管理画面に繋がってしまい、IP直打ちだとSSLエラーになる、といった事情に陥っている方は結構いらっしゃると思います。
私もその一人でしたので内向きDNSサーバーを立ててローカルからのDNSの問い合わせにサーバーのローカルIPを回答するようにしたわけです。
ところが、IPv6化しただけで当該事象が解決したので、これがIPv6化最大のメリットだと感じました。

Unboundの記事でUnbound不要になったぜーなんて書くのはあほらしいんですが、お役に立てれば幸いです。

理屈

IPv4ではルーターにグローバルIPアドレスが割り振られ、宅内はローカルIPのみで管理されていたと思います。
DNSから回答されるのはルーターのグローバルIPアドレスになりますので、WAN側からのアクセスはNAT転送で目的サーバーに繋がりますが、LAN側からルーターの80ポートへのアクセス=>ルーター管理画面となってしまうのです。

しかし、IPv6では全ての機器にグローバルIPアドレスが設定されますのでローカルのサーバーもグローバルIPを持つことになります。
このサーバーが持っているグローバルIPアドレスをDNSに通知した場合、回答されたグローバルIPアドレスで一意にサーバーへ到達が可能ですので、仮にローカルからのアクセスだとしてもサーバーにたどり着くことができるわけです。
この場合、ルーターで宅外からIPv6のパケットをホスト非公開(セキュリティ上推奨)の設定があったとしても、IPパケットは(宛先はグローバルIPアドレスですが)ローカル内でのやり取りにとどまるので問題なくアクセスが可能です。
グローバルIPアドレスは自動割り振りが基本なのでルーターでのファイアウォール設定がやや複雑になります。
IPv4とのデュアルスタックであれば宅外からのアクセスはIPv4としてNATを活用したセキュリティの維持が可能です。
IPv6で80と443全開放などはくれぐれもなさらないで、IPv6で宅外からアクセスを望まれる場合は、MACアドレス等で宛先をサーバーに限定して、ファイアウォールを開けてくださいますようお願いいたします。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2