Automatizar criação de contas no HestiaCP é etapa obrigatória quando o painel precisa operar acoplado a billing (WHMCS), ERP interno ou pipeline de onboarding. O ganho é direto: menos intervenção manual, menor tempo de entrega e trilha operacional auditável.
Neste artigo, documento exatamente como estruturei esse fluxo: validação local de comandos, chamada remota via API, troubleshooting de autenticação/conectividade e controles de segurança para produção.
1. Arquitetura da API do HestiaCP#
A API do HestiaCP atua como wrapper dos comandos CLI (v-*). Na prática, você envia um POST para o endpoint de administração e o painel executa o comando correspondente no backend.
Endpoint padrão:
https://SEU_SERVIDOR:8083/api/
Parâmetros essenciais:
user: usuário administrativo (ou usuário com permissão adequada)password: senha do usuário da APIhash: access hash/api keycmd: comando (v-add-user,v-add-domain, etc.)arg1 ... argN: argumentos na ordem esperada pelo comando CLI
Regra operacional: a API só é previsível quando você já validou o comando equivalente no shell.
2. Riscos de segurança críticos na automação de API#
A. O perigo da flag --insecure (ou -k)#
Desabilitar a verificação de certificados SSL/TLS no curl expõe a comunicação a ataques de Man-in-the-Middle (MITM). Um atacante posicionado na rede (ou um proxy intermediário) pode interceptar o tráfego e extrair as chaves e senhas administrativas em texto claro.
- Correção: Garanta que o hostname do HestiaCP tenha um certificado válido (Let's Encrypt). Se usar certificados auto-assinados em ambiente de testes, passe a autoridade certificadora local via flag
--cacert.
B. Senhas expostas nos argumentos da linha de comando#
Passar senhas diretamente nos parâmetros de comandos do shell faz com que a credencial fique visível no histórico (~/.bash_history), na listagem de processos ativos do sistema (ps aux | grep curl) e no diretório proc.
- Correção: Passe senhas através de arquivos de configuração restritos (
chmod 600), variáveis de ambiente temporárias ou leia-as dinamicamente a partir de um prompt protegido.
3. Gerenciamento seguro de chaves de acesso e variáveis#
O método mais recomendado de autenticação na API é o uso de Access Keys com privilégios limitados, evitando expor a senha principal do usuário admin.
Gerando a access key#
Você pode criar a chave pela interface web (Admin > Configure Server > API Access Key) ou diretamente pelo CLI do servidor:
# Adiciona uma chave de API com permissão para todos os comandos (dependendo da versão do HestiaCP, o comando CLI correspondente pode ser v-add-access-key ou v-generate-api-key)
v-add-access-key admin api-bot "HestiaCP API Token" all
Armazenando chaves com segurança#
Salve as chaves de acesso em um arquivo de configuração restrito ao usuário do script (normalmente root):
# Criar o diretório e arquivo de configuração
mkdir -p /root/.config
touch /root/.config/hestia-api.env
chmod 600 /root/.config/hestia-api.env
Edite o arquivo hestia-api.env e salve as variáveis:
HESTIA_HOST="https://seu-servidor.com:8083"
ACCESS_KEY="sua-access-key-gerada"
SECRET_KEY="sua-secret-key-gerada"
Exemplos avançados de chamadas cURL seguras de API segura#
Criando um usuário novo#
# Carrega as variáveis de ambiente com segurança
source /root/.config/hestia-api.env
# Lê a senha do usuário de forma segura sem expor no console
read -s -p "Digite a senha para o novo usuário: " NOVO_USER_PASS
echo ""
# Executa a chamada HTTPS com verificação de certificado ativa
curl -s -X POST "$HESTIA_HOST/api/v1/" \
-d "hash=$ACCESS_KEY:$SECRET_KEY" \
-d "returncode=json" \
-d "cmd=v-add-user" \
-d "arg1=cliente01" \
-d "arg2=$NOVO_USER_PASS" \
-d "[email protected]" \
-d "arg4=default" \
-d "arg5=Joao" \
-d "arg6=Silva"
Adicionando um domínio web e configurando let's encrypt#
Diferente dos comandos básicos, o comando v-add-letsencrypt-domain requer o usuário, o domínio principal e a lista de aliases explícita (ex: www.dominio ou mail.dominio):
# 1. Adicionar o domínio
curl -s -X POST "$HESTIA_HOST/api/v1/" \
-d "hash=$ACCESS_KEY:$SECRET_KEY" \
-d "returncode=json" \
-d "cmd=v-add-web-domain" \
-d "arg1=cliente01" \
-d "arg2=cliente.com.br"
# 2. Solicitar e aplicar SSL Let's Encrypt incluindo aliases
curl -s -X POST "$HESTIA_HOST/api/v1/" \
-d "hash=$ACCESS_KEY:$SECRET_KEY" \
-d "returncode=json" \
-d "cmd=v-add-letsencrypt-domain" \
-d "arg1=cliente01" \
-d "arg2=cliente.com.br" \
-d "arg3=www.cliente.com.br"
4. Pré-validação obrigatória no servidor (CLI primeiro)#
Antes de integrar PHP/WHMCS, valide diretamente no host se a sintaxe e permissões do comando estão corretas.
Exemplo de criação de usuário:
v-add-user novo_usuario 'SenhaForte123!' [email protected] default Nome Sobrenome
echo $?
Se o retorno (exit code) não for 0, não avance para API. Corrija no nível CLI primeiro.
Checklist mínimo no host:
which v-add-user
v-list-user admin json
v-list-packages json
Isso evita perder tempo depurando aplicação quando o problema está no próprio painel/pacote/permissão.
5. Testes isolados de API com cURL#
Com CLI validado, repliquei o mesmo fluxo via HTTP para eliminar variáveis do sistema chamador.
curl -k -X POST "https://seu-servidor.com:8083/api/" \
-d "user=admin" \
-d "password=SUA_SENHA" \
-d "hash=SEU_HASH" \
-d "cmd=v-add-user" \
-d "arg1=novo_usuario" \
-d "arg2=SenhaForte123" \
-d "[email protected]" \
-d "arg4=default" \
-d "arg5=Nome" \
-d "arg6=Sobrenome"
Durante debug, usei -k apenas para isolar TLS. Em produção, o correto é certificado válido + verificação habilitada.
Diagnóstico HTTP detalhado:
curl -vk -X POST "https://seu-servidor.com:8083/api/" -d "..."
Esse passo mostrou rapidamente se a falha era autenticação, payload malformado ou bloqueio de rede.
6. Implementação robusta de integração em PHP#
No integrador PHP, padronizei envio com http_build_query() para garantir encoding correto e evitar quebra de parâmetros com caracteres especiais.
<?php
$endpoint = 'https://seu-servidor.com:8083/api/';
$payload = [
'user' => 'admin',
'password' => 'SUA_SENHA',
'hash' => 'SEU_HASH',
'cmd' => 'v-add-user',
'arg1' => 'novo_usuario',
'arg2' => 'SenhaForte123',
'arg3' => '[email protected]',
'arg4' => 'default',
'arg5' => 'Nome',
'arg6' => 'Sobrenome',
];
$ch = curl_init($endpoint);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($payload),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'],
]);
$response = curl_exec($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($errno) {
throw new RuntimeException("cURL error [$errno]: $error");
}
if ($httpCode < 200 || $httpCode >= 300) {
throw new RuntimeException("HTTP error [$httpCode] response: " . ($response ?? ''));
}
if ($response === false || trim($response) === '') {
throw new RuntimeException('API response empty. Check auth/hash/firewall.');
}
echo $response;
Pontos que salvaram tempo de troubleshooting:
- log de
httpCode - log de tempo/timeout
- log de payload sem segredo (redaction de senha/hash)
- fallback de retry controlado para timeout de rede
7. Script de provisionamento completo em Bash com retries e rollback#
O script a seguir foi desenhado para produção. Ele verifica conectividade e dependências, possui uma rotina de retentativas se o painel estiver temporariamente ocupado (ex: executando rotinas de backup), valida erros no retorno JSON e executa rollback automático de criação de usuário caso a adição do domínio falhe.
#!/usr/bin/env bash
# hestia-provision.sh - Provisionamento automatizado e seguro via API
# ⚠️ Teste exaustivamente em homologação antes de rodar em produção.
set -euo pipefail
# ==================== CONFIGURAÇÃO ====================
CONFIG_FILE="/root/.config/hestia-api.env"
LOG_FILE="/var/log/hestia-provision.log"
# Garantir que o arquivo de configuração existe e está protegido
if [ ! -f "$CONFIG_FILE" ]; then
echo "❌ Arquivo de configuração ausente: $CONFIG_FILE" >&2
exit 1
fi
source "$CONFIG_FILE"
# ==================== FUNÇÕES ====================
log() {
echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] $1" | tee -a "$LOG_FILE"
}
error() {
log "❌ ERRO: $1"
exit 1
}
# Função principal com tratamento de conexões e validação de TLS
api_call() {
local cmd="$1"; shift
local i=1
local args=()
for arg in "$@"; do
args+=("-d" "arg${i}=${arg}")
((i++))
done
# Realiza a chamada segura. Se o certificado do HestiaCP for auto-assinado em testes,
# substitua por --cacert /caminho/ca.crt, mas NUNCA use --insecure em produção.
local response
response=$(curl -s --connect-timeout 10 --max-time 60 \
-X POST "$HESTIA_HOST/api/v1/" \
-d "hash=$ACCESS_KEY:$SECRET_KEY" \
-d "returncode=json" \
-d "cmd=$cmd" \
"${args[@]}") || error "Falha física de rede ao conectar à API HestiaCP"
# Validar se a resposta retornou JSON válido e verificar se há erros reportados
if ! echo "$response" | jq empty 2>/dev/null; then
log "⚠️ Resposta da API não é um JSON válido: $response"
return 1
fi
local api_error
api_error=$(echo "$response" | jq -r '.error // empty' 2>/dev/null)
if [ -n "$api_error" ] && [ "$api_error" != "null" ]; then
log "⚠️ Chamada à API [$cmd] retornou erro: $api_error"
return 1
fi
echo "$response"
}
# Wrapper de retentativas inteligentes com Backoff Exponencial
api_call_with_retry() {
local max_retries=3
local retry_count=0
local response
while [ $retry_count -lt $max_retries ]; do
if response=$(api_call "$@"); then
echo "$response"
return 0
fi
retry_count=$((retry_count + 1))
local wait_time=$((retry_count * 5))
log "⏳ Chamada falhou ou o painel está ocupado. Tentativa $retry_count/$max_retries. Recuando e aguardando ${wait_time}s..."
sleep $wait_time
done
error "Falha persistente após $max_retries tentativas no comando: $1"
}
# ==================== CONTROLE DE ERROS E CLEANUP (ROLLBACK) ====================
USUARIO_CRIADO=""
cleanup_on_error() {
local exit_code=$?
if [ $exit_code -ne 0 ]; then
log "⚠️ Falha crítica detectada durante o provisionamento!"
if [ -n "$USUARIO_CRIADO" ]; then
log "🔄 Iniciando ROLLBACK: Removendo usuário parcial '$USUARIO_CRIADO'..."
# Executa a remoção do usuário para evitar lixo órfão no sistema
api_call v-delete-user "$USUARIO_CRIADO" >/dev/null || true
log "✅ Rollback concluído. Conta parcial deletada."
fi
fi
}
trap cleanup_on_error EXIT
# ==================== PRÉ-REQUISITOS ====================
for dep in curl jq openssl; do
command -v "$dep" >/dev/null 2>&1 || error "Dependência obrigatória ausente no sistema: $dep"
done
# Validar conectividade básica com a porta do painel
if ! curl -s --connect-timeout 5 "$HESTIA_HOST/api/v1/" > /dev/null 2>&1; then
error "Endpoint da API do HestiaCP inacessível em: $HESTIA_HOST. Verifique o firewall."
fi
# ==================== RECEBIMENTO DE PARÂMETROS ====================
if [ $# -lt 3 ]; then
echo "Uso: $0 <usuario> <email> <dominio>"
exit 1
fi
USUARIO="$1"
EMAIL="$2"
DOMINIO="$3"
# Gerar credenciais e senhas randômicas fortes de forma programática
SENHA_USER=$(openssl rand -base64 16)
SENHA_DB=$(openssl rand -base64 16)
log "🚀 Iniciando provisionamento para o usuário: $USUARIO ($DOMINIO)"
# 1. Criação do Usuário
log "👤 Criando usuário '$USUARIO'..."
api_call_with_retry v-add-user "$USUARIO" "$SENHA_USER" "$EMAIL" "default"
USUARIO_CRIADO="$USUARIO" # Sinaliza que o usuário foi criado para controle do trap de rollback
# 2. Adição do Domínio Web
log "🌐 Adicionando domínio web '$DOMINIO'..."
api_call_with_retry v-add-web-domain "$USUARIO" "$DOMINIO"
# 3. Criação do Banco de Dados
log "🗄️ Configurando banco de dados MySQL para '$USUARIO'..."
api_call_with_retry v-add-database "$USUARIO" "db_${USUARIO}" "user_${USUARIO}" "$SENHA_DB" "mysql"
# 4. Emissão e aplicação do Certificado Let's Encrypt SSL
log "🔒 Solicitando certificado SSL Let's Encrypt para '$DOMINIO'..."
# Se falhar aqui (ex: DNS não propagado), registramos aviso mas não quebramos o script principal
api_call_with_retry v-add-letsencrypt-domain "$USUARIO" "$DOMINIO" "www.$DOMINIO" || \
log "⚠️ SSL falhou (DNS pode não estar apontando para este host). Configure manualmente mais tarde."
# Salvar as credenciais geradas em local seguro com permissão restrita
SECRETS_DIR="/root/.secrets"
mkdir -p "$SECRETS_DIR"
touch "$SECRETS_DIR/${USUARIO}.info"
chmod 600 "$SECRETS_DIR/${USUARIO}.info"
cat <<EOF > "$SECRETS_DIR/${USUARIO}.info"
[CLIENTE: $USUARIO]
Data: $(date)
Domínio: $DOMINIO
Senha do Usuário: $SENHA_USER
---
Banco de Dados: db_${USUARIO}
Usuário do Banco: user_${USUARIO}
Senha do Banco: $SENHA_DB
EOF
log "✅ Provisionamento do cliente '$USUARIO' concluído com sucesso!"
log "🔑 Credenciais de acesso gravadas com segurança em: $SECRETS_DIR/${USUARIO}.info"
8. Diagnóstico de falhas recorrentes em produção#
9. Medidas de hardening e auditoria em produção#
Whitelisting de IP para API#
Mesmo com chaves de acesso fortes, restrinja o tráfego da API HestiaCP apenas para os servidores de controle (como o WHMCS). Configure o firewall do sistema e libere IPs na configuração de controle do painel:
# Adiciona acesso à API apenas para um IP específico
v-add-api-access-admin 192.168.1.50
Rotação periódica de chaves#
Chaves de API não devem ser eternas. Estabeleça uma rotina cron de substituição trimestral das chaves:
# Deleta a chave antiga (dependendo da versão do painel, use v-delete-access-key ou v-revoke-api-key)
v-delete-access-key ID_DA_CHAVE_ANTIGA
# Cria uma nova credencial temporária (use v-add-access-key ou v-generate-api-key)
v-add-access-key admin api-bot-$(date +%Y%m) "HestiaCP API Token" all
10. Runbook de validação pós-implementação e conclusão técnica#
Após deploy da automação, executei sequência de aceite:
- criar usuário via API
- criar domínio vinculado ao usuário
- validar conta no painel
- validar estrutura em disco
- validar serviço web/dns/mail conforme pacote
Comandos usados no pós-check:
v-list-user novo_usuario json
v-list-web-domains novo_usuario json
v-list-dns-domains novo_usuario json
Com isso, a automação ficou observável e previsível, sem "provisionamento fantasma".
Resultado técnico e considerações finais#
Com esse modelo, passei de criação manual para fluxo API com tempo de entrega de segundos, mantendo controle de segurança e trilha de auditoria. O principal ganho não foi só velocidade, foi consistência operacional.
Resumo prático:
- valide CLI primeiro
- replique com cURL
- só depois integre no PHP/WHMCS
- trate segurança como requisito de projeto, não como pós-ajuste
Este artigo foi útil?
Deixe uma reação rápida para apoiar o conteúdo:
Este post está licenciado sob CC BY-NC.



Comentários
Participe da discussão abaixo.
0 comentários