VPSとVPN(WireGuard)を使用したグローバルIPの割り当てがない環境でのサーバー構築

2023/05/24に公開約6,100字

背景

グローバルIPが割り当てられない環境(マンション, 職場, 研究室)に機械学習サーバーを設置したい。でも、グローバルIPがないとLAN外から接続できませんね。そこで、VPSを借りてVPNサーバーとして利用し、出先のラップトップからVPSのグローバルIPを踏み台として、機械学習サーバーに接続できます。

ネットで調べるとVPNのフレームワークとして、主にopenvpn, wireguard, softetherの3つが主流のようです。今回は特にモダンでシンプルと評判のwireguardを使用したVPN環境構築を行います。

ネットワーク図

以下のようなネットワーク構成になります。今回はクライアントがWindowsと想定して行います。また、機械学習環境をWSLにセットアップすることを想定し、WSLに接続する方法も紹介します。

下準備

  1. VPSサーバーを借りる
  2. デスクトップPCにWSLを入れる

VPN環境構築

サーバーサイド(VPS)のセットアップ その1

  1. Wireguardのインストール

    sudo apt install wireguard -y
    
  2. 鍵の生成

    sudo -i <<EOF
    cd /etc/wireguard/
    wg genkey | tee private_key | wg pubkey > public_key
    exit
    EOF
    
  3. 鍵の閲覧

    sudo -i <<EOF
    cd /etc/wireguard/
    cat private_key
    cat public_key
    exit
    EOF
    
  4. 接続設定

    sudo nano /etc/wireguard/wg0.conf
    
    # /etc/wireguard/wg0.conf
    
    [Interface]
    PrivateKey = <Server Private Key Value>
    Address = 10.10.10.1/24
    PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
    PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
    ListenPort = <portnumber>
    
    [Peer]
    
  5. ファイアウォールとフォワーディング設定

    sudo ufw allow <portnumber>/udp
    sudo ufw reload
    
    sudo nano /etc/sysctl.conf
    
    # /etc/sysctl.conf
    net.ipv4.ip_forward = 1
    
    sudo sysctl -p
    

クライアントサイド(PC without a global IP)のセットアップ

  1. GUIアプリのインストール
    https://www.wireguard.com/install/ からインストールできます。
  2. 接続設定
    アプリを起動して、左下のAdd Tunnel -> Add Empty Tunnelをクリックし以下を記載
    [Interface]
    PrivateKey = <Client Private Key Value>
    Address = 10.10.10.2/24
    DNS = 1.1.1.1
    
    [Peer]
    PublicKey = <Server Public Key Value>
    EndPoint = <Global IP of Server>:<portnumber>
    AllowedIPs = 10.10.10.0/24
    PersistentKeepalive = 25
    

クライアントサイド(Laptop)のセットアップ

  1. クライアントサイド(PC without a global IP)のセットアップと同様ですが、割り当てるアドレスは変えてください
    [Interface]
    PrivateKey = <Client Private Key Value>
    Address = 10.10.10.3/24
    DNS = 1.1.1.1
    
    [Peer]
    PublicKey = <Server Public Key Value>
    EndPoint = <Global IP of Server>:<portnumber>
    AllowedIPs = 10.10.10.0/24
    PersistentKeepalive = 25
    

サーバーサイド(VPS)のセットアップ その2

  1. クライアント情報の追加
    [Interface]
    PrivateKey = <Server Private Key Value>
    Address = 10.10.10.1/24
    PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
    PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
    ListenPort = <portnumber>
    
    [Peer]
    PublicKey = <Client1 Public Key Value>
    AllowedIPs = 10.10.10.2/32
    
    [Peer]
    PublicKey = <Client2 Public Key Value>
    AllowedIPs = 10.10.10.3/32
    

VPN接続とそのテスト

  1. サーバーサイド(VPS)の接続開始

    sudo wg-quick up wg0
    
  2. クライアントサイド(PC without a global IP / Laptop)の接続開始
    Activateボタンをクリック

  3. サーバーサイド(VPS)から接続テスト

    ping 10.10.10.2
    
    ping 10.10.10.3
    
  4. クライアントサイド(PC without a global IP)から接続テスト

    ping 10.10.10.1
    
    ping 10.10.10.3
    
  5. クライアントサイド(Laptop)から接続テスト

    ping 10.10.10.1
    
    ping 10.10.10.2
    

LaptopからWSL(機械学習サーバー)に接続

  1. PC without a global IPでOpen SSHサーバーを構築
  2. WSLでOpen SSHサーバーを構築
  3. PC without a global IPでポートフォワード設定
    netsh interface portproxy add v4tov4 listenaddress=10.10.10.2 listenport=22 connectaddress=localhost connectport=22
    
  4. LaptopからWSL(機械学習サーバー)に接続
    ssh user@10.10.10.2
    
  5. ホストの登録
    鍵の登録も済ましたら以下のようにホストを登録すると、ホスト名で接続できるようになります。
    # C:\Users\<name>\.ssh
    Host WSL_Server
    HostName 10.10.10.2
    Port XXX    
    User <name>
    IdentityFile C:\Users\<name>\.ssh\id_rsa
    
    ssh WSL_Server
    

まとめ

以上を持って無事、グローバルIPのないパソコンの仮想環境に構築した機械学習サーバーに、VPSを踏み台として、出先のノーパソから接続できるようになりました。快適なリモート環境を楽しんでください。

あとがき

VS Codeの拡張機能であるRemote SSHは、現時点ではLinuxかMacでのみリモート上でDockerコンテナを開くことができます。つまり、WindowsのDocker Desktopを使用してDevcontainer環境をリモートから利用することは難しいです。

私は意地でもDevcontainerを使用したいと考えていたため、WSLにDockerをインストールし、最初はWSLがWireguardクライアントとしてVPN接続することで解決できると思っていました。しかし、Wireguardはopenresolvコマンドを使用する一方で、WSLはopenresolvを使用しない特殊な仕組みを持っているため、WSLからのVPN接続はできないことがわかりました。

そのため、WindowsがWireguardでVPN接続し、内部でポートフォワードを行うことで、環境構築を完了させました。WSLのネットワークの特殊さに悩まされました。

付録

クライアントを増やす

クライアントはもっと増やせます。その場合サーバーサイドの設定ファイルにクライアントを追記するだけです。

# /etc/wireguard/wg0.conf

[Interface]
...

[Peer]
PublicKey = <ClientN Public Key Value>
AllowedIPs = 10.10.10.X/32

もしもクライアントがLinuxだったら

クライアントがLinuxの場合でも同様に行えます。

  1. Wireguardのインストール

    sudo apt install wireguard -y
    
  2. 鍵の生成

    sudo -i <<EOF
    cd /etc/wireguard/
    wg genkey | tee private_key | wg pubkey > public_key
    exit
    EOF
    
  3. 鍵の閲覧

    sudo -i <<EOF
    cd /etc/wireguard/
    cat private_key
    cat public_key
    exit
    EOF
    
  4. 接続設定

    sudo nano /etc/wireguard/wg0.conf
    
    # クライアント(Linux)サイド /etc/wireguard/wg0.conf
    
    [Interface]
    PrivateKey = <Client Private Key Value>
    Address = 10.10.10.X/24
    DNS = 1.1.1.1
    
    [Peer]
    PublicKey = <Server Public Key Value>
    EndPoint = <Global IP of Server>:<portnumber>
    AllowedIPs = 10.10.10.0/24
    PersistentKeepalive = 25
    
  5. サーバーにPeer追加

    # サーバーサイド /etc/wireguard/wg0.conf 
    
    [Interface]
    ...
    
    [Peer]
    PublicKey = <ClientN Public Key Value>
    AllowedIPs = 10.10.10.X/32
    
  6. 接続開始

    sudo wg-quick up wg0
    

すべての通信をVPN経由で行いたい場合

今回、クライアントは10.10.10.Xに当てた通信だけをVPN経由で行いました。しかしながら、出先ではブラウジングもVPN経由で行いたいはずです。すべての通信をVPN経由で行うためには、クライアントの設定ファイルのAllowedIPsを0.0.0.0/0に変更してください。

[Interface]
...

[Peer]
...
AllowedIPs = 0.0.0.0/0
...

Discussion

ログインするとコメントできます