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

Knockerは、Homelab向けにWeb、CLI + GNOME、Androidクライアントを備えた、HTTPベースの「ノックノック」シングルパケット認証(SPA)ゲートウェイを提供するセルフホスト型サービスです。 Caddyのようなリバースプロキシの認証として、あるいはFirewallD連携を使ってファイアウォールレベルでも使用できます。これにより、サービスを完全にプライベートに保ち、認可されたIPアドレスに対してのみオンデマンドで開放できます。
これは、常時接続のVPNなしでサービスをインターネットに公開したいが、公開側の攻撃対象領域を最小限に抑えたいホームラボ環境に最適です。
Knocker-Web リロード時にノック(ホワイトリスト登録)をサポートする静的PWA Webアプリ
Knocker-CLI IP変更によってオプションでトリガーされるバックグラウンドノックをサポートするGoで書かれたCLI。
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に少なくとも1つの安全でランダムなAPIキーを追加します。サンプルには意図的に使用可能なデフォルト値が含まれていません。knocker.yamlのtrusted_proxiesリストを確認します。これにはリバースプロキシ自身のアドレス(複数可)のみを含める必要があり、そのサブネットを含めてはいけません。KnockerはリストされたピアからのX-Forwarded-Forのみを信頼するため、サブネット全体のエントリ(例: 172.16.238.0/24)は、そのネットワーク上のすべてのコンテナをID権限者にしてしまいます。そのうちのどれでも任意のクライアントIPを主張したり、通常のキーで任意のアドレスをホワイトリストに登録したりできます。Dockerはデフォルトで安定したコンテナIPを割り当てないため、ipv4_address / ipv6_addressでプロキシを固定し(docker-compose.yml内のコメントアウトされたcaddyサービスを参照)、それらの正確な/32および/128アドレスをリストします。whitelist.storage_pathはアプリの作業ディレクトリ、/data、または/tmpの下に保ちます。firewalld.enabled: trueを設定し、関連する設定を調整してfirewalld連携を構成します。注意: これにはコンテナをrootとして実行する必要があります。サービスの実行:
docker compose up -d
これにより、ビルド済みのknockerイメージがプルされ、knockerとcaddyの両方のサービスが起動します。
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 {
request_body {
max_size 4KB
}
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インターフェースをデーモンから分離できる能力のために選ばれました。これにより、KnockerはシステムのD-BusソケットをマウントすることでDockerコンテナ内からfirewalldを制御でき、またFirewallDはタイムドルールをサポートしているため、knockerルールはTTLの終了時に自動的に期限切れになります。
FIREWALLDはDOCKERの公開ポートでは機能しません。詳細はこのissueを確認してください。
前提条件
設定
アクティブなルールの監視:
# 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をホワイトリストに登録します。