DHCPでプロキシサーバーのアドレス通知してプロキシ自動設定させる方法

# はじめに

:::note warn
細かな動作については未検証のため、本番ネットワークに組み込む際は十分検証の上実装してください。
記載されている内容を実施して事故っても（インターネットに繋がらないなど）責任を負いかねます。
:::

# 環境

- サーバ冗長化はしない
- サーバにはLinux OSを使用
- ネットワーク的にはすべて同セグにする
- DHCPサーバにはラズパイ4を使用（部屋の中で転がってたやつ）
- DHCPサーバとHTTPサーバ（`proxy.pac`配るだけ）は同じラズパイ上で動作
- 処理を軽くするためHTTPサーバはnginx
- Proxyサーバはキャッシュしたりさせるので、RAM/SSD容量そこそこ積んだ元デスクトップPC
- DHCPサーバ: Linux/Ubuntu/5.4.0-1122-raspi/isc-dhcpd-4.4.1 with nginx/1.18.0
    ```sh
    $ dhcpd --version
    isc-dhcpd-4.4.1
    $ nginx -v
    nginx version: nginx/1.18.0 (Ubuntu)
    ```
- Proxyサーバ: Linux/Almalinux/4.18.0-553.37.1.el8_10.x86_64/squid-4.15
    ```sh
    $ squid -v
    Squid Cache: Version 4.15
    ```
- Proxyサーバにしているサーバでは他にもいろんなプロセスが動いているが割愛

# 通信イメージ

実際にはもっと細かいですがだいぶ端折ってます

```
       Client                Server
      --------              --------
          |                    |
[1]       | -----------------> |
          | (DHCP Discover)    |
          |                    |
[2]       | <----------------- |
          | (DHCP Offer)       |
          |                    |
[3]       | -----------------> |
          | (DHCP Request)     |
          |                    |
[4]       | <----------------- |
          | (DHCP ACK)         |
          |  \ (Option 252)    |
          |                    |
[5]       | -----------------> |
          | (HTTP REQUEST)     |
          |  \ (GET proxy.pac) |
          |                    |
[6]       | <----------------- |
          | (HTTP Response)    |
          |  \ (proxy.pac)     |
          |                    |
[7]       | <----------------> |
          | (via Proxy)        |
          |                    |
```

# サーバ

インストールは割愛。
苦じゃないはずなのでパッケージ管理ツールごとのやつで入れればいいかと。

## DHCPサーバの設定

- `#`以降はコメント
- <font color="#ff0000">**DHCP Option 252**</font>は標準で設定項目として無い？ので項目を定義（未確認）
(`option wpad-proxy-server code 252 = text;`)

https://learn.microsoft.com/en-us/answers/questions/1483857/how-to-know-wpad-is-enabled-on-dhcp-client

- iOS系(Apple OS)は`Option 252`ではなく、`Option 128`や`Option 66`, `Option 120`らしい。
- Option 128は `echo "192.168.1.25" | openssl enc -base64` のように BASE64 でエンコードする必要があるらしい

https://discussions.apple.com/thread/2309560?sortBy=rank

- Proxyサーバのアドレス+ファイルパスを<font color="#ff0000">**DHCP Option 252**</font>(If: Windows)で通知
(`option wpad-proxy-server "http://172.18.3.10/proxy.pac";`)
- 登録外のMACアドレスにはアドレスを配らないよう設定
(`deny unknown-clients;`)
- 自称権威DHCPサーバとして設定
(`authoritative;`)
- DDNSはそもそもDNSサーバ動いてないので無し
(`ddns-update-style none;`)
- tftpとかvoipの設定はもう使ってないけど残してる（過去にIP-PBX立ててた）
(`option tftp-server-name "172.18.3.1";`)
(`option voip-tftp-server 172.18.3.1;`)

もともと定義されていないDHCP Optionは定義すれば使えそう
使いそうなのは、Option 82(DHCP Relay-agent)とか Option 150(Cisco Voip-TFTP-Address)

https://atmarkit.itmedia.co.jp/fnetwork/netcom/netcom06/option.html

```conf:/etc/dhcp/dhcpd.conf
#
# DHCP Server Configuration file.
#   see /usr/share/doc/dhcp*/dhcpd.conf.example
#   see dhcpd.conf(5) man page
#
# Config files test command is dhcpd -t
#

default-lease-time 86400; # 1 day
max-lease-time 259200; # 3 days

#log-facility daemon;
log-facility local1;
ddns-update-style none;
authoritative;

option voip-tftp-server code 150 = { ip-address };
option wpad-proxy-server code 252 = text;
option domain-name-servers 1.1.1.1, 1.0.0.1;

subnet 172.17.0.0 netmask 255.255.0.0 {
        # Docker network
        not authoritative;
}

subnet 172.18.0.0 netmask 255.255.252.0 {
        # Honban network
        range dynamic-bootp 172.18.0.1 172.18.0.254;

        #option routers 172.18.0.1;
        option routers 172.18.3.254;
        option broadcast-address 172.18.3.255;

        option domain-name "n138.local";
        option domain-name-servers 1.1.1.1, 1.0.0.1; #Cloudflare(master), Cloudflare(backup)
        option netbios-name-servers 172.18.3.1;
        option wpad-proxy-server "http://172.18.3.10/proxy.pac";

        option log-servers 172.18.3.1;
        option tftp-server-name "172.18.3.1";
        option voip-tftp-server 172.18.3.1;

        deny unknown-clients;
}

# known-clients
host apple_iPhone-01 { hardware ethernet F4:BE:EC:00:00:01; }
host apple_iPhone-02 { hardware ethernet F4:BE:EC:00:00:02; }
```

## HTTPサーバの設定

- nginxインストールしてサービス自動起動するようしただけ
`systemctl enable --now nginx`
- 特に設定していない

## プロキシ自動設定ファイル proxy.pac

- `FindProxyForURL()`関数の名前は固定(大文字小文字区別するので正しく記述)
    - `isPlainHostName()`はURIのホスト名部分(http://<font color="#ff0000">www.google.com</font>/hoge/foo/index.html)にピリオド(`.`)がないと`True` (isFQDN()みたいな?)
        - `http://www.google.com/hoge/foo/index.html` -&gt; `False`
        - `http://www/hoge/foo/index.html` -&gt; `True`
    - `isInNet()`はセグメント単位でチェックしマッチすれば`True`
        - CIDRのような形式でなくてもOK(bit 1が連続しなくてもOK)
            - `isInNet(host, "192.168.0.0", "255.255.15.0)`でも良いらしい
            - 管理しやすくするならサブネットマスクと同じ形式を個人的に推奨
- Javascript形式で記述
- その他の[定義済みの関数](https://developer.mozilla.org/ja/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_PAC_file#%E5%AE%9A%E7%BE%A9%E6%B8%88%E3%81%BF%E3%81%AE%E9%96%A2%E6%95%B0%E3%81%A8%E7%92%B0%E5%A2%83)は下記ページ参照

https://developer.mozilla.org/ja/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_PAC_file#%E5%AE%9A%E7%BE%A9%E6%B8%88%E3%81%BF%E3%81%AE%E9%96%A2%E6%95%B0%E3%81%A8%E7%92%B0%E5%A2%83

```javascript:/var/www/html/proxy.pac
function FindProxyForURL(url, host)
{
        if (isPlainHostName (host)) {
                return "DIRECT";
        }
        if (isInNet(host, "172.16.0.0", "255.240.0.0")) {
                /* Local-Class-B 172.16.0.0/12 **/
                return "DIRECT";
        }
        return "PROXY 172.18.3.1:3128";
}
```

## Proxyサーバの設定

- デフォルトの設定にキャッシュの設定など追加しただけ。
    - `cache_mem 8 GB`
    - `coredump_dir /var/spool/squid`
- DHCP連携の設定は無い（DHCPサーバの設定が主）
- squidGuard連携してたりするけど、今回の記事の内容から外れるので割愛
    - `url_rewrite_program /usr/bin/squidGuard -c /etc/squid/squidGuard.conf`

```conf:/etc/squid/squid.conf
#
# Recommended minimum configuration:
#

# Example rule allowing access from your local networks.
# Adapt to list your (internal) IP networks from where browsing
# should be allowed
acl localnet src 0.0.0.1-0.255.255.255  # RFC 1122 "this" network (LAN)
acl localnet src 10.0.0.0/8             # RFC 1918 local private network (LAN)
acl localnet src 100.64.0.0/10          # RFC 6598 shared address space (CGN)
acl localnet src 169.254.0.0/16         # RFC 3927 link-local (directly plugged) machines
acl localnet src 172.16.0.0/12          # RFC 1918 local private network (LAN)
acl localnet src 192.168.0.0/16         # RFC 1918 local private network (LAN)
acl localnet src fc00::/7               # RFC 4193 local private network range
acl localnet src fe80::/10              # RFC 4291 link-local (directly plugged) machines

（以下略）
```

# Refs

https://learn.microsoft.com/en-us/answers/questions/1483857/how-to-know-wpad-is-enabled-on-dhcp-client

https://discussions.apple.com/thread/2309560?sortBy=rank

https://atmarkit.itmedia.co.jp/fnetwork/netcom/netcom06/option.html

https://qiita.com/n138-kz/items/5a418a60f2d08d8bc3cf

https://developer.mozilla.org/ja/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_PAC_file#%E5%AE%9A%E7%BE%A9%E6%B8%88%E3%81%BF%E3%81%AE%E9%96%A2%E6%95%B0%E3%81%A8%E7%92%B0%E5%A2%83

https://www.cisco.com/c/ja_jp/support/docs/security/web-security-appliance/118076-configure-wsa-00.html

https://qiita.com/shuncask/items/0b762a34db4ffffe98a2

https://qiita.com/free-honda/items/c27011b619190baf6523