Como Isolar o Acesso SSH e WP-CLI de Programadores no HestiaCP com Jailkit

Como Isolar o Acesso SSH e WP-CLI de Programadores no HestiaCP com Jailkit

Dar acesso SSH com a shell padrão (/bin/bash) a programadores freelancers ou agências externas num servidor web multi-cliente é uma falha grave de segurança. Mesmo que a conta tenha privilégios normais sem acesso sudo, um utilizador numa shell aberta consegue inspecionar o diretório /etc, ver a lista de domínios alojados na máquina, consultar processos ativos de outros clientes e sondar portas de serviços locais (127.0.0.1:3306, 127.0.0.1:6379).

Em ambientes geridos com painéis como o HestiaCP, onde múltiplos sites partilham o mesmo sistema operativo, essa visibilidade facilita a movimentação lateral: se um site for vulnerável ou um prestador for comprometido, toda a infraestrutura vizinha fica exposta.

A forma correta de conceder acesso a programadores que precisam de executar git pull, composer install ou comandos wp-cli é através de uma jaula chroot hermética configurada com o Jailkit. Com a shell jailbash, o utilizador é trancado dentro do seu próprio diretório /home/utilizador, tendo acesso apenas aos binários estritamente necessários e às respetivas bibliotecas, sem qualquer visibilidade sobre o restante sistema.

No Webxtek Studio, onde administramos servidores para Especialistas em DevOps e Arquitetura Cloud e setores de alta sensibilidade como Centros de Monitorização de Alarmes, aplicamos esta segregação rigorosa para complementar o isolamento perimetral do nosso Alerta CISA, NSA e FBI: Blindagem PHP, Node e Portas Expostas.

Abaixo mostro como funciona o isolamento chroot no kernel Linux e os passos exatos para configurar perfis personalizados do Jailkit no HestiaCP.

1. Do Corredor Isolado à Chamada de Sistema chroot(): Como Funciona o Jailkit no HestiaCP

Para isolares o acesso SSH de programadores num servidor HestiaCP sem lhes tirares ferramentas essenciais como Git, PHP-CLI, Composer e WP-CLI, tens de definir secções personalizadas no ficheiro /etc/jailkit/jk_init.ini, atribuir o shell jailbash através do comando v-change-user-shell e restringir contas de transferência de ficheiros ao subsistema internal-sftp do OpenSSH.

Para perceberes porque é que não basta mudar as permissões das pastas em /home com chmod 750, tens de compreender como funcionam as permissões POSIX no Linux. Para que o servidor web Nginx, o motor PHP-FPM e as ferramentas de sistema consigam operar num servidor normal, centenas de ficheiros em /etc, /usr e /var têm permissão de leitura global (0644). Se um programador entra por SSH com /bin/bash, ele está proibido de entrar em /home/outro_cliente, mas nada o impede de executar cat /etc/nginx/conf.d/domains/*.conf (descobrindo todos os domínios, IPs e caminhos de certificados do teu servidor) ou correr ss -tulpn para mapear serviços internos como Redis, MySQL ou memcached.

Como analisámos ao detalhe na investigação sobre o Pânico nos Fóruns de cPanel, Plesk e HestiaCP: Mitigação PHP2Shell, portas locais desprotegidas e permissões abertas no painel são a porta de entrada para escalada horizontal de privilégios entre utilizadores da mesma máquina.

A solução clássica de engenharia Unix para este problema é a chamada de sistema chroot() (Change Root), implementada de forma segura pela suite open-source Jailkit de Olivier Sessink e integrada nativamente nas configurações de sistema do HestiaCP:

Comparativo de Visibilidade no Sistema de Ficheiros (VFS do Kernel Linux):

[Cenario 1: Login SSH com /bin/bash Padrao (INSEGURO)]
Raiz vista pelo utilizador (/): Raiz real do servidor Linux
- Consegue ler /etc/passwd, /etc/hosts, /etc/nginx/conf.d/* (ve todos os dominios dos clientes!)
- Consegue executar binarios globais em /usr/bin (curl, wget, nc, python3, find, gcc)
- Consegue inspecionar sockets locais e processos se hidepid nao estiver ativo em /proc

[Cenario 2: Login SSH com HestiaCP jailbash (/usr/sbin/jk_chrootsh)]
Raiz vista pelo utilizador (/): O diretorio /home/utilizador passa a ser a nova raiz (/)!
- Quando o programador faz 'cd /' e corre 'ls', ve apenas as pastas dentro de /home/utilizador.
- /etc/nginx, /root, /var/log e as pastas /home dos outros clientes sao matematicamente inalcancaveis.
- Apenas existem dentro de /home/utilizador/usr/bin os binarios que copiaste explicitamente com jk_init.

Como é que isto funciona nos bastidores ao milissegundo em que o programador se liga por SSH?

  1. No ficheiro /etc/passwd do servidor principal, o utilizador não tem /bin/bash como shell; tem /usr/sbin/jk_chrootsh.
  2. O binário jk_chrootsh corre um conjunto estrito de verificações de segurança: confirma que a pasta /home/utilizador pertence ao root:root com permissões 0755 (para impedir que o próprio utilizador substitua ficheiros de sistema dentro da jaula para escalar privilégios).
  3. O jk_chrootsh invoca a syscall chroot("/home/utilizador") no kernel Linux, larga imediatamente todos os privilégios de superutilizador (setuid / setgid para o UID não privilegiado do cliente) e lança o /bin/bash que vive dentro da jaula (/home/utilizador/bin/bash).
  4. A partir desse instante, o kernel Linux trava qualquer tentativa de subir na árvore de diretórios (cd ../../../): quando o processo está no topo da sua jaula, o diretório pai (..) aponta para si próprio!

2. O Desafio das Bibliotecas Dinâmicas (.so) e Como Construir um Perfil jk_init.ini Perfeito

No meu dia a dia em produção, vejo muitos administradores ativarem o jailbash no HestiaCP com a configuração predefinida e receberem logo a seguir uma reclamação do programador: “Entrei por SSH, mas quando corro composer install ou wp plugin list dá erro de comando não encontrado ou erro de certificado SSL!”.

Porque é que isto acontece? Porque quando fechas um utilizador numa jaula chroot, ele perde acesso à pasta /usr/bin e /lib/x86_64-linux-gnu do servidor principal. Se queres que ele possa usar o PHP 8.4, o Composer, o WP-CLI e o Git dentro da jaula, tens de copiar não só o executável (/usr/bin/php8.4), mas também:

  • Todas as bibliotecas partilhadas em C (.so) de que o binário depende (que podes listar com ldd /usr/bin/php8.4).
  • Os módulos de extensões do PHP (/usr/lib/php/20240924/*.so) e ficheiros .ini do CLI (/etc/php/8.4/cli).
  • Os certificados de autoridades raiz TLS (/etc/ssl/certs) e as bibliotecas de resolução DNS (libnss_dns.so.2 e /etc/resolv.conf), sem os quais o Git e o Composer não conseguem validar o HTTPS do GitHub ou do Packagist.

Felizmente, não tens de copiar nada disto à mão. O utilitário jk_init do Jailkit resolve automaticamente todas as dependências de bibliotecas ldd quando defines uma secção bem estruturada no ficheiro /etc/jailkit/jk_init.ini.

Passo 1: Instalar o WP-CLI e Preparar o Perfil [webxtek_dev] no /etc/jailkit/jk_init.ini

Abre o teu terminal SSH como root no servidor HestiaCP (Debian 12 ou Ubuntu 24.04) e adiciona esta secção personalizada ao final do ficheiro /etc/jailkit/jk_init.ini:

# ============================================================================
# Adicionar ao final de /etc/jailkit/jk_init.ini
# Perfil Blindado para Programadores Full-Stack / WordPress / Laravel (Webxtek)
# ============================================================================

[webxtek_dev]
comment = Ambiente Chroot isolado com Git, PHP 8.3/8.4 CLI, Composer, WP-CLI e Node/SSH

# Secções base obrigatórias do Jailkit para resolução DNS, UID e consola básica
includesections = netbasics, uidbasics, basicshell, git, ssh

# Binários e bibliotecas dinâmicas a copiar automaticamente (com resolução ldd)
paths = /usr/bin/php8.3, /usr/bin/php8.4, /usr/bin/php, /etc/alternatives/php, /usr/lib/php, /etc/php/8.3/cli, /etc/php/8.4/cli, /etc/php/8.3/mods-available, /etc/php/8.4/mods-available, /usr/local/bin/composer, /usr/local/bin/wp, /usr/bin/curl, /usr/bin/unzip, /usr/bin/zip, /usr/bin/tar, /usr/bin/gzip, /usr/bin/nano, /usr/bin/less, /usr/bin/clear, /usr/bin/env, /usr/bin/which, /usr/bin/md5sum, /usr/bin/sha256sum, /etc/ssl/certs, /usr/share/ca-certificates, /usr/share/zoneinfo

# Dispositivos virtuais seguros obrigatórios dentro da jaula (/dev/null, /dev/urandom, /dev/zero)
devices = /dev/null, /dev/zero, /dev/urandom, /dev/random

Repara que não incluímos nesta lista ferramentas perigosas de reconhecimento de rede ou compilação (como nmap, nc, gcc, python3, find, ps, ss ou top). O programador tem todas as ferramentas de trabalho para gerir o seu código, mas zero ferramentas para sondar o servidor.

Passo 2: Ativar o jailbash no HestiaCP e Injetar o Perfil na Jaula do Utilizador

No HestiaCP, nunca edites /etc/passwd à mão porque os scripts de manutenção do painel (v-rebuild-user) iriam sobrescrever a tua alteração. Usa sempre os binários oficiais da linha de comandos do HestiaCP localizados em /usr/local/hestia/bin/:

# Definir o nome do utilizador HestiaCP que queremos isolar
HESTIA_USER="cliente_agencia"

# 1. Ativar a jaula SSH (jailbash) através do comando nativo do HestiaCP
/usr/local/hestia/bin/v-change-user-shell "$HESTIA_USER" jailbash

# 2. Injetar o nosso perfil personalizado [webxtek_dev] dentro da jaula /home/$HESTIA_USER
sudo jk_init -j "/home/${HESTIA_USER}" -k webxtek_dev

# 3. Garantir que os dispositivos /dev/null e /dev/urandom existem com permissões corretas dentro da jaula
sudo chmod 0666 "/home/${HESTIA_USER}/dev/null" "/home/${HESTIA_USER}/dev/zero" "/home/${HESTIA_USER}/dev/urandom" 2>/dev/null || true

# 4. Criar diretório temporário isolado dentro da jaula (para o Composer e WP-CLI não usarem o /tmp global)
sudo mkdir -p "/home/${HESTIA_USER}/tmp"
sudo chown "${HESTIA_USER}:${HESTIA_USER}" "/home/${HESTIA_USER}/tmp"
sudo chmod 0700 "/home/${HESTIA_USER}/tmp"

Repara na flag -k (--hardlink) que passei ao comando jk_init: sempre que o diretório /home e o /usr residem na mesma partição de disco, o jk_init -k cria hardlinks para os binários e bibliotecas em vez de duplicar os ficheiros fisicamente. Isto significa que injetar o PHP, o Git e o curl numa jaula gasta zero megabytes adicionais de espaço em disco e demora menos de meio segundo!


3. Como Ligar o PHP-CLI e o WP-CLI Dentro da Jaula ao MySQL/MariaDB e ao Redis

Aqui chegamos a um detalhe de engenharia de sistemas que separa os amadores dos especialistas em produção.

Quando o programador está fechado dentro da jaula /home/cliente_agencia e executa wp db export ou php artisan migrate, o que acontece se o ficheiro wp-config.php ou .env tiver DB_HOST=localhost? No Linux, quando especificas localhost no driver PDO ou mysqli, o cliente tenta abrir o ficheiro de socket UNIX /run/mysqld/mysqld.sock. Mas como o programador está dentro de uma jaula chroot em /home/cliente_agencia, a pasta /run/mysqld/ do sistema principal não existe dentro da jaula dele, e a ligação à base de dados falha com No such file or directory!

Tens duas soluções limpas para resolver isto em produção:

  • Solução Rápida por Loopback TCP (127.0.0.1): Definir DB_HOST=127.0.0.1 no .env ou wp-config.php para forçar a ligação local na porta 3306 de loopback.
  • Solução de Alta Performance por Bind Mount de Socket UNIX (Padrão Webxtek): Se queres manter a velocidade superior dos sockets UNIX e até permitir que o programador aceda apenas ao socket específico do seu pool Redis, expões apenas o diretório do socket dentro da jaula através de um mount --bind em modo controlado ou via fstab:
# Criar o ponto de montagem do socket MariaDB dentro da jaula do utilizador
sudo mkdir -p "/home/${HESTIA_USER}/run/mysqld"

# Montar de forma segura o diretório do socket do MariaDB dentro da jaula
sudo mount --bind /run/mysqld "/home/${HESTIA_USER}/run/mysqld"

# Tornar persistente no /etc/fstab para sobreviver a reinícios do servidor
echo "/run/mysqld /home/${HESTIA_USER}/run/mysqld none bind,ro,nofail 0 0" | sudo tee -a /etc/fstab

Com aquele bind,ro, o wp-cli e o artisan dentro da jaula encontram o socket /run/mysqld/mysqld.sock instantaneamente sem precisarem de sair do chroot.


4. Isolamento Estrito de SFTP (internal-sftp) e Limites de Recursos (cgroups / limits.conf)

Nem todos os utilizadores precisam de correr comandos em terminal. Se vais dar acesso a um designer externo (por exemplo, durante um projeto de Design de Logótipo e Identidade Core) ou a um gestor de conteúdos que apenas precisa de carregar imagens e ficheiros por FileZilla ou Cyberduck via SFTP, não uses sequer o jailbash: atribui o shell nologin no HestiaCP (/usr/local/hestia/bin/v-change-user-shell utilizador nologin).

O HestiaCP configura automaticamente no final do /etc/ssh/sshd_config um bloco Match para utilizadores restritos. No entanto, por defeito, esse bloco ainda permite encaminhamento de portas TCP (AllowTcpForwarding yes), o que permitiria a um utilizador apenas de SFTP usar o seu cliente SSH como um proxy SOCKS5 para aceder a portas internas do teu servidor!

Para além do confinamento local, o acesso por chaves SSH estáticas deve ser gradualmente substituído por identidades assinadas criptograficamente com tempo de vida limitado, tal como estruturámos no nosso guia de SSH Zero-Trust: Certificados Efémeros com step-ca.

Endurece o bloco SFTP e os limites de processos do utilizador com estas duas configurações:

A. Blindar o Subsistema internal-sftp no /etc/ssh/sshd_config

Garante que o bloco Match no teu /etc/ssh/sshd_config desativa qualquer túnel de rede ou alocação de terminal para contas apenas de SFTP:

# Bloco no final de /etc/ssh/sshd_config para utilizadores SFTP sem consola (grupo sftp-only)
Match Group sftp-only
    ChrootDirectory %h
    ForceCommand internal-sftp -u 0022
    AllowTcpForwarding no
    AllowAgentForwarding no
    PermitTunnel no
    X11Forwarding no
    PermitTTY no

B. Travar Bombas Fork e Exaustão de RAM Dentro da Jaula (/etc/security/limits.d/)

E se um programador dentro do jailbash correr acidentalmente um loop infinito em PHP-CLI ou um script malicioso tentar lançar uma fork bomb (:(){ :|:& };:) para esgotar a CPU do servidor? Uma jaula chroot isola o sistema de ficheiros, mas não limita o uso de RAM ou o número de processos sozinha!

Cria um ficheiro de limites PAM (/etc/security/limits.d/90-hestia-jailkit.conf) para garantir que nenhum utilizador SSH isolado consegue abrir mais de 64 processos simultâneos ou bloquear a máquina:

# Ficheiro: /etc/security/limits.d/90-hestia-jailkit.conf
# Limitar processos (nproc), ficheiros abertos (nofile) e memória virtual para utilizadores de hosting
# Aplicar a utilizadores individuais ou ao grupo de clientes SSH
cliente_agencia soft nproc 40
cliente_agencia hard nproc 64
cliente_agencia soft nofile 4096
cliente_agencia hard nofile 8192
cliente_agencia hard as 2097152

Além disso, ativa a opção hidepid=2 (ou hidepid=invisible) na montagem de /proc no teu /etc/fstab para que, mesmo que alguém tente consultar processos, o kernel Linux esconda todos os processos que não pertençam ao UID exato daquele utilizador:

# Remontar /proc imediatamente com hidepid=invisible (utilizadores normais só veem os seus próprios processos)
sudo mount -o remount,rw,nosuid,nodev,noexec,relatime,hidepid=invisible /proc

5. Matriz Comparativa: Acesso SSH no HestiaCP por Nível de Risco

Na tabela seguinte comparo os três modos de acesso disponíveis num servidor HestiaCP e o impacto real de segurança que verificamos nas nossas auditorias de infraestrutura:

Dimensão de Segurança / Operação Shell Padrão (/bin/bash Global) SFTP Restrito (nologin + internal-sftp) Padrão Webxtek (jailbash + Perfil jk_init + limits.d)
Visibilidade de Outros Clientes (/etc/nginx) Exposto (consegue listar todos os domínios do servidor) 0% Visível (confinado a /home/user sem terminal) 0% Visível (chroot hermético em /home/user)
Suporte a git, composer e wp-cli Sim (mas com acesso a todos os binários do sistema) Não (apenas transferência de ficheiros SFTP) Sim (apenas os binários declarados em [webxtek_dev])
Binários de Ataque (nc, nmap, gcc, python) Disponíveis se instalados no sistema operativo Inacessíveis Inexistentes dentro da jaula
Proteção contra Túneis SOCKS / Port Forwarding Permitido por defeito Bloqueado com AllowTcpForwarding no Bloqueado por grupo SSH e firewall local
Proteção contra Exaustão de Processos (Fork Bomb) Vulnerável sem limites PAM/cgroups Imune (PermitTTY no) Blindado (máximo de 64 processos via limits.d + hidepid)

Esta arquitetura permite que equipas de Especialistas em DevOps e Arquitetura Cloud colaborem com programadores externos em projetos de Websites de Alta Performance mantendo todas as garantias de um ambiente de Alojamento Gerido para Websites e Aplicações de nível bancário.


6. Manutenção de Jaulas (jk_update) e Checklist Prática de 5 Passos

Há uma regra de ouro quando operas jaulas chroot em Linux: sempre que atualizas os pacotes do sistema operativo principal com apt update && apt upgrade (por exemplo, quando atualizas o PHP 8.4 para corrigir um CVE crítico), tens de sincronizar os novos binários para dentro das jaulas existentes usando o utilitário jk_update.

Executa este comando ou adiciona-o a um hook de pós-atualização do apt:

# Sincronizar binários e bibliotecas atualizadas do sistema principal para dentro da jaula chroot
sudo jk_update -j "/home/${HESTIA_USER}"

Antes de entregares as credenciais SSH ao programador externo, valida estes cinco passos no teu terminal:

  1. Testa a Fuga da Jaula como Utilizador: Faz login por SSH com a conta do programador, executa pwd, tenta correr cd / && ls -la e confirma que só vês a estrutura interna de /home/utilizador, sem qualquer vestígio das pastas dos outros clientes.
  2. Valida o TLS do Composer e do Git: Dentro da sessão isolada do utilizador, executa composer diagnose e git ls-remote https://github.com/wp-cli/wp-cli.git para confirmares que a resolução DNS e os certificados /etc/ssl/certs estão operacionais dentro do chroot.
  3. Confirma as Permissões da Raiz da Jaula (0755 root:root): Executa ls -ld /home/utilizador no servidor principal. A pasta de topo tem obrigatoriamente de pertencer a root:root com permissões 0755 (o utilizador escreve apenas nas subpastas web/, tmp/ e home/utilizador/). Se mudares o dono do topo para o utilizador, o jk_chrootsh e o internal-sftp recusam o login imediatamente por segurança!
  4. Verifica a Sincronização Automática (jk_update): Cria um cron job semanal em /etc/cron.weekly/update-hestia-jails que execute jk_update -j em todas as pastas /home/* que possuam um subdiretório /usr/bin ativo.
  5. Revoga o Shell Quando o Projeto Termina: Assim que o programador externo concluir a intervenção ou o deploy inicial, executa /usr/local/hestia/bin/v-change-user-shell utilizador nologin para desativar o terminal interativo até à próxima janela de manutenção.

Dar autonomia técnica às equipas de desenvolvimento nunca deve significar entregar as chaves de todo o teu servidor. No Webxtek Studio, configuramos cada camada de permissões Linux para que possas escalar projetos com múltiplos fornecedores sem comprometer a integridade da tua infraestrutura. Através dos nossos serviços de Alojamento Gerido para Websites e Aplicações, engenharia de Websites de Alta Performance e criação de ativos visuais com Design de Logótipo e Identidade Core, garantimos que a tua presença digital cresce sobre uma base segura, rápida e profissionalmente isolada.

[+] [ INDEX_DIRECTORY ] 19 tópicos ▼
[ SYSTEM.FAQ ]

Perguntas Frequentes

Qual é o perigo real de atribuir uma consola /bin/bash padrão a um programador externo num servidor HestiaCP?

Uma shell /bin/bash normal concede visibilidade sobre a árvore global do sistema de ficheiros Linux. Mesmo sem privilégios de root, esse utilizador consegue ler ficheiros em /etc/nginx/conf.d/ para enumerar todos os outros clientes alojados na máquina, inspecionar processos em execução (/proc), verificar portas locais em escuta (127.0.0.1) e procurar ficheiros com permissões mal configuradas noutros diretórios.

Como é que o Jailkit (jk_chrootsh) isola um utilizador no HestiaCP sem partir ferramentas como o Git, Composer ou WP-CLI?

O binário SUID jk_chrootsh executa a chamada de sistema chroot() no momento do login SSH, transformando a pasta /home/utilizador na nova raiz (/) daquela sessão. Como um processo em chroot não consegue aceder a /usr/bin fora da sua pasta, o utilitário jk_init copia apenas os binários estritamente autorizados (como php8.4, git, composer e wp) e as respetivas bibliotecas dinâmicas (.so) para dentro da jaula do utilizador.

Porque é que o PHP-CLI ou o Git falham nas ligações HTTPS dentro de uma jaula chroot mal configurada?

Porque as ferramentas que comunicam por TLS e resolvem nomes de domínio dependem de ficheiros de configuração de rede (/etc/resolv.conf, /etc/hosts, /etc/nsswitch.conf), das bibliotecas de resolução de nomes do GNU C Library (libnss_dns.so, libnss_files.so) e do pacote de certificados raiz (/etc/ssl/certs/ca-certificates.crt). Se não incluíres as secções netbasics e os certificados SSL no perfil do /etc/jailkit/jk_init.ini, o Composer e o Git falham ao validar o certificado do GitHub ou Packagist.

Qual é a diferença entre usar o shell nologin com ChrootDirectory internal-sftp do OpenSSH e usar o jailbash do HestiaCP?

O subsistema internal-sftp com shell nologin bloqueia completamente a execução de comandos em terminal, permitindo apenas a transferência de ficheiros por SFTP (ideal para designers ou editores de conteúdo). Já o jailbash utiliza o Jailkit para fornecer um terminal interativo confinado em chroot, desenhado para programadores que precisam de executar comandos de linha como composer install, wp cache flush ou git pull sem verem o resto do servidor.

> INICIAR_PROJETO

Precisa de um website que transmita confiança, apareça na pesquisa e dê mais força à sua presença digital? Comece a conversa aqui.