
Knocker, 홈랩을 위한 노크 기반 접근 제어 서비스

Knocker는 Homelab을 위한 HTTP 기반 "knock-knock" 단일 패킷 인증(SPA) 게이트웨이를 제공하는 자체 호스팅 서비스로, 웹, CLI + gnome 및 Android 클라이언트를 지원합니다. Caddy와 같은 리버스 프록시의 인증으로 사용하거나, FirewallD 통합을 통해 방화벽 수준에서도 사용할 수 있습니다. 이를 통해 서비스를 완전히 비공개로 유지하고, 인증된 IP 주소에 대해서만 온디맨드로 개방할 수 있습니다.
이는 지속적인 VPN 연결 없이 인터넷에 서비스를 노출하면서 공개적으로 노출되는 공격 표면을 최소화하려는 홈랩 환경에 이상적입니다.
Knocker-Web 새로고침 시 knock(화이트리스팅)을 지원하는 정적 PWA 웹 앱
Knocker-CLI IP 변경에 의해 선택적으로 트리거되는 백그라운드 knock을 지원하는 Go로 작성된 CLI.
Knocker-gnome Knocker-cli 위에 구축된 gnome 확장.
Knocker-EXPO 백그라운드 knock 요청을 지원하는 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 목록을 검토합니다. 이 목록에는 리버스 프록시 자체의 주소만 포함되어야 하며, 서브넷은 포함되어서는 안 됩니다. knocker는 나열된 피어의 X-Forwarded-For만 신뢰하므로, 전체 서브넷 항목(예: 172.16.238.0/24)은 해당 네트워크의 모든 컨테이너를 신원 권한 기관으로 만들어 -- 그 중 어느 것이든 임의의 클라이언트 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를 통해 고급 방화벽 통합을 제공하여, knock 요청에 지정된 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는 zone priority 기능에 대한 의존성으로 인해 FirewallD 2.0+가 필요합니다. Debian 13, Ubuntu 24.04 LTS 및 기타 최신 안정 배포판에서 사용할 수 있습니다.
FirewallD는 CLI 인터페이스를 데몬과 분리할 수 있는 능력 때문에 선택되었습니다. 이를 통해 Knocker는 시스템의 D-Bus 소켓을 마운트하여 Docker 컨테이너 내에서 firewalld를 제어할 수 있으며, FirewallD는 시간 제한 규칙도 지원하므로 knocker 규칙이 TTL이 끝나면 자동으로 만료됩니다.
FIREWALLD는 DOCKER 게시 포트와 함께 작동하지 않습니다, 자세한 내용은 이 이슈를 확인하세요
사전 요구 사항
구성
활성 규칙 모니터링:
# 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에 대해 knocking을 활성화하는 경우, userland-proxy가 작동하는 방식으로 인해 문제가 발생할 수 있으며, 실제 IP 주소와 다른 요청 IP를 받을 수 있습니다.