Hyper-V 等の VM 上にプロキシと DNS を立てる

Pocket

本ブログでは、ちょいちょいプロキシ環境下での設定方法を紹介しているが、
そういったプロキシ環境下での動作の検証のため、以下を満たすプロキシサーバーを立てたい。

  • OS は Ubuntu 24.04
  • squid によって 8080 ポートにて HTTP プロキシする
  • bind9 によって自身のアドレスを proxy.example.com として名前解決する DNS サーバーとなる

とりあえず、 squid と bind9 をいれて、設定ファイルを必要最低限書き換えてみよう。

export CURRENT_IP_ADDRESS=`hostname -I | tr -s ' ' '\n' | tail -n 1`
sudo apt update
sudo apt -y install squid bind9 bind9utils
sudo sed -i -r -e 's/^(#\s?)?(http_access allow localnet)/\2/' -e 's/^http_port 3128/http_port 8080/' /etc/squid/squid.conf
sudo sed -i -e 's%^\s*\(//\)\?\s*forwarders {%        forwarders {\n                8.8.8.8;\n        };\n\0%' -e 's%^};%\0\nzone "proxy.example.com" in {\n  type master;\n  file "proxy.example.com.zone";\n};%' /etc/bind/named.conf.options
sudo tee /var/cache/bind/proxy.example.com.zone << EOF > /dev/null
\$TTL 86400
@       IN      SOA     proxy.example.com. root.proxy.example.com. (
                        2023070301      ;Serial
                        3600            ;Refresh
                        1800            ;Retry
                        604800          ;Expire
                        86400           ;Minimum TTL
)
@       IN      NS      proxy.example.com.
@       IN      A       $CURRENT_IP_ADDRESS
EOF
sudo systemctl restart squid named

これだけ。

解説

環境変数 CURRENT_IP_ADDRESShostname -I コマンドで自身の IPアドレス の一つを設定しているが、あらかじめアドレスがわかっているなら、手動で設定しても良い。

プロキシでの動作を検証したい VM と、上記のプロキシ VM を、(Hyper-V の場合)「内部ネットワーク」または「プライベートネットワーク」の仮想スイッチに接続させる。

検証対象 VM の IP アドレスは同じサブネットになるように設定し、 DNS のアドレスも上記プロキシ VM のものを設定する。
一方、プロキシ VM のほうは、 Default Switch と 前述の『「内部ネットワーク」または「プライベートネットワーク」』の両方の仮想スイッチに繋いで、直接インターネット側にも出られるようにしておく。

architecture-beta
    service internet(cloud)[Internet]
    group host(server)[Host PC]
    service winnat(cloud)[WinNAT Default Switch] in host
    internet:B -- T:winnat
    group vms(database)[VMs] in host

    service vm1(server)[proxy] in vms
    service inlsw(cloud)[Internal Switch] in vms
    service vm2(server)[target] in vms
    winnat:B -- T:vm1
    vm1:R -- L:inlsw
    inlsw:R -- L:vm2

このように繋ぐことで、プロキシ経由を想定した通信のテストが行える。

検証したい VM の方は netplan などで、 DNS サーバーとして bind9 を入れたサーバーを指すように設定しておく。

sudo vim /etc/netplan/50-cloud-init.yaml
network:
  ethernets:
    eth0:
      dhcp4: false
      dhcp6: false
      addresses:
        # proxy を通した検証をしたい対象自身のアドレス
        - 192.168.193.55/24
      nameservers:
        addresses:
          # bind9 をいれたサーバーのアドレス
          - 192.168.193.53
  version: 2

vim で設定を書き換えたら netplan apply して有効にする。

sudo netplan apply

この状態で、以下のように実行し、

$ export http_proxy=http://proxy.example.com:8080; export https_proxy=$http_proxy
$ curl https://example.com/ -v

プロキシを通して https://example.com の HTML が返されれば成功だ。

* Uses proxy env variable https_proxy == 'http://proxy.example.com:8080'
* Host proxy.example.com:8080 was resolved.
...
* Connected to proxy.example.com (192.168.192.53) port 8080
* CONNECT tunnel: HTTP/1.1 negotiated
* allocate connect buffer
* Establish HTTP proxy tunnel to example.com:443
> CONNECT example.com:443 HTTP/1.1
> Host: example.com:443
> User-Agent: curl/8.5.0
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established
<
...
<!doctype html>
<html>
<head>
    <title>Example Domain</title>
...

なお、 Ubuntu で上述の target のようにオンライン状態になっていないネットワークにのみ繋がっていると、 OS 起動時に systemd-networkd-wait-online.service のチェックで強制的に2分待たされる。
これが鬱陶しい場合は、以下の記事のように systemd-networkd-wait-online の待機を回避すると良い。

応用① 多段プロキシ

もし、多段プロキシする場合、以下のように /etc/squid/squid.conf に追記しよう。
dns_nameservers にはアクセス可能なイントラネットの DNS アドレスを、 cache_peer の第1引数と第3引数には親となるプロキシサーバーのアドレスとポートを、それぞれ指定する。

$ sudo tee -a /etc/squid/squid.conf << EOF > /dev/null
# for multi step proxy
dns_nameservers 8.8.8.8 8.8.4.4
cache_peer parent-proxy.example.com parent 8080 0 no-query
never_direct allow all
forwarded_for off
request_header_access Via deny all
request_header_access X-Forwarded-For deny all
request_header_access Cache-Control deny all
cache deny all
EOF

squid サービスを再起動すれば、設定が反映される。
(以降、 /etc/squid/squid.conf を書き換える度同じ)

sudo systemctl restart squid

応用② ログの出力先と内容を変更する

ログの出力先を /var/log/squid/access.log の代わりに journal に記録してみよう。
また、更にログの書式 (logformat) をデフォルトの squid から combined にしたければ、以下のように /etc/squid/squid.conf を書き換えよう。

sudo sed -i -r -e 's/^(#\s?)?(access_log daemon:.*$)/access_log syslog:local7.info combined/' /etc/squid/squid.conf

journal に記録したログは、 journalctl を使って以下のようにリアルタイムでアクセスしている様子が見られる。

journalctl -u squid -f

デフォルト (squid) では、

1733237904.267    552 192.168.192.55 TCP_TUNNEL/200 6035 CONNECT example.com:443 - HIER_DIRECT/93.184.215.14 -

のようなログ書式だったのが

192.168.192.55 - - [03/Dec/2024:14:56:34 +0000] "CONNECT example.com:443 HTTP/1.1" 200 6035 "-" "curl/8.5.0" TCP_TUNNEL:HIER_DIRECT

のような書式に変わる。

応用③ 複数のポートの待ち受け

更に /etc/squid/squid.conf の設定を更に書き換えてカスタムな書式を設定すれば、ログにどのポートでアクセスされたのか書き出す事もできる。 1

http_port 8080
http_port 8081
http_port 8082
http_port 8083
logformat combined-with-client   %>a %[ui %[un [%tl] %la:%lp "%rm %ru HTTP/%rv" %>Hs %<st "%{Referer}>h" "%{User-Agent}>h" %Ss:%Sh
access_log syslog:local7.info combined-with-client
cache deny all

squid は複数のポートで同時にプロキシを待ち受けられるので、 例えば「アプリケーションの設定ファイル」と「環境変数」で異なるポート番号でプロキシを設定しておき、 journalctl でリアルタイムで監視しながら使用し、どちらの設定にてプロキシされているのか確認したり…といった使い方ができる。


  1. デフォルトの書式や、カスタムフォーマット詳しい書式は squid : logformat configuration directive を参照。 

コメントを残す

メールアドレスが公開されることはありません。

This site uses Akismet to reduce spam. Learn how your comment data is processed.