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

2019-06-24 10:49:52AIX、UNIX、Linux, Containercontainer, docker, ipsec, iptables, linux

vanveenjf-1136697-unsplash
photo by unsplash.

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

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

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

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

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

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

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

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

POINT

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

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

/etc/ipsec.conf

# 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 
CAUTION

このままでは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(送信元のアドレス変換)する、ということになります。

POINT

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

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

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

ITエンジニアになる! チャレンジDocker仮想化環境構築入門
秀和システム (2018-10-15)
売り上げランキング: 20,392

2019-06-24 10:49:52AIX、UNIX、Linux, Containercontainer, docker, ipsec, iptables, linux