Sensu is a flexible monitoring platform with event pipeline capabilities. As an event processor that can trigger downstream actions and integrations, Sensu must be treated as a privileged system. This guide covers security measures for production Sensu deployments.
Sensu architecture includes multiple security-sensitive components:
Key security concerns include agent authentication, API access control, secrets management for integrations, and securing the event pipeline.
Configure firewall rules for Sensu components:
# Sensu backend
ufw allow from 10.0.0.0/8 to any port 8080 proto tcp # API
ufw allow from 10.0.0.0/8 to any port 8081 proto tcp # WebSocket
ufw allow from 10.0.0.0/8 to any port 3000 proto tcp # Dashboard
# Sensu agent
ufw allow from 10.0.1.0/24 to any port 8081 proto tcp # Agent connections
# etcd cluster (if using embedded)
ufw allow from 10.0.0.0/8 to any port 2379 proto tcp # Client
ufw allow from 10.0.0.0/8 to any port 2380 proto tcp # Peer
# Block external access
ufw deny from any to any port 8080 proto tcp
ufw deny from any to any port 8081 proto tcp
ufw deny from any to any port 2379 proto tcp
ufw deny from any to any port 2380 proto tcp
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: sensu-network-policy
spec:
podSelector:
matchLabels:
app: sensu-backend
ingress:
- from:
- namespaceSelector:
matchLabels:
name: monitoring
ports:
- protocol: TCP
port: 8080
- protocol: TCP
port: 8081
- protocol: TCP
port: 3000
Configure Sensu backend binding:
# /etc/sensu/backend.yml
state-dir: /var/lib/sensu
log-level: warn
# API configuration
api-host: 10.0.1.100
api-port: 8080
# WebSocket configuration
agent-host: 10.0.1.100
agent-port: 8081
# Dashboard configuration
dashboard-host: 127.0.0.1
dashboard-port: 3000
# etcd configuration
etcd-listen-client-urls: "https://10.0.1.100:2379"
etcd-listen-peer-urls: "https://10.0.1.100:2380"
etcd-initial-advertise-peer-urls: "https://10.0.1.100:2380"
etcd-initial-cluster: "sensu-backend-0=https://10.0.1.100:2380"
Configure Sensu agent binding:
# /etc/sensu/agent.yml
name: "agent-hostname"
namespace: "default"
subscriptions:
- linux
- production
backend-url:
- "wss://sensu.company.com:8081"
Configure Sensu RBAC:
# Cluster role for admins
type: ClusterRole
api_version: core/v2
metadata:
name: cluster-admin
spec:
rules:
- verbs: ["*"]
resources: ["*"]
# Cluster role for viewers
type: ClusterRole
api_version: core/v2
metadata:
name: cluster-viewer
spec:
rules:
- verbs: ["get", "list"]
resources: ["*"]
# Role for operators
type: Role
api_version: core/v2
metadata:
name: operator
namespace: default
spec:
rules:
- verbs: ["get", "list", "create", "update", "delete"]
resources: ["checks", "entities", "events", "silenced"]
- verbs: ["get", "list"]
resources: ["assets", "filters", "handlers", "hooks", "mutators"]
Create users with specific roles:
# Create admin user
sensuctl user create admin \
--password '${ADMIN_PASSWORD}' \
--groups cluster-admins
# Create operator user
sensuctl user create operator \
--password '${OPERATOR_PASSWORD}' \
--groups operators
# Create viewer user
sensuctl user create viewer \
--password '${VIEWER_PASSWORD}' \
--groups viewers
Configure agent authentication:
# /etc/sensu/agent.yml
name: "agent-hostname"
namespace: "default"
subscriptions:
- linux
- production
backend-url:
- "wss://sensu.company.com:8081"
# Authentication
user: "agent-user"
password: "${AGENT_PASSWORD}"
# TLS configuration
trusted-ca-file: "/etc/sensu/ssl/ca.crt"
cert-file: "/etc/sensu/ssl/agent.crt"
key-file: "/etc/sensu/ssl/agent.key"
Secure API access with JWT tokens:
# Generate API key
sensuctl api-key create my-api-key \
--groups cluster-admins \
--ttl 86400
# Use API key
curl -H "Authorization: Key ${API_KEY}" \
https://sensu.company.com:8080/api/core/v2/namespaces/default/entities
Configure OIDC authentication:
# /etc/sensu/backend.yml
oidc-providers:
- name: company-sso
issuer: https://sso.company.com
client_id: sensu-backend
client_secret: ${OIDC_CLIENT_SECRET}
redirect_uri: https://sensu.company.com:8080/api/oidc/callback
scopes:
- openid
- profile
- email
groups_claim: groups
username_claim: email
Configure TLS for Sensu backend:
# /etc/sensu/backend.yml
# API TLS
api-https-enabled: true
api-cert-file: /etc/sensu/ssl/backend.crt
api-key-file: /etc/sensu/ssl/backend.key
api-trusted-ca-file: /etc/sensu/ssl/ca.crt
# Agent WebSocket TLS
agent-websockets-https-enabled: true
agent-websockets-cert-file: /etc/sensu/ssl/backend.crt
agent-websockets-key-file: /etc/sensu/ssl/backend.key
agent-websockets-trusted-ca-file: /etc/sensu/ssl/ca.crt
# etcd TLS
etcd-cert-file: /etc/sensu/ssl/backend.crt
etcd-key-file: /etc/sensu/ssl/backend.key
etcd-trusted-ca-file: /etc/sensu/ssl/ca.crt
etcd-client-cert-auth: true
Configure TLS for Sensu agents:
# /etc/sensu/agent.yml
backend-url:
- "wss://sensu.company.com:8081"
# TLS configuration
trusted-ca-file: "/etc/sensu/ssl/ca.crt"
cert-file: "/etc/sensu/ssl/agent.crt"
key-file: "/etc/sensu/ssl/agent.key"
insecure-skip-tls-verify: false
Generate agent certificates:
# Generate agent certificate
openssl req -new -nodes -newkey rsa:2048 \
-keyout agent.key \
-out agent.csr \
-subj "/CN=agent-hostname/O=Sensu Agents"
# Sign with CA
openssl x509 -req -in agent.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out agent.crt -days 365
Configure TLS for Sensu dashboard:
# /etc/sensu/dashboard.yml
host: 127.0.0.1
port: 3000
# If serving directly (not recommended - use reverse proxy)
# cert-file: /etc/sensu/ssl/dashboard.crt
# key-file: /etc/sensu/ssl/dashboard.key
Use reverse proxy for TLS termination:
# /etc/nginx/sites-available/sensu
server {
listen 443 ssl http2;
server_name sensu.company.com;
ssl_certificate /etc/nginx/certs/sensu.crt;
ssl_certificate_key /etc/nginx/certs/sensu.key;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://localhost:3000;
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 Sensu API access:
| Endpoint | Risk Level | Access Control |
|---|---|---|
GET /api/core/v2/entities |
Low | Viewer role |
GET /api/core/v2/events |
Low | Viewer role |
POST /api/core/v2/checks |
Medium | Operator role |
PUT /api/core/v2/checks |
Medium | Operator role |
DELETE /api/core/v2/entities |
High | Admin role |
POST /api/core/v2/silenced |
Medium | Operator role |
GET /api/core/v2/users |
High | Admin role |
POST /api/core/v2/users |
Critical | Admin role |
Implement API rate limiting:
# Nginx rate limiting for Sensu API
limit_req_zone $binary_remote_addr zone=sensu_api:10m rate=30r/s;
location /api/ {
limit_req zone=sensu_api burst=50 nodelay;
proxy_pass http://localhost:8080;
}
Configure dashboard security:
# /etc/sensu/dashboard.yml
# Session settings
session-timeout: 3600
# Authentication
authentication:
providers:
- type: basic
- type: oidc
name: company-sso
Secure handlers and integrations:
# Handler with secrets
type: Handler
api_version: core/v2
metadata:
name: slack
namespace: default
spec:
type: pipe
command: "sensu-slack-handler --token ${SLACK_TOKEN}"
env:
- name: SLACK_WEBHOOK
value: "${SLACK_WEBHOOK_URL}"
filters:
- is_incident
- not_silenced
timeout: 10
Restrict check execution:
# Check with restricted execution
type: CheckConfig
api_version: core/v2
metadata:
name: check-http
namespace: default
spec:
command: "check-http -u https://example.com"
interval: 60
subscriptions:
- web
timeout: 10
runtime_assets:
- sensu-plugins/sensu-plugins-http
secrets:
- name: HTTP_AUTH
secret: http-auth-secret
Secure etcd cluster:
# /etc/sensu/backend.yml
# etcd authentication
etcd-auth: true
etcd-user: "sensu"
etcd-password: "${ETCD_PASSWORD}"
# etcd TLS
etcd-cert-file: /etc/sensu/ssl/backend.crt
etcd-key-file: /etc/sensu/ssl/backend.key
etcd-trusted-ca-file: /etc/sensu/ssl/ca.crt
etcd-client-cert-auth: true
Enable etcd encryption:
# /etc/sensu/backend.yml
# etcd encryption
etcd-auto-tls: false
etcd-peer-auto-tls: false
Secure data directory:
# Set restrictive permissions
chown -R sensu:sensu /var/lib/sensu
chmod -R 700 /var/lib/sensu
# Use encrypted filesystem
# Mount /var/lib/sensu on encrypted volume
Use Sensu secrets provider:
# Secrets provider configuration
type: Secret
api_version: secrets/v1
metadata:
name: slack-token
spec:
provider: env
id: SLACK_TOKEN
# Reference in handler
type: Handler
api_version: core/v2
metadata:
name: slack
spec:
command: "sensu-slack-handler --token ${SLACK_TOKEN}"
secrets:
- name: SLACK_TOKEN
secret: slack-token
For external secrets:
# HashiCorp Vault provider
type: Secret
api_version: secrets/v1
metadata:
name: db-password
spec:
provider: vault
id: secret/data/sensu/db-password
Secure stored credentials:
# Use sensuctl to store credentials securely
sensuctl secret create slack-token \
--provider env \
--id SLACK_TOKEN
# Or use external secrets management
export SENSU_VAULT_ADDR="https://vault.company.com"
export SENSU_VAULT_TOKEN="${VAULT_TOKEN}"
Enable audit logging:
# /etc/sensu/backend.yml
log-level: debug
log-format: json
# Audit events
audit-logs-enabled: true
audit-logs-file: /var/log/sensu/audit.log
Configure API access logging:
# /etc/sensu/backend.yml
log-level: info
log-format: json
Monitor access via reverse proxy:
# /etc/nginx/sites-available/sensu
access_log /var/log/nginx/sensu_access.log combined;
error_log /var/log/nginx/sensu_error.log warn;
Create Sensu checks for security events:
# Alert on failed authentication
type: CheckConfig
api_version: core/v2
metadata:
name: check-auth-failures
namespace: default
spec:
command: "check-log --log-file /var/log/sensu/backend.log --pattern 'authentication failed' --warning 5 --critical 10"
interval: 60
subscriptions:
- sensu-backend
handlers:
- pagerduty
# Alert on API errors
type: CheckConfig
api_version: core/v2
metadata:
name: check-api-errors
namespace: default
spec:
command: "check-log --log-file /var/log/sensu/backend.log --pattern '500 Internal Server Error' --warning 10 --critical 50"
interval: 300
subscriptions:
- sensu-backend
Forward logs to SIEM:
# /etc/rsyslog.d/sensu.conf
:programname, isequal, "sensu-backend" /var/log/sensu/backend.log
:programname, isequal, "sensu-backend" @siem.company.com:514