VPSをリバースプロキシにせずにただの自宅サーバー公開用(VPN)ルーターとして使っている話

最近増えてきたマンション備え付けのインターネット回線。
築30年のマンションである拙宅にも引かれており、無料（というか、家賃に含まれている）で1Gbpsのインターネットが利用できます。
どうしても自前の回線を通すのが
諸々の工事の手続き取るのが面倒くさい
コスト的に引き合わない（だってテレワークの普及著しい昨今に100MbpsのVDSLしか引けなかったのです（当時））ので、IPv4 over IPv6なIPoE回線だからまぁいっか、的な感じでそのまま回線を引かずの状態になっています。
が、当たり前ながらほぼ自由に使えるグローバルなIPv4アドレスは落ちてこないし（そりゃそうだ）、グローバルなIPv6アドレスは/64でもらえるけどInternet側からReachableなフィルタ設定ではないし（そりゃそうだ）、
サーバー公開には甚だ不向き
サーバー公開したけりゃ自分で光回線契約しろやオラァ！なわけです（そりゃそうだ）。
そこをなんとかして家の外からもアクセス可能な環境を作るのがエンジニアの腕の見せ所でもあるわけです。

:::note alert
※とは言ったものの、理論的に何とか出来るからと言って、備え付けの回線の利用規約をガン無視してサーバー公開してはいけません。中には利用規約でサーバー公開に供してはいけないと定めているところもあるそうですので。
:::

さて、前置きが長くなりましたが、要するにこういうことです。
・拙宅では各居室毎にインターネット回線を引くよりも、マンションに備え付けられた共有回線の方が高速（だった）
・ただしサーバー公開には使えない
それをどうにかこうにかした（飽くまでも）夢の記録です。

結論から申しますと、先達の知見がありますように、VPSにインストールした<b>
みんな大好きけしからんインターネット技術代表
</b>SoftEtherでトンネルを掘り、そのトンネルの中にTCP80/443とかファイル共有とかリモートデスクトップを通してしまえ、ということになります。
別にSoftEtherではなくVPNが張れてその中にVPSへ着信したアクセスをフォワード出来るならVyOSでもLibreSwanでもWireguardでも何でも良いのですが、検索してヒットする知見が多めなのとSoftEther推しなのでここではSoftEtherを使いました。
先達の知見と比べて（セキュリティ的な点を無視するなら）工夫したと自負している（意味があるかどうかは別として）のは次の点です。

:::note info
・VPSは1台に集約する
・VPSにリバースプロキシ(というか、HTTP/HTTPSサーバー)を兼用させない
・IPv4/IPv6デュアルスタックとし、どちらからでも利用できるようにする
:::

特に問題になるのは3番目であろうかと思います。VPSにリバースプロキシも担わせるのであれば、IPv6でアクセスしてもIPv4でアクセスしても素直にLAN内の端末へとVPN越しにIPv4で投げられるのですが（その手法についてはここでは割愛します）、リバースプロキシを担わせないとなると、途端に知見が減ります。
特にIPv6でアクセスした時に内部的にIPv4へ変換する方法。これを見つけ出すのはそれなりに大変でした（高度なエンジニアには既知過ぎて却って情報が無いのかもしれませんが、私にとってはそうでした）。
IPv4だけでもええじゃないかと言うのはまぁその通りなんですが、グローバルIPv4アドレスの枯渇が叫ばれて久しい中、仮にもエンジニアの末席を汚している身としては、貴重なグローバルIPv4アドレスを専有するばかりかIPv6への移行を阻害するような行いは厳に慎むべきと思い、IPv4/IPv6デュアルスタックでサービス公開することに挑んだ訳です。
答えを申しますと、<b>VPS側のIPv6アドレスで6tunnelかSocatをListenさせ、VPSかリバースプロキシ側のIPv4アドレスに投げてしまえば良い</b>ということになります。
いやそれプロキシだよね？

閑話休題。
拙宅ではIPv6アドレス有効化済みのUbuntu Server 20.04 LTSでやっております。
またVPS上でIPv6アドレスを有効化する方法は各サービス毎に異なる（と思う）ので適宜読み替えてください。
先ずはUFWを有効化してポート開放します。

```
sudo ufw enable
sudo ufw allow 500/udp
sudo ufw allow 1701
sudo ufw allow 4500/udp
sudo ufw allow 5555/tcp
sudo ufw allow 80,443/tcp
sudo ufw allow proto esp from any
```
:::note warn
ここで sudo ufw allow proto esp from any を入れ忘れたので、最初RTXとIPv6でのL2TPv3/IPsecが成立しなくてドハマりしました。
最初は上流ルーターのフィルタ設定が原因だと思ってました。

:::
:::note warn
IPv6だけで着信する場合は sudo ufw allow 4500 は不要ですが、IPv4での接続もある（具体的にはIPv6でのL2TP/IPsecに非対応なiPhone/iPadとか）の
とIPv4/IPv6で個別に設定するのが面倒だったの
で入れています。
:::
次に、以下の各ファイルに以下のように追記またはコメントアウトを削除します。
/etc/default/ufw

```/etc/default/ufw
DEFAULT_FORWARD_POLICY=“ACCEPT"
```
/etc/ufw/sysctl.conf

```
net/ipv4/ip_forward=1
```

/etc/sysctl.conf

```
net.ipv4.ip_forward=1
```

/etc/ufw/before.rules
(一番上のブロックの上に記述する)

```
# NAT
*nat
-F
:POSTROUTING ACCEPT [0:0]
# Outgoing
-A POSTROUTING -j MASQUERADE
# Incoming
-A PREROUTING -i <VPSのグローバルIPv4アドレスが割り当てられているインターフェイス名> -d <VPSのグローバルIPv4アドレス> -p tcp --dport 80 -j DNAT --to-destination <LAN内のHTTPサーバーのアドレス>:80
-A PREROUTING -i <VPSのグローバルIPv4アドレスが割り当てられているインターフェイス名> -d <VPSのグローバルIPv4アドレス> -p tcp --dport 443 -j DNAT --to-destination <LAN内のHTTPSサーバーのアドレス>:443
COMMIT
```

以上が完了したら、次のコマンドを入力します。

```
cd /etc
sudo ln -sf ../run/systemd/resolve/resolv.conf resolv.conf
sudo systemctl restart systemd-resolved
sudo systemctl start ufw
```

次に、SoftEtherをインストールします。

```
sudo apt -y install make gcc
wget https://github.com/SoftEtherVPN/SoftEtherVPN_Stable/releases/download/v4.37-9758-beta/softether-vpnserver-v4.37-9758-beta-2021.08.16-linux-x64-64bit.tar.gz
sudo tar zxvf softether-vpnserver-v4.37-9758-beta-2021.08.16-linux-x64-64bit.tar.gz
cd ./vpnserver &&make
```
:::note info
※ダウンロードURLは当時のものですので、適宜読み替えてください。
:::
英文でインストールのガイダンスが表示されますので、ここは全部「1」で進めました。

```
cd ..&&sudo mv vpnserver/ /usr/local/
cd /usr/local/vpnserver/
sudo chmod 600 *
sudo chmod 700 vpncmd
sudo chmod 700 vpnserver
sudo ./vpncmd
```
SoftEther VPNコマンドが起動しますので「3」を入力します。

```
VPN Tools>check
```
「すべてのチェックに合格しました。このシステム上で SoftEther VPN Server / Bridge が正しく動作する可能性が高いと思われます。

コマンドは正常に終了しました。」
と表示されたら、Exitを入力してVPNコマンドから脱出してください。
次に、SoftEther VPNのサービスを作成していきます。

```
cd /etc/systemd/system
sudo vi softethervpn.service
```

```softethervpn.service
[Unit]
Description=SoftEther VPN Server
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/vpnserver/vpnserver start
ExecStartPost=/usr/bin/sleep 15s
ExecStartPost=/usr/sbin/ip addr add <VPSに与えたいLANアドレス/ネットマスク> dev tap_tap1
ExecStop=/usr/local/vpnserver/vpnserver stop

[Install]
WantedBy=multi-user.target
```

```
cd /usr/local/vpnserver/
sudo vi vpn_server.config
```

```vpn_server.config
declare ListenerList
{
        declare Listener0
        {
                bool DisableDos false
                bool Enabled false
                uint Port 443
        }
        declare Listener1
        {
                bool DisableDos false
                bool Enabled false
                uint Port 992
        }
        declare Listener2
        {
                bool DisableDos false
                bool Enabled false
                uint Port 1194
        }
        declare Listener3
        {
                bool DisableDos false
                bool Enabled true
                uint Port 5555
        }
}
```
SoftEther VPNのListenポートの設定まで完了したら、次のコマンドでSoftEther VPNを起動します。

```
sudo systemctl enable softethervpn
sudo systemctl start softethervpn
```

:::note info
このままだと無限にSoftEther VPNのログファイルが増殖しますので、定期的に削除するように設定することを推奨します。具体的には、
sudo vi /etc/cron.daily/deleteSoftEtherVpnLog

#!/bin/sh
find /usr/local/vpnserver/*_log '*.log' -mtime +30 -delete

sudo chmod +x /etc/cron.daily/deleteSoftEtherVpnLog
で良いかと思います。
:::

:::note info
SSHを動作させている場合、VPSのtapデバイスにIPアドレスを振っていると、SSHが動作しなくなることがあります（※なった）。
その場合、SSHの起動スクリプトを After=network.target auditd.service softethervpn.service に書き換える必要があります。
:::

ここまで到達しましたら、SoftEther VPN Server管理ツールを使ってGUIで設定できるようになっているはずです。ここから先のRTXとのL2TPv3/IPsec接続を取れるようにするSoftEther VPN側の設定はQiita以外にも多くの先例がネット上に転がっていますので、一旦割愛したいと思います。

:::note alert
なお、ここでVPNを実際に張る前にSoftEther VPN Server管理ツールを使ってIPv6パケットをSoftEther VPNトンネルに通さない設定にしていないとトラブルになることだけは明記しておきたいと思います（※tapデバイスがIPv6アドレスを取ってしまっておかしなことになった）。
:::
RTX側の設定については、該当箇所のみ例示することとしたいと思います。

```
ipv6 routing on
ipv6 routing process fast
ipv6 prefix 1 ra-prefix@lan3::/64
bridge member bridge1 lan2 tunnel1
ip bridge1 address <適当なプライベートIPv4アドレス>/24
ip bridge1 secure filter in 400033 400000 400010 499998
ip bridge1 secure filter out 400033 400000 400010 499998
ipv6 bridge1 secure filter in 699998
ipv6 bridge1 secure filter out 699998

description lan1 LAN
ipv6 lan1 address ra-prefix@lan3::<適当なIPv6プリフィックス>/64
ipv6 lan1 rtadv send 1 m_flag=off o_flag=on
ipv6 lan1 dhcp service server
ipv6 lan1 mld router version=2

description lan2 Bridge
ip lan2 secure filter in 400033 400000 400010 499998
ip lan2 secure filter out 400033 400000 400010 499998
ipv6 lan2 secure filter in 699998
ipv6 lan2 secure filter out 699998

description lan3 IPoE
ipv6 lan3 secure filter in 601000 601001 601002 601003 601004 601005 601006 601011 601012 699998
ipv6 lan3 secure filter out 601000 601001 601002 601003 601004 601005 601006 601011 601012 699999 dynamic 906080 906081 906082 906083 906084 906085 906086 906087 906088 906098 906099

tunnel select 1
 tunnel encapsulation l2tpv3
 tunnel endpoint address <VPSのIPv6アドレス>
 ipsec tunnel 101
  ipsec sa policy 101 1 esp aes-cbc sha-hmac
  ipsec ike duration ipsec-sa 1 691200 rekey 90%
  ipsec ike duration isakmp-sa 1 691200 rekey 90%
  ipsec ike keepalive log 1 on
  ipsec ike keepalive use 1 on dpd 10 6 0
  ipsec ike local address 1 ipv6 prefix ra-prefix@lan3::<適当なIPv6プリフィックス> on lan1
  ipsec ike local name 1 <VPSのSoftEther VPNに設定したISAKMP Phase 1 ID> fqdn
  ipsec ike pre-shared-key 1 text <VPSのSoftEther VPNに設定したIPsec事前共有鍵>
  ipsec ike remote address 1 <VPSのIPv6アドレス>
  ipsec ike restrict-dangling-sa 1 off
 l2tp always-on on
 l2tp tunnel disconnect time off
 l2tp keepalive use on 5 10
 l2tp keepalive log on
 l2tp syslog on
 l2tp remote end-id vpn
 ip tunnel tcp mss limit auto
 ipv6 tunnel secure filter in 601000 601001 601002 601003 601004 601005 601006 699998
 ipv6 tunnel secure filter out 601000 601001 601002 601003 601004 601005 601006 699998
 tunnel enable 1

ipsec auto refresh on
ipsec transport 1 101 udp 1701
ipv6 filter 601000 pass * * icmp6 * *
ipv6 filter 601001 pass * * tcp * ident
ipv6 filter 601002 pass * * udp * 546
ipv6 filter 601003 pass <VPSのIPv6アドレス> * esp * *
ipv6 filter 601004 pass * <VPSのIPv6アドレス> esp * *
ipv6 filter 601005 pass <VPSのIPv6アドレス> * tcp,udp * 500,1701,4500
ipv6 filter 601006 pass * <VPSのIPv6アドレス> tcp,udp * 500,1701,4500
ipv6 filter 601011 reject * * udp,tcp 135,netbios_ns-netbios_ssn,445,3389,5900 *
ipv6 filter 601012 reject * * udp,tcp * 135,netbios_ns-netbios_ssn,445,3389,5900
ipv6 filter 699998 reject * * * * *
ipv6 filter 699999 pass * * * * *
ipv6 filter dynamic 906080 * * ftp
ipv6 filter dynamic 906081 * * domain
ipv6 filter dynamic 906082 * * www
ipv6 filter dynamic 906083 * * smtp
ipv6 filter dynamic 906084 * * pop3
ipv6 filter dynamic 906085 * * submission
ipv6 filter dynamic 906086 * * https
ipv6 filter dynamic 906087 * * ntp
ipv6 filter dynamic 906088 * * netmeeting
ipv6 filter dynamic 906098 * * tcp
ipv6 filter dynamic 906099 * * udp
```
以上の設定をRTXに投入したら、LAN2とLAN1をケーブルで繋ぎます（この辺りがちょっと怪しい）。
RTX側で show status l2tp してVPSとRTXとの間にL2TPv3/IPsecトンネルがestablishになっていれば成功です。

次に、VPSにSocatをインストールします。

```
sudo apt install socat -y
```
Socatをインストールしたら、Socatの起動スクリプトを作成します。
ここでは例として、VPSのIPv6アドレスに着信したHTTP接続を、VPN越しにLAN内のHTTPサーバーのIPv4プライベートアドレスへ転送するスクリプトを作成します。

```
sudo cd /usr/local
sudo mkdir -p socat
sudo vi socat-http.sh
```
```socat-http.sh
#!/bin/sh

/usr/bin/socat -d -d -lm TCP6-LISTEN:80,reuseaddr,fork,setgid=root,setuid=root tcp:<LAN内のHTTPサーバーのIPv4プライベートアドレス>:80 </dev/null &

echo $! >/run/socat-http.pid
```
:::note notice
"LAN内のHTTPサーバーのIPv4プライベートアドレス" は適宜読み替えてください
:::
:::note notice
ログの設定はRecommendedの -d -d にしています。
最大で -d -d -d -d に出来るようですが、試していません。
:::
:::note notice
HTTPSも使用する場合は同じ要領でポート番号を443に変えたものを別名で保存します
:::
スクリプトを保存したら、実行権限を与えます。

```
sudo chmod 0755 socat-http.sh
```
次に、/etc/systemd/system/ へ移動してサービスを作成します。

```
sudo cd /etc/systemd/system/
sudo vi socat-http.service
```

```socat-http.service
[Unit]
Description = Socat Proxy HTTP-IPv6
After=network.target
ConditionPathExists=/usr/local/socat

[Service]
Type=forking
PIDFile=/run/socat-http.pid
ExecStart=/usr/local/socat/socat-http.sh
ExecStop=/bin/kill -s QUIT $MAINPID

[Install]
WantedBy = multi-user.target
```
:::note notice
しつこいですが、HTTPSも使用する場合は同じ要領でポート番号を443に変えたものを別名で保存します
:::
サービスを作成して保存したら、次のコマンドを実施します。

```
sudo systemctl enable socat-http
sudo systemctl start socat-http
sudo reboot
```
VPSの再起動が完了したら、ブラウザでIPv6アドレスを叩いてみましょう。素直にLAN内のHTTPサーバーが公開しているページが表示されれば成功です！

:::note notice
ブラウザに表示しているURLのIPアドレスを判定する拡張機能を入れるのもアリかもしれません。
:::