
Knocker, um serviço de controle de acesso baseado em knock para seu homelab.

Knocker é um serviço auto-hospedado que fornece um gateway de autorização de pacote único (SPA) baseado em HTTP do tipo "knock-knock" para o seu Homelab, com clientes web, CLI + GNOME e Android. Pode ser usado como autenticação para seu proxy reverso, como Caddy, ou até mesmo no nível do firewall usando a integração com FirewallD. Permite manter seus serviços completamente privados, abrindo-os sob demanda apenas para endereços IP autorizados.
Isto é ideal para ambientes homelab onde você deseja expor serviços à internet sem uma conexão VPN persistente, minimizando sua superfície de ataque voltada ao público.
Knocker-Web Aplicativo web PWA estático que suporta knock (colocar na lista de permissões) ao recarregar
Knocker-CLI Uma CLI escrita em Go com suporte para knocks em segundo plano, opcionalmente acionados por mudanças de IP.
Knocker-gnome uma extensão do GNOME construída sobre o Knocker-cli.
Knocker-EXPO Um aplicativo Android experimental escrito em React EXPO com suporte para solicitações de knock em segundo plano
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)Este projeto foi projetado para ser implantado como um contêiner Docker usando o arquivo docker-compose.yml fornecido. Utiliza as imagens Docker pré-construídas com suporte para AMD64, ARMv8 e ARMv7.
O Knocker fornece diferentes tags de imagem para diferentes casos de uso:
latest Última versão estável (recomendado para produção)v1.2.3 Tags de versão específica (versões fixas)main Ramo de desenvolvimento (atualizações contínuas, pode ser instável)Configuração:
knocker.example.yaml para knocker.yaml.knocker.yaml para suas próprias strings seguras e aleatórias.trusted_proxies em knocker.yaml. Ela deve corresponder à sub-rede da rede do proxy reverso (docker network inspect xxx).whitelist.storage_path sob o diretório de trabalho do aplicativo, /data ou /tmp.firewalld.enabled: true e ajustando as configurações relacionadas. Nota: Isso requer que o contêiner seja executado como root.Executar o Serviço:
docker compose up -d
Isso irá puxar a imagem knocker pré-construída e iniciar os serviços knocker e caddy.
O Knocker funciona como um gateway de autenticação para seu proxy reverso. Ele oferece um endpoint de verificação para checar se o IP solicitante está na lista de permissões ou não; se não estiver, ele responderá com um 401 e o proxy reverso recusará a conexão.
O Caddy possui a diretiva forward_auth para verificar conexões usando um endpoint de autenticação.
Definir um Snippet Reutilizável: É uma boa prática definir um snippet em seu Caddyfile para a verificação de autenticação.
Proteger Seus Serviços: Importe o snippet para qualquer serviço que deseja proteger.
Exemplo de Caddyfile:
# Caddyfile
# Define um snippet reutilizável para a verificação knock-knock.
# Aponta para o serviço knocker usando o DNS interno do Docker.
(knocker_auth) {
forward_auth knocker:8000 {
uri /verify
}
}
# O endpoint público para realizar o knock.
# Certifique-se de que este domínio aponte para o IP do seu servidor Caddy.
knock.your-domain.com {
reverse_proxy knocker:8000
}
# Um exemplo de serviço protegido.
jellyfin.your-domain.com {
import knocker_auth # Aplica a verificação forward_auth
reverse_proxy jellyfin_service_name:8096
}
Quando um usuário não está na lista de permissões, a diretiva forward_auth do Caddy retornará uma resposta 401 Unauthorized com corpo vazio.
Nota Importante: A diretiva handle_errors do Caddy não funciona com respostas forward_auth. A resposta de erro vem diretamente do serviço de autenticação (knocker), não do próprio Caddy, portanto handle_errors não pode interceptar ou modificar essas respostas.
O Knocker fornece integração avançada com firewall através do firewalld, criando regras de firewall dinâmicas e temporizadas que expiram automaticamente com base no TTL especificado nas solicitações de knock. Este recurso opera no nível da rede, permitindo usar o knocker para serviços não-HTTP, como SSH ou servidores de jogos.
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 expiryO Knocker requer FirewallD 2.0+ devido à dependência do recurso de prioridade de zona. Está disponível no Debian 13, Ubuntu 24.04 LTS e outras distribuições estáveis recentes.
O FirewallD foi escolhido pela capacidade de separar a interface CLI do daemon. Isso permite que o Knocker controle o firewalld de dentro de um contêiner Docker montando o socket D-Bus do sistema, e o FirewallD também possui suporte para regras temporizadas, portanto as regras do knocker expiram automaticamente no final do TTL.
O FIREWALLD NÃO FUNCIONARÁ COM PORTAS PUBLICADAS PELO DOCKER, veja esta issue para mais detalhes
Pré-requisitos
Configuração
Monitore regras ativas:
# Verificar zona knocker
firewall-cmd --zone=knocker --list-all
# Visualizar regras robustas
firewall-cmd --zone=knocker --list-rich-rules
# Monitorar mudanças de regras
journalctl -u firewalld -f
Para configuração detalhada, arquitetura e informações de solução de problemas, consulte o Guia Completo de Integração com FirewallD.
Se você estiver habilitando knock para IPs atrás do tailscale ou outros IPs, pode enfrentar problemas devido ao funcionamento do userland-proxy. Você pode obter um IP de solicitação diferente do endereço IP real.
Desabilitar o userland-proxy deve resolver, mas certifique-se de testar sua configuração. Você também pode usar rede do host.
/knock (POST)Este endpoint valida uma chave de API e coloca um IP na lista de permissões.
Cabeçalhos:
X-Api-Key: Sua chave de API secreta.Corpo (Opcional):
allow_remote_whitelist: true):
{"ip_address": "YOUR_TARGET_IP_OR_CIDR"}
Exemplo (Colocando seu próprio IP na lista):
curl -i -H "X-Api-Key: YOUR_SECRET_KEY" https://knock.your-domain.com/knock
Resposta de Sucesso (200 OK):
{
"whitelisted_entry": "1.2.3.4",
"expires_at": 1672534800,
"expires_in_seconds": 3600
}
/verify (GET)Este endpoint é usado pelo forward_auth do Caddy para verificar se o IP do cliente está na lista de permissões. Retorna 200 OK em caso de sucesso e 401 Unauthorized em caso de falha. X-Forwarded-For, X-Forwarded-Host e X-Forwarded-Uri são confiáveis apenas quando a solicitação se origina de server.trusted_proxies.
O Caddy já encaminha os cabeçalhos de solicitação X-Forwarded-* relevantes para o Knocker, para que /verify possa tomar a decisão de autenticação.
O projeto inclui uma suíte completa de testes.
Este projeto usa a cadeia de ferramentas Python da Astral:
uv para gerenciamento de dependências, ambientes e execução de comandosruff para linting e formataçãoty para verificação de tiposPara executar os testes localmente:
Instalar uv:
curl -LsSf https://astral.sh/uv/install.sh | sh
Sincronizar o ambiente do projeto:
uv sync --all-groups
Executar as verificações:
uv run pytest
uv run --group lint ruff check .
uv run --group lint ruff format --check .
uv run --group type ty check
Há um ambiente de desenvolvimento em dev, com scripts bash para testes de integração com Caddy e outro separado com firewalld.
As pilhas de teste padrão são dev/docker-compose.yml e dev/docker-compose.ci.yml; ambas expõem o Caddy em http://localhost:18080 e https://localhost:18443.
O CI executa os testes do Caddy, mas o firewalld precisa de um runner privilegiado, por isso precisa ser executado localmente e não faz parte do CI.
Os endpoints de documentação interativa (/docs, /redoc, /openapi.json) estão desabilitados por padrão. Para expô-los, defina o seguinte em knocker.yaml:
documentation:
enabled: true
openapi_output_path: "openapi.json"
Quando a documentação está desabilitada (padrão), o Knocker remove esses endpoints e exclui qualquer arquivo de esquema gerado anteriormente para evitar artefatos obsoletos.
Para uma especificação formal da API e um resumo das escolhas arquiteturais, consulte a documentação.
Knocker foi totalmente "vibe-coded" (codificado por vibe). A implementação inicial foi feita com Gemini 2.5 Pro, graças aos tokens fornecidos no hackathon da Roo Code/Requesty.
Funcionalidades posteriores foram feitas principalmente com o GitHub Copilot Agent (Sonnet 4 / posteriormente 4.5), que precisava de muitas correções, feitas principalmente pelo GPT-5 mini/CODEX no Roo Code, Opencode e na extensão padrão do Copilot.
Eu fiz o meu melhor com isso, sempre planejando mudanças e testando tudo após cada alteração, mas se você é contra IA, provavelmente não conseguiria mudar sua opinião sobre isso.