Skip to content
このページは AI で翻訳されており、不正確な表現が含まれる場合があります。GitHub で改善にご協力ください。

ポートノッキング ​

fwknop と互換のシングルパケット認可(SPA、Single Packet Authorization)です。保護対象のポートは通常は外部から接続できず、クライアントが暗号化・HMAC 認証付き・リプレイ防止のノックパケットを 1 つ送信し、Gately が認証に成功すると、その送信元アドレスだけに限られた時間アクセスを許可します。SSH の直接接続の保護によく使われます。Web ターミナルでは対応できない用途(scp、IDE のリモート開発、ポート転送)も外部から安全に接続でき、スキャナーやブルートフォース攻撃にはポートが見えません。

機能 ​

  • fwknop クライアント互換:公式の fwknop クライアント(Linux、macOS、WSL)をそのまま使えます。鍵の形式は fwknop -k で生成される KEY_BASE64、HMAC_KEY_BASE64 と同じで、ルール画面でクライアントのコマンドと ~/.fwknoprc のセクションをワンクリックで生成できます;
  • セキュリティ:パケットは AES-256-CBC で暗号化され、先に HMAC(SHA-256 / 384 / 512)を検証してから復号します。鍵が違うパケットは復号処理に進みません。各パケットは 1 回しか使えず(リプレイ防止)、タイムスタンプが有効期間(デフォルト 120 秒)を超えると拒否します。ユーザー名の限定や、パケットへの許可アドレスの明記の要求もできます;
  • ファイアウォールによるブロック:保護対象のポートをシステムファイアウォール(nftables。カーネルに nf_tables がない場合は iptables + ipset)に書き込み、ノックに成功したアドレスだけを許可します。ローカルの sshd など Gately を経由しないサービスも保護できます;
  • Gately の入口でノックによる許可:ポート転送、プロトコル振り分け、STUN 内蔵転送のルールと、プロトコル振り分けの個別ルートで「ノックが必要」を選べます。Gately 自身が判定するためファイアウォール権限は不要で、Windows、macOS、NET_ADMIN のないコンテナでも使えます;
  • ポートは 1 つだけ:プロトコル振り分けは TCP で届いたノックパケット(fwknop -P tcp)を識別でき、同じポートでノックを受け付けつつ、ノックの結果に応じて SSH などのルートを許可できます;
  • 侵入防御との連携:拒否されたノックは「ポートノッキング拒否」イベントを発行し、侵入防御の組み込みテンプレート「ポートノッキング失敗」で回数に応じて送信元をブロックできます。すでにブロックされたアドレスからのノックは直接破棄します;
  • 現在の認可一覧(残り時間、取り消し可能)を表示し、ノックの成功と拒否は実行ログに記録され、通知センターで購読できます。

クイックスタート:ローカルの SSH を保護する ​

  1. 「ポートノッキング」を開いてルールを追加します。ポートに tcp/22 を入力し、「ファイアウォールによるブロック」を有効にします。鍵を空にすると自動生成されます;
  2. ルールカードの「クライアント」をクリックし、サーバーのドメインまたはグローバル IP を入力して、コマンドラインまたは ~/.fwknoprc のセクションをコピーします;
  3. クライアントに fwknop をインストールします。macOS は brew install fwknop、Debian / Ubuntu は apt install fwknop-client、Windows は WSL 内にインストールします;
  4. ノック後 30 秒以内に SSH へ接続します:
bash
fwknop -n gately-ssh && ssh user@example.com

ノックパケットはデフォルトで UDP 62201 ポートに送られます。Gately がルーターの内側にある場合は、UDP 62201 を保護対象ポートと一緒に Gately のホストへ転送する必要があります。「ファイアウォールを自動で開放」を有効にすると、ローカルファイアウォール(OpenWrt fw4、firewalld、ufw など)がノックポートを自動で開放し、ブロックの有効化後は保護対象ポートも開放されるため、接続可否はノックだけで決まります。

自分を締め出さないために

  • デフォルトで「内部ネットワークはノック不要」が有効です。プライベートアドレス、リンクローカルアドレス、このマシンのネットワークインターフェースが属するグローバル IPv6 範囲(インターフェースのプレフィックスが /64 以下ならそのプレフィックス、クラウドサーバーでよくある /124 や /128 ならそのプレフィックス自体のみ)はノック不要で、設定でノック免除の許可リストを追加できます。キャリアグレード NAT のアドレス 100.64.0.0/10 は含まれません。同じ ISP の他の利用者もこの範囲を使うためです。Tailscale を使う場合は 100.64.0.0/10 を許可リストに追加してください;
  • ファイアウォールによるブロックを有効にしても確立済みの接続は影響を受けず、認可の期限が切れても確立済みの接続はそのまま維持されます;
  • Gately が正常に停止するとブロックルールは削除されます。強制終了(kill -9、docker rm -f)するとルールは残り、Gately を再起動するまで維持されます。内部ネットワークなど別の手段でこのマシンにログインできることを確認してください。

ルール ​

フィールド説明
ポートクライアントが許可を要求できるポート。tcp/22、udp/51820 など最大 16 個。クライアントの -A パラメーターのポートはすべてこの一覧に含まれている必要があり、含まれない場合はノック全体が拒否されます
ファイアウォールによるブロック有効にするとシステムファイアウォールでこれらのポートをブロックし、ノックに成功したアドレスだけを許可します。無効の場合はファイアウォールを変更せず、ルールは「ノックが必要」な入口からの参照にのみ使われます
暗号鍵 / HMAC 鍵base64 エンコード。空にすると自動生成されます(32 / 64 バイト、fwknop -k と同じ)。既存の fwknop サーバーの鍵も入力できます。ルールを複製すると新しい鍵が生成され、ルールの変更・無効化・削除ではそのルールが発行したすべての認可が取り消されます
HMAC アルゴリズムSHA-256(fwknop のデフォルト)、SHA-384、SHA-512。クライアントの --hmac-digest-type と一致させる必要があります
アクセス許可時間ノック成功後に新しい接続を確立できる時間。デフォルトは 30 秒
クライアントの最大時間クライアントが -f でより長い時間を要求したときの上限(デフォルト 300 秒)。0 はクライアントが要求した時間を無視します。許可時間より短い要求は常にそのまま採用されます
ユーザー名の限定パケット内のユーザー名がこの値のノックだけを受け付けます(fwknop はデフォルトでローカルのログイン名を送信し、-U で指定できます)
アドレスの明記を要求アドレスを含まないパケット(fwknop -s)を拒否します。クライアントは -R または -a で許可アドレスを明記する必要があります

ポート転送、プロトコル振り分け、STUN から参照されているルールは削除できません。カードに参照元が表示されます。

どのアドレスを許可するか ​

  • -R(推奨):クライアントが先に自分のグローバル IPv4 を調べてパケットに書き込みます。パケットが途中で傍受されても、他のアドレスには使えません;
  • -s:パケットにアドレスを含めず、パケットの送信元アドレスを許可します。パケットを傍受した者が自分のアドレスから先にリプレイできる可能性があるため、ルールで「アドレスの明記を要求」を有効にするとこの種のパケットを拒否できます;
  • -a アドレス:指定したアドレスを許可します。たとえば別のデバイスの代わりにノックする場合です。

fwknop 2.6 クライアントは IPv4 経由でしかノックパケットを送信できず、許可できるのも IPv4 アドレスのみのため、サーバーには IPv4 でアクセスできる必要があります。Gately 自体は IPv6 経由で届いたノックパケットも受け付け、その送信元アドレスを許可します。

グローバル設定 ​

  • UDP ノックポート:デフォルトは 62201(fwknop クライアントのデフォルトポート)。0 にすると UDP では待ち受けず、プロトコル振り分け経由のノックだけを受け付けます。有効なルールがない場合は待ち受けません;
  • ファイアウォールを自動で開放:上記を参照;
  • 内部ネットワークはノック不要 / ノック免除の許可リスト:ファイアウォールによるブロックにのみ影響します。「ノックが必要」な入口は各自の IP リストで制御します;
  • パケットの有効期間:パケットのタイムスタンプとローカル時刻の最大許容差。デフォルトは 120 秒で、クライアントとサーバーの両方の時計が正確である必要があります。

ファイアウォールによるブロック ​

環境ファイアウォールによるブロックGately の入口でノックによる許可
Linux(root または CAP_NET_ADMIN、nftables あり)利用可利用可
Docker(host ネットワーク + cap_add: NET_ADMIN)利用可、ルールはホストに書き込まれます利用可
Synology DSM など nf_tables のない Linuxiptables(legacy)+ ipset を使用。公式 Docker イメージに組み込み済み利用可
Windows、macOS、FreeBSD、権限のない Linux利用不可、画面上部に理由を表示利用可

Gately は nftables に独立した inet gately_knock テーブルを作成し、システムの既存ルールは変更しません。prerouting チェーン(優先度 -160、コネクショントラッキングの後、宛先アドレス変換の前)が、保護対象ポート宛のパケットを gate チェーンに渡します。gate チェーンは順に、宛先がローカルでないトラフィック(ローカル経由で他のデバイスへ転送される接続は影響を受けません)、ループバックインターフェース、確立済みの接続、TCP で接続開始でないパケット、ノック免除ネットワークとノック許可セット内の アドレス + プロトコル + ポート を許可し、それ以外は破棄します。判定は prerouting で行うため、Docker コンテナにマッピングされたポートはマッピング前のポートでブロックされます。許可はタイムアウト付きのセット要素として書き込まれ、期限が切れるとカーネルが自動で削除します。

nf_tables がない場合は iptables の mangle テーブルを使います。PREROUTING から GATELY_KNOCK へジャンプし、保護対象ポートごとに 1 つの ipset セット(gkn4_tcp_22 など、タイムアウト付き)を持ちます。テーブルやジャンプがファイアウォールのリロードや nft flush ruleset などで消された場合、10 秒以内に自動で書き直し、まだ有効な許可も復元します。画面の「ファイアウォールルールを表示」で現在書き込まれているルールを確認できます。

ポート転送で入ってくる SSH

Gately のポート転送でローカルの 127.0.0.1:22 に転送された接続はループバックインターフェースから来るため、ファイアウォールによるブロックの対象外です。この場合はポート転送ルールで「ノックが必要」を選ぶか、ファイアウォールによるブロックで外部向けの転送ポートを保護してください。

注意

  • Docker のポートマッピングが送信元アドレスを書き換える:Gately をブリッジネットワークで動かし、ノックポートを -p 62201:62201/udp でマッピングしている場合、Docker の userland-proxy がそのマッピングを処理すると(IPv6 やループバックアドレス経由のアクセス、iptables 連携を無効にした場合など)、Gately にはパケットの送信元が Docker ゲートウェイのアドレスに見えます。-s のパケットはゲートウェイのアドレスを許可することになり、実際のクライアントは許可されず、同じゲートウェイ経由で転送されるすべての接続を許可してしまうおそれもあります。Gately を host ネットワークで動かす(ファイアウォールブロックにも必要です)か、クライアントで -R / -a を使って許可するアドレスを明記し、「アドレスの明記を要求」を有効にしてください;
  • SYN だけをブロックする設計のトレードオフ:有効化前に確立され、まだコネクショントラッキングに記録されていない接続を切らないよう、TCP は接続を開始する SYN パケットだけを破棄し、SYN を含まないパケットはそのまま通します。理論上、SYN cookie(net.ipv4.tcp_syncookies)が有効なシステムでは、SYN キューがあふれている間に有効な cookie を推測した裸の ACK を受け入れて接続が成立します。cookie はカーネルの秘密鍵と時刻から計算される 32 ビットの初期シーケンス番号で、推測は事実上不可能です。非常に高い安全性が求められる場合は SYN cookie を無効にしてください(sysctl net.ipv4.tcp_syncookies=0)。

Gately の入口でノックによる許可 ​

ポート転送、プロトコル振り分け、STUN(内蔵転送)の「IP アクセス制御」で、ノックルールを 1 つ選んで「ノックが必要」にできます。送信元アドレスがそのルールの有効な認可を持っている場合だけ新しい接続を確立でき(UDP は新しいセッションを作るときに判定します。認可の期限が切れても WireGuard などの既存セッションはそのまま転送され、ファイアウォールブロックで確立済みの接続が維持されるのと同じです。許可リスト / ブラックリストは引き続きパケットごとに判定します)、それ以外の接続は直接切断されます。許可リスト / ブラックリストと同時に設定した場合は両方を満たす必要があります。ノックルールは「ファイアウォールによるブロック」を無効にして、これらの入口専用にできます。

プロトコル振り分けでは個別のルートにも「ノックが必要」を選べます。たとえば 443 ポートで HTTPS ルートは誰にでも公開し、SSH ルートはノックに成功したアドレスだけに公開できます。

ポートを 1 つだけ開放する ​

プロトコル振り分けのルールに「ポートノッキング」ルート(プロトコル spa)を追加し、クライアントは TCP で同じポートにノックパケットを送ります:

bash
fwknop -A tcp/443 -P tcp -p 443 -R -D example.com --use-hmac \
  --key-base64-rijndael <encryption key> --key-base64-hmac <HMAC key>
ssh -p 443 user@example.com
  • ノックルートはパケットをポートノッキングモジュールに渡し、転送も応答もしません。-A のポートはノックルールのポート一覧に含まれている必要があります(例:tcp/443);
  • ルール全体でノックを要求している場合、ノックしていない送信元はノックルートしか使えず、ノック後に他のルートとデフォルトターゲットを使えます;
  • ルールにノックルートがある場合のみノックパケットを識別し、他のルールのプロトコル識別には影響しません;
  • ノックルールの UDP ポートを 0 にすると、外部にはプロトコル振り分けのポートだけを開放できます;
  • ノックルートを持つプロトコル振り分けのポートと UDP ノックポートは、ファイアウォールブロックを有効にしたルールに含められません(保存時と有効化時に確認します。ブロックするとノックパケット自体が届かなくなるため)。ファイアウォールルールの生成時にも常に除外されます。

侵入防御との連携 ​

拒否されたノックは「ポートノッキング拒否」イベント(via フィールドは udp または tcp)を発行し、同じ送信元につき最大 5 秒に 1 回です。UDP パケットの送信元アドレスは偽造できるため、攻撃者が侵入防御を使って任意の IP をブロックさせないよう、UDP で届き HMAC 認証に失敗したパケットは数えるだけでイベントを発行しません。HMAC 認証に成功した(鍵を持つクライアントが作成した)うえでルールに拒否された場合(復号失敗、リプレイ、期限切れ、未許可ポートの要求、ユーザー名の不一致など)にだけ発行します。プロトコル振り分けの TCP ポート経由のパケットはハンドシェイクを経ており送信元を偽造できないため、HMAC 不一致でも発行します。侵入防御で「ポートノッキング失敗」テンプレートを使ってルールを作成すると(デフォルトは 10 分間に 5 回で 1 日ブロック)、偽造パケットを送り続ける送信元がブロックされ、ブロックされたアドレスからのノックは直接破棄されます。ノック形式でないデータ(ポートスキャン、他のプロトコル)は数えるだけでイベントは発行しません。

認証の前に送信元の範囲ごと(IPv4 は /24、IPv6 は /64 ごとに毎秒最大 10 個)に速度を制限し、さらに全体で毎秒 200 個の上限をかけ、超過分は直接破棄します。偽造パケットの洪水で CPU が埋まることはなく、1 つの送信元からの洪水が他の送信元の正常なノックを押しのけることもありません。HMAC 認証に失敗したパケットは 1 件ずつログに記録せず、1 分ごとに「N 個の送信元から M 個の無効なノックパケット」として 1 行にまとめます。

制限 ​

  • fwknop の Rijndael(AES)CBC モード + HMAC のみ対応し、GnuPG、旧版の IV モード、HMAC なしのパケットには対応しません;
  • コマンドメッセージ(fwknop -C)は実行せず、NAT アクセス要求(-N)にも対応しません;
  • ノックの認可はメモリ上に保存されるため、Gately の再起動後は再度ノックが必要です(確立済みの接続は影響を受けません)。リプレイ防止の記録もメモリ上だけにあるため、再起動後はタイムスタンプが起動時刻より前のパケットをすべて拒否します。クライアントの時計がサーバーより遅れていると、再起動直後の数秒間のノックが拒否されることがありますが、もう一度ノックすれば通ります。

Apache-2.0 ライセンスで公開されています。