Activating DoS Protection for Validators
Validator nodes are critical infrastructure in the IOTA network and should be protected against Denial of Service (DoS) attacks. The TrafficController is a built-in mechanism that helps protect validator nodes by monitoring and limiting traffic from potential attackers.
This guide explains how to configure the TrafficController for your validator node.
A node whose configuration has no policy-config runs the shipped default policy.
That default runs with dry-run: true, thus it only counts and logs the clients it would block.
Set dry-run: false to make the node block them, for connection-blocklist-ttl-sec seconds each.
TrafficController Overview
The TrafficController monitors incoming requests to your validator node and can block IP addresses that exceed certain thresholds. It can operate in two modes:
- Non-proxy mode - For validators that receive traffic directly from clients.
- Proxy mode - For validators behind load balancers or proxies.
Configuration Parameters
The TrafficController is configured through two main configuration structures:
PolicyConfig
This structure defines the traffic control policies:
# The following parameters are defined in the PolicyConfig struct, which controls
# how the TrafficController identifies clients and applies rate limiting policies.
# The struct provides configuration for both direct client connections and proxied
# connections through load balancers.
policy-config:
# How to identify client IPs (socket-addr or x-forwarded-for)
client-id-source: socket-addr # or x-forwarded-for: <hops>, where hops is the number of proxy hops
# Serve these client IPs only, and refuse every other request. Rate limiting
# does not run in this mode. Omit the field to rate limit instead.
# allow-list: ["10.0.0.1", "10.0.0.2"]
# How long to block direct client IPs (in seconds)
connection-blocklist-ttl-sec: 60
# How long to block proxied client IPs (in seconds)
proxy-blocklist-ttl-sec: 60
# Policy for handling spam traffic
spam-policy-type:
freq-threshold:
client-threshold: 1000 # Sustained tallies per second per direct client; requests are sampled by spam-sample-rate, so the tolerated request rate is proportionally higher
proxied-client-threshold: 10 # Sustained tallies per second per proxied client
burst-secs: 30 # Burst allowance: a client can send client-threshold * burst-secs tallies back to back
# Policy for handling error traffic
error-policy-type: NoOp # NoOp, or a freq-threshold map like the spam policy above
# Sampling rate for spam detection (0.0 to 1.0)
spam-sample-rate: 0.2
# Whether to run in dry-run mode (log but don't block)
dry-run: false
The burst-secs key had the name window-size-secs before. The node continues to accept the old name, but the old name is deprecated. Change your configuration to burst-secs.
Non-Proxy Mode Configuration
If your validator node receives traffic directly from clients (no proxy or load balancer in front), use this configuration:
policy-config:
client-id-source: socket-addr
connection-blocklist-ttl-sec: 60
proxy-blocklist-ttl-sec: 0
spam-policy-type:
freq-threshold:
client-threshold: 100 # Sustained tallies per second
proxied-client-threshold: 10 # Currently ignored, as proxied client tallies are not reported; 0 would block every proxied client
burst-secs: 30
error-policy-type: NoOp
spam-sample-rate: 0.2
dry-run: false # Set to true initially to monitor without blocking
Proxy Mode Configuration
If your validator is behind a load balancer or proxy, use this configuration:
policy-config:
# Configure with the number of proxy hops between client and node
# For example, if you have one proxy: client -> proxy -> node, use 1
client-id-source:
x-forwarded-for: 1 # Adjust based on your infrastructure
connection-blocklist-ttl-sec: 60
proxy-blocklist-ttl-sec: 60
spam-policy-type:
freq-threshold:
client-threshold: 1000 # Higher threshold for proxies that aggregate traffic
proxied-client-threshold: 10 # Threshold for individual clients behind proxies
burst-secs: 30
error-policy-type: NoOp
spam-sample-rate: 0.2
dry-run: false
The node reads the entry your proxy appended, so a client cannot hide itself by putting unreadable bytes in the part of the header it sent. The node still ends up with no client when the header is absent, when the entry it selects is not an address, or when the header holds fewer entries than the configured hop count. With a rate-limiting policy such a request is served and charged to no client, which means it is not rate limited. In allowlist mode it is refused, because an allowlist admits the clients it names.
The traffic_control_unresolved_client_requests metric counts these requests. On a validator it does not sit at zero: other nodes call the validator directly, their requests carry no header, and each one is counted. Read it as a change from its usual level rather than as an absolute number. A rise above that level means that client traffic reaches the node on another path, or that the configured hop count does not match the number of proxies. On a node that serves clients only, and where every request arrives through the proxy, the metric does sit at zero.
An allow-list on a validator has to name every other node that calls it, because those calls arrive on the same listener. Two configurations are possible, and only one of them works. With client-id-source: socket-addr the node resolves the address of each caller, so listing the other nodes does admit them, at the cost of a list that has to change with the committee. With x-forwarded-for listing them does not help at all: their requests carry no header, the node resolves no address to match against the list, and it refuses them whatever the list holds. Prefer to leave an allow-list off a validator.
Determining the Correct Number of Hops
While x-forwarded-for is set to 0 the node determines no client at all. With a rate-limiting policy that means no client is limited, and in allowlist mode it means every request is refused. Use this setting only to read the hop count off the logs, and set the correct value straight after.
To determine the correct number of hops for the x-forwarded-for setting:
- Set
x-forwarded-for: 0temporarily. - Run the node and query any endpoint from a known IP address.
- Check the logs for lines containing
x-forwarded-for. - The correct value is 1 + the number of IP addresses that appear after your known IP in the header.
What if I'm Unsure of the Number of Proxies?
If you're unsure about the correct number of proxies to configure, you can use the config-traffic-control.sh script included in the IOTA codebase. This script analyzes your node logs to determine the correct configuration.
Follow these steps:
- Configure
x-forwarded-forin the policy config to0, which is a special case setting that allows the node to provide logging details of thex-forwarded-forheader contents:
policy-config:
client-id-source:
x-forwarded-for: 0
- Restart your node.
- Tail the node logs and pipe them into the script, providing as an additional argument an IP address of a known host that you can use to query any endpoint of your validator:
journalctl -fu iota-node | setups/validator/config-traffic-control.sh "YOUR_KNOWN_IP_ADDRESS"
- Using the host whose IP was provided to the script, query any endpoint of your validator. The script will collect request data and provide a recommended configuration:
journalctl -fu iota-node | setups/validator/config-traffic-control.sh "YOUR_KNOWN_IP_ADDRESS"
x-forwarded-for contents: YOUR_KNOWN_IP_ADDRESS
Configuration:
client-id-source:
x-forwarded-for: 1
- Set the configuration as specified by the script.
- Restart your node to apply the new configuration.
The script automatically calculates the correct number of hops by analyzing the position of your known IP address in the x-forwarded-for header.
Recommended Production Settings
For production validator nodes, we recommend:
policy-config:
# Use socket-addr for direct connections or x-forwarded-for for proxied setups
client-id-source: socket-addr # Adjust based on your setup
connection-blocklist-ttl-sec: 300 # Block for 5 minutes
proxy-blocklist-ttl-sec: 300 # Block for 5 minutes
spam-policy-type:
freq-threshold:
# Adjust these thresholds based on your expected legitimate traffic
client-threshold: 50 # Sustained tallies per second
proxied-client-threshold: 10 # Sustained tallies per second per client behind proxy
burst-secs: 30
# Consider enabling error policy for production
error-policy-type:
freq-threshold:
client-threshold: 20 # Error tallies per second
proxied-client-threshold: 5
burst-secs: 30
spam-sample-rate: 0.5 # Sample half of all requests
dry-run: false # Ensure this is false for actual protection
Monitoring and Tuning
After enabling the TrafficController, monitor your node's metrics to ensure legitimate traffic isn't being blocked. Key metrics to watch:
talliesconnection_ip_blocklist_lenproxy_ip_blocklist_lenrequests_blocked_at_protocoltraffic_control_num_dry_run_blocked_requeststraffic_control_unresolved_client_requestsconnection_blocklist_ttl_secproxy_blocklist_ttl_sec
Start with dry-run: true to observe what would be blocked without actually blocking traffic, then adjust thresholds as needed before setting dry-run: false.
Troubleshooting
If legitimate traffic is being blocked:
- Increase the
client-thresholdandproxied-client-thresholdvalues. - Decrease the
connection-blocklist-ttl-secandproxy-blocklist-ttl-secvalues. - Increase
burst-secsto permit larger bursts.
If you're still experiencing DoS attacks:
- Decrease the thresholds.
- Increase the blocklist TTL values.