スマホの通信を横抜きするのであれば、LAN内にmitmproxyを立ててWi-Fiを使えばいい。
だが、キャリア網を通る際の通信を確認する必要が出てきたかなと思いこんだ感があり、dockerの勉強がてら試してみたメモ
注意事項
- 今までdockerは食わず嫌いと言うか、触る必要があまりなかったというかで無知レベルです
- モバイル網の通信をとる必要上、インターネットに公開するプロキシサーバを立てる形になります
- セキュリティ周りについてはあまり記載していないので、もしもこの記事を参考にする場合はそのあたりご注意ください
- ツッコミなどあればキレ良くやさしく入れていただけると悦びます
要件
- スマホがモバイル通信でやり取りするデータの確認
- IPv6を優先させたい
- スマホの基本動作に必要な通信は邪魔させない(未実現)
- AppleやGoogleとの通信でSSL Pinningされているので、mitmproxyを通すと通信しなくなる
- そもそもプロキシを通れないブツも存在する
- 除外はpacかignore_hostsオプションで場当たり的に逃げる
- mitmproxyで用いる証明書は保存が効くように
- 踏み台防止のために、気休めにプロキシ認証を設ける
環境
- さくらVPS
- IPv4/v6 デュアルスタック
- Ubuntu Server 20.04.03
- docker/docker-compose
- Docker version 20.10.7, build 20.10.7-0ubuntu1~20.04.1/docker-compose version 1.25.0, build unknown
- Ubuntuのパッケージ
- Docker version 20.10.8, build 3967b7d/docker-compose version 1.29.2, build 5becea4c
- 公式のパッケージ
- Docker version 20.10.7, build 20.10.7-0ubuntu1~20.04.1/docker-compose version 1.25.0, build unknown
手順
あらすじ
- Ubuntuのインストールと設定
- IPv4/v6の両方で疎通が取れるように
- ポートの穴開け
- SSH用の22/tcpとmitmoroxyの8080/tcp、mitmwebの8081/tcpをufwで
- timezoneとかapt upgradeとか
- dockerとdocker-composeのインストール
- Ubuntuパッケージでも公式リポジトリでも使えた(たぶん)
docker-compose.yamlを作成
とりあえず /opt/mitmweb
にファイルを作っていく
docker-compose.yamlの中身
version: "2.4"
services:
mitmweb:
image: "mitmproxy/mitmproxy"
ports:
- "8080:8080/tcp"
- "8081:8081/tcp"
volumes:
- "./certs:/home/mitmproxy/.mitmproxy"
- "./config/gai.conf:/etc/gai.conf"
- "./config/mitm.htpasswd:/etc/mitmproxy/mitm.htpasswd"
command: "mitmweb --listen-host 0.0.0.0 --web-host 0.0.0.0 --proxyauth=@/etc/mitmproxy/mitm.htpasswd --set block_global=false"
networks:
default:
enable_ipv6: true
driver: bridge
ipam:
driver: default
config:
- subnet: fd01:dead:beef::/64
docker-compose.yamlの内容
-
version
は、後のenable_ipv6
を使うために2.x系でないとダメと見かけたので2.4
と -
image
はdockerhubにあるmitmproxy公式のもの を利用 -
ports
- 8080/tcp はプロキシ用のポート
- 8081/tcp はmitmweb画面アクセス用のポート
-
volumes
-
./certs:/home/mitmproxy/.mitmproxy
は、SSL Bumpに用いられる証明書類- ファイルが無い場合は、初回起動時にmitmproxyが生成
-
./config/gai.conf:/etc/gai.conf
は、IPv4/v6の優先順位設定ファイル- 後の
subnet
でULAを指定しているので優先度が低い
- 後の
-
"./config/mitm.htpasswd:/etc/mitmproxy/mitm.htpasswd"
- プロキシ認証用のhtpasswdファイル
-
-
command
- imageの標準コマンドを書き換え
-
mitmweb
- Web画面で見ることができるようにしておきたかっただけ
-
--listen-host 0.0.0.0
- たぶんいらない(デフォ値)
-
--web-host 0.0.0.0
- Web画面がデフォでローカルホストからしか見られないため、それの解除
-
--proxyauth=@/etc/mitmproxy/mitm.htpasswd
- プロキシ認証を設ける設定
-
--set block_global=false
- デフォでプロキシサーバとしてローカルアドレスからしかアクセスを受け付けないため、それの解除
-
- imageの標準コマンドを書き換え
-
networks
-
enable_ipv6: true
- 作成されるdockerネットワーク、コンテナでIPv6設定を使うように
-
subnet: fd01:dead:beef::/64
- 適当なULAアドレスを指定
- それ以外
- ネット上でのかき集め
-
gai.confを作成
./configに突っ込む
gai.confの中身
label ::1/128 0
label ::/0 1
label 2002::/16 2
label ::/96 3
label ::ffff:0:0/96 4
gai.confの内容
getAddressInfoにおけるIPv4/v6の優先順位の設定
ubuntuの /etc/gai.conf
に記述されているlabelを一部コメントアウトしたもの
ULAの定義である #label fc00::/7 6
のコメントアウトと、valueを6から1 (または4未満) への書き換えでも良いらしい
mitm.htpasswdを作成
./configに突っ込む
mitm.htpasswdの作り方(例)
/opt/mitmproxy/config# htpasswd -cB /opt/mitmproxy/config/mitm.htpasswd [ユーザー名]
New password:[パスワードを入れてエンター]
Re-type new password:[もう一回パスワードを入れてエンダー]
Adding password for user [ユーザー名]
- htpasswdコマンドで作るのが手っ取り早い
-
-cB
のうち、c
はファイルを新規作成するためのオプション- 更新時はいらない
-
-cB
のうち、B
はbcrypt方式を使うためのオプション- 省略するとデフォはMD5で弱い
- かといって、bcryptにするとmitmproxyにエラーログが残る
- 暗号強度を上げつつ、エラーログを抑止するのであれば、
openssl passwd -6
を使えばいいのかも知れない
- 以降はファイル名とユーザー名
- ユーザー名とパスワードは任意で
mitm.htpasswdの中身(例)
[ユーザー名]:$2y$~~~
dockerの設定変更
/etc/docker
に daemon.json
を作成する
/etc/docker/daemon.jsonの中身
{
"experimental": true,
"ip6tables": true
}
/etc/docker/daemon.jsonの内容
-
experimental
-
ip6tables
の有効化に必要っぽいので、true
にする
-
-
ip6tables
- IPv6の通信がdocker networkから外に出るときに、フォワードとマスカレードが行われるよう自動的にip6tablesに設定させる
-
ipv6
とfixed-cidr-v6
は、デフォのbridgeネットワークに係る設定っぽいので、今回は省略- docker-composeを使わず、docker runでやるのであれば必要っぽい
ファイルを作成したら、設定を反映させるためにdockerサービスを再起動させる
動作確認
先ずは -d
オプションを付けてコンテナを起動させる。
# docker-compose up -d
Creating network "mitmweb_default" with driver "bridge"
Pulling mitmweb (mitmproxy/mitmproxy:)...
latest: Pulling from mitmproxy/mitmproxy
33847f680f63: Pull complete
b693dfa28d38: Pull complete
ef8f1a8cefd1: Pull complete
248d7d56b4a7: Pull complete
7ae7b795eae0: Pull complete
35aca2d857da: Pull complete
6db2f6c8ec18: Pull complete
e8df46025ee3: Pull complete
af52cbe639e0: Pull complete
45c61fab0022: Pull complete
facc414d9057: Pull complete
Digest: sha256:d904261d75663de45978ea535abad16c85b0f7d00a437195e48cb4c7d05b5269
Status: Downloaded newer image for mitmproxy/mitmproxy:latest
Creating mitmweb_mitmweb_1 ... done
ネットワーク状態確認
# ip a
(前略)
6: br-209c58edc94f: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:18:79:bf:cf brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global br-209c58edc94f
valid_lft forever preferred_lft forever
inet6 fd01:dead:beef::1/64 scope global
valid_lft forever preferred_lft forever
inet6 fe80::42:18ff:fe79:bfcf/64 scope link
valid_lft forever preferred_lft forever
inet6 fe80::1/64 scope link
valid_lft forever preferred_lft forever
(以下省略)
新たに作られたインタフェースに、docker-compose.yamlのsubnetで指定したセグメントのアドレスである fd01:dead:beef::1/64
が割り当てられている。
# ip6tables-save | grep dead:beef
-A POSTROUTING -s fd01:dead:beef::/64 ! -o br-209c58edc94f -j MASQUERADE
-A POSTROUTING -s fd01:dead:beef::2/128 -d fd01:dead:beef::2/128 -p tcp -m tcp --dport 8081 -j MASQUERADE
-A POSTROUTING -s fd01:dead:beef::2/128 -d fd01:dead:beef::2/128 -p tcp -m tcp --dport 8080 -j MASQUERADE
-A DOCKER ! -i br-209c58edc94f -p tcp -m tcp --dport 8081 -j DNAT --to-destination [fd01:dead:beef::2]:8081
-A DOCKER ! -i br-209c58edc94f -p tcp -m tcp --dport 8080 -j DNAT --to-destination [fd01:dead:beef::2]:8080
-A DOCKER -d fd01:dead:beef::2/128 ! -i br-209c58edc94f -o br-209c58edc94f -p tcp -m tcp --dport 8081 -j ACCEPT
-A DOCKER -d fd01:dead:beef::2/128 ! -i br-209c58edc94f -o br-209c58edc94f -p tcp -m tcp --dport 8080 -j ACCEPT
ip6tablesに設定が入っている
# docker network inspect mitmweb_default
[
{
"Name": "mitmweb_default",
"Id": "209c58edc94f0488727d3f716cae25fabdcf3c510dedd11cb388889ae6e1c0d0",
"Created": "2021-09-04T23:58:47.48758425+09:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": true,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
},
{
"Subnet": "fd01:dead:beef::/64"
}
]
},
"Internal": false,
"Attachable": true,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"2fcfb19e55fd2f7edc694a4ef81f4c29f8f6d20992db81932ffab9f3fe094c9e": {
"Name": "mitmweb_mitmweb_1",
"EndpointID": "b336b45cbb68a97067c7519d5cdd993c93771f75b01b697285e520792e3c6847",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": "fd01:dead:beef::2/64"
}
},
"Options": {},
"Labels": {
"com.docker.compose.network": "default",
"com.docker.compose.project": "mitmweb",
"com.docker.compose.version": "1.25.0"
}
}
]
docker-composeによって作られたdocker networkにもIPv6の設定が反映されている
mitmproxyを通した通信の確認
Web画面側で見づらい部分なので、(docker attach mitmweb_mitmweb_1
)しておく。
別のPCからプロキシを通した通信をさせてみる。
面倒くさいので、証明書は入れずに-kで逃げる。
# curl -x http://[ユーザー名]:[パスワード]@[dockerホストのGIP]:8080/ -k -sv -o /dev/null https://qiita.com/
* Trying 198.51.100.0:8080...
* TCP_NODELAY set
* Connected to 198.51.100.0 (198.51.100.0) port 8080 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to qiita.com:443
* Proxy auth using Basic with user '[ユーザー名]'
> CONNECT qiita.com:443 HTTP/1.1
> Host: qiita.com:443
> Proxy-Authorization: Basic bWl0bXVzZXI6bWl0bXBhc3N3b3Jk #※「[ユーザー名]:[パスワード]」をBase64符号化したもの。ここでは「mitmuser:mitmpassword」、伏せ忘れたのは内緒
> User-Agent: curl/7.68.0
> Proxy-Connection: Keep-Alive
>
(中略)
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=qiita.com
* start date: Sep 2 15:04:30 2021 GMT
* expire date: Sep 4 15:04:30 2022 GMT
* issuer: CN=mitmproxy; O=mitmproxy
(以下省略)
指定したプロキシを指定したユーザーで認証していることと、issuerが「mitmproxy」に変わっていることから、SSL Bumpまでできていることが判る。元は「issuer: C=US; O=Amazon; OU=Server CA 1B; CN=Amazon」
# docker attach mitmweb_mitmweb_1
203.0.113.0:63952: client connect
203.0.113.0:63952: server connect qiita.com:443 ([2406:da14:add:901:8220:56ac:dcfa:3cde]:443)
203.0.113.0:63952: client disconnect
203.0.113.0:63952: server disconnect qiita.com:443 ([2406:da14:add:901:8220:56ac:dcfa:3cde]:443)
qiita.comへアクセスする際に、IPv6で通信していることが判る。gai.confの設定が反映されていない場合は、IPv6 ULAよりもIPv4が優先されて、IPv4アドレスが出る。
ついでに、他ホストにもアクセスしてみる。
203.0.113.0:63952: client connect
203.0.113.0:63952: server connect www.google.com:443 ([2404:6800:4004:81f::2004]:443)
203.0.113.0:63952: client disconnect
203.0.113.0:63952: server disconnect www.google.com:443 ([2404:6800:4004:81f::2004]:443)
203.0.113.0:63952: client connect
203.0.113.0:63952: server connect www.kame.net:443 ([2001:2f0:0:8800::1:1]:443)
203.0.113.0:63952: client disconnect
203.0.113.0:63952: server disconnect www.kame.net:443 ([2001:2f0:0:8800::1:1]:443)
203.0.113.0:63952: client connect
203.0.113.0:63952: server connect ipv4.google.com:443 (216.58.220.110:443)
203.0.113.0:63952: client disconnect
203.0.113.0:63952: server disconnect ipv4.google.com:443 (216.58.220.110:443)
203.0.113.0:63952: client connect
203.0.113.0:63952: server connect www.yahoo.co.jp:443 (182.22.28.252:443)
203.0.113.0:63952: client disconnect
203.0.113.0:63952: server disconnect www.yahoo.co.jp:443 (182.22.28.252:443)
きちんと、IPv4/v6デュアルスタックである「qiita.com」「www.google.com」「www.kame.net」はIPv6アドレスでアクセスされている。
IPv4シングルスタックである「ipv4.google.com」「www.yahoo.co.jp」へはIPv4アドレスでアクセスされている。
あとはmitmwebの画面(:8081)を見て、通信の内容が取れてりゃOK
雑記
- iOSからモバイル網経由でインターネット上のmitmproxyを使う場合は、グローバルHTTPプロキシで設定を
- APNペイロードではpacが使えないので面倒くさい
- Androidもだいたい同じ
- testdpcを使えばたぶんなんとかなった気がする
- 迂回させるべき通信はログやwiresharkとにらめっこ
- mitmwebのアクセスにnginxでも通して証明書認証でも設けるといいかも
- 素で認証持ってないっぽい
- nginxのコンテナくっつけりゃ、proxy.pacの配信もできて一石二鳥かも
- dockerでコンテナ上に既にファイルがあるディレクトリをローカルとvolumeでくっつけた時に、ローカルにコンテナのファイルを引っ張ってくる方法は無いものか
- experimental設定とか正直避けたいんだけど、いつ正式対応するんだろ
- docker-compose.yamlが3.xだと使えないオプションって、廃止されるのだろうか