Beszel is a lightweight server monitoring hub that collects system metrics from multiple servers. As a monitoring system that aggregates data from multiple sources, Beszel requires proper security configuration to protect collected metrics and prevent unauthorized access. This guide covers security measures for production Beszel deployments.
Beszel architecture includes these security-sensitive components:
Key security concerns include agent authentication, API access control, web interface protection, and securing inter-component communication.
Configure firewall rules for Beszel:
# Beszel Hub web interface
ufw allow from 10.0.0.0/8 to any port 8080 proto tcp
ufw allow from 10.0.0.0/8 to any port 443 proto tcp
# Beszel Agent (if using TCP)
ufw allow from 10.0.1.0/24 to any port 4567 proto tcp
# Block external access
ufw deny from any to any port 8080 proto tcp
ufw deny from any to any port 4567 proto tcp
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: beszel-network-policy
spec:
podSelector:
matchLabels:
app: beszel-hub
ingress:
- from:
- namespaceSelector:
matchLabels:
name: monitoring
ports:
- protocol: TCP
port: 8080
Configure Beszel Hub binding:
# config.yaml
server:
host: 127.0.0.1 # Bind to localhost for reverse proxy
port: 8080
# Or for direct access on specific interface
# server:
# host: 10.0.1.100
# port: 8080
Configure reverse proxy:
# /etc/nginx/sites-available/beszel
server {
listen 80;
server_name beszel.company.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name beszel.company.com;
ssl_certificate /etc/nginx/certs/beszel.crt;
ssl_certificate_key /etc/nginx/certs/beszel.key;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Configure Beszel authentication:
# config.yaml
auth:
enabled: true
# Session settings
session_timeout: 3600
# Password policy
min_password_length: 12
require_special_char: true
require_number: true
Create users:
# Via Beszel CLI or web interface
beszel user create --username admin --password '${PASSWORD}' --role admin
beszel user create --username operator --password '${PASSWORD}' --role operator
beszel user create --username viewer --password '${PASSWORD}' --role viewer
Configure user roles:
Role Permissions:
- admin: Full access including user management
- operator: Can manage systems and view all data
- viewer: Read-only access to dashboards
Configure in Beszel UI:
Secure agent-to-hub communication:
# Agent configuration
hub:
url: https://beszel.company.com
token: ${AGENT_TOKEN}
# Generate token on hub
beszel agent token create --name "web-server-01"
Agent registration:
# On monitored server
beszel-agent install --hub-url https://beszel.company.com --token ${AGENT_TOKEN}
Secure API access:
# Generate API token
beszel api token create --name "Monitoring Integration" --scope read
# Use token for API access
curl -H "Authorization: Bearer ${API_TOKEN}" \
https://beszel.company.com/api/v1/systems
Configure external authentication (if supported):
# config.yaml
auth:
providers:
- type: oidc
issuer: https://sso.company.com
client_id: beszel
client_secret: ${OIDC_CLIENT_SECRET}
redirect_uri: https://beszel.company.com/auth/callback
Configure HTTPS via reverse proxy:
# /etc/nginx/sites-available/beszel
server {
listen 443 ssl http2;
server_name beszel.company.com;
ssl_certificate /etc/nginx/certs/beszel.crt;
ssl_certificate_key /etc/nginx/certs/beszel.key;
ssl_trusted_certificate /etc/nginx/certs/ca-bundle.crt;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Secure agent communication:
# Agent configuration
hub:
url: https://beszel.company.com
token: ${AGENT_TOKEN}
tls:
verify: true
ca_cert: /etc/beszel-agent/ca.crt
Generate certificates:
# Create CA
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \
-subj "/CN=Beszel CA/O=Company"
# Create hub certificate
openssl req -new -nodes -newkey rsa:2048 \
-keyout hub.key -out hub.csr \
-subj "/CN=beszel.company.com"
openssl x509 -req -days 365 -in hub.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out hub.crt
Configure TLS for database connections:
# config.yaml (if using PostgreSQL)
database:
type: postgres
host: localhost
port: 5432
name: beszel
user: beszel
password: ${DB_PASSWORD}
ssl_mode: require
ssl_root_cert: /etc/postgresql/ca.crt
Secure Beszel API endpoints:
| Endpoint | Risk Level | Access Control |
|---|---|---|
GET /api/v1/systems |
Low | Read scope |
GET /api/v1/metrics |
Low | Read scope |
POST /api/v1/systems |
Medium | Write scope |
PUT /api/v1/systems/{id} |
Medium | Write scope |
DELETE /api/v1/systems/{id} |
High | Admin scope |
POST /api/v1/users |
High | Admin scope |
GET /api/v1/users |
Medium | Admin scope |
Implement API rate limiting:
# Nginx rate limiting for Beszel API
limit_req_zone $binary_remote_addr zone=beszel_api:10m rate=30r/s;
location /api/ {
limit_req zone=beszel_api burst=50 nodelay;
proxy_pass http://127.0.0.1:8080;
}
Configure web UI security:
# config.yaml
server:
# Session settings
session_timeout: 3600
session_secure: true
# CORS settings
cors_origins:
- https://beszel.company.com
Restrict agent capabilities:
# Agent configuration
permissions:
metrics:
- cpu
- memory
- disk
- network
# Disable sensitive metrics
# - processes
# - users
Control which systems can be monitored:
# Hub configuration
systems:
allowed_networks:
- 10.0.0.0/8
- 192.168.0.0/16
# Require agent token for registration
require_token: true
Secure Beszel database:
SQLite:
# Set restrictive permissions
chown beszel:beszel /var/lib/beszel/beszel.db
chmod 600 /var/lib/beszel/beszel.db
# Use encrypted filesystem
# Mount /var/lib/beszel on encrypted volume
PostgreSQL:
-- Create dedicated database user
CREATE USER beszel WITH PASSWORD '${DB_PASSWORD}';
CREATE DATABASE beszel OWNER beszel;
GRANT ALL PRIVILEGES ON DATABASE beszel TO beszel;
-- Enable SSL requirement
ALTER USER beszel WITH PASSWORD '${DB_PASSWORD}';
Enable database encryption:
# For SQLite, use encrypted filesystem
# For PostgreSQL, enable TDE or use pgcrypto
# Encrypt backup files
beszel backup create | gpg -e --recipient security@company.com > backup.sql.gpg
Secure sensitive configuration:
# config.yaml
# Use environment variables
database:
password: ${DB_PASSWORD}
auth:
jwt_secret: ${JWT_SECRET}
# Or use external secrets file
# include: /etc/beszel/secrets.yaml
Protect secrets file:
# Set restrictive permissions
chown root:beszel /etc/beszel/secrets.yaml
chmod 640 /etc/beszel/secrets.yaml
Protect agent tokens:
# Store tokens securely
chown root:root /etc/beszel-agent/token
chmod 600 /etc/beszel-agent/token
# Rotate tokens regularly
beszel agent token rotate --id <agent-id>
Enable logging:
# config.yaml
logging:
level: info
file: /var/log/beszel/beszel.log
max_size: 10MB
max_backups: 5
# Audit logging
audit:
enabled: true
file: /var/log/beszel/audit.log
Configure reverse proxy access logging:
# /etc/nginx/sites-available/beszel
access_log /var/log/nginx/beszel_access.log combined;
error_log /var/log/nginx/beszel_error.log warn;
Monitor Beszel for security events:
#!/bin/bash
# /usr/local/bin/check-beszel-security.sh
# Check for failed authentication
FAILED_AUTH=$(grep -c "authentication failed" /var/log/beszel/beszel.log 2>/dev/null || echo 0)
if [ "$FAILED_AUTH" -gt 10 ]; then
echo "CRITICAL: Multiple authentication failures"
exit 2
fi
# Check for unauthorized access attempts
UNAUTH=$(grep -c "403 Forbidden" /var/log/beszel/beszel.log 2>/dev/null || echo 0)
if [ "$UNAUTH" -gt 20 ]; then
echo "WARNING: Multiple unauthorized access attempts"
exit 1
fi
# Check for agent disconnections
DISCONNECTS=$(grep -c "agent disconnected" /var/log/beszel/beszel.log 2>/dev/null || echo 0)
if [ "$DISCONNECTS" -gt 5 ]; then
echo "WARNING: Multiple agent disconnections"
exit 1
fi
Forward logs to SIEM:
# /etc/rsyslog.d/beszel.conf
:programname, isequal, "beszel" /var/log/beszel/syslog.log
:programname, isequal, "beszel" @siem.company.com:514