Squid は CONNECT 要求に対して、最初に一致した http_access 行で応答し、その行が拒否であることもあります。この一点だけで、VPN エンドポイントの例外がたいてい最初の試行で失敗するのは、要求が末尾に誰かが追加した許可に決して到達しないからだと説明できます。限定的な修正は既存のポートガードを維持したまま、レビュー済みの送信元、宛先、ポートを一組だけ教え込みます。プロキシ上の他の挙動は以前とまったく同じです。

Squid が CONNECT 要求をどう判定するか

CONNECT 要求はホストとポートを指定し、Squid は http_access 行を順番にたどって一致するものを見つけます。一致した拒否は評価を即座に終わらせ、クライアントはプロキシから 403 を受け取ります。だからこそルールの位置は書き方よりも重要です。同じ行を一段上へ動かすだけで結果は完全に変わります。

従来のガードは Safe_ports の外をすべて拒否し、SSL_ports の外の CONNECT を拒否します。これらの名前は自分の設定で定義された ACL であり、その中身はプロトコルではなくあなたのものです。ディストリビューションによっては Safe_ports として広いポート範囲を許可しつつ、CONNECT を別に制限しています。ポートが設計上ブロックされていると決めつける前に、実際の値を読んでください。

要求を拒否しているのがプロキシであることを確認します。Squid が生成する 403 は VPN サーバーの TLS 警告とはまったく似ておらず、両者を一つの問題にまとめると最初の一時間を失います。要求のアクセスログ行は、実行された動作とそれを生んだルールの両方を記録しているので確認してください。

緩い ACL 三つではなく、結合した一致を一つ作る

例外は、承認済みの送信元範囲、承認済みの宛先アドレス一つ、そしてエンドポイントが待ち受ける唯一のポートという三つの事実から組み立てます。緩い ACL 二つと結合した一致一つでは、評価されるのが組み合わせであるため、まったく異なるポリシーになります。結合 ACL は、それが運ぶメソッドではなく、許可する内容で命名してください。

下の形は、レビュー済みの値に置き換える必要があるドキュメント用の範囲を使っています。Squid はこれらの名前をどれも予約していないため、選ぶのも一貫性を保つのもあなたの責任です。この手順は all-of グループをサポートする設定が文書化されている Squid 6 と 7 に限定されます。現在のリファレンスではこれらのディレクティブは Squid 8 で利用不可とされているため、移行経路ではなくバージョン限定の手順として扱い、適用前に自分のリリースのリファレンスで確認してください。

acl pvpn_src src 192.0.2.128/25
acl pvpn_dst dst 198.51.100.77/32
acl pvpn_port port 1194
acl pvpn_ok all-of CONNECT pvpn_port pvpn_dst pvpn_src

all-of グループはすべてのメンバーが一致したときだけ一致するため、送信元、宛先、ポートが一行で検査されます。グループ自体は何も許可せず、三つの事実のうち二つだけに一致する要求では満たせません。この性質により、後から二台目のゲートウェイが追加されても例外は限定的なままです。新しい宛先が意図的に追加されるか、要求が元のガードに戻るかのどちらかです。

ポートガードを削除せずに曲げる

現在の位置では、従来のガード行二本はこのようになっています。ふつうは認証の後、何でも許可するポリシー行の前に置かれます。

http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports

それぞれに否定した結合一致を加えると、一致する例外は、そうでなければ拒否するガードを通過できます。変わるのはガード行だけで、しかも各行に一項を足すだけです。

http_access deny CONNECT !SSL_ports !pvpn_ok
http_access deny !Safe_ports !pvpn_ok

一本のアクセス行の項は結合されるため、その行は元の検査と例外の両方で失敗した要求だけを拒否します。承認済みエンドポイントへの要求は通過し、他のすべての要求は従来のポート挙動を保ちます。これは ポートをグローバルな SSL_ports リストに加えるより狭く、その方法ならすべての宛先にそのポートを許可してしまいます。ポートがすでにそのリストにある場合、この ACL は何も狭めず、古く広い許可が他の宛先に依然として適用されるので、例外がポートを開いたと主張する前に実際のリストを読んでください。二行が元の形に戻るため、元に戻すこともできます。アクセス制御のリファレンスには、この変更が依存する順序と従来のガードが記述されています。

例外を置いてはいけない場所

ガードの上に許可を置いて終わりにしたくなります。その配置は、動作する設定の最上部付近にある認証とローカル宛先の保護も飛ばしてしまいます。結果は限定的な例外ではなく、上にコメントが付いた穴です。新しい許可はクライアントポリシーの他の部分と同じレビュー済みの段階に保ってください。

ポートガードの後で要求にまだ何が必要かを確認します。認証済みアカウント、許可されたクライアントアドレス、宛先ポリシーはどれも引き続き適用され、例外はそのいずれも変えません。プロキシがすでに承認済みアカウントを任意の宛先へ到達させているなら、ポートガードは本来の制御ではありませんでした。

エンドポイントがそもそも TCP かどうかを考えます。CONNECT 例外は TCP ストリームを運ぶため、UDP を使う OpenVPN プロファイルはそれを通じて到達可能にはなりません。サーバーがそのように待ち受け、運用者が挙動の違いを受け入れる場合にだけ、プロファイルを TCP に変えてください。

パーサーと実際の要求で境界を確認する

再読み込みの前に設定をパースし、その間は二つ目のセッションを開いたままにします。再読み込み時の構文エラーは、プロキシを古い設定のままにするか、新しい設定を完全に拒否することがあります。パーサーは拒否された行を報告するので、クライアントのエラーより多くの情報が得られます。

そのうえで再読み込みし、承認済みの送信元からプロキシ経由で実際の CONNECT を一本送ります。パーサーの実行が先なのは、打ち間違いのある再読み込みではポリシーについて何も学べないからです。

squid -k parse
squid -k reconfigure
curl -v -p -x http://proxy.lab.internal:3128 https://vpn.lab.internal:1194/ --max-time 5

三つのケース、つまり承認済みの送信元とアカウント、承認されていない送信元、同じポート上の別の宛先を試します。三番目が例外の狭さを証明するのは、宛先の一致なしにポートを試すからです。クライアントのメッセージを信じるのではなく、三つともアクセスログを読んでください。成功した CONNECT は TCP ストリームを確立するだけで、VPN のハンドシェイクは別の試験のままです。

関連記事