[Container][IPsec] Containerからの通信をホスト側に設定されたIPsecトンネルに流したい。

AIX、UNIX、LinuxContainer
vanveenjf-1136697-unsplash
photo by unsplash.

はじめに

頭悪い内容の記事で申し訳ありません。

とあるdockerホスト側にIPsecトンネルを張っていたのですが、Container側からの通信をdockerホスト側のIPsecトンネルに流すにはどうすれば良いのか悩んだので記事にしておきます。知ってる人には簡単なことかもしれませんが・・・

Four ways to connect a docker container to a local network
Update (2018-03-22) Since I wrote this document back in 2014, Docker has developed the macvlan network driver. That give...

サーバ同士を接続するsite-to-siteなVPNの接続方法についてはたくさん記事はありますが、ひねった感じの記事が無かったので悩むことになりました。

今回の構成

今回の構成は以下の通り。

site-to-site vpn docker
とても分かりづらい図で申し訳ありません

某VPSの中にZabbix環境をdocker Containerとして実装しています。ちなみに使用しているzabbix-dockerは以下のものです。

GitHub - zabbix/zabbix-docker: Official Zabbix Dockerfiles
Official Zabbix Dockerfiles. Contribute to zabbix/zabbix-docker development by creating an account on GitHub.

基本的にzabbixは同一LAN内で使用することがほぼ前提条件になると思いますが(zabbix proxyを使用する方法はありますが今回はなしでお願いします)、今回はIPsecで担保しています。

で、今回の場合、dockerホスト側にIPsecトンネルを張ることにしました。使用したパッケージはstrongswanです。

ちなみにまたも横道に逸れますが、libreswanとどっちが良いのでしょうか? ipsec verifyはlibreswanにしか無いと思うので、libreswanの方が良いのでしょうか?

設定内容

以下のようなconfigを用意しました。

# ipsec.conf - strongSwan IPsec configuration file
# basic configuration
config setup
        charondebug="all"
        uniqueids=yes
        strictcrlpolicy=no
# Add connections here
conn mikrotik-to-linux
        keyexchange=ikev2
        mobike=no
        keyingtries=%forever
        authby=secret
        auto=start
        type=tunnel
        left=%defaultroute
        leftid=<VPSのEIP>
        leftsubnet=10.15.0.0/16
        right=<接続先IPアドレス>
        rightsubnet=192.168.88.0/24
        ike=aes256-sha2_256-modp3072!
        esp=aes256-sha2_256-modp3072!
        ikelifetime=3h
        lifetime=1h
        dpddelay=30
        dpdtimeout=120
        dpdaction=restart
        forceencaps=yes
        fragmentation=yes
        compress=no
        closeaction=restart
        installpolicy=yes

/etc/ipsec.secrets 下記にあるPSK(共通鍵)は実際に使用していません。

# This file holds shared secrets or RSA private keys for authentication.
# RSA private key for this host, authenticating it to any other host
# which knows the public part.
<接続元外側IP> <接続先外側IP> : PSK "wIG8N2jrkrxo7uSP5DJvnIEb0Xd494hy"

接続確認

接続先については割愛。で実施にIPsecが張れているか確認する。

$ sudo ipsec status
Security Associations (1 up, 0 connecting):
mikrotik-to-linux[2]: ESTABLISHED 23 minutes ago, 2.xxx.xxx.xxx[2.xxx.xxx.xxx]...1.yyy.yyy.yyy[1.yyy.yyy.yyy]
mikrotik-to-linux{2}:  INSTALLED, TUNNEL, reqid 2, ESP in UDP SPIs: c9012d78_i 05dc8810_o
mikrotik-to-linux{2}:   10.15.0.0/16 === 192.168.88.0/24
$ sudo ip xfrm policy 
src 10.15.0.0/16 dst 192.168.88.0/24 
        dir out priority 379519 
        tmpl src 2.xxx.xxx.xxx dst 1.yyy.yyy.yyy
                proto esp spi 0x05dc8810 reqid 2 mode tunnel
src 192.168.88.0/24 dst 10.15.0.0/16 
        dir fwd priority 379519 
        tmpl src 1.yyy.yyy.yyy dst 2.xxx.xxx.xxx
                proto esp reqid 2 mode tunnel
src 192.168.88.0/24 dst 10.15.0.0/16 
        dir in priority 379519 
        tmpl src 1.yyy.yyy.yyy dst 2.xxx.xxx.xxx
                proto esp reqid 2 mode tunnel
src 0.0.0.0/0 dst 0.0.0.0/0 
        socket in priority 0 
src 0.0.0.0/0 dst 0.0.0.0/0 
        socket out priority 0 
src 0.0.0.0/0 dst 0.0.0.0/0 
        socket in priority 0 
src 0.0.0.0/0 dst 0.0.0.0/0 
        socket out priority 0 
src ::/0 dst ::/0 
        socket in priority 0 
src ::/0 dst ::/0 
        socket out priority 0 
src ::/0 dst ::/0 
        socket in priority 0 
src ::/0 dst ::/0 
        socket out priority 0 
src 2.xxx.xxx.xxx/32 dst 1.yyy.yyy.yyy/32 proto udp sport 1701 dport 1701 
        dir out priority 366847 
        tmpl src 0.0.0.0 dst 0.0.0.0
                proto esp spi 0x0f255f8e reqid 1 mode transport
src 1.yyy.yyy.yyy/32 dst 2.xxx.xxx.xxx/32 proto udp sport 1701 dport 1701 
        dir in priority 366847 
        tmpl src 0.0.0.0 dst 0.0.0.0
                proto esp reqid 1 mode transport
src 1.yyy.yyy.yyy/32 dst 2.xxx.xxx.xxx/32 proto udp sport 1701 dport 1701 
        dir fwd priority 366847 
        tmpl src 1.yyy.yyy.yyy dst 2.xxx.xxx.xxx
                proto esp reqid 1 mode tunnel

ちなみに

strongswanってポリシーベースルーティングが設定されるんですな。

$ sudo ip route get 192.168.88.3
192.168.88.3 via 2.xxx.xxx.1 dev eth0 table 220 src 10.15.0.1 uid 0 
    cache 

このままではContainer内部からはグローバルにしか疎通できません。そのためホスト側のiptablesに以下のルールを追加します。dockerと共用しているので扱いには注意が必要です。

$ sudo iptables -t nat -I POSTROUTING -s 172.16.238.x -d 192.168.88.0/24 -j SNAT --to-source 10.15.0.2

NATテーブルのPOSTROUTINGチェインに、送信元が該当のContainerのIPアドレスを、送信先にIPsecで接続された接続先IPアドレスを、SNAT(送信元のアドレス変換)する、ということになります。

特定の通信先に対し、送信元のアドレスを変換するSNATを使用して、ホスト側のIPアドレスに変換して、接続されているIPsecトンネルに通信を流す、ということになります。

最初は、IPsec側のサブネット(leftsubnet)を0.0.0.0/0とかにすることも試してみたんですが、接続先の通信がVPS側に流れてしまって通信がおかしいことになったので(たぶん自分の勉強不足)、通信を制限した上で接続先に流す方法にしました。

これで良いかと言われれば、Container自体がdockerのネットワークブリッジの設定に従って自動的に採番されるためこのままでは、Containerを起動するたびにiptablesのルールを修正する必要があります。さすがにそれではあまりにも非効率なので、なんとか自動的にする方法を模索中です。

この記事を書いた人

kometchtech

うつ病を患いながら、IT業界の末席にいるおっさんエンジニア。科学計算をしたことがないのに、HPC分野にお邪魔している。興味のある分野で学習したことをblogにまとめつつ、うつ病の経過症状のメモも置いておく日々。じつはRouterboard User Group JPの中の人でもある。 Amazon欲しいものリスト / Arm板を恵んでくれる人募集中

kometchtechをフォローする
タイトルとURLをコピーしました