HestiaCP is a modern, actively maintained open-source web hosting control panel fork of VestaCP. Built for GNU/Linux distributions (primarily Debian and Ubuntu), it provides a clean interface for managing web domains, databases, email accounts, DNS records, and SSL certificates. While HestiaCP has a strong security track record and active development, comprehensive hardening is essential to protect server infrastructure and customer data.
HestiaCP supports TOTP-based 2FA for all user accounts. Enable it immediately after installation.
Enable 2FA via HestiaCP UI:
Force 2FA for all admin accounts via CLI:
# HestiaCP doesn't have a built-in CLI flag to force 2FA
# But you can enforce it via database or configuration
# Edit /usr/local/hestia/conf/hestia.conf
sudo nano /usr/local/hestia/conf/hestia.conf
# Add or modify:
FORCE_SSL='yes'
ENFORCE_TWOFA='yes' # If supported in your version
Enable 2FA for specific user via CLI:
# Generate 2FA key for user
/usr/local/hestia/bin/v-generate-2fa admin
# This will output the secret key and QR code URL
# Use an authenticator app to scan and generate codes
Secure the default admin account:
# Change admin password
/usr/local/hestia/bin/v-change-user-password admin 'YourNewStrongPassword123!'
# Or interactively
/usr/local/hestia/bin/v-change-user-password admin
Best practices:
Create new admin user:
# Create new admin user
/usr/local/hestia/bin/v-add-admin-account newadmin 'StrongPassword123!' 'admin@example.com'
# Assign admin role
/usr/local/hestia/bin/v-change-user-role newadmin ADMIN
Delete or suspend unused accounts:
# Suspend account (temporary)
/usr/local/hestia/bin/v-suspend-user oldadmin
# Delete account (permanent - use with caution)
/usr/local/hestia/bin/v-delete-user oldadmin
Restrict panel access by IP using HestiaCP’s built-in feature:
# Allow only specific IP addresses
/usr/local/hestia/bin/v-add-firewall-rule ACCEPT 443 tcp 10.0.0.0/24 "Admin access"
# Block all other access to panel port
/usr/local/hestia/bin/v-add-firewall-rule DROP 443 tcp 0.0.0.0/0 "Block others"
# List current rules
/usr/local/hestia/bin/v-list-firewall
Configure via HestiaCP UI:
Using iptables directly:
# Allow only from management network
sudo iptables -A INPUT -p tcp --dport 443 -s 10.0.0.0/24 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j DROP
sudo iptables -A INPUT -p tcp --dport 8083 -s 10.0.0.0/24 -j ACCEPT # HestiaCP default
sudo iptables -A INPUT -p tcp --dport 8083 -j DROP
sudo iptables-save > /etc/iptables/rules.v4
Using firewalld:
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" port port="8083" protocol="tcp" accept'
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" port port="8083" protocol="tcp" reject'
sudo firewall-cmd --reload
Using UFW:
sudo ufw allow from 10.0.0.0/24 to any port 8083
sudo ufw deny 8083
sudo ufw enable
Configure session settings in HestiaCP:
Edit /usr/local/hestia/conf/hestia.conf:
# Session timeout (seconds) - default 15 minutes
SESSION_TIMEOUT='900'
# Force SSL for all sessions
FORCE_SSL='yes'
# Session IP validation (prevent session hijacking)
SESSION_IP_VALIDATION='yes'
Secure session handling in PHP (/etc/php/*/apache2/php.ini or /etc/php/*/fpm/php.ini):
[Session]
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_only_cookies = 1
session.cookie_samesite = Strict
session.use_strict_mode = 1
session.gc_maxlifetime = 900
Restart services:
sudo systemctl restart apache2 # or nginx/php-fpm
sudo systemctl restart hestia
Configure password requirements:
# Edit Hestia configuration
sudo nano /usr/local/hestia/conf/hestia.conf
# Add password policy settings
PASSWORD_MIN_LENGTH='12'
PASSWORD_REQUIRE_SPECIAL='yes'
PASSWORD_REQUIRE_NUMBERS='yes'
PASSWORD_REQUIRE_UPPERCASE='yes'
Enforce password history (prevent reuse):
# Install pam_pwhistory
sudo apt install libpam-pwquality
# Configure /etc/pam.d/common-password
password required pam_pwhistory.so use_authtok remember=12 enforce_for_root
password required pam_unix.so use_authtok sha512
HestiaCP uses HTTPS by default with self-signed certificate. Replace with valid certificate:
Option 1: Let’s Encrypt via HestiaCP (Recommended)
# Generate certificate for panel hostname
/usr/local/hestia/bin/v-add-letsencrypt-domain admin panel.example.com '' 'yes'
# Or via UI: Server → Configure → Hostname → Enable Let's Encrypt
Option 2: Manual Let’s Encrypt
sudo apt install certbot
# Generate certificate
sudo certbot certonly --standalone -d panel.example.com
# Link to HestiaCP
sudo cp /etc/letsencrypt/live/panel.example.com/fullchain.pem /usr/local/hestia/ssl/certificate/hestia.crt
sudo cp /etc/letsencrypt/live/panel.example.com/privkey.pem /usr/local/hestia/ssl/certificate/hestia.key
sudo chmod 600 /usr/local/hestia/ssl/certificate/hestia.key
# Restart HestiaCP
sudo systemctl restart hestia
Option 3: Self-signed certificate (testing only)
sudo mkdir -p /usr/local/hestia/ssl/certificate
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /usr/local/hestia/ssl/certificate/hestia.key \
-out /usr/local/hestia/ssl/certificate/hestia.crt \
-subj "/C=US/ST=State/L=City/O=Organization/CN=panel.example.com"
sudo chmod 600 /usr/local/hestia/ssl/certificate/hestia.key
sudo systemctl restart hestia
HestiaCP includes built-in firewall management:
Default HestiaCP ports:
| Port | Service | Required |
|---|---|---|
| 8083 | HestiaCP Admin Panel | Yes |
| 80 | HTTP (web domains) | Yes |
| 443 | HTTPS (web domains) | Yes |
| 21 | FTP | If FTP enabled |
| 22 | SSH | Recommended |
| 25 | SMTP | If mail enabled |
| 53 | DNS | If DNS enabled |
| 110/995 | POP3 | If mail enabled |
| 143/993 | IMAP | If mail enabled |
| 3306 | MySQL | No (localhost only) |
Configure firewall via CLI:
# Enable firewall
/usr/local/hestia/bin/v-add-firewall
# Allow essential services
/usr/local/hestia/bin/v-add-firewall-rule ACCEPT 22 tcp 0.0.0.0/0 "SSH"
/usr/local/hestia/bin/v-add-firewall-rule ACCEPT 80 tcp 0.0.0.0/0 "HTTP"
/usr/local/hestia/bin/v-add-firewall-rule ACCEPT 443 tcp 0.0.0.0/0 "HTTPS"
/usr/local/hestia/bin/v-add-firewall-rule ACCEPT 8083 tcp 10.0.0.0/24 "HestiaCP Admin"
# Block panel from public access
/usr/local/hestia/bin/v-add-firewall-rule DROP 8083 tcp 0.0.0.0/0 "Block Public Panel"
# List rules
/usr/local/hestia/bin/v-list-firewall
# Delete rule (by ID)
/usr/local/hestia/bin/v-delete-firewall-rule 5
Configure CSF (if installed):
# Edit /etc/csf/csf.conf
TCP_IN = "20,21,22,25,53,80,110,143,443,465,587,993,995,8083"
TCP_OUT = "20,21,25,53,80,110,443,465,587"
# Enable port scan protection
PS_INTERVAL = "60"
# Restart CSF
csf -r
HestiaCP includes built-in fail2ban integration:
Enable fail2ban protection:
# Install fail2ban (if not already installed)
sudo apt install fail2ban
# Enable HestiaCP jails
/usr/local/hestia/bin/v-add-firewall-ban-rule
# Configure in /usr/local/hestia/install/deb/fail2ban/jail.local
HestiaCP fail2ban jails (/etc/fail2ban/jail.local):
[hestia-auth]
enabled = true
port = 8083,443
filter = hestia-auth
logpath = /var/log/hestia/auth.log
maxretry = 5
bantime = 3600
findtime = 300
[hestia-bruteforce]
enabled = true
port = 8083,443
filter = hestia-bruteforce
logpath = /var/log/hestia/auth.log
maxretry = 3
bantime = 7200
findtime = 300
[nginx-limit-req]
enabled = true
port = http,https
filter = nginx-limit-req
logpath = /var/log/nginx/*error.log
maxretry = 5
bantime = 1800
findtime = 300
Create HestiaCP filter (/etc/fail2ban/filter.d/hestia-auth.conf):
[Definition]
failregex = ^.*Failed login.*<HOST>.*$
^.*Authentication failure.*<HOST>.*$
^.*Invalid.*password.*<HOST>.*$
^.*Login failed.*<HOST>.*$
ignoreregex =
Restart fail2ban:
sudo systemctl restart fail2ban
View banned IPs:
fail2ban-client status hestia-auth
fail2ban-client set hestia-auth unbanip <IP>
Configure Nginx rate limiting:
# /etc/nginx/nginx.conf or /etc/nginx/conf.d/rate-limit.conf
http {
# Rate limiting zone
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
# Apply to login endpoints
server {
location /login/ {
limit_req zone=login burst=3 nodelay;
}
location / {
limit_req zone=one burst=10 nodelay;
}
}
}
Configure Apache rate limiting:
# /etc/apache2/mods-available/ratelimit.conf
<IfModule mod_ratelimit.c>
SetOutputFilter RATE_LIMIT
SetEnv rate-limit 400
SetEnv rate-limit-burst 200
</IfModule>
Edit /usr/local/hestia/conf/hestia.conf:
# Security settings
# Disable debug in production
DEBUG_MODE='no'
# Session security
SESSION_TIMEOUT='900'
SESSION_IP_VALIDATION='yes'
# SSL/TLS settings
FORCE_SSL='yes'
SSL_STRICT='yes'
# Password requirements
PASSWORD_MIN_LENGTH='12'
PASSWORD_REQUIRE_SPECIAL='yes'
PASSWORD_REQUIRE_NUMBERS='yes'
# Login attempt limits
LOGIN_ATTEMPTS='5'
LOGIN_BLOCK_TIME='300'
# File manager restrictions
FILE_MANAGER_ENABLED='yes'
FILE_MANAGER_CHROOT='yes'
# Disable features if not needed
BACKUPS_ENABLED='yes'
CRON_JOBS_ENABLED='no' # Disable if not needed
FILE_MANAGER_EDIT_ENABLED='yes'
Apply configuration:
sudo systemctl restart hestia
HestiaCP uses MariaDB/MySQL. Secure it:
# Run secure installation
sudo mysql_secure_installation
# Key settings:
# - Set root password
# - Remove anonymous users
# - Disallow root login remotely
# - Remove test database
Configure /etc/mysql/mariadb.conf.d/50-server.cnf:
[mysqld]
# Network security - bind to localhost only
bind-address = 127.0.0.1
skip-networking = 1
# Disable local infile
local-infile = 0
# Secure file handling
secure_file_priv = /var/lib/mysql-files
# Logging
log_error = /var/log/mysql/error.log
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
# HestiaCP-specific: use dedicated database user
Create restricted HestiaCP database user:
-- Login to MySQL
mysql -u root -p
-- HestiaCP uses specific database users per website
-- Ensure database user privileges are minimal
-- Example for a specific database:
CREATE USER 'web_user'@'localhost' IDENTIFIED BY 'strong-password-here';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON web_database.* TO 'web_user'@'localhost';
FLUSH PRIVILEGES;
-- Do NOT grant: FILE, PROCESS, SUPER, RELOAD, SHUTDOWN, GRANT OPTION
Edit /etc/php/*/apache2/php.ini or /etc/php/*/fpm/php.ini:
[Security]
expose_php = Off
allow_url_fopen = Off
allow_url_include = Off
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,passthru,leak,fopen,readfile
display_errors = Off
log_errors = On
error_reporting = E_ALL
html_errors = Off
[Resource Limits]
max_execution_time = 30
max_input_time = 60
memory_limit = 256M
post_max_size = 64M
upload_max_filesize = 64M
max_file_uploads = 20
[Session]
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_only_cookies = 1
session.cookie_samesite = Strict
session.use_strict_mode = 1
session.gc_maxlifetime = 900
[Open Basedir]
open_basedir = /home/%user%:/tmp:/var/tmp
Restart web server:
sudo systemctl restart apache2 # Apache
sudo systemctl restart php*-fpm # PHP-FPM
sudo systemctl restart nginx # Nginx
Secure HestiaCP installation:
# HestiaCP directory permissions
sudo chown -R root:root /usr/local/hestia
sudo chmod -R 755 /usr/local/hestia
sudo chmod 600 /usr/local/hestia/conf/hestia.conf
# User home directories
sudo chmod 711 /home
sudo chmod 711 /home/username
# Web directories
sudo chmod 755 /home/username/public_html
sudo chown username:username /home/username/public_html
# Configuration files
sudo chmod 600 /home/username/.my.cnf
sudo chmod 644 /home/username/public_html/.htaccess
# SSH directories
sudo chmod 700 /home/username/.ssh
sudo chmod 600 /home/username/.ssh/authorized_keys
Protect sensitive files:
# /etc/apache2/conf-available/hestia-hardening.conf
<FilesMatch "(config\.php|\.sql|\.log|\.bak|\.old)$">
Require all denied
</FilesMatch>
# Prevent directory listing
<Directory /home/*/public_html>
Options -Indexes
</Directory>
Disable PHP execution in upload directories:
# /etc/apache2/conf-available/disable-php-uploads.conf
<Directory "/home/*/public_html/wp-content/uploads">
php_flag engine off
RemoveHandler .php .php3 .php4 .php5 .phtml
<FilesMatch "\.(php|php3|php4|php5|phtml)$">
Deny from all
</FilesMatch>
</Directory>
<Directory "/home/*/public_html/images">
php_flag engine off
</Directory>
Secure SSH access:
# Edit /etc/ssh/sshd_config
sudo nano /etc/ssh/sshd_config
# Key settings:
Port 2222 # Change from default
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
AllowUsers admin-user
X11Forwarding no
AllowTcpForwarding no
Restart SSH:
sudo systemctl restart sshd
Update HestiaCP:
# Update via apt (recommended)
sudo apt update
sudo apt upgrade hestia
# Or use HestiaCP update script
/usr/local/hestia/install/upgrade.sh
# Check current version
/usr/local/hestia/bin/v-list-sys-info version
Enable automatic security updates:
# Install unattended-upgrades
sudo apt install unattended-upgrades
# Configure
sudo dpkg-reconfigure --priority=low unattended-upgrades
Subscribe to security announcements:
HestiaCP log locations:
| Log File | Purpose |
|---|---|
/var/log/hestia/ |
Main panel logs |
/var/log/hestia/auth.log |
Authentication logs |
/var/log/hestia/error.log |
Panel errors |
/var/log/hestia/nginx.log |
Nginx operations |
/var/log/hestia/apache2.log |
Apache operations |
/var/log/nginx/ |
Nginx access/error logs |
/var/log/apache2/ |
Apache access/error logs |
/var/log/mysql/error.log |
Database errors |
View recent activity:
# HestiaCP logs
tail -f /var/log/hestia/auth.log
# Filter for login events
grep -i "login\|auth" /var/log/hestia/auth.log
# Failed login attempts
grep -i "failed\|invalid" /var/log/hestia/auth.log | tail -50
# User actions
tail -f /var/log/hestia/user_actions.log
# Nginx logs
tail -f /var/log/nginx/error.log
Set up alerts for:
Configure log rotation:
# /etc/logrotate.d/hestia
/var/log/hestia/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 640 root adm
sharedscripts
postrotate
systemctl reload rsyslog > /dev/null 2>&1 || true
endscript
}
Rsyslog configuration:
# /etc/rsyslog.d/hestia.conf
:filename, contains, "hestia" /var/log/hestia/central.log
:filename, contains, "hestia" @syslog.example.com:514
# Restart rsyslog
sudo systemctl restart rsyslog
Filebeat for ELK stack:
# /etc/filebeat/modules.d/hestia.yml
- module: hestia
auth:
enabled: true
var.paths: ["/var/log/hestia/auth.log"]
error:
enabled: true
var.paths: ["/var/log/hestia/error.log"]
nginx:
enabled: true
var.paths: ["/var/log/nginx/access.log", "/var/log/nginx/error.log"]
Install AIDE to detect file changes:
# Install AIDE
sudo apt install aide
# Initialize database
sudo aideinit
# Configure HestiaCP paths in /etc/aide/aide.conf
/usr/local/hestia/ p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha512
/var/log/hestia/ p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha512
/home/ p+i+n+u+g+s+m+c+acl+selinux+xattrs+sha512
# Schedule daily checks
0 5 * * * /usr/bin/aide --check
HestiaCP provides powerful CLI commands. Secure access:
Restrict CLI access:
# Only root and admin users should have sudo access
sudo visudo
# Add specific HestiaCP commands allowed for specific users
admin-user ALL=(ALL) NOPASSWD: /usr/local/hestia/bin/v-list-users
admin-user ALL=(ALL) NOPASSWD: /usr/local/hestia/bin/v-list-web
Audit CLI usage:
# Log all HestiaCP CLI commands
echo 'ACTION="exec" EXE="/usr/local/hestia/bin/v-*" LOGFILE="/var/log/hestia-cli.log"' > /etc/rsyslog.d/hestia-cli.conf
# Or use auditd
sudo apt install auditd
sudo auditctl -w /usr/local/hestia/bin/ -p x -k hestia-cli
Secure HestiaCP backups:
# Configure backup location
/usr/local/hestia/bin/v-change-sys-config BACKUP '/backup/hestia'
# Set backup retention
/usr/local/hestia/bin/v-change-sys-config BACKUP_CAPACITY '7'
# Encrypt backups
/usr/local/hestia/bin/v-add-backup-host Local encrypted-backup /backup/encrypted user@example.com 'password'
Backup security best practices:
chmod 700 /backup| Control | Status | Notes |
|---|---|---|
| 2FA enabled for all admins | ☐ | Via user settings |
| HTTPS with valid certificate | ☐ | Let’s Encrypt recommended |
| Firewall configured | ☐ | HestiaCP firewall + CSF |
| fail2ban deployed | ☐ | HestiaCP jails configured |
| Database hardened | ☐ | mysql_secure_installation |
| PHP hardening applied | ☐ | Disable dangerous functions |
| File permissions secured | ☐ | Config 600, directories 755 |
| SSH hardened | ☐ | Key-only auth, non-standard port |
| Backups encrypted | ☐ | Off-server storage |
| Centralized logging | ☐ | Forward to SIEM |
| Regular updates scheduled | ☐ | HestiaCP + system packages |
| File integrity monitoring | ☐ | AIDE configured |
If you suspect a security breach:
Isolate the server
# Block external access via HestiaCP firewall
/usr/local/hestia/bin/v-add-firewall-rule DROP 8083 tcp 0.0.0.0/0 "Emergency Block"
/usr/local/hestia/bin/v-add-firewall-rule DROP 22 tcp 0.0.0.0/0 "Emergency Block SSH"
# Or via iptables
sudo iptables -P INPUT DROP
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
Preserve evidence
cp -r /var/log/hestia /root/hestia-logs-$(date +%Y%m%d-%H%M%S)
cp -r /var/log/nginx /root/nginx-logs-$(date +%Y%m%d-%H%M%S)
cp -r /var/log/apache2 /root/apache-logs-$(date +%Y%m%d-%H%M%S)
mysqldump -u root -p --all-databases > /root/all-db-$(date +%Y%m%d-%H%M%S).sql
Review recent activity
# Failed logins
grep -i "failed" /var/log/hestia/auth.log | tail -100
# User actions
tail -200 /var/log/hestia/user_actions.log
# New accounts
/usr/local/hestia/bin/v-list-users
# New domains
/usr/local/hestia/bin/v-list-web
Check for unauthorized changes
# Compare with AIDE database
sudo aide --check
# Look for recently modified files
find /usr/local/hestia -type f -mtime -7 -ls
find /home -type f -mtime -7 -ls
Change all credentials - Admin passwords, database passwords, FTP accounts, SSH keys
Scan for malware
sudo apt install clamav clamav-daemon
sudo freshclam
sudo clamscan -r /home --move=/home/quarantine
Patch vulnerabilities - Update HestiaCP, PHP, web server, and all packages
Restore from clean backup - If compromise is severe
Notify affected users - If customer data was exposed