Secure
Web servers are constantly under attack. So much so some people way back before Y2K termed this “internet background radiation”.
There are two broad categories of attacks; ones that are just probing IPs, and ones that know a little about your web presence.
If an adversary just connects to your IP without knowing the domain, you can:
- Return an error
- Abort the request
The default configuration, and one that is consistent with the protocol, is to return an error. But the general consensus is to drop invalid requests. This prevents adversaries from profiling the system.
If they know your domains it more difficult. But they are usually asking for things that don’t exist, or trying to access a login page as fast as possible to guess passwords. In either case, you should apply a limit to how fast they can make requests.
So as a fist step, let’s drop or rate-limit every request.
Drop Unknown Domains
A default config will take connections to port 80, announce that it’s a Caddy web server and redirect you to https. Instead, let’s look at the host headers and SNI (Server Name Indicator) first. Then we can immediately abort any requests for things we don’t even have.
#
# Global Options Block
#
{
...
...
}
#
# Importable Sections
#
...
...
#
# Site Blocks
#
# Accept secure requests specific to our domain
https://*.some.org, https://some.org {
# Serve any known host
@some host some.site.org
handle @some {
encode
reverse_proxy * http://some:8080
}
@other host other.site.org
handle @other {
import auth
encode
reverse_proxy * http://other:8080
}
# Abort any unknown host
handle {
abort
}
}
# Accept any insecure request to our domain
http://*.some.org, http://some.org {
# Redirect only known host headers to https
@secure {
host some.site.org
host other.site.org
}
handle @secure {
redir https://{host}{uri} permanent
}
# Abort anything else.
handle {
abort
}
}
# Handle requests without the right domain or SNI
# so we can abort them early
:80, :443 {
# Log it so we have a record of the probe
import logging
abort
}
Monitor Valid Domains
Other attackers will connect to a valid domain and making requests for potential exploits. You’ll send them back 401 Unauthorized or 404 Not Found, but these are bots and they’ll just keep chugging along, ignoring the errors.
Rate Limiting
A basic protection is to rate-limit your responses. You’re already aborting requests for invalid sites, but if you’re hosting a web form or basic-auth protected site it can be useful. While a web app using a form may have it’s own rate limiting mechanism, there’s no inherent limit to the BA prompt. The only way to handle this is apply a rate limit to the specific site.
sudo caddy add-package github.com/mholt/caddy-ratelimit
...
...
https://*.some.org, https://some.org {
# A fairly generous over-all limit
rate_limit {
zone descriptive-name-1 {
key {http.request.remote_ip}
events 200
window 5s
}
}
@some host some.site.org
handle @some {
# A conservative one for this site
rate_limit {
zone descriptive-name-1 {
key {http.request.remote_ip}
events 20
window 5s
}
}
encode
reverse_proxy * http://other:8080
}
}
You’ll need to tune this as different sites may have radically different setups, some needing just one large request and another 50 small ones to fill a page load. It’s also mostly targeted at mitigating denial of service and credential stuffing. A distributed or patient bot makes it through just fine. Importantly, if you’re using a proxy service like Cloudflare, you’ll need to pull out the actual client IP.
Deploy a Protection System
For actual threat detection & response, consider adding Fail2Ban or CrowdSec
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.