Best practice is 'defense in depth', not 'we'll just lock down the perimeter'.
I also like nginx's limit_zone module. You can put limit_zone in the proxy stanza, and only throttle dynamic requests without throttling access to fast static files.
We often use Rack::Attack to throttle particular HTTP paths differently. Say, the homepage isn't throttled, but the login action is. That layer 7 knowledge is Rack::Attack's main advantage.
As I say in the README, Rack::Attack is complementary to iptables and the limit_zone module.
| blocking ports where there are no services doesn't
| do much
True, but it can be a useful 'just in-case' against things listening on ports that you were unaware of. It's obviously bad for you not to know about services that are listening on your box, but you could view it as a safety net.So it's something that's good for Heroku apps?
So if your user authentication code was also a Rack middleware, and you inserted Rack::Attack after it in the middleware stack, you could rate limit based on user account as well as IP address. That would be harder to do at the firewall or web server level.
This isn't for preventing DOS attacks (for which you'd want to completely avoid hitting application code), it's just for preventing unauthorised or excessive usage.
It's a good way when you don't have privilege access to the server OR skills to do it manually on Nginx etc.
you also can configure your edge proxies to ignore X-Forwarded-For, or at least move it to another untrusted header if you want to preserve its contents.
there's an nginx module (has to be compiled in) that lets you whitelist hosts which can send X-Forwarded-For, and turns that into the actual remote address provided to your upstreams.
http://wiki.nginx.org/HttpRealipModule | http://nginx.org/en/docs/http/ngx_http_realip_module.html
It's not mentioned in the article, but this implementation uses the standard Rails Cache:
https://github.com/kickstarter/rack-attack/blob/master/lib/r...
There are particular hooks in there for Redis. So if you've got "n" servers, it seems the preferred approach is to use a central Redis store.