固定IP不要のVPNサーバをDockerコンテナで構築する

# はじめに

機械学習など負荷のかかる計算を実行するために、GPUなどが乗った専用サーバを自宅に設置している方は多いかと思います。WEBアプリをホスティングしている方もいると思います。基本的には、サーバへは適当な端末でリモートログインするので、自宅であれば場所を選ばないのですが、帰省などで自宅を離れる時これらのリソースが利用できなくなるのでかなり困ります。
技術的にはVPNを利用すれば外出先からアクセスすることは可能ですが構築には一定のコツが必要です。または集合住宅などに住んでいて固定IPを貰えないお家では構築のハードルはさらに上がります。そこで本記事では、クラウドのVPNサーバを使って、外出先からのリモートアクセスをお手軽に実現する方法を紹介します。

VPNは便利な反面、使用方法を誤るとサイバー犯罪・攻撃の踏み台として利用される可能性があります。本内容は鵜呑みにせず理解した上で設定するようにしてください。お決まりですが、本記事で損害を被った場合、私は当該損害に関して一切の責任を負うことができません。

# 前提
以下４点は、読者への前提としています。不足している知識は他のサイトなどで補ってください。

* Dockerやdocker-composeを使ったことがある
* クラウドに仮想マシンをホストできる
* 自宅に安定稼働しているLinuxサーバがある・用意できる
* ネットワークセキュリティに関する知識


# 構築の方針・要件
* SoftEtherのカスケード接続とSecureNAT機能を使った構成とします。https://ja.softether.org/4-docs/1-manual/A/10.B
* IOS、Android、MacOS X などが持つ L2TP/IPsec クライアントに対応します。
* 環境による依存をなくすためソフトウェアは全てコンテナ上で動作させます。
* 再度設定したり見直すことを踏まえ、起動・設定はdocker-composeのみで完結するようにします。

# 構築
大まかな構築の流れは以下の通りです。各章で詳細を説明します。

1. クラウドに仮想マシンをホストし、SoftEtherServer(VPNサーバ)を起動します。
1. 自宅サーバでSoftEtherBridgeを起動し、先ほど起動したVPNサーバとのカスケード接続を確立します。(図中の青点線1)
1. 端末から自宅サーバにアクセスしてみます。(図中の青点線2)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96637/760ce5d2-c560-33e4-6c80-3428d6395c48.png)

## 1.VPNサーバの準備

1. VPNサーバは最低限のスペックを準備します。AWSだとts2.smallで十分です。サーバは`500/udp`、`4500/udp`、`1701/tcp`、`5555/tcp`のインバウンドを許可する必要があります。
1. docker-compose,dockerコマンドをインストールします。
1. 以下のdocker-compose.ymlファイルを準備します。

    ```docker-compose.yml
    version: "3"

    services:
      softether-server:
        image: siomiz/softethervpn
        cap_add:
          - NET_ADMIN
        ports:
          - 500:500/udp
          - 4500:4500/udp
          - 1701:1701/tcp
          - 5555:5555/tcp
        container_name: softether-server
        environment:
          - USERNAME=vpnname
          - PASSWORD=vpnpassword
          - PSK=vpnsecret
          - VPNCMD_HUB=SecureNatDisable
    ```

1. `USERNAME`、`PASSWORD`、`PSK`は任意の文字列を指定してください。それぞれVPNのユーザ名、パスワード、シークレットです。
1. `docker-compose up -d` で起動します。

## 2.VPNブリッジの準備
1. 自宅サーバにdocker-compose,dockerコマンドをインストールします。
1. 以下のdocker-compose.ymlファイルを準備します。

    ```docker-compose.yml
    version: "3"

    services:
      softether-bridge:
        image: sammrai/softether-bridge:latest
        container_name: softether-bridge
        environment:
          - USERNAME=vpnname
          - PASSWORD=vpnpassword
          - PSK=vpnsecret
          - VPN_SERVER=your.vpn.server.com:5555
    ```

1. `USERNAME`、`PASSWORD`、`PSK`は[1.VPNサーバの準備](#1vpnサーバの準備)で設定した文字と同じものを指定してください。
1. `VPN_SERVER`はVPNサーバのパブリックIPまたはFQDNとポート5555を指定します。
  例)ec2-54-168-155-92.ap-northeast-1.compute.amazonaws.com:5555、54.168.155.92:5555
1. `docker-compose up -d` で起動します。
1. `docker-compose logs`でOnline(Established)が表示されていればOKです。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96637/aa18b555-ee19-3951-3a6b-a1df2454312e.png)


## 3.クライアントの設定
あとは接続の確認です。

### MAC

1. システム設定　> ネットワーク　> ＋　を選択し、インタフェースに「VPN」、VPNタイプに「L2TP over IPSec」、サービス名に任意の名称を入力し、作成を押下します。
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96637/bd514df3-5baa-8e0b-5cef-bf0d694832f9.png)
1. サーバアドレスにVPNサーバのパブリックIPまたはFQDNを指定し、アカウント名に[1.VPNサーバの準備](#1vpnサーバの準備)で設定したUSERNAMEの値を入力します。
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96637/d0b2c110-1c22-aff1-b85f-2ec45ba74043.png)
1. 認証設定を押下し、パスワード、共有シークレットに(#1.VPNサーバの準備)で設定したPASSWORD、PSKの値を入力し、OKを押下します。
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96637/886bdf19-d797-be9d-6111-66d6899a621f.png)
1. 詳細を押下し、オプションタブの「すべてのトラフィックをVPN接続経由で送信」にチェックを入れOKを押下します。 
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96637/24a3940f-9ca3-d9a0-1367-055e08206f0d.png)
1. Connectを押下します。ステータスが接続になれば成功です！自宅のリソースにアクセスできることを確認してください。
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96637/0a800981-1aaa-70b9-1685-ddfe8f5b5464.png)



### IPhone
1. 設定 > 一般 > VPN を選択し、VPN設定を追加するを押下します。
![cropped_68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f39363633372f30643437626539342d346139382d376639332d646430632d3666656264333033356165652e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96637/4f4a089d-38b2-d709-5844-09ec041de253.png)
1. TypeにL2TPを選択し、サーバにVPNサーバのパブリックIPまたはFQDNを指定します。
1. アカウント、パスワード、シークレットにそれぞれ、(#1.VPNサーバの準備)で設定したUSERNAME、PASSWORD、PSKの値を入力し、完了を押下します。
![cropped_68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f39363633372f30363661656630612d653032312d393263312d333537622d6632616162353331313631382e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96637/e44ad8da-d009-e1cc-2876-04a072564e77.png)
1. VPN接続のトグルをONにし、接続と表示されれば成功です！
![cropped_68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f39363633372f31316561306133622d613466392d366432382d643438382d3262636334633034626562622e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96637/5444b6d7-2b57-b71a-a3b3-5498700416cf.png)



# おまけ
## SoftEther VPN コマンドライン管理ユーティリティ
VPNサーバ/ブリッジの設定を対話式で行いたい場合、「VPN サーバー管理マネージャ」、または「コマンドライン管理ユーティリティ(vpncmd)」が用意されています。Windowsを持っていない場合は後者のvpncmdを使用する必要があります。VPNサーバの場合環境変数に`VPNCMD_SERVER=ServerPasswordSet youradminpassword`を追加後コンテナを再起動(down/up)し、以下のコマンドを実行することでログインすることができます。

```sh
docker exec -it softether-server vpncmd localhost /SERVER /PASSWORD: youradminpassword
```

VPNブリッジの場合は以下のコマンドでログインでいます。

```
docker exec -it softether-bridge /usr/local/vpnbridge/vpncmd localhost /SERVER
```

https://ja.softether.org/4-docs/1-manual/6

## 外部通信を直接インターネットにルーティングする

:::note warn
(2024/5/1追記)
当該機能を利用するためには、`docker pull sammrai/softether-bridge:latest`で最新のイメージを使用してください
:::



[vpnブリッジの準備](#2vpnブリッジの準備)の章で、`docker-compose.yml`の環境変数に`ROUTE_PRIVATE_IPS_ONLY=true` を指定し、クライアントの設定から「すべてのトラフィックをVPN接続経由で送信」を無効にすると外部通信を直接インターネットにルーティングすることができます。詳細は下記リンクから確認してください。

https://qiita.com/sammrai/items/6c3f54267357de22a88f#softether-bridgeで使ってみる


## 通信のメカニズム

[構築](#構築)の青点線2の経路をがどのように実現されるか、VPN、自宅LANを中心に下に図示しました。赤がサーバ、グレーがDockerコンテナ、黒の実線がサブネットを表しています。SoftEtherBridgeコンテナを起動すると以下が自動的に走るようにDockerイメージを作成しています。

> ソース: https://github.com/sammrai/softether-bridge/blob/main/entrypoint.sh#L31-L37

1. SecureNAT(②仮想DHCP、③仮想NATのこと)をユーザモードに変更
1. SecureNATを有効にする。(②③が有効になったあと、サブネット①が自宅サーバに展開されます。このタイミングでは①はVPNサーバまで到達していません。)
1. VPNサーバへのカスケード接続を作成し、認証情報を設定します（まだ接続していません）
1. VPNサーバへのカスケード接続をオンラインに変更(①がVPNサーバまで展開され、②③がVPNサーバにも提供されます。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96637/7d38a4cc-db8d-529e-7456-003a207a6bdf.png)

自宅サーバ2にアクセスする時、端末からは以下の流れで接続します。

1. 端末は認証情報を使ってVPNサーバに接続ます。このとき②から192.168.30.10のIPと、デフォルトゲートウェイ192.168.30.1が通知されます。
1. 自宅サーバ2の192.168.x.zへアクセスはまず①のデフォルトゲートウェイである192.168.30.1にルーティングされます。
1. アクセス元は、③によってVPNから自宅LANのIPに変換されます。この時、自宅サーバ1のIP 192.168.x.yが割り当てられるでしょう。
1. 自宅サーバ2は、自宅サーバ1の192.168.x.yからの接続と思っていつもの通り応答を返します。
1. 応答を逆順に返します。

# 参考にしたサイト

https://github.com/siomiz/SoftEtherVPN

https://ja.softether.org/4-docs/1-manual/A/10.B

https://itkotsukotsu.hatenablog.com/entry/2020/10/13/235324