OpenVPN のフルトンネルが SSH セッションを切る:戻り経路を直す
デフォルトルートが SSH の応答をトンネルへ送ります。ピアを固定し、redirect-gateway を除外し、あるいは送信元で経路を分けてから、サーバーへの接続を失わないようにします。
SSH で接続するマシンで OpenVPN クライアントをフルトンネルにすると、一瞬でセッションが切れることがあり、マシン側に落ち度はありません。トンネルはパケットがサーバーに届く経路を変えず、変わるのは応答の出方です。デフォルトルートが tun0 を指した瞬間、SSH クライアントへの応答はプロバイダーのネットワークに入り、そこからあなたのアドレスへ戻る経路はありません。接続は止まり、次の試行はタイムアウトし、その間デーモンは 22 番ポートで待ち続けています。
VPN のログを読む前に、ルーティングテーブルを読みます。ログはトンネルが上がったと告げ、それは正しい。問題は、それぞれの応答がどのインターフェースに渡されるかであり、それはトンネルがたった今書き換えたテーブルに対するパケットごとの照会です。
応答が間違った扉から出る理由
受信パケットは今も物理インターフェースに届き、VPN クライアントにはインターネットがサーバーの公開アドレスへ届く経路を変える力がありません。応答は別の判断であり、パケットごとにカーネルが宛先を調べて、--redirect-gateway の後はデフォルトの項目がトンネルを指します。プロバイダーの出口は自分が扱わない宛先を見て、パケットを捨てます。
OpenVPN 自身の保護が覆う宛先はちょうど一つです。2.6 のマニュアルは --redirect-gateway を一連の手順として説明しており、トンネル以前に使っていたゲートウェイ経由で VPN ピアへのホストルートを追加し、暗号化された通信が自分自身へ戻らないようにしてから、デフォルトルートを置き換えます。そのホストルートはピア専用です。あなたの SSH クライアントはただの別のインターネットアドレスであり、例外は与えられません。
接頭辞の間違いは早めに排除する価値があります。長さのない ip route add -net 0.0.0.0 gw 10.8.0.1 dev tun0 は単一アドレス 0.0.0.0 に一致し、インターネットには一致せず、0.0.0.0/0 でなければなりません。そしてデフォルトを取ることがトンネル自身の転送を壊すなら、端点を物理インターフェース経由のホストルートとして追加し、プロバイダーの実際のアドレスを使ってください。
すぐ直すためにクライアントのアドレスを固定する
最短の修理は、トンネルができる前にサーバーが使っていたゲートウェイ経由でクライアントのアドレスへのホストルートを置くことであり、手元でサーバーが見るアドレスを調べたうえでサーバーのテーブルを読み、古いゲートウェイを確認します。
curl -s https://ifconfig.co
ip route
サーバー側では、クライアントが起動する前に例外を追加しますが、ここでのアドレスは仮のものであり、形が重要です。
sudo ip route add 80.128.240.55 via 192.168.178.1
これは報告された事例で使われた修理であり、すぐに効きます。同時に二つの予測できる弱点があり、再起動でルートが消え、ISP が別のアドレスを渡した瞬間に誤りになります。応急処置として扱い、ネットワークユニットやディスパッチャーのフックで意図的に恒久化するか、一時的だと認めて復旧手順を書き留めてください。
クライアントにデフォルトを取らせない
よりきれいな選択は、トンネルにデフォルトルートを置き換えさせないことです。route-nopull を --client や --pull と使うと、ルート、block-outside-dns、DNS サーバーのような DHCP 風の項目を除いて押し出された設定を受け入れるため、必要な接頭辞だけを足せますが、より狭い道具はプルフィルターで、問題のある指示を一つ落として残りを保ちます。
route-nopull
pull-filter ignore "redirect-gateway"
どちらか一方だけを使い、両方は使わず、何が変わるのかを明確にしてください。トンネル経由のデフォルトルートがなければ、サーバーはプロバイダー経由でインターネットを閲覧しなくなり、明示的に経路を設定した先にしか届きません。フルトンネルが設定の目的そのものだったなら、これは間違ったつまみであり、上のホストルートが正解です。
戻り経路を送信元または接続で分ける
受信アドレスが安定しているなら、トンネル自身のルートには触れず、送信元ルールで応答をローカルに保ち、物理インターフェースのアドレスには LAN 接頭辞と物理側デフォルトを持つ専用テーブルを与えて、そこから出る通信にそのテーブルを選びます。
ip rule add from 192.168.1.11 table 128
ip route add table 128 to 192.168.1.0/24 dev eth0
ip route add table 128 default via 192.168.1.1
これは、受信 SSH 接続への応答がクライアントの接続先アドレスを送信元とするために機能します。受信側の全サービスを動かし続ける必要があった報告事例は、まさにこの形を使っていました。受信アドレスが変動する、あるいは経路が動的なら、代わりに接続に印を付け、物理インターフェースから来る最初のパケットに conntrack マークを付けて、OUTPUT で応答パケットに写し、印の付いたパケットを物理ゲートウェイへ向かうテーブルで経路制御します。
iptables -t mangle -A PREROUTING -i eth0 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1234
iptables -t mangle -A OUTPUT -m connmark --mark 1234 -j MARK --set-mark 4321
ip route add default via 192.168.1.1 dev eth0 table 3412
ip rule add fwmark 4321 table 3412
試験中は、接続マーク、ファイアウォールマーク、テーブル番号を別々に保ち、一つの番号を二つの役割に使い回すと、正しく見えて誤ったパケットに一致するルールができます。テーブルのデフォルト項目には、通常の Ethernet リンクで LAN ゲートウェイとともに via も必要です。裸の dev eth0 が効くのは、インターフェースがポイントツーポイントか、次のホップが別の方法で解決されるときだけです。報告された Jessie のホストはたまたま裸の形が効くリンク上にありましたが、それはまねるべきディストリビューションのルールではありません。
閉め出されずに確認する
切断して試すのではなく、最初のセッションを開いたまま、トンネルを上げて、自分のアドレスがどの経路を取るかをカーネルに尋ねます。
ip route get 80.128.240.55
答えはトンネルではなく、物理インターフェースと古いゲートウェイを示すはずです。次に二本目の SSH 接続を開きますが、これが本当の試験で、古いセッションは新しい接続では再現できない状態の上で生き残ることがあります。二本目が止まり、一本目がまだ応答するなら、ルールは重要な通信に一度も一致しておらず、見るべきはマークか送信元アドレスです。
作業の間ずっと、帯域外のコンソールを手元に用意します。唯一の経路が今壊したトンネルであるサーバーはそのトンネル越しには直せず、追加したテーブル番号と接頭辞を書き留めておけば、巻き戻しも容易です。