"Pode entrar. Você, não." 🚪
O Porteiro é um Shell Script simples e cirúrgico que resolve um problema clássico de quem sobe um servidor Linux com phpMyAdmin: a porta de entrada fica escancarada pra internet inteira.
Enquanto você dorme, bots do mundo todo ficam batendo na porta do seu /phpmyadmin/ tentando entrar na força bruta. O Porteiro resolve isso do jeito mais elegante possível — ele tranca tudo e só abre a porta quando você mesmo aparece via SSH.
A lógica é simples:
- 🌍 Pra internet: erro 403. Nem existe.
- 🔑 Pra você (via SSH):
sudo porteiro-on. Acesso liberado na hora em todas as rotas. - ⏱️ Depois do tempo configurado:
porteiro-off. A porta tranca sozinha, mesmo que você esqueça.
Zero dependência externa. Zero banco de dados. Zero token obrigatório. O SSH já é sua identidade.
Desenvolvido por: Carlos Henrique Tourinho Santana
- 🔍 Detecção Automática de IP: Lê seu IP via
$SSH_CLIENT. Fallback automático parawho am i— funciona em tmux, screen e sudo su. - 🌍 Isolamento Total: Bloqueia as rotas com
deny allpara o resto da internet. O/phpmyadmin/simplesmente não existe. - ⚡ Liberação Instantânea: Um comando (
sudo porteiro-on) e seu navegador já acessa. Nginx recarrega na hora. - 👥 Multi-IP: Vários admins podem abrir acesso simultaneamente. Cada
porteiro-onadiciona o IP sem sobrescrever os anteriores. - ⏱️ Tempo Configurável:
sudo porteiro-on 30,sudo porteiro-on 30m,sudo porteiro-on 2h— número puro é tratado como minutos. Formato inválido é rejeitado com mensagem clara. - ⏱️ Auto-Off Individual por IP: Cada admin tem seu próprio timer independente. Se dois admins abrirem acesso, o Auto-Off de um não afeta o outro. Tag
#porteiro-IPgarante que nunca cancela jobs externos do servidor. - 🔒 Fechamento Manual: Terminou antes?
sudo porteiro-offtranca na hora, sem esperar o timer. - 🛡️ Nginx Sempre Validado: Antes de qualquer
reload, o Porteiro rodanginx -t. Se a configuração estiver quebrada, ele avisa e aborta — nunca derruba o servidor. - 📊 Status em Tempo Real:
sudo porteiro-statusmostra estado, IPs ativos, rotas protegidas e log recente — com notificação Telegram se configurado. - 📋 Log de Auditoria com Rotação: Cada evento registrado em
/var/log/porteiro.log. Logrotate configurado automaticamente — o log nunca cresce infinito em produção. - 📣 Notificação via Telegram: Receba uma mensagem no celular sempre que a porta abrir, fechar, o status for consultado, um IP for revogado ou o Porteiro for desinstalado. Totalmente opcional — configurado com wizard durante a instalação.
- 📋 Listagem de IPs Ativos:
sudo porteiro-listexibe todos os IPs autorizados no momento, com data e hora de abertura de cada um. Lê direto do arquivo e do log — sem banco de dados. - 🚫 Revogação Individual:
sudo porteiro-revoke <IP>remove o acesso de um IP específico sem afetar os demais. Valida formato IPv4, escapa o input antes dosede registraREVOGADOno log. - 🛣️ Multi-rota: Proteja
/phpmyadmin/,/adminer/,/wp-admin/ou qualquer rota sensível. Umporteiro-onlibera tudo, umporteiro-offbloqueia tudo. Rotas escolhidas interativamente durante a instalação. - 🪶 Levíssimo: Shell Script puro. Zero dependências externas. Funciona até em VPS de R$15/mês.
A stack mais enxuta possível — porque segurança não precisa ser complicada:
- Shell Script (Bash) — A lógica toda. Sem framework, sem runtime.
- Nginx — O portão. Lê o IP injetado e decide quem passa.
at— O reloginho que tranca a porta sozinho após o tempo definido.$SSH_CLIENT— A variável nativa do SSH que entrega seu IP de bandeja.- Telegram Bot API — Notificações opcionais via
curl. Zero biblioteca externa.
Sem npm install. Sem docker-compose up. Um único script faz tudo.
git clone https://github.com/henriquetourinho/porteiro.git
cd porteirosudo bash install.shO instalador guia você por dois wizards interativos antes de criar qualquer arquivo:
Wizard 1 — Rotas protegidas:
🛣️ Rotas Protegidas (Multi-rota)
==============================
✅ /phpmyadmin/ — adicionada por padrão.
Deseja proteger mais rotas? Selecione pelos números
separados por espaço (ex: 1 3) ou pressione Enter para pular.
[1] /adminer/
[2] /wp-admin/
[3] /wp-login.php
[4] /panel/
[5] Digitar manualmente
Opções (ex: 1 2): 1 2
✅ '/adminer/' adicionada.
✅ '/wp-admin/' adicionada.
Rotas que serão protegidas:
→ /phpmyadmin/
→ /adminer/
→ /wp-admin/
Wizard 2 — Telegram (opcional):
📣 Notificações via Telegram (opcional)
==============================
Deseja configurar o Telegram agora? (s/N): s
Token do bot: SEU_TOKEN
Chat ID: SEU_CHAT_ID
✅ Bot validado! Notificações ativadas.
Após os wizards, o instalador também cuida de:
- Verificar se o Nginx está instalado — alerta antes de continuar se não encontrar
- Instalar o
at(se não estiver presente) — suportaapt-get,dnfeyum - Criar o diretório
/opt/porteiro/com os scripts - Criar o arquivo de configuração
/opt/porteiro/porteiro.confcom as rotas escolhidas - Criar o arquivo
/etc/nginx/porteiro_ips.confcom permissões640eroot:root - Criar o log em
/var/log/porteiro.logcom permissões640eroot:root - Configurar o logrotate em
/etc/logrotate.d/porteiro(rotação mensal, 6 meses) - Aplicar permissões
755eroot:rootem todos os scripts e noporteiro.conf - Registrar os comandos globais:
porteiro-on,porteiro-off,porteiro-status,porteiro-listeporteiro-revoke - Gerar os blocos Nginx prontos para cada rota escolhida
Ao final da instalação, o script exibe os blocos Nginx prontos para copiar — um para cada rota escolhida no wizard. Exemplo para /phpmyadmin/ e /adminer/:
# --- PHPMYADMIN ---
location ^~ /phpmyadmin/ {
include /etc/nginx/porteiro_ips.conf;
deny all;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php-fpm.sock;
}
}
# --- ADMINER ---
location ^~ /adminer/ {
include /etc/nginx/porteiro_ips.conf;
deny all;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php-fpm.sock;
}
}💡 Multi-rota: todas as rotas compartilham o mesmo
/etc/nginx/porteiro_ips.conf. Umporteiro-onlibera tudo. Umporteiro-offbloqueia tudo.
Abra o arquivo do Nginx, cole os blocos e recarregue:
sudo nano /etc/nginx/sites-available/default
sudo nginx -t && sudo systemctl reload nginxA partir daqui, as rotas retornam 403 Forbidden para o mundo inteiro. O Porteiro assumiu o plantão.
Se pulou o Telegram durante a instalação, edite o porteiro.conf:
sudo nano /opt/porteiro/porteiro.confTELEGRAM_TOKEN="seu_token_aqui"
TELEGRAM_CHAT_ID="seu_chat_id_aqui"Como obter:
- TOKEN → Fale com
@BotFatherno Telegram e crie um bot. - CHAT_ID → Fale com
@userinfobotno Telegram.
Testar manualmente:
curl "https://api.telegram.org/botSEU_TOKEN/getMe"sudo bash uninstall.shO desinstalador também é interativo:
- Fecha o acesso e limpa o Nginx antes de remover qualquer coisa
- Envia notificação Telegram informando a desinstalação (se configurado)
- Cancela agendamentos do Auto-Off (todos os timers individuais por IP, via
at -c) - Remove scripts, links simbólicos e arquivos de configuração
- Remove o logrotate em
/etc/logrotate.d/porteiro - Pergunta se deseja remover o log de auditoria
- Lista as rotas que estavam protegidas e oferece abrir o Nginx para remover os blocos manualmente
porteiro/
├── install.sh # Instalador automático com wizards interativos
├── uninstall.sh # Desinstalador interativo (remove tudo limpo)
├── README.md # Este arquivo
└── LICENSE # MIT
# Após instalar, os scripts ficam em:
/opt/porteiro/
├── porteiro-on # Libera seu IP em todas as rotas protegidas
├── porteiro-off # Bloqueia todas as rotas para todo mundo
├── porteiro-status # Mostra estado, rotas ativas e log recente
├── porteiro-list # Lista todos os IPs ativos com data de abertura
├── porteiro-revoke # Revoga acesso de um IP específico
└── porteiro.conf # Configurações (tempo, rotas, Telegram)
# Comandos globais registrados em:
/usr/local/bin/porteiro-on
/usr/local/bin/porteiro-off
/usr/local/bin/porteiro-status
/usr/local/bin/porteiro-list
/usr/local/bin/porteiro-revoke
# Arquivos gerados no servidor:
/etc/nginx/porteiro_ips.conf # IPs autorizados (compartilhado por todas as rotas)
/var/log/porteiro.log # Log de auditoria
/etc/logrotate.d/porteiro # Rotação automática do log (mensal, 6 meses)
No dia a dia, é só isso:
sudo porteiro-on # Usa o tempo padrão (porteiro.conf)
sudo porteiro-on 30 # Libera por 30 minutos (número puro)
sudo porteiro-on 30m # Libera por 30 minutos
sudo porteiro-on 2h # Libera por 2 horasSaída esperada:
✅ Acesso liberado!
IP autorizado : 189.x.x.x
Duração : 2 hora(s)
Auto-Off em : 120 minuto(s)
Rotas ativas : /phpmyadmin/ /adminer/
sudo porteiro-listSaída esperada:
🚪 Porteiro — IPs Ativos
==============================
🟢 IPs atualmente autorizados:
→ 189.x.x.x (aberto em 2026-02-22 21:45:12)
→ 200.y.y.y (aberto em 2026-02-22 22:10:05)
Rotas protegidas:
• /phpmyadmin/
• /adminer/
sudo porteiro-revoke 189.x.x.xSaída esperada:
🔒 Acesso revogado: 189.x.x.x
Se o IP não estiver na lista:
⚠️ IP não encontrado na lista de autorizados: 189.x.x.x
IPs ativos no momento:
→ 200.y.y.y
sudo porteiro-offSaída esperada:
🔒 Acesso bloqueado!
Rotas isoladas: /phpmyadmin/ /adminer/
sudo porteiro-statusSaída esperada:
🚪 Porteiro — Status
========================
Estado : 🟢 ABERTO
IP ativo: 189.x.x.x
Rotas : /phpmyadmin/ /adminer/
Auto-Off: 22:45:00
📋 Últimas 10 entradas do log:
------------------------
[2026-02-22 21:45:12] ABERTO | IP: 189.x.x.x | Duração: 1 hora(s) | Rotas: /phpmyadmin/,/adminer/ | Host: meuservidor
[2026-02-22 20:10:03] FECHADO | Rotas: /phpmyadmin/,/adminer/ | Host: meuservidor
[Você faz SSH no servidor]
↓
[porteiro-on lê $SSH_CLIENT e extrai seu IP]
↓
[Processa argumento de tempo (ou usa DEFAULT_TIME do porteiro.conf)]
↓
[Verifica se IP já está na lista — adiciona apenas se não estiver (multi-IP)]
↓
[nginx -t valida a configuração antes de qualquer reload]
↓
[Nginx recarrega — todas as rotas com include porteiro_ips.conf liberam o IP]
↓
[Registra no /var/log/porteiro.log com IP e rotas]
↓
[Envia notificação no Telegram com IP, rotas e duração (se configurado)]
↓
[at agenda porteiro-revoke <SEU_IP> com tag #porteiro-IP — timer individual]
↓
[Tempo esgotado: só seu IP é revogado — outros admins continuam ativos]
A mágica do multi-rota está no arquivo /etc/nginx/porteiro_ips.conf — compartilhado por todos os blocos location. Alterar esse arquivo uma vez afeta todas as rotas simultaneamente. O Porteiro nunca toca diretamente na configuração do Nginx.
| Cenário | Sem Porteiro | Com Porteiro |
|---|---|---|
| Rotas sensíveis expostas na internet | ✅ Sim (vulnerável) | ❌ Não (403 pra todos) |
| Ataques de força bruta | ✅ Possível | ❌ Impossível (porta fechada) |
| Acesso do administrador | ✅ Sim | ✅ Sim (via SSH + porteiro-on) |
| Múltiplos admins simultâneos | ❌ Conflito de IPs | ✅ Multi-IP nativo |
| Ver quem está com acesso | ❌ Não | ✅ porteiro-list |
| Revogar um admin sem fechar tudo | ❌ Não | ✅ porteiro-revoke |
| Esqueceu a porta aberta | ✅ Problema seu | ❌ Auto-Off resolve |
| Jobs externos cancelados pelo script | ✅ Risco real | ❌ Tag #porteiro protege |
| Nginx derrubado por config quebrada | ✅ Possível | ❌ nginx -t valida antes |
| Controle do tempo de acesso | ❌ Não | ✅ porteiro-on 30m / 2h |
| Proteger múltiplas rotas | ❌ Configuração manual | ✅ Multi-rota com wizard |
| Log cresce infinito em produção | ✅ Problema | ❌ Logrotate automático |
| Auditoria de acessos | ❌ Não | ✅ /var/log/porteiro.log |
| Alerta no celular | ❌ Não | ✅ Telegram (opcional) |
| Configuração necessária | — | ~5 minutos |
| Dependências externas | — | Zero |
- Rotas inacessíveis por padrão (403)
- Liberação apenas para IP autenticado via SSH
- Multi-IP: vários admins simultâneos sem sobrescrever
-
^allowancorado — imune a match em linhas comentadas -
flockno append — escrita atômica, sem race condition em multi-admin simultâneo - Timer individual por IP — Auto-Off de um admin não afeta os outros
- Jobs
atidentificados por tag#porteiro-IPe inspecionados viaat -c— nunca afeta jobs externos - Validação de formato e faixa IPv4 (0-255) antes de qualquer operação no
porteiro-revoke - Input escapado no
sed— proteção contra regex injection -
grep -Fno wizard de rotas — sem interpretação de regex em input do usuário - Fechamento manual disponível (
porteiro-offfecha tudo de uma vez) - Nginx validado com
nginx -tantes de qualquer reload - Verificação de Nginx instalado no início da instalação
- Sem credenciais armazenadas em disco
- Multi-rota com arquivo compartilhado — espaços normalizados com
xargs - Permissões explícitas em todos os arquivos: scripts
755, config/log/nginx_conf640, tudoroot:root - Compatível com qualquer sistema Unix (sem
grep -P, POSIX puro)
- Log de auditoria com IP e rotas em
/var/log/porteiro.log - Logrotate configurado automaticamente (mensal, 6 meses, comprimido)
-
porteiro-statuscom estado e rotas em tempo real -
porteiro-listlista IPs ativos com data de abertura -
porteiro-revokerevoga IP individual com registro em log - Notificação Telegram em todos os eventos:
porteiro-on,porteiro-off,porteiro-revoke,porteiro-statuse desinstalação (opcional) - Escape MarkdownV2 correto via
sedencadeado — IPs, timestamps e parênteses escapados corretamente
- Zero dependências npm/pip/gem
- Zero banco de dados
- Zero tokens obrigatórios
- Shell Script puro
- Funciona em qualquer VPS com Nginx
- Ubuntu / Debian
- Rocky Linux / AlmaLinux / CentOS (via
dnf/yum) - Qualquer versão do PHP-FPM (socket comentado com opções 8.1/8.2/8.3)
- Nginx (qualquer versão recente)
- Qualquer rota sensível
- POSIX puro — sem
grep -P, funciona em Alpine, BusyBox e macOS
O arquivo /opt/porteiro/porteiro.conf centraliza tudo:
# Tempo padrão em minutos (quando nenhum argumento é passado)
DEFAULT_TIME=60
# Rotas protegidas (separadas por espaço)
# Cada rota deve ter um bloco location no Nginx com:
# include /etc/nginx/porteiro_ips.conf;
# deny all;
ROTAS="/phpmyadmin/ /adminer/ /wp-admin/"
# Telegram (deixe vazio para desativar)
TELEGRAM_TOKEN=""
TELEGRAM_CHAT_ID=""1. Edite o porteiro.conf:
sudo nano /opt/porteiro/porteiro.conf
# Adicione a nova rota em ROTAS:
ROTAS="/phpmyadmin/ /adminer/"2. Adicione o bloco no Nginx:
location ^~ /adminer/ {
include /etc/nginx/porteiro_ips.conf;
deny all;
}3. Recarregue o Nginx:
sudo nginx -t && sudo systemctl reload nginxEdite DEFAULT_TIME no porteiro.conf ou passe direto no comando:
sudo porteiro-on 30 # 30 minutos (número puro)
sudo porteiro-on 30m # 30 minutos
sudo porteiro-on 2h # 2 horasO porteiro-on depende da variável $SSH_CLIENT, que só existe em sessões SSH remotas. Se estiver no próprio servidor:
sudo SSH_CLIENT='SEU_IP 0 0' porteiro-on- Suporte a Apache — Versão equivalente para
.htaccess - Suporte a IPv6 — Para servidores modernos
porteiro-off --self— Revoga apenas o IP da sessão atual, sem afetar outros adminsporteiro-offcom delay —porteiro-off 10mfecha em 10 minutosporteiro-listcom tempo restante — mostrar quando cada IP expira- Múltiplos ambientes — suporte a
/etc/nginx/porteiro/<nome>.confpara múltiplos domínios
O Porteiro é uma ferramenta de segurança legítima desenvolvida para administradores de servidores protegerem seus próprios ambientes.
- ✅ Use apenas em servidores que você administra
- ✅ Compatível com qualquer VPS ou servidor dedicado
- ❌ Não nos responsabilizamos pelo uso indevido
- ❌ Não substitui outras boas práticas de segurança (senhas fortes, atualizações, firewall)
Não. Cada porteiro-on agenda um porteiro-revoke <IP> com tag #porteiro-IP específica para aquele IP. Quando o timer expira, só aquele IP é revogado — os demais continuam ativos com seus próprios timers.
Não. Antes de qualquer operação, o script valida que o argumento é um IPv4 válido ([0-9]{1,3}.[0-9]{1,3}...). Argumentos maliciosos são rejeitados com mensagem de erro.
30 (minutos), 30m, 2h, 1hora, 90min. Número puro é tratado como minutos. Qualquer formato inválido é rejeitado com mensagem de erro antes de tocar no servidor.
Use sudo porteiro-list. Ele lê o porteiro_ips.conf e cruza com o log para mostrar cada IP ativo com data e hora de abertura.
Sim. sudo porteiro-revoke 189.x.x.x remove apenas a linha daquele IP no porteiro_ips.conf, valida com nginx -t e recarrega. Os demais IPs continuam ativos.
Não — ele age na camada do Nginx (HTTP), enquanto o firewall age na camada de rede. Os dois se complementam. Use ambos.
Sim! O porteiro-on sempre lê o IP atual da sessão SSH ativa. Cada vez que você rodar, ele adiciona o novo IP sem remover os anteriores.
Sim! Cada porteiro-on adiciona o IP ao arquivo sem sobrescrever os já existentes. O porteiro-off limpa tudo de uma vez.
O Auto-Off cuida disso. Após o tempo configurado, o acesso é bloqueado automaticamente em todas as rotas.
Não. O agendamento é criado com a tag #porteiro e o cancelamento filtra apenas por ela — outros jobs do at ficam intactos.
O Porteiro roda nginx -t antes de qualquer reload. Se detectar erro, aborta com mensagem clara e não toca no servidor.
Não. Deixe TELEGRAM_TOKEN e TELEGRAM_CHAT_ID em branco no porteiro.conf e as notificações são ignoradas silenciosamente.
O arquivo /etc/nginx/porteiro_ips.conf é compartilhado por todos os blocos location que você configurar no Nginx. Quando o porteiro-on injeta seu IP e recarrega o Nginx, todas as rotas com include /etc/nginx/porteiro_ips.conf são liberadas de uma vez. Um porteiro-off limpa o arquivo e bloqueia tudo simultaneamente.
Sim! Edite ROTAS no porteiro.conf, adicione o bloco correspondente no Nginx e recarregue. Veja a seção Configurações e Personalização acima.
A versão atual é exclusiva para Nginx. O suporte ao Apache está no Roadmap v3.0.
Sim, desde que rode Ubuntu/Debian com Nginx. Testado em VPS de entrada (1vCPU, 1GB RAM).
Este projeto está sob a licença MIT — veja o arquivo LICENSE para detalhes.
TL;DR: Pode usar, modificar, distribuir. Só dê os créditos.
Carlos Henrique Tourinho Santana
"Segurança boa é a que funciona enquanto você dorme."
- 📧 Email: henriquetourinho@riseup.net
- 📱 Instagram: @henrique.ntxa
- 💻 GitHub: henriquetourinho
- 🐧 Wiki Debian: henriquetourinho
Se o Porteiro salvou seu servidor de algum ataque:
- ⭐ Dê uma star no repositório
- 🐛 Reporte bugs ou sugira melhorias
- 🤝 Contribua com código
- 📢 Compartilhe com outros sysadmins
- À comunidade open source que inspira soluções simples para problemas sérios
- A todo sysadmin que já ficou com o coração na mão vendo o log do Nginx cheio de tentativas de invasão
- Aos bots de força bruta — sem vocês, o Porteiro não existiria
Feito com paranoia saudável e café em Salvador, Bahia 🌴☕
Código aberto. Porta fechada.
Este projeto não tem afiliação com Nginx Inc. ou qualquer distribuição Linux. © 2026 Carlos Henrique Tourinho Santana — MIT License