Skip to content
BHIM KVH / Operations / v0.4.0

Security guides

Configure SSH response, HTTP DoS mitigation, trusted proxies, evidence, and explicit recovery without losing administrative access.

Verified against the local 0.4.0 source. Examples target Linux with systemd; inspect your installed version with --help.

Start with observations

Before enabling response, inspect normal traffic and verify your client-IP topology. Establish a tested console or other recovery route. The examples use reserved documentation addresses; replace them with the real management and test addresses for your environment.

sudo -u bhim bhim-protection health --strict
sudo -u bhim bhim-protection incidents --limit 50
sudo -u bhim bhim-protection export --format json --limit 200
sudo journalctl -u bhim-agent.service -n 100 --no-pager

SSH detection and explicit response

SSH uses either journald or configured files, with a threshold and sliding time window. The checked-in policy uses journal, 5 failures, and 120 seconds. Configure actual administrator IPs before enabling blocking. allow_ips accepts individual IP addresses, not CIDR networks.

[protection]
auto_block = false
auto_restore = false

[blocking]
# Merge into the existing section; retain split executor settings.
enabled = true
block_ssh = true
block_http = false
duration_seconds = 600
allow_ips = ["127.0.0.1", "::1", "192.0.2.25"]
ssh_ports = [22]
http_ports = [80, 443]

Block, inspect, and unblock

Run these as the bhim service account in the split profile so the helper authenticates the configured peer UID/GID. The command uses the configured duration; there is no --duration flag. A new block cannot overwrite an active or pending policy in the same scope.

sudo -u bhim bhim-protection block 192.0.2.10 --scope ssh --reason operator-test
sudo -u bhim bhim-protection blocks
sudo -u bhim bhim-protection unblock 192.0.2.10 --scope ssh

Country-based access blocking

All countries are allowed by default. Explicitly select countries to deny by their two-letter codes. BHIM downloads and validates IPv4 and IPv6 ranges from IPdeny over HTTPS, caches them locally, and applies an atomic dedicated nftables table. No online lookup is needed per request.

Rules cover direct host INPUT TCP and UDP ports 80 and 443, leaving SSH and other ports unchanged. Behind a CDN, external proxy, load balancer, or container FORWARD path, enforce country policy at the edge. Geolocation is approximate; VPNs and proxies can change the apparent country.

# Required only when using country blocking
sudo apt install -y nftables
sudo bhimkvh country status
# Example: replace the denied list; use your actual management IP if needed
sudo bhimkvh country set --block CN,RU --allow-ip 203.0.113.10
sudo bhimkvh country refresh
sudo bhimkvh country disable

Direct clients, proxies, and shared IPs

Direct-client HTTP host blocking requires http_rate.enabled=true, direct_peer_verified=true, blocking.block_http=true, and forwarded_clients=false. Only mark a peer verified after checking the actual traffic path. Forwarded clients require explicit trusted proxy CIDRs and cannot be host-blocked by this policy. Shared-IP networks use their own detection threshold and are excluded from automatic host bans.

Native Nginx rate limiting

Inline limiting is configured in Nginx separately from post-request BHIM log observation. The following example follows integrations/nginx/http-context.conf and requires ngx_http_realip_module for $realip_remote_addr. Put these directives inside the existing http context.

# BHIM offline HTTP rate enforcement via Nginx native limit_req module.
# Place inside the nginx `http {}` block (not in a server/location block).
# Uses $binary_remote_addr: configure REAL-IP trust to known ALB/proxy CIDRs
# separately, otherwise this may rate-limit the proxy, not each end user.
limit_req_zone $binary_remote_addr zone=bhim_per_ip:20m rate=100r/s;
limit_req_status 429;
limit_req_log_level warn;

# Recommended observation format: socket peer remains distinct from real-IP rewrites.
# Requires ngx_http_realip_module for $realip_remote_addr (verify with nginx -V).
# Point http_rate.log_paths at this file. msec gives millisecond sliding windows.
log_format bhim_observed escape=json '{"peer_ip":"$realip_remote_addr","msec":"$msec","xff":"$http_x_forwarded_for"}';
access_log /var/log/nginx/bhim-access.jsonl bhim_observed;

Apply the zone to an existing location

Add the limit to the location that already serves the application. Do not create a duplicate location / block. This example permits 100 requests/second per key with burst 100; these native settings are independent of http_rate.threshold.

location / {
    limit_req zone=bhim_per_ip burst=100 nodelay;
    # Keep your existing proxy_pass, try_files, or application directives.
}

# Run in the shell after editing, before applying:
# sudo nginx -t
# sudo systemctl reload nginx

Trusted proxy configuration

Configure Nginx real-IP trust only for your actual ingress networks. This example uses documentation CIDRs and must be edited. Keep the socket peer distinct from the rewritten client address in BHIM observation logs.

# http context. Replace documentation CIDRs with your actual ingress networks.
# Never trust 0.0.0.0/0 or arbitrary client-supplied forwarding headers.
set_real_ip_from 192.0.2.0/24;
set_real_ip_from 2001:db8:1234::/48;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# The BHIM JSON format keeps $realip_remote_addr as the socket peer even after
# $remote_addr has been rewritten for native Nginx rate enforcement.

# Corresponding BHIM TOML, after replacing the documentation networks:
# [http_rate]
# enabled = true
# forwarded_clients = true
# trusted_proxies = ["192.0.2.0/24", "2001:db8:1234::/48"]
# log_paths = ["/var/log/nginx/bhim-access.jsonl"]
# [blocking]
# block_http = false

DoS protection and AWS WAF boundaries

Local rate detection, native Nginx limits, and host INPUT blocking mitigate different traffic paths. They are not volumetric upstream DDoS protection or a complete payload-inspection WAF. The separate integrations/aws/waf_sync.py adapter can update a dedicated AWS WAF IP set from a reviewed plan. It needs AWS CLI credentials only for an explicit apply; the offline agent does not call it.

python3 integrations/aws/waf_sync.py reviewed-plan.json
# Read plan_sha256 from review output; no AWS request is made above.
python3 integrations/aws/waf_sync.py reviewed-plan.json --apply-sha256 REVIEWED_PLAN_SHA256

Integrity baselines and investigation

Baseline trusted critical files, then inspect changes. Do not use baseline --replace to silence an unexplained alert: it accepts current content as the new baseline. Event confidence describes observations, not proof of compromise.

sudo bhim-protection baseline
sudo -u bhim bhim-protection verify
sudo -u bhim bhim-protection incidents --limit 20
sudo -u bhim bhim-protection timeline INCIDENT_ID
sudo -u bhim bhim-protection evidence-verify

Snapshots, restore, and quarantine

These are explicit operator operations. Stop competing configuration writers before restore, review the file and current hash, and retain the returned snapshot ID. The snapshot manifest is JSON. Restore requires the current content hash you reviewed, or absent when the target must not exist.

sudo bhim-protection snapshot /etc/ssh/sshd_config
sudo bhim-protection snapshot-show SNAPSHOT_UUID
sudo sha256sum /etc/ssh/sshd_config
sudo bhim-protection restore SNAPSHOT_UUID --expected-current-sha256 REVIEWED_CURRENT_HASH

# Quarantine a reviewed non-critical file, on the same filesystem:
sudo sha256sum /var/www/html/suspect.php
sudo bhim-protection quarantine /var/www/html/suspect.php --expected-sha256 REVIEWED_FILE_HASH
sudo bhim-protection restore QUARANTINE_UUID --expected-current-sha256 absent

Optional kernel and scanners

Kernel telemetry requires the separate BCC collector, matching kernel headers, tracepoints, and suitable privileges. Validate on the target kernel before enabling. Malware scanning can use pinned offline hash signatures; vulnerability scanning requires a pinned Trivy binary and database.

sudo /usr/bin/python3 scripts/ebpf_acceptance.py --binary target/release/bhim-protection
# Only after target validation and setting kernel.enabled=true:
sudo systemctl enable --now bhim-kernel.service
sudo systemctl restart bhim-agent.service

Monitor coverage and evidence loss

Inspect health reasons, metrics, spool depth, and unavailable/drop/gap events. Bounded storage and sampling protect the host but can reduce coverage. A locally verified evidence chain has no independent trust anchor; a sufficiently privileged attacker can rewrite local evidence.

sudo -u bhim bhim-protection health --strict
sudo -u bhim bhim-protection metrics
sudo -u bhim bhim-protection export --format json --limit 500
sudo tail -n 50 /var/log/bhim-protection/security.log
Next guideAPI documentation
Need help with your environment? Contact BHIM ↗