Complete reference for all configuration options.
global:
log: { ... }
geoip_db_path: string
metrics_addr: string
profiles:
- id: string
listeners: [ ... ]
backends: [ ... ]
rules: { ... }
decoy: { ... }
shaping: { ... }| Field | Type | Default | Description |
|---|---|---|---|
level |
string | info |
Log level: debug, info, warn, error |
format |
string | json |
Output format: json, text |
output |
string | stdout |
Destination: stdout, stderr, or file path |
global:
log:
level: info
format: json
output: /var/log/shadowgate/access.logPath to MaxMind GeoIP2 database file (.mmdb). Required for geo_allow, geo_deny, asn_allow, asn_deny rules.
global:
geoip_db_path: /opt/geoip/GeoLite2-Country.mmdbAddress for the metrics API endpoint.
global:
metrics_addr: "127.0.0.1:9090"CIDRs of trusted proxies for X-Forwarded-For header handling. When configured, the X-Forwarded-For and X-Real-IP headers are only trusted when the request originates from an IP within these ranges. This prevents IP spoofing attacks.
If not configured, the legacy behavior is used (always trust X-Forwarded-For), which is suitable for deployments where ShadowGate is only accessible through trusted proxies.
global:
trusted_proxies:
- "10.0.0.0/8" # Internal load balancers
- "172.16.0.0/12" # Docker networks
- "192.168.1.100" # Specific proxy IPSecurity Note: In production, always configure trusted_proxies to prevent X-Forwarded-For spoofing from untrusted sources.
Maximum allowed request body size in bytes. Requests exceeding this limit will be rejected with a 413 error. Default is 10MB (10485760 bytes).
global:
max_request_body: 5242880 # 5MBThis setting helps protect against denial-of-service attacks using large request bodies.
Graceful shutdown timeout in seconds. During shutdown, ShadowGate will wait up to this duration for active connections to drain before forcefully closing them. Default is 30 seconds.
global:
shutdown_timeout: 60 # Wait up to 60 seconds for connections to drainRecommendations:
- Set this higher than your longest expected request duration
- For long-polling or WebSocket applications, consider a longer timeout
- The default of 30 seconds is suitable for most API workloads
Security configuration for the admin API. When enabled, all endpoints except /health require authentication.
| Field | Type | Default | Description |
|---|---|---|---|
token |
string | (none) | Bearer token required for API access |
allowed_ips |
[]string | (none) | CIDRs allowed to access the admin API |
global:
admin_api:
token: "your-secret-token-here"
allowed_ips:
- "127.0.0.1" # Localhost only
- "10.0.0.0/8" # Internal networkSecurity Notes:
- The
/healthendpoint is always accessible without authentication (for load balancer health checks) - If
tokenis set, all other endpoints requireAuthorization: Bearer <token>header - If
allowed_ipsis set, requests from IPs not in the list receive 403 Forbidden - Both can be combined: IP check happens first, then token validation
- In production, always configure at least one of these options
Each profile defines an independent traffic handling configuration.
Unique identifier for the profile. Used in logging and metrics.
| Field | Type | Required | Description |
|---|---|---|---|
addr |
string | Yes | Listen address (e.g., 0.0.0.0:443) |
protocol |
string | No | http or https (default: http) |
tls.cert_file |
string | No | Path to TLS certificate |
tls.key_file |
string | No | Path to TLS private key |
listeners:
- addr: "0.0.0.0:443"
protocol: https
tls:
cert_file: /etc/shadowgate/server.crt
key_file: /etc/shadowgate/server.key| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Backend identifier |
url |
string | Yes | Backend URL (e.g., http://10.0.1.10:8080) |
weight |
int | No | Load balancing weight (default: 1) |
health_check_path |
string | No | Health check endpoint path (default: /) |
timeout |
string | No | Request timeout duration (default: 30s) |
backends:
- name: primary
url: http://10.0.1.10:8080
weight: 10
health_check_path: /health
timeout: 30s
- name: secondary
url: http://10.0.1.11:8080
weight: 5
health_check_path: /api/status
timeout: 60s # longer timeout for slow APIEach backend can have its own health check endpoint and timeout. This is useful when backends have different health check paths, response times, or when you need to check application-specific endpoints.
Timeout Configuration:
- The
timeoutfield accepts Go duration strings (e.g.,30s,1m,500ms) - This controls how long ShadowGate waits for the backend to respond
- Use longer timeouts for slow backends or APIs with heavy processing
- Use shorter timeouts for fast backends to fail quickly and try alternatives
Rules determine whether traffic is forwarded to backends or served a decoy.
- If
denyrules match → serve decoy - If
allowrules exist and don't match → serve decoy - Otherwise → forward to backend
Rules can be combined using and, or, not, or used as a single rule:
rules:
allow:
and:
- type: ip_allow
cidrs: ["10.0.0.0/8"]
- type: method_allow
methods: ["GET", "POST"]rules:
deny:
or:
- type: ua_blacklist
patterns: ["(?i)nmap"]
- type: path_deny
paths: ["^/admin"]rules:
allow:
rule:
type: ip_allow
cidrs: ["0.0.0.0/0"]ip_allow / ip_deny
Filter by client IP address or CIDR range.
| Field | Type | Description |
|---|---|---|
cidrs |
[]string | List of CIDR ranges or single IPs |
- type: ip_allow
cidrs:
- "10.0.0.0/8"
- "192.168.1.100"
- "2001:db8::/32"geo_allow / geo_deny
Filter by country using MaxMind GeoIP database.
| Field | Type | Description |
|---|---|---|
countries |
[]string | ISO 3166-1 alpha-2 country codes |
- type: geo_allow
countries:
- "US"
- "CA"
- "GB"asn_allow / asn_deny
Filter by Autonomous System Number.
| Field | Type | Description |
|---|---|---|
asns |
[]uint | List of AS numbers |
- type: asn_deny
asns:
- 14061 # DigitalOcean
- 16276 # OVH
- 14618 # AWSua_whitelist / ua_blacklist
Filter by User-Agent header using regex patterns.
| Field | Type | Description |
|---|---|---|
patterns |
[]string | Regex patterns to match |
- type: ua_blacklist
patterns:
- "(?i)nmap"
- "(?i)nikto"
- "(?i)sqlmap"
- "(?i)masscan"
- "(?i)zgrab"method_allow / method_deny
Filter by HTTP method.
| Field | Type | Description |
|---|---|---|
methods |
[]string | HTTP methods (GET, POST, PUT, etc.) |
- type: method_allow
methods:
- "GET"
- "POST"path_allow / path_deny
Filter by URL path using regex patterns.
| Field | Type | Description |
|---|---|---|
paths |
[]string | Regex patterns for paths |
- type: path_deny
paths:
- "^/admin"
- "^/debug"
- "^/\\.git"
- "\\.php$"header_allow / header_deny
Filter by HTTP header presence and value.
| Field | Type | Description |
|---|---|---|
header_name |
string | Header name to check |
patterns |
[]string | Regex patterns for header value |
require_header |
bool | If true, header must be present |
- type: header_allow
header_name: "Authorization"
patterns:
- "^Bearer .+"
require_header: truetls_version
Filter by TLS version.
| Field | Type | Description |
|---|---|---|
tls_min_version |
string | Minimum TLS version (1.2, 1.3) |
tls_max_version |
string | Maximum TLS version |
- type: tls_version
tls_min_version: "1.2"sni_allow / sni_deny
Filter by TLS Server Name Indication.
| Field | Type | Description |
|---|---|---|
sni_patterns |
[]string | Regex patterns for SNI |
require_sni |
bool | If true, SNI must be present |
- type: sni_allow
sni_patterns:
- ".*\\.example\\.com$"
require_sni: truerate_limit
Limit requests per source IP.
| Field | Type | Description |
|---|---|---|
max_requests |
int | Maximum requests per window |
window |
string | Time window (e.g., 1m, 1h) |
- type: rate_limit
max_requests: 100
window: "1m"time_window
Allow/deny based on time of day.
| Field | Type | Description |
|---|---|---|
time_windows |
[]TimeWindow | List of allowed time windows |
- type: time_window
time_windows:
- days: ["mon", "tue", "wed", "thu", "fri"]
start: "09:00"
end: "17:00"Decoy responses are served when traffic is denied.
| Field | Type | Description |
|---|---|---|
mode |
string | static or redirect |
status_code |
int | HTTP status code (static mode) |
body |
string | Inline response body |
body_file |
string | Path to response body file |
redirect_to |
string | Redirect URL (redirect mode) |
decoy:
mode: static
status_code: 404
body: |
<!DOCTYPE html>
<html>
<head><title>404 Not Found</title></head>
<body><h1>Not Found</h1></body>
</html>decoy:
mode: redirect
redirect_to: "https://www.google.com"decoy:
mode: static
status_code: 200
body_file: /etc/shadowgate/decoy/index.htmlNote: Traffic shaping configuration is parsed but not yet implemented. Use tarpit decoy mode for delayed responses.
| Field | Type | Description |
|---|---|---|
delay_min |
duration | Minimum delay (planned) |
delay_max |
duration | Maximum delay (planned) |
global:
log:
level: info
format: json
output: /var/log/shadowgate/access.log
geoip_db_path: /opt/geoip/GeoLite2-Country.mmdb
metrics_addr: "127.0.0.1:9090"
profiles:
- id: c2-front
listeners:
- addr: "0.0.0.0:443"
protocol: https
tls:
cert_file: /etc/shadowgate/server.crt
key_file: /etc/shadowgate/server.key
backends:
- name: c2-primary
url: http://10.0.1.10:8080
weight: 10
- name: c2-secondary
url: http://10.0.1.11:8080
weight: 5
rules:
deny:
or:
- type: ua_blacklist
patterns:
- "(?i)nmap"
- "(?i)nikto"
- type: path_deny
paths:
- "^/admin"
- "^/\\.git"
- type: asn_deny
asns:
- 14061
- 16276
allow:
and:
- type: geo_allow
countries: ["US", "CA"]
- type: rate_limit
max_requests: 100
window: "1m"
decoy:
mode: static
status_code: 404
body_file: /etc/shadowgate/decoy/404.html