プロキシでVPNスプリットトンネルさせてみた

# はじめに
一部の宛先だけVPN経由の通信にしたいけど、Client側にVPNの設定を入れ込めない、みたいなときに回避策としてVPNスプリットトンネルの設定をしたプロキシを立ててそのプロキシ経由でアクセスする、ということをやってみました。  
図にするとこんな感じです。
![構成.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959481/6b415bed-7842-5374-39ce-def3113d1487.jpeg)

今回はProxyサーバにはSquidを利用し、そのサーバ上でOpenVPN Clientの設定を行います。  
ClientからcurlをProxy経由でたたき、2つの異なる接続元のグローバルIPを返してくれるサービス(httpbin.orgとipinfo.io)にリクエストを行いそのレスポンスを見ていきます。  
httpbin.orgをVPN経由、ipinfo.ioをVPNを経由せずにアクセスするようにします。なので正確にはこんな感じの構成になります。  
![構成2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959481/6c96bfca-ecd3-d145-cdef-f6d8ae3e49d3.jpeg)



# 必要なもの
- Docker(OpenVPNサーバの起動に利用)
- [OpenVPN](https://www.openvpn.jp/)
- [Squid](http://www.squid-cache.org/)(Proxyサーバ)
- curl(Client)

OpenVPNサーバのホスト、Dockerネットワーク、Proxyサーバ(Clientと同じネットワーク前提)のCIDRは被らないようにしておきます。またOpenVPNにより払い出されるtunのCIDRが192.168.255.0/24なのでそれとも被らないようしておきます。  
本記事ではホストOSはすべてUbuntuを利用しています。


# OpenVPNサーバの設定
まずはProxyサーバがVPN接続を行うためのOpenVPNサーバを立てます。  
ProxyサーバとはグローバルIPが異なるかつProxyサーバから疎通が通る場所に構築します。今回はEC2をPublicサブネットに建てました。  
(作業用にSSHやVPNに利用する1194ポート(UDP)はアクセスできるようにしておきます。)  
少し古いですが[kylemanna/openvpn](https://hub.docker.com/r/kylemanna/openvpn/)という使えそうなDockerイメージがあったため、こちらを利用しました。ドキュメントに沿って構築します。

```bash
# Volumeの代わりにホストのディレクトリを利用するため作成
# このディレクトリにもろもろファイルが作成されます
$ mkdir ~/opvn_data

# 初期設定
# <YOUR-SERVER-NAME>はEC2のPublicDNS名を設定。Clientからの接続に利用するFQDNと同一にする必要が有るかは不明
$ cd ~
$ sudo docker run -v $(pwd)/opvn_data:/etc/openvpn --rm kylemanna/openvpn ovpn_genconfig -u udp://<YOUR-SERVER-NAME>
# CAが作成される。CNを聞かれるが、上記で設定したServer Nameと同じものにしておく
# crtファイルのパスワードも聞かれるため任意のものを設定
$ sudo docker run -v $(pwd)/opvn_data:/etc/openvpn --rm -it kylemanna/openvpn ovpn_initpki

# OpenVPNサーバの起動
$ sudo docker run -v $(pwd)/opvn_data:/etc/openvpn -d -p 1194:1194/udp --cap-add=NET_ADMIN kylemanna/openvpn
```

これでOpenVPNサーバが起動します。  
起動した後はProxyサーバからアクセスするためにクライアント証明書を生成しておきます。  
```bash
# <CLIENTNAME>は任意のもので問題ありません
$ sudo docker run -v $(pwd)/opvn_data:/etc/openvpn --rm -it  kylemanna/openvpn easyrsa build-client-full <CLIENTNAME> nopass
```
以下のファイルがClientとなるProxyサーバに必要なため、送信しておきます。
- ~/opvn_data/pki/ca.crt
- ~/opvn_data/pki/ta.key
- ~/opvn_data/pki/issued/<CLIENTNAME>.crt
- ~/opvn_data/pki/private/<CLIENTNAME>.key



# Proxyサーバの設定
## OpenVPN Clientの設定
残念ながらVPNの設定に明るくないため、ChatGPTにServer側のconfig(~/opvn_data/openvpn.conf)を渡してClient側のconfigを生成してもらいました。    
生成されたconfigは以下です。routeに登録してあるIPはhttpbin.orgのものです。このファイルをProxyサーバの任意のディレクトリに配置しておきます。    

```config: client.conf
client
dev tun
proto udp
remote <YOUR-SERVER-NAME> 1194        # OpenVPNサーバのFQDNに置き換えてください
resolv-retry infinite
nobind
user nobody
group nogroup
persist-key
persist-tun
mute-replay-warnings
ca /path/to/your/ca.crt               # Clientに配置したファイルのパスに置き換えてください
cert /path/to/your/<CLIENTNAME>.crt   # Clientに配置したファイルのパスに置き換えてください
key /path/to/your/<CLIENTNAME>.key    # Clientに配置したファイルのパスに置き換えてください
tls-auth /path/to/your/ta.key 1       # Clientに配置したファイルのパスに置き換えてください
cipher AES-256-CBC
comp-lzo no
verb 3

# 手動でスプリットトンネルのためのroute設定追加
# 参考: https://www.330k.info/essay/openvpn-client-split-tunnel/
route-nopull
route 54.161.176.214 255.255.255.255
route 35.153.249.234 255.255.255.255

```

設定後、以下のコマンドでOpenVPN Clientを起動しVPN接続を行います。
```bash
# 実行後、Initialization Sequence Completedと表示されていればOKです。
$ sudo openvpn --config /path/to/your/file/client.conf
```


## Squidの設定
[【Linux】Ubuntu 22.04 Proxyサーバ構築手順](https://tech.willserver.asia/2022/06/10/lix-ubt2204-proxy-setup/)を参考にさせてもらいました。
```bash
# install
$ sudo apt-get install squid

# 設定の編集
$ sudo vim /etc/squid/squid.conf
# 以下の内容を追加、my_rangeの範囲はcurlを実行するIPレンジを指定してください
acl my_range src x.x.x.x/xx
http_access allow my_range

# 再起動
$ sudo systemctl restart squid.service
```

# Clientからcurlを実行し確認
httpbin.org と ipinfo.ioで通信経路が違うことを確認します。  
Clientから以下のコマンドを実行します。
```bash
# VPN経由しない宛先
# レスポンスのIPがProxyサーバ環境のグローバルIPで返ってくる
$ curl https://ipinfo.io/ip -x http://<YOUR-PROXY-SERVER>:3128

# VPN経由する宛先
# レスポンスのIPがOpenVPNサーバ環境のグローバルIPで返ってくる
$ curl https://httpbin.org/ip -x http://<YOUR-PROXY-SERVER>:3128
```

OpenVPN Clientを停止後(ctrl + c)、再度httpbin.orgにリクエストを送ってみるとProxyサーバ環境のグローバルIPが返ってくることも確認できます。  

# 停止
起動したものは以下で停止できます。削除などはそれぞれ行ってください。  
OpenVPN Clientはctrl+cで停止できます。
```bash
# Squid
$ sudo systemctl stop squid.service

# OpenVPN Server
$ sudo docker stop <YOUR OPENVPN SERVER DOCKER ID>
```