Icinga is an enterprise monitoring solution with a modular architecture including the monitoring core, Icinga 2 API, web interface (Icinga Web 2), and various agents. Each component requires specific security hardening to protect the monitoring infrastructure. This guide covers security measures for production Icinga deployments.
Icinga architecture consists of multiple security boundaries:
Key security concerns include API access control, web interface protection, agent authentication, and database security.
Configure firewall rules for Icinga components:
# Icinga 2 API
ufw allow from 10.0.0.0/8 to any port 5665 proto tcp
# Icinga Web 2 (Apache/Nginx)
ufw allow from 10.0.0.0/8 to any port 80 proto tcp
ufw allow from 10.0.0.0/8 to any port 443 proto tcp
# Icinga Agent (if running)
ufw allow from 10.0.1.0/24 to any port 5668 proto tcp
# Database (restrict to localhost or specific IPs)
ufw allow from 127.0.0.1 to any port 3306 proto tcp # MySQL
ufw allow from 127.0.0.1 to any port 5432 proto tcp # PostgreSQL
# Block external access
ufw deny from any to any port 5665 proto tcp
ufw deny from any to any port 5668 proto tcp
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: icinga-network-policy
spec:
podSelector:
matchLabels:
app: icinga2
ingress:
- from:
- namespaceSelector:
matchLabels:
name: monitoring
ports:
- protocol: TCP
port: 5665
- protocol: TCP
port: 5666
Configure Icinga 2 API binding:
# /etc/icinga2/features-available/api.conf
object ApiListener "api" {
bind_host = "10.0.1.100"
bind_port = "5665"
accept_commands_from = [ "master-node", "satellite-node" ]
accept_config_from = [ "master-node" ]
ticket_salt = "${TICKET_SALT}"
}
Configure Icinga Web 2 binding:
# Apache virtual host
<VirtualHost 10.0.1.100:443>
ServerName icinga.company.com
DocumentRoot /usr/share/icingaweb2/public
SSLEngine on
SSLCertificateFile /etc/ssl/certs/icinga.crt
SSLCertificateKeyFile /etc/ssl/private/icinga.key
</VirtualHost>
Configure authentication backends:
Database Authentication:
# /etc/icingaweb2/authentication.ini
[icingaweb2]
backend = "db"
resource = "icingaweb2"
user_table = "icingaweb2_user"
user_password_column = "password_hash"
LDAP Authentication:
# /etc/icingaweb2/authentication.ini
[ldap-icinga]
backend = "ldap"
resource = "ldap-icinga"
user_base_dn = "ou=users,dc=company,dc=com"
user_filter = "(objectClass=inetOrgPerson)"
user_name_attribute = "uid"
group_filter = "(objectClass=groupOfNames)"
group_name_attribute = "cn"
group_member_attribute = "member"
Active Directory Authentication:
# /etc/icingaweb2/authentication.ini
[ad-icinga]
backend = "ldap"
resource = "ad-icinga"
user_base_dn = "dc=company,dc=com"
user_filter = "(&(objectClass=user)(objectCategory=person))"
user_name_attribute = "sAMAccountName"
group_base_dn = "dc=company,dc=com"
group_filter = "(objectClass=group)"
group_name_attribute = "sAMAccountName"
group_member_attribute = "member"
Configure Icinga Web 2 roles:
# /etc/icingaweb2/roles.ini
[admins]
users = "admin,ops-lead"
permissions = "*"
[operators]
users = "operator1,operator2"
permissions = "monitoring/*"
restrictions = "monitoring/filter/objects=Host,Service"
[viewers]
users = "viewer1,viewer2"
permissions = "monitoring"
restrictions = "monitoring/permission/objects=Host,Service"
Configure API authentication:
# /etc/icinga2/zones.d/master/api-users.conf
object ApiUser "admin" {
password = "${API_ADMIN_PASSWORD}"
permissions = [ "*" ]
}
object ApiUser "readonly" {
password = "${API_READONLY_PASSWORD}"
permissions = [ "objects/query/*", "status/query" ]
}
object ApiUser "monitoring-service" {
password = "${API_SERVICE_PASSWORD}"
permissions = [
"objects/query/Host",
"objects/query/Service",
"objects/query/Downtime",
"events/SendNotification"
]
}
SAML Authentication:
# /etc/icingaweb2/modules/monitoring/config.ini
[auth]
backend = "external"
# /etc/icingaweb2/authentication.ini
[saml-icinga]
backend = "external"
environment = "REMOTE_USER"
Configure Apache for SAML:
LoadModule auth_mellon_module modules/mod_auth_mellon.so
<Location /icingaweb2>
AuthType Mellon
MellonEnable auth
Require valid-user
</Location>
Enable two-factor authentication:
TOTP with Icinga Web 2:
# Install TOTP module
icingacli module enable totp
# Configure in Icinga Web 2 UI
# Administration → Security → Authentication
Configure TLS for Icinga 2 API:
# /etc/icinga2/features-available/api.conf
object ApiListener "api" {
cert = "/etc/icinga2/pki/$(hostname).crt"
key = "/etc/icinga2/pki/$(hostname).key"
ca = "/etc/icinga2/pki/ca.crt"
cipher_list = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256"
tls_protocolmin = "TLSv1.2"
require_tls = true
require_client_certificate = true
}
Generate certificates:
# Generate CA
icinga2 pki new-ca
# Generate certificate for node
icinga2 pki new-cert --cn $(hostname)
# Sign certificate
icinga2 pki sign-cert --cn $(hostname)
# Trust CA on all nodes
icinga2 pki trust --ca /etc/icinga2/pki/ca.crt
Configure HTTPS for Icinga Web 2:
# /etc/apache2/sites-available/icingaweb2.conf
<VirtualHost *:443>
ServerName icinga.company.com
DocumentRoot /usr/share/icingaweb2/public
SSLEngine on
SSLCertificateFile /etc/ssl/certs/icinga.crt
SSLCertificateKeyFile /etc/ssl/private/icinga.key
SSLCertificateChainFile /etc/ssl/certs/ca-bundle.crt
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
SSLHonorCipherOrder on
# Security headers
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src * data:; font-src 'self' data:; connect-src 'self'"
<Directory /usr/share/icingaweb2/public>
Options None
AllowOverride None
Require all granted
</Directory>
</VirtualHost>
Secure agent communications:
# /etc/icinga2/zones.d/satellite/endpoints.conf
object Endpoint "agent-host" {
host = "10.0.2.50"
}
object Zone "agent-zone" {
endpoints = [ "agent-host" ]
parent = "master-zone"
}
Restrict Icinga 2 API access:
| Endpoint | Risk Level | Access Control |
|---|---|---|
/v1/status |
Low | Authenticated users |
/v1/objects/hosts |
Low | Authenticated users |
/v1/objects/services |
Low | Authenticated users |
/v1/objects/downtimes |
Medium | Operators |
/v1/actions/process-check-result |
High | Service accounts |
/v1/config |
Critical | Admin only |
/v1/actions/shutdown-process |
Critical | Admin only |
Implement API rate limiting:
# Nginx rate limiting
limit_req_zone $binary_remote_addr zone=icinga_api:10m rate=10r/s;
location /v1/ {
limit_req zone=icinga_api burst=20 nodelay;
proxy_pass https://localhost:5665;
}
Configure Icinga Web 2 security:
# /etc/icingaweb2/config.ini
[security]
protected_customvars = "*pw*,*pass*,*secret*,*token*,*api_key*"
[logging]
log = "file"
path = "/var/log/icingaweb2/icingaweb2.log"
level = "INFO"
Restrict command execution:
# /etc/icinga2/zones.d/master/commands.conf
object CheckCommand "check_ssh" {
command = [ PluginDir + "/check_ssh" ]
arguments = {
"-H" = "$address$"
}
}
object NotificationCommand "notify-service-by-email" {
command = [ "/usr/bin/printf" ]
arguments = {
"%b" = "$notification_author$"
}
}
Secure Icinga IDO database:
-- Create dedicated database user
CREATE USER 'icinga'@'localhost' IDENTIFIED BY 'strong_password';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE TEMPORARY TABLES ON icinga.* TO 'icinga'@'localhost';
FLUSH PRIVILEGES;
-- Enable SSL for database connection
ALTER USER 'icinga'@'localhost' REQUIRE SSL;
Configure database connection:
# /etc/icingaweb2/resources.ini
[icinga]
type = "db"
db = "mysql"
host = "localhost"
port = "3306"
dbname = "icinga"
username = "icinga"
password = "${DB_PASSWORD}"
charset = "utf8mb4"
use_ssl = "1"
Implement database encryption:
MySQL:
-- Enable TDE (Enterprise Edition)
INSTALL PLUGIN keyring_file SONAME 'keyring_file.so';
ALTER TABLE icinga_host ENCRYPTION='Y';
PostgreSQL:
-- Enable pgcrypto
CREATE EXTENSION IF NOT EXISTS pgcrypto;
Secure sensitive configuration:
# Use environment variables
export ICINGA_DB_PASSWORD="secure_password"
export ICINGA_API_PASSWORD="secure_api_password"
# Or use file-based secrets
cat > /etc/icinga2/.db_password << EOF
secure_password
EOF
chmod 600 /etc/icinga2/.db_password
For Kubernetes:
apiVersion: v1
kind: Secret
metadata:
name: icinga-secrets
type: Opaque
data:
db-password: <base64-encoded>
api-password: <base64-encoded>
Secure Icinga configuration files:
# Set restrictive permissions
chown -R root:icinga /etc/icinga2/
chmod -R 750 /etc/icinga2/
# Protect sensitive configuration files
chmod 600 /etc/icinga2/features-available/ido-mysql.conf
chmod 600 /etc/icinga2/zones.d/master/api-users.conf
Enable logging:
# /etc/icinga2/features-available/debuglog.conf
object DebugLogger "debuglog" {
path = "/var/log/icinga2/debug.log"
severity = "warning"
}
# /etc/icinga2/icinga2.conf
log {
severity = "warning"
}
Enable audit logging:
# /etc/icingaweb2/logging.ini
[log]
log = "file"
path = "/var/log/icingaweb2/icingaweb2.log"
level = "INFO"
[audit]
log = "file"
path = "/var/log/icingaweb2/audit.log"
Create Icinga alerts for security events:
# /etc/icinga2/zones.d/master/security.conf
apply Service "api-failed-auth" {
import "generic-service"
check_command = "check_log"
vars.log_path = "/var/log/icinga2/error.log"
vars.log_pattern = "Authentication failed"
vars.log_count = "5"
assign where host.name == "icinga-master"
}
apply Service "config-changes" {
import "generic-service"
check_command = "check_file_age"
vars.file = "/etc/icinga2/icinga2.conf"
vars.warning = 3600
vars.critical = 7200
assign where host.name == "icinga-master"
}
Forward logs to SIEM:
# /etc/rsyslog.d/icinga.conf
:programname, isequal, "icinga2" /var/log/icinga2/syslog.log
:programname, isequal, "icinga2" @siem.company.com:514
:programname, isequal, "icingaweb2" @siem.company.com:514