Setting up a transparent proxy over SSL #3 – squid again

前回は非透過プロキシを構築しました。クライアントから送られるパケットの宛先が Web サーバーではなくプロキシ サーバーになっていて、プロキシはペイロードの内容を「解釈して」 宛先を割り出すという動作がポイントでした。

3. squid による HTTP 透過プロキシ

透過プロキシ (ここでは intercepting proxy を意味します) の場合、クライアントにはプロキシが存在することを教えません。したがってクライアントは、ただ単に Web サーバーに向けて HTTP 要求を送信します。何もしなければ HTTP 要求は Client –> VyOS –> 物理ルーター、という経路で外に流れてしまい、Squid には流入しません。つまり、透過プロキシを動作させるためには、ルーティングを少し捻じ曲げる必要があります。

Capture

設定で参考にさせていただいたページがこちら。

vyattaを使って透過型プロキシ環境を構築する時の設定内容
http://www.virment.com/vyatta/vyatta-proxy/12/

squidを透過型プロキシとして使う時の設定
http://www.virment.com/vyatta/vyatta-proxy/15/

ネットワーク構成はほぼ同じ、実行するコマンドもかなり共通しています。VyOS 上で PBR (Policy Based Routing) の設定を行ない、特定の条件に合致するパケットは eth1 に流すのではなく Squid に流すように設定します。さらに、Squid に流入したパケットが透過プロキシ用の待機ポートである 3129/tcp (前回の記事で squid.conf に設定しています) に流れるように、Squid サーバー上で iptables コマンドを使ってポート転送の設定をします。

VyOS 側の設定コマンドは以下の通りです。

$ configure
# set protocols static table 100 route 0.0.0.0/0 next-hop 10.10.90.11
# set policy route PROXYROUTE rule 100 protocol tcp
# set policy route PROXYROUTE rule 100 destination address 0.0.0.0/0
# set policy route PROXYROUTE rule 100 destination port 80
# set policy route PROXYROUTE rule 100 source address 10.10.0.0/16
# set policy route PROXYROUTE rule 100 source mac-address !00:15:5d:01:02:12
# set policy route PROXYROUTE rule 100 set table 100
# set interfaces ethernet eth0 policy route PROXYROUTE
# commit
# save
# exit
$

ここで設定したルールは以下のようなものです。言い換えると、「VyOS に対して Squid サーバー以外の Internal ネットワーク内アドレスから HTTP パケット (80/tcp) が来たら Squid に流す」 という設定です。

  • table 100
    – 10.10.90.11 (= Squid サーバー) にルーティング
  • rule 100 (ポリシー名 PROXYROUTE)
    – TCP パケットである
    – 宛先 TCP ポートが 80 である
    – 送信元の IP アドレスが 10.10.0.0/16 ネットワークに所属している
    – 送信元の MAC アドレスが 00:15:5d:01:02:12 (=Squid サーバーのもの) ではない
    – 上記に該当するパケットはルーティングの table 100 を適用
  • Internal ネットワーク側の eth0 に対して PROXYROUTE を適用

これだけだと、Squid に来たパケットの宛先ポートはまだ 80/tcp になっているので、透過プロキシのターゲットである 3129/tcp に変更する必要があります。Squid サーバー上で以下のコマンドを実行します。

$ sudo iptables -t nat -A PREROUTING -i eth0 -p tcp –dport 80 \
> -j REDIRECT –to-port 3129

設定は以上です。Squid 側は、前回の状態から変更はありません。squid が起動していなければ起動しておいてください。また、クライアントからプロキシの設定を消すのを忘れないように。

image
透過プロキシなのでプロキシ設定は削除

クライアントでブラウザーを開いて HTTP のページにアクセスすると、プロキシの設定をしていないにも関わらず、Squid のアクセス ログにログが記録されます。これが透過プロキシの動作です。この状態で HTTPS にアクセスするとどうなるでしょうか。もちろんアクセスできます。なぜなら何も設定していないため、単純に VyOS を素通りしているだけだからです。

ここで Squid の話は終わってもよいのですが、実は設定時に少し違和感がありました。まあ私自身が iptables に明るくないからなんでしょうけど。ふと、「なぜポート転送したパケットを Squid の 3129/tcp が受信することができるのか」 という疑問が浮かびました。

クライアントから送られるパケットの旅を考えてみます。今回は非透過プロキシであり、クライアントから送信された時点でパケットは Web サーバーを宛先としています。VyOS で PBR が適用されて、Squid に送られます。このルーティングはどのように実現されているでしょうか。もし、Squid 上でポートが変更されたときに宛先の IP アドレスが Web サーバーのままだったら、そのアドレスはどう考えても Squid サーバーのアドレスである 10.10.90.11 とは似ても似つかないものであるわけで、ただ単に 3129/tcp ポートが一致したからといって Squid が受信できるのはおかしいだろう、という疑問です。

上記疑問を調べるため、Squid 上の iptables の設定を削除し、代わりに Squid が 80/tcp ポートで非透過プロキシを処理するように設定します。

iptables の削除は、-A を -D に置き換えて実行するだけです。-L オプションで、現在のルールの一覧を確認できます。

$ sudo iptables -t nat -D PREROUTING -i eth0 -p tcp –dport 80 \
> -j REDIRECT –to-port 3129

$ sudo iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination

/usr/local/squid/current/etc/squid.conf を開き、ポートの部分を 3129 から 80 に変更します。

# Squid normally listens to port 3128
http_port 3128
http_port 80 transparent

squid を再起動します。以下のログから、今度は 80/tcp が透過モードでの待機ポートであることが分かります。

2015/01/08 17:46:10| Accepting HTTP Socket connections at local=[::]:3128 remote=[::] FD 9 flags=9
2015/01/08 17:46:10| Accepting NAT intercepted HTTP Socket connections at local=[::]:80 remote=[::] FD 10 flags=41

クライアントから HTTP のページにアクセスします。今度は "This page can’t be displayed" エラーで繋がらないはずです。これが意味することは、ポートが一致しているだけでは Squid はパケットを受信できない、ということです。また、VyOS の PBR は、宛先をアドレスを Squid の 10.10.90.11 には変更していないようです。

ということは、iptables によるポート転送は、ポートだけではなく宛先アドレスも自分のものに変更するのでしょうか。ちょっとこれは確かめようがありません。iptables の実装を見るのはさすがにしんどい。

とりあえず 3129/tcp ポートを使う設定に戻してから、今度は Client、VyOS、Squid、Web サーバーの 4 点でパケット キャプチャーを行ない、様子を確認することにします。Web サーバーは Azure 上に作った仮想マシン (http://ubuntu-web.cloudapp.net/) を使います。

Ubuntu Server 上でのキャプチャーには tcpdump コマンドが使えます。作成されたファイルは Network Monitor や Wireshark で開くことができます。

$ sudo tcpdump -nn -s0 -i eth0 -w squid.cap

ここで少し注意。VyOS には eth0 と eth1 の 2 枚の NIC があります。tcpdump コマンドで -i any オプションを使うと、全ての NIC 上でキャプチャーを開始することができます、がしかし、パケットのイーサネット フレームが正確に記録されません。

For example, the "any" device on Linux will have a link-layer header type of DLT_LINUX_SLL even if all devices on the system at the time the "any" device is opened have some other data link type, such as DLT_EN10MB for Ethernet.

— Manpage of PCAP http://www.tcpdump.org/pcap3_man.html

ここで出てくる DLT_LINUX_SLL とは・・・

DLT_LINUX_SLL – Linux "cooked" capture encapsulation

— pcap(3): Packet Capture library – Linux man page http://linux.die.net/man/3/pcap

実際に any でキャプチャーして Network Monitor で開いた様子を以下に示します。イーサネット フレーム部分が Linuxecookedmode という名前で表示され、MAC アドレスは送信元の情報だけが記録されています。イーサネット部分を見ない場合はこれでよいですが、今回はこれだと不便です。tcpdump では -i any は使わず、VyOS 上では eth0 と eth1 それぞれのために 2 つの tcpdump プロセスを起動することにします。

image

そんなこんなでキャプチャーを取り、HTTP GET メソッドの要求と応答に着目し、送信元及び送信先のアドレスについてまとめました。細かいので Excel のキャプチャー画像で。

image

 
ルーティングによって、MAC アドレスや IP アドレスが変更されている様子が確認できます。ポートについては、宛先は常に 80 を指していますが、クライアント側の動的ポートは 53281 と 33788 の 2 つが見えます。前者が Windows クライアント上のポートであり、後者は Squid サーバー上の動的ポートです。

一つ気が付くのは、透過プロキシ用のポートである 3129/tcp ポートが見えないことです。上記テーブルの 3 行目、VyOS から Squid に転送されたパケットの宛先 IP アドレスは依然として ubuntu-web.cloudapp.net を名前解決した結果である 104.42.10.172 を指していますが、ポートも 80 のままです。このことから、tcpdump コマンドは iptables の PREROUTING チェーンが処理される前にパケットを取得していると考えられます。ポート転送された後のパケット内容がどうなっているかを、tcpdump から見ることはできません。

広告

Setting up a transparent proxy over SSL #2 – squid

前回の記事で、VyOS をインストールした Hyper-V 仮想マシンをルーターとして使えるようになりました。次に HTTP プロキシを導入します。

2. squid による HTTP/HTTPS 非透過プロキシ

最終的に作るのは透過プロキシですが、先に非透過プロキシを作ります。ここで透過プロキシという単語の定義は曖昧なので注意が必要です。よく使われる意味としては 2 つあります。

  1. クライアント側での設定変更を必要としないように構成されたプロキシ
    プログラムの種類ではなく、ネットワーク構成で区別されるプロキシの種類です。いろいろと検索した結果、この意味で使われることが多いです。英語でも transparent proxy と言えばこちらの意味が多い気がします。その他に intercepting proxy とも言います。
  2. 要求や応答を一切変更しないプロキシ
    HTTP/1.1 についての RFC 2616 (http://tools.ietf.org/html/rfc2616) には、A "non-transparent proxy" is a proxy that modifies the request or response という部分があります。1. の意味とは違い、プログラムの種類につけられた名前です。どちらかというと 1. の意味で使われることの方が多い気がします。intercepting proxy と言ったときには 2. の意味は持ちません。

日本語の wiki には 1. の意味しか載っていませんが、英語の方には両方載っています。

Proxy server – Wikipedia, the free encyclopedia
http://en.wikipedia.org/wiki/Proxy_server

プロキシの意味についても確認しておくと、サーバーとクライアントの間に入って通信を中継するプログラムのことです。RFC 2616 には、サーバーとしてもクライアントとしても動作する、と書かれています。通信を中継するためには、まずサーバーとして要求を受け取って、何か処理して、クライアントとして要求を送信することが必要だからです。プロキシとは英語で代理人を意味する単語ですが、これは、受け取った要求をそのまま送るのではなく、内容を変更するかどうかに関わらず、プロキシが自分を差出人として要求を再送信することからつけられた名前と考えられます。この定義にしたがうと、ルーティングのように、アドレス部分だけ書き換えて、ペイロードが同じパケットを受け流すようなプログラムはプロキシとは呼ばないことになります。

この記事、及びタイトルにおける transparent proxy は、1. の意味です。つまりクライアント側で設定を変更せずに、パケットを中継させるような環境を作ります。

今回は、Squid というオープン ソースのプログラムを選びました。検索して有名そうだったから、という理由だけです。イカのロゴかわいいし。

squid : Optimising Web Delivery
http://www.squid-cache.org/

インストールは、squid3 というパッケージを apt-get するだけでいいのですが、後々プロキシの動作を書き換えたい意図があったので、ソースからビルドすることにしました。2015/1/8 現在の最新バージョンである 3.4.10 をダウンロードします。

予め開発ツールである gcc や gdb をインストールしておきましょう。前に書いた記事が参考になると思います。

Apache Live-Debugging on Linux | すなのかたまり
https://msmania.wordpress.com/2014/12/23/apache-live-debugging-on-linux/

今回実行したコマンドはこのような感じです。ビルドされた OpenSSL が /usr/local/openssl/current にインストールされているという前提です。

$ sudo apt-get install build-essential libtool manpages-dev gdb
$ wget
http://www.squid-cache.org/Versions/v3/3.4/squid-3.4.10.tar.bz2
$ tar jxvf squid-3.4.10.tar.bz2
$ cd squid-3.4.10/
$ ./configure –prefix=/usr/local/squid/squid-3.4.10 \
> –with-openssl=/usr/local/openssl/current \
> –enable-ssl
$ make
$ sudo make install
$ sudo ln -s /usr/local/squid/squid-3.4.10 /usr/local/squid/current

インストール後、Squid サーバーの実行可能ファイルは bin ではなく sbin の方の /usr/local/squid/current/sbin/squid になります。

john@ubuntu-proxy:~$ /usr/local/squid/current/sbin/squid -v
Squid Cache: Version 3.4.10
configure options:  ‘–prefix=/usr/local/squid/squid-3.4.10’ ‘–with-openssl=/usr/local/openssl/current’ ‘–enable-ssl’

john@ubuntu-proxy:~$ /usr/local/squid/current/sbin/squid -h
Usage: squid [-cdhvzCFNRVYX] [-s | -l facility] [-f config-file] [-[au] port] [-k signal]
       -a port   Specify HTTP port number (default: 3128).
       -d level  Write debugging to stderr also.
       -f file   Use given config-file instead of
                 /usr/local/squid/squid-3.4.10/etc/squid.conf
       -h        Print help message.
       -k reconfigure|rotate|shutdown|restart|interrupt|kill|debug|check|parse
                 Parse configuration file, then send signal to
                 running copy (except -k parse) and exit.
       -s | -l facility
                 Enable logging to syslog.
       -u port   Specify ICP port number (default: 3130), disable with 0.
       -v        Print version.
       -z        Create missing swap directories and then exit.
       -C        Do not catch fatal signals.
       -D        OBSOLETE. Scheduled for removal.
       -F        Don’t serve any requests until store is rebuilt.
       -N        No daemon mode.
       -R        Do not set REUSEADDR on port.
       -S        Double-check swap during rebuild.
       -X        Force full debugging.
       -Y        Only return UDP_HIT or UDP_MISS_NOFETCH during fast reload.

上記 -h の出力にあるように、何も指定しなければ etc/squid.conf が設定ファイルとして勝手に読み込まれます。

設定ファイルを変更する前に、ここで実現したいことを確認しておきます。前回構築した VyOS も含め、下図のようなネットワークができました。

Capture

Client からインターネットに接続するには、Hyper-V 仮想ネットワークの Internal から External に出るための VyOS によるルーターと、家の LAN からインターネットに出るための物理ルーターの 2 つのルーターを通る必要があります。ただし、今回の記事において Hyper-V の External から先はほとんど意識する必要がありません。

Squid を入れたプロキシ サーバーは、Client と同じように Hyper-V 仮想マシンとして作成し、Internal のネットワークだけに接続しています。(正確には、apt-get をするためにインターネット接続が必要なので、External にも繋げていますが、普段は無効化しています。)

この状態で Squid をプロキシとして介在させるには、Client –> Internal network –> VyOS –> External network となっている経路のどこかで Squid サーバーを見に行くように設定しないといけません。

透過ではないプロキシであれば、ブラウザーの設定でプロキシのアドレスとして 10.10.90.11 を設定すればいいことになります。この場合の経路は、Client –> Squid –> VyOS –> External network になります。クライアントから Internal network にある Squid に直接接続するところがポイントです。

冒頭の定義で確認したように、今回はクライアントの設定を変えずにプロキシを通すことが目標です。この場合、Client –> Squid というホットラインが使えないので、何らかの設定を VyOS 側で行わないといけないのが分かると思います。すなわち経路としては、Client –> VyOS –> Squid –> VyOS –> External network としたいのです。Squid も External へのアクセスを持たないので、VyOS を 2 度通らないといけません。

いずれの場合も、Squid が VyOS をゲートウェイとして使う必要があるので、忘れずにゲートウェイの設定をしておきます。Squid サーバー上で /etc/network/interfaces を以下のように設定しました。

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
address 10.10.90.11
netmask 255.255.0.0
gateway 10.10.90.12
dns-nameservers 10.10.10.10

iface eth1 inet dhcp

DNS サーバー参照の設定が必要な理由は後述します。DNS の設定を /etc/resolv.conf に反映させるため、eth0 を再起動します。

$ sudo ifdown eth0
$ sudo ifup eth0

OS 側の設定は終了です。次に設定ファイル squid.conf をこのように変更しました。一部の抜粋で、青字が変更部分です。

# Example rule allowing access from your local networks.
# Adapt to list your (internal) IP networks from where browsing
# should be allowed
# acl localnet src 10.0.0.0/8   # RFC1918 possible internal network
acl localnet src 10.10.0.0/16   # Hyper-V Internal
acl localnet src 172.16.0.0/12  # RFC1918 possible internal network
acl localnet src 192.168.0.0/16 # RFC1918 possible internal network
acl localnet src fc00::/7       # RFC 4193 local private network range
acl localnet src fe80::/10      # RFC 4291 link-local (directly plugged) machines

# Squid normally listens to port 3128
http_port 3128
http_port 3129 transparent

(..snip..)

# Impersonation for logging to a file
cache_effective_user john

定義で確認した通り、プロキシは、サーバーとクライアント両方の役割を持っています。非透過プロキシとして 3128/tcp、透過プロキシとして 3129/tcp のポートを使ってサーバーとして動作するようにしました。3128/tcp は Squid のデフォルト ポートです。設定や動作の違いを見るため、非透過プロキシと透過プロキシの両方を設定してみます。

cache_effective_user は、Squid の動作仕様による設定です。Squid を root 権限で起動させると、その後の動作は root ではなく cache_effective_user で設定されたユーザーの UIDを使って動作します。何も設定しないと nobody というユーザーの UID が使われ、ログファイルなどを書き込もうとするときに、書き込み権限がないためのエラーが出ます。Squid 用の専用アカウントを作るのが標準的だとは思いますが、ここでは作業用ユーザーの john を設定しておきます。

ログが書き込めるように、var/logs ディレクトリの所有者を john にしておきます。

john@ubuntu-proxy:~$ ls -la /usr/local/squid/current/var/logs
total 8
drwxr-xr-x 2 root root 4096 Jan  8 11:43 .
drwxr-xr-x 5 root root 4096 Jan  8 11:28 ..
john@ubuntu-proxy:~$ sudo chown john:john  /usr/local/squid/current/var/logs
john@ubuntu-proxy:~$ ls -la /usr/local/squid/current/var/logs
total 8
drwxr-xr-x 2 john john 4096 Jan  8 11:43 .
drwxr-xr-x 5 root root 4096 Jan  8 11:28 ..
john@ubuntu-proxy:~$

これで準備が整いました。今回はテストなので、デーモンとしてバックグラウンドで動かすのではなく、-N をつけて非デーモン モードで実行します。ログがファイルの var/logs/cache.log だけでなく標準出力にも出るので分かりやすいです。

john@ubuntu-proxy:~$ sudo /usr/local/squid/current/sbin/squid -Nd1
2015/01/08 13:04:49| Set Current Directory to /usr/local/squid/squid-3.4.10/var/cache/squid
2015/01/08 13:04:49| Starting Squid Cache version 3.4.10 for x86_64-unknown-linux-gnu…
2015/01/08 13:04:49| Process ID 55453
2015/01/08 13:04:49| Process Roles: master worker
2015/01/08 13:04:49| With 1024 file descriptors available
2015/01/08 13:04:49| Initializing IP Cache…
2015/01/08 13:04:49| DNS Socket created at [::], FD 5
2015/01/08 13:04:49| DNS Socket created at 0.0.0.0, FD 6
2015/01/08 13:04:49| Adding nameserver 10.10.10.10 from squid.conf
2015/01/08 13:04:49| Logfile: opening log daemon:/usr/local/squid/squid-3.4.10/var/logs/access.log
2015/01/08 13:04:49| Logfile Daemon: opening log /usr/local/squid/squid-3.4.10/var/logs/access.log
2015/01/08 13:04:49| Store logging disabled
2015/01/08 13:04:49| Swap maxSize 0 + 262144 KB, estimated 20164 objects
2015/01/08 13:04:49| Target number of buckets: 1008
2015/01/08 13:04:49| Using 8192 Store buckets
2015/01/08 13:04:49| Max Mem  size: 262144 KB
2015/01/08 13:04:49| Max Swap size: 0 KB
2015/01/08 13:04:49| Using Least Load store dir selection
2015/01/08 13:04:49| Set Current Directory to /usr/local/squid/squid-3.4.10/var/cache/squid
2015/01/08 13:04:49| Finished loading MIME types and icons.
2015/01/08 13:04:49| HTCP Disabled.
2015/01/08 13:04:49| Squid plugin modules loaded: 0
2015/01/08 13:04:49| Accepting HTTP Socket connections at local=[::]:3128 remote=[::] FD 9 flags=9
2015/01/08 13:04:49| Accepting NAT intercepted HTTP Socket connections at local=[::]:3129 remote=[::] FD 10 flags=41
2015/01/08 13:04:50| storeLateRelease: released 0 objects

起動したようです。プロセスと TCP ポートを見てみると、確かに squid は john として動いています。

john@ubuntu-proxy:~$ ps -ef | grep squid
root     55452  1115  0 13:04 pts/1    00:00:00 sudo /usr/local/squid/current/sbin/squid -Nd1
john     55453 55452  0 13:04 pts/1    00:00:00 /usr/local/squid/current/sbin/squid -Nd1
john     55454 55453  0 13:04 ?        00:00:00 (logfile-daemon) /usr/local/squid/squid-3.4.10/var/logs/access.log
john     55465 55016  0 13:07 pts/2    00:00:00 grep –color=auto squid
john@ubuntu-proxy:~$ netstat -aon | grep 31
tcp6       0      0 :::3128                 :::*                    LISTEN      off (0.00/0/0)
tcp6       0      0 :::3129                 :::*                    LISTEN      off (0.00/0/0)

実はこれで非透過プロキシとしての設定は終了です。クライアント側の設定を変更して動作を確認します。

inetcpl.cpl –> Connections タブ –> LAN settings でプロキシの設定ができます。全てのプロトコルで 10.10.90.11:3128 を使うように設定しました。

image
Windows のプロキシ設定

これでブラウザーは通常通り動作するはずです。bing.com にアクセスした際の Squid のアクセスログを見てみると、何やらいろいろとログが書き込まれており、プロキシとしてまともに動いている、はずです。Squid の目玉機能であるキャッシュの機能は見ないので、ブラウザーからページが開ければ OK なのです。

john@ubuntu-proxy:~$ tail /usr/local/squid/current/var/logs/access.log
1420753075.003    463 10.10.20.71 TCP_MISS/200 73782 GET
http://www.bing.com/ – HIER_DIRECT/204.79.197.200 text/html
1420753075.205    157 10.10.20.71 TCP_MISS/200 710 POST
http://www.bing.com/rewardsapp/reportActivity – HIER_DIRECT/204.79.197.200 application/x-javascript
1420753075.206    106 10.10.20.71 TCP_MISS/200 503 GET
http://www.bing.com/fd/ls/l? – HIER_DIRECT/204.79.197.200 image/gif
1420753075.251    112 10.10.20.71 TCP_MISS/200 2255 GET
http://www.bing.com/HPImageArchive.aspx? – HIER_DIRECT/204.79.197.200 application/json
1420753075.367    171 10.10.20.71 TCP_CLIENT_REFRESH_MISS/200 493 HEAD
http://www.bing.com/rms/Framework/jc/9aaeda73/23d0202d.js? – HIER_DIRECT/204.79.197.200 application/x-javascript
1420753075.391    256 10.10.20.71 TCP_MISS/200 20970 GET
http://www.bing.com/hpm? – HIER_DIRECT/204.79.197.200 text/html
1420753075.397    202 10.10.20.71 TCP_MISS/200 519 GET
http://www.bing.com/notifications/render? – HIER_DIRECT/204.79.197.200 text/html
1420753075.708    100 10.10.20.71 TCP_MISS/200 503 GET
http://www.bing.com/fd/ls/l? – HIER_DIRECT/204.79.197.200 image/gif
1420753075.957    328 10.10.20.71 TCP_MISS/200 7528 CONNECT login.live.com:443 – HIER_DIRECT/131.253.61.66 –
1420753076.656    110 10.10.20.71 TCP_MISS/200 982 GET
http://www.bing.com/Passport.aspx? – HIER_DIRECT/204.79.197.200 t

では次に、twitter.com にアクセスしてみます。URL を見ると https:// で始まっているように、ツイッターとの通信は SSL で暗号化されています。クライアントの設定で、全プロトコルがプロキシを使うようにしているので、SSL でも Squid が介在するはずであり、結果は問題なくページを開くことができます。アクセス ログを見てみます。ログに出てくる CONNECT というのは HTTP の CONNECT メソッドのことです。

john@ubuntu-proxy:~$ tail /usr/local/squid/current/var/logs/access.log
1420753532.051 110352 10.10.20.71 TCP_MISS/200 51273 CONNECT twitter.com:443 – HIER_DIRECT/199.59.150.39 –
1420753532.051  88048 10.10.20.71 TCP_MISS/200 956439 CONNECT abs.twimg.com:443 – HIER_DIRECT/93.184.216.146 –
1420753532.052  70182 10.10.20.71 TCP_MISS/200 5350 CONNECT abs.twimg.com:443 – HIER_DIRECT/93.184.216.146 –
1420753532.052  70181 10.10.20.71 TCP_MISS/200 6320 CONNECT abs.twimg.com:443 – HIER_DIRECT/93.184.216.146 –
1420753532.052  70182 10.10.20.71 TCP_MISS/200 5318 CONNECT abs.twimg.com:443 – HIER_DIRECT/93.184.216.146 –
1420753532.052  70182 10.10.20.71 TCP_MISS/200 6320 CONNECT abs.twimg.com:443 – HIER_DIRECT/93.184.216.146 –
1420753532.113  85991 10.10.20.71 TCP_MISS/200 8153 CONNECT iecvlist.microsoft.com:443 – HIER_DIRECT/93.184.215.200 –
1420753532.113 109447 10.10.20.71 TCP_MISS/200 4111 CONNECT twitter.com:443 – HIER_DIRECT/199.59.150.39 –

実は、Squid が HTTPS を HTTP と同じようにプロキシできる、というのはおかしいのです。というか、実際には HTTPS についてはプロキシはしていないので、HTTP と同じようにページのキャッシュなどは行われません。理由は簡単で、Squid は暗号化された通信を解読できないためです。

冒頭に書いたように、プロキシはルーティングと異なり、サーバーとしてデータを受け取ってから、新しいリクエストを自分の力で代理人として送信しています。この過程で、パケットの内容を 「解釈」 する必要があるのです。ここで疑問として、「いやいやプロキシがデータを解釈しなくても、HTTPS だろうがなんだろうが、受け取った TCP パケットをまるっとコピーして、新しいパケットとして送信すればいいじゃないか」 と考えるかもしれません。しかしここでのポイントは、プロキシに入ってくるパケットにおいて、宛先 IP アドレス、宛先 TCP ポートは、最終目的地の Web サーバーのものではなく、プロキシのアドレス、ポート番号になっていることです。Windows のプロキシ設定を行なうと、クライアントから要求が送信される時点で、既に宛先はプロキシに設定されているのです。

これは同時に、なぜ Squid サーバー側で DNS サーバー参照設定を行わなければならないか、という理由に繋がります。SSL ではなく HTTP をプロキシする場合、Squid は、IP レベル (OSI 参照モデルのネットワーク層) の宛先 IP アドレスではなく、HTTP レベル (OSI 参照モデルのアプリケーション層) の HTTP 要求に必ず含まれている (たぶん HTTP ヘッダーの Host) 宛先情報を読み取って、データを送信しています。多くの場合、HTTP 要求ヘッダーに含まれる宛先情報は IP アドレスではなくホスト名であるため、Squid が名前解決をしないといけません。これは逆に考えると、クライアント側では名前解決が必要ないということになります。実は、クライアント側で DNS サーバー参照の設定を行わなくても、非透過プロキシを経由した通信は問題なく可能です。

そこで問題となるのが HTTPS です。Squid は暗号化されたペイロードを読めず、自分に送られてきたパケットをどこに送信すればいいか分かりません。では、上の例だと、なぜクライアントはツイッターにアクセスすることができたのでしょうか。理由は、Squid は 「ズル」 をさせてもらっているからです。どういうズルかというと、Squid は、これから始まる  TCP セッションの本当の宛先を、ブラウザーから前もって平文で教えてもらっているのです。これがログに出てくる HTTP CONNECT メソッドです。

クライアント側でキャプチャーを取って、HTTP CONNECT の内容を見てみました。どちらが使われているか分かりませんが、URI と Host という二つのフィールドに、本当の宛先である twitter.com:443 という値が設定されています。また、パケットの宛先が全て 10.10.90.11:3128 になっていることも確認できます。

image

HTTP CONNECT で宛先を教えてもらった後は、Squid は何も頭を使わず、送られてくる暗号化データを受け流す簡単なお仕事をするだけです。トンネリングとも呼びます。ブラウザー側はプロキシの設定に基づいて CONNECT メソッドを送り、プロキシから OK が返ってきたら、ClientHello から始まる鍵交換や、その後の暗号化通信を何事もなかったかのように始めます。CONNECT メソッドは Web サーバーには送信されません。