
Knocker, сервис контроля доступа на основе стука для вашего домашнего сервера

Knocker — это самостоятельно размещаемый сервис, предоставляющий HTTP-шлюз авторизации по принципу «стук-стук» (single-packet authorization, SPA) для вашего домашнего сервера с веб-, CLI-, GNOME- и Android-клиентами. Его можно использовать для аутентификации в вашем обратном прокси, например Caddy, или даже на уровне брандмауэра с помощью интеграции с FirewallD. Он позволяет держать ваши сервисы полностью закрытыми, открывая их по требованию только для авторизованных IP-адресов.
Это идеально подходит для домашних лабораторий, где вы хотите предоставлять сервисы в интернет без постоянного VPN-подключения, минимизируя при этом публичную поверхность атаки.
Knocker-Web Статическое PWA-веб-приложение, поддерживающее стук (добавление в белый список) при перезагрузке
Knocker-CLI CLI, написанный на Go, с поддержкой фоновых стуков, опционально запускаемых при изменении IP.
Knocker-gnome — расширение GNOME, построенное на базе Knocker-cli.
Knocker-EXPO Экспериментальное Android-приложение, написанное на React EXPO, с поддержкой фоновых запросов стука
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-контейнера с использованием предоставленного файла docker-compose.yml. Он использует готовые образы Docker с поддержкой AMD64, ARMv8 и ARMv7
Knocker предоставляет различные теги образов для разных случаев использования:
latest Последний стабильный релиз (рекомендуется для продакшена)v1.2.3 Теги конкретных версий (зафиксированные версии)main Ветка разработки (постоянные обновления, может быть нестабильной)Конфигурация:
knocker.example.yaml в knocker.yaml.knocker.yaml; пример намеренно не содержит рабочих значений по умолчанию.trusted_proxies в knocker.yaml: он должен содержать только собственные адреса обратного прокси, а не его подсеть. Поскольку knocker доверяет X-Forwarded-For только от перечисленных узлов, запись целой подсети (например, 172.16.238.0/24) превращает каждый контейнер в этой сети в источник идентичности -- любой из них мог бы заявить любой клиентский IP или добавить произвольные адреса в белый список с обычным ключом. Docker по умолчанию не назначает стабильные IP-адреса контейнерам, поэтому закрепите прокси с помощью ipv4_address / ipv6_address (см. закомментированный сервис caddy в docker-compose.yml) и укажите именно эти адреса /32 и /128.whitelist.storage_path в рабочем каталоге приложения, /data или /tmp.firewalld.enabled: true и скорректировав связанные параметры. Примечание: Это требует запуска контейнера от имени root.Запуск сервиса:
docker compose up -d
Это загрузит готовый образ knocker и запустит сервисы knocker и caddy.
Knocker работает как шлюз аутентификации для вашего обратного прокси. Он предоставляет endpoint verify для проверки, находится ли запрашивающий IP в белом списке; если нет, он ответит 401, и обратный прокси отклонит соединение.
Caddy имеет директиву forward_auth для проверки соединений с использованием endpoint аутентификации.
Определите переиспользуемый сниппет: Хорошей практикой является определение сниппета в вашем 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
}
Когда пользователь не находится в белом списке, директива forward_auth в Caddy вернёт ответ 401 Unauthorized с пустым телом.
Важное примечание: Директива handle_errors в Caddy не работает с ответами forward_auth. Ответ об ошибке приходит напрямую от сервиса аутентификации (knocker), а не от самого Caddy, поэтому handle_errors не может перехватить или изменить эти ответы.
Knocker предоставляет расширенную интеграцию с брандмауэром через firewalld, создавая динамические временные правила брандмауэра, которые автоматически истекают в соответствии с TTL, указанным в запросах стука. Эта функция работает на сетевом уровне, позволяя использовать knocker для не-HTTP сервисов, таких как ssh или игровые серверы.