Lighttpd
上QQ阅读APP看书,第一时间看更新

Traffic Shaping

We may either want to set up an anonymous download zone, where download speeds are throttled, and a high-speed zone for our paying customers, or we may just keep our server within a monthly budget. To achieve these goals, we can use the following settings:

server.kbytes-per-second = 1024   # for all connections
connection.kbytes-per-second = 32 # per connection

And disable the settings within a selector for the paying customers:

server.kbytes-per-second = 0 # disabled traffic shaping
connection.kbytes-per-second = 0

Note that since Lighttpd version 1.5.0, mod_evasive has gained the functionality to let a response header with the name of X-LIGHTTPD-KBytes-per-second be used as value for the connection speed setting. To enable this, we add the following to our configuration:

speed.just-copy-header = "enable"

However, this solution will turn very complex for most cases. If a user complains of slow downloads, is Lighttpd the problem, or was the X-LIGHTTPD-KBytes-per-second header wrong? Unless we want to calculate the speed for every user, we are better off with a direct setting.

Some users will use download managers that try to open a multitude of connections to bypass throughput limits. To keep those users at bay, we need a way of limit the number of connections per user. As of Lighttpd version 1.4.9, we can use mod_evasive to do this:

server.modules = (..., "mod_evasive", ...)
evasive.max-conns-per-ip = 2 # limit connections per IP

This will still give maximum throughput of 64 kilobytes per second per user, up to 16 simultaneous users. To keep our paying users at the maximum download speed, we can limit the reach of this configuration through selectors, as the tip from above also applies here.

Still, there is a problem with this approach because some users trying to download simultaneously may sit behind a proxy as their IP address is same. The mod_extforward module handles this by letting us add a list of trusted proxies, so that the X-forwarded for header value will be used as a client IP:

# the order matters, otherwise mod_accesslog and mod_evasive
# will get the proxy IP instead of the client IP
server.modules = (
...
"mod_accesslog",
"mod_evasive",
"mod_extforward",
...
)
# trust proxy at 1.2.3.4
extforward.forwarder = (
"1.2.3.4" => "trust",
)

We could also tell mod_extforward to trust all proxies with an extforward.forwarder entry of "all" => "trust". However, the documentation warns us that this is a bad idea, and here is why: a bad user could set up her own proxy that gives each request a new X-forwarded for IP address to fool our Lighttpd into thinking it is serving different users, annulling the effect of mod_evasive. Worse, if we use the client IP for session handling, they could hijack other legitimate users' sessions.

In a normal setting, it should suffice to start with a list of well-known proxies (search the Internet for "proxy server" or even start with an empty list) and add proxies to the list when users complain.