
Knocker、ホームラボ向けのノックベースのアクセス制御サービス

Knockerは、Web、CLI + GNOME、Androidクライアントを備えた、ホームラボ向けのHTTPベースの「ノックノック」シングルパケット認証(SPA)ゲートウェイを提供するセルフホスト型サービスです。Caddyなどのリバースプロキシの認証として使用できるほか、FirewallD連携を使用してファイアウォールレベルで使用することもできます。これにより、サービスを完全に非公開に保ち、許可されたIPアドレスのみに対してオンデマンドで開放できます。
これは、常時接続のVPNを使用せずにサービスをインターネットに公開しつつ、公開されている攻撃対象領域を最小限に抑えたいホームラボ環境に最適です。
Knocker-Web リロード時にノック(ホワイトリスト登録)をサポートする静的PWAウェブアプリ
Knocker-CLI Goで書かれたCLI。オプションでIP変更をトリガーにできるバックグラウンドノックをサポートしています。
Knocker-gnome Knocker-CLI上に構築されたGNOME拡張機能です。
Knocker-EXPO バックグラウンドでのノックリクエストをサポートする、React EXPOで書かれた実験的なAndroidアプリ
sequenceDiagram
participant User
participant Caddy as Reverse Proxy (Caddy)
participant Knocker
participant Service as Protected Service
User->>Caddy: HTTP request to protected service
Caddy->>Knocker: GET /verify (copies X-Forwarded-For)
Knocker-->>Knocker: check always_allowed_ips / excluded_paths / whitelist
alt IP whitelisted
Knocker-->>Caddy: 200 OK (empty body)
Caddy->>Service: forward request
Service-->>Caddy: 200 OK
Caddy-->>User: 200 OK
else IP not whitelisted
Knocker-->>Caddy: 401 Unauthorized (empty body)
Caddy-->>User: 401 Unauthorized
end
Note over User,Knocker: Performing a "knock" (to add whitelist entry)
User->>Knocker: POST /knock (X-Api-Key, optional ip_address, ttl)
Knocker->>Knocker: validate API key, determine client IP
Knocker->>Knocker: update whitelist.json with expiry
Knocker-->>User: 200 OK (whitelisted_entry, expires_at, expires_in_seconds)
このプロジェクトは、提供されている docker-compose.yml ファイルを使用してDockerコンテナとしてデプロイするように設計されています。AMD64、ARMv8、ARMv7をサポートするビルド済みDockerイメージを使用します。
Knockerは、さまざまなユースケースに応じて異なるイメージタグを提供します:
latest 最新の安定版リリース(本番環境向け推奨)v1.2.3 特定のバージョンタグ(固定バージョン)main 開発ブランチ(ローリングアップデート、不安定な場合があります)設定:
knocker.example.yaml を knocker.yaml にリネームします。knocker.yaml 内のデフォルトのAPIキーを、自分自身の安全なランダム文字列に変更してください。knocker.yaml の trusted_proxies リストを確認してください。リバースプロキシのネットワークのサブネットと一致する必要があります(docker network inspect xxx)。whitelist.storage_path はアプリの作業ディレクトリ、/data、または /tmp 配下に保ってください。firewalld.enabled: true を設定し、関連する設定を調整することでFirewalld連携を構成できます。注意: これにはコンテナをrootとして実行する必要があります。サービスを実行する:
docker compose up -d
これによりビルド済みの knocker イメージがプルされ、 と の両方のサービスが起動します。
Knockerは、リバースプロキシの認証ゲートウェイとして機能します。リクエスト元IPがホワイトリストに登録されているかどうかを確認するためのverifyエンドポイントを提供します。登録されていない場合は401で応答し、リバースプロキシは接続を拒否します。
Caddyには、認証エンドポイントを使用して接続を確認する forward_auth ディレクティブがあります。
再利用可能なスニペットを定義する: 認証チェック用のスニペットを Caddyfile に定義するのがベストプラクティスです。
サービスを保護する: 保護したい各サービスに対してスニペットをインポートします。
Caddyfile の例:
# Caddyfile
# Define a reusable snippet for the knock-knock check.
# It points to the knocker service using Docker's internal DNS.
(knocker_auth) {
forward_auth knocker:8000 {
uri /verify
}
}
# The public endpoint for performing the knock.
# Make sure this domain points to your Caddy server's IP.
knock.your-domain.com {
reverse_proxy knocker:8000
}
# An example protected service.
jellyfin.your-domain.com {
import knocker_auth # Apply the forward_auth check
reverse_proxy jellyfin_service_name:8096
}
ユーザーがホワイトリストに登録されていない場合、Caddyの forward_auth ディレクティブは空のボディを持つ 401 Unauthorized 応答を返します。
重要な注意: Caddyの handle_errors ディレクティブは forward_auth 応答では機能しません。エラー応答はCaddy自体ではなく認証サービス(knocker)から直接返されるため、handle_errors はこれらの応答を傍受したり変更したりできません。
Knockerはfirewalldを通じて高度なファイアウォール連携を提供し、ノックリクエストで指定されたTTLに基づいて自動的に期限切れとなる動的な時間ベースのファイアウォールルールを作成します。この機能はネットワークレベルで動作するため、sshやゲームサーバーなどの非HTTPサービスにもknockerを使用できます。
sequenceDiagram
participant Client as User
participant Firewall as Firewalld (knocker zone)
participant Knocker
participant Service as Protected Service (port 22)
Note over Client,Firewall: Initial state — monitored port is blocked by default
Client->>Firewall: TCP SYN to Service:22
Firewall-->>Client: DROP (no response)
Note over Client,Knocker: User performs a knock to whitelist their IP
Client->>Knocker: POST /knock (X-Api-Key, optional ip_address, ttl)
Knocker->>Knocker: validate API key & determine client IP
Knocker->>Firewall: add rich accept rule for client IP on port 22 with timeout
Firewall-->>Knocker: success
Note over Firewall,Client: New rule overrides DROP due to higher priority
Client->>Firewall: TCP SYN to Service:22
Firewall->>Service: forward packet
Service-->>Client: TCP SYN-ACK (connection established)
Knocker->>Knocker: update whitelist.json with expiry
Knockerはゾーン優先度機能に依存しているため、FirewallD 2.0以上が必要です。これはDebian 13、Ubuntu 24.04 LTS、その他の最近の安定版ディストリビューションで利用できます。
FirewallDは、CLIインターフェースをデーモンから分離できるという点で選ばれました。これにより、システムのD-BusソケットをマウントすることでKnockerはDockerコンテナ内からfirewalldを制御でき、さらにFirewallDは時間制ルールをサポートしているため、knockerのルールはTTLの終了時に自動的に期限切れになります。
FIREWALLDはDockerの公開ポートでは動作しません。詳細はこのissueを確認してください。
前提条件
設定
knocker.yaml 設定でFirewallDを有効にします。設定項目は既にサンプル設定に含まれています。アクティブなルールを監視する:
# Check knocker zone
firewall-cmd --zone=knocker --list-all
# View rich rules
firewall-cmd --zone=knocker --list-rich-rules
# Monitor rule changes
journalctl -u firewalld -f
詳細な設定、アーキテクチャ、トラブルシューティング情報については、完全なFirewallD統合ガイドを参照してください。
Tailscaleやその他のIPの背後にあるIPに対してノックを有効にしている場合、userland-proxyの動作方法が原因で問題が発生する可能性があります。実際のIPアドレスとは異なるリクエストIPになることがあります。
Userland-proxyを無効にすると問題が解決するはずですが、必ずセットアップをテストしてください。ホストネットワークを使用することもできます。
/knock (POST)このエンドポイントはAPIキーを検証し、IPをホワイトリストに登録します。
ヘッダー:
X-Api-Key: あなたのシークレットAPIキー。ボディ(任意):
allow_remote_whitelist: true が必要):
{"ip_address": "YOUR_TARGET_IP_OR_CIDR"}
例(自分のIPをホワイトリストに登録):
curl -i -H "X-Api-Key: YOUR_SECRET_KEY" https://knock.your-domain.com/knock
成功レスポンス(200 OK):
{
"whitelisted_entry": "1.2.3.4",
"expires_at": 1672534800,
"expires_in_seconds": 3600
}
/verify (GET)このエンドポイントは、クライアントのIPがホワイトリストに登録されているかを確認するためにCaddyの forward_auth によって使用されます。成功時には 200 OK、失敗時には 401 Unauthorized を返します。X-Forwarded-For、X-Forwarded-Host、X-Forwarded-Uri は、リクエストが server.trusted_proxies から送信された場合にのみ信頼されます。
Caddyは関連する X-Forwarded-* リクエストヘッダーをKnockerに転送するため、/verify は認証判断を行うことができます。
このプロジェクトには完全なテストスイートが含まれています。
このプロジェクトはAstralのPythonツールチェーンを使用しています:
uv 依存関係管理、環境、コマンド実行用ruff リントとフォーマット用ty 型チェック用テストをローカルで実行するには:
uvをインストール:
curl -LsSf https://astral.sh/uv/install.sh | sh
プロジェクト環境を同期:
uv sync --all-groups
チェックを実行:
uv run pytest
uv run --group lint ruff check .
uv run --group lint ruff format --check .
uv run --group type ty check
dev の下に開発環境があり、caddyとの統合テスト用のbashスクリプトと、firewalld用の別のbashスクリプトがあります。標準のテストスタックは dev/docker-compose.yml と dev/docker-compose.ci.yml です。どちらもCaddyを http://localhost:18080 と https://localhost:18443 で公開します。
CIはcaddyテストを実行しますが、firewalldは特権ランナーを必要とするため、ローカルで実行する必要があり、CIの一部ではありません。
インタラクティブなドキュメントエンドポイント(/docs、/redoc、/openapi.json)はデフォルトで無効になっています。これらを公開するには、knocker.yaml に以下を設定します:
documentation:
enabled: true
openapi_output_path: "openapi.json"
ドキュメントが無効(デフォルト)の場合、Knockerはこれらのエンドポイントを削除し、古いアーティファクトを防ぐために以前に生成されたスキーマファイルも削除します。
正式なAPI仕様とアーキテクチャ上の選択の概要については、ドキュメント を参照してください。
Knockerは完全にVibe Codingで作られました。初期実装はGemini 2.5 Proで行われました。roo code/requestyハッカソンで提供されたトークンに感謝します。
その後の機能はほとんどGitHub Copilot Agent(Sonnet 4、後に4.5)を使用して行われましたが、多くの修正が必要でした。修正は主にRoo Code、Opencode、標準のCopilot拡張機能でのGPT-5 mini/CODEXによって行われました。
私はこれに最善を尽くし、常に変更を計画し、変更のたびにすべてをテストしました。しかし、もしあなたがAI反対派なら、私があなたの意見を変えることはおそらくできないでしょう。
knockercaddy