Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

list request IP in log to be able to block spammers #268

Open
Yannik opened this issue Jul 16, 2021 · 11 comments · May be fixed by #271
Open

list request IP in log to be able to block spammers #268

Yannik opened this issue Jul 16, 2021 · 11 comments · May be fixed by #271

Comments

@Yannik
Copy link
Contributor

Yannik commented Jul 16, 2021

My acme-dns instance is flooded by requests like this:

DEBU[0009] Answering question for domain                 domain=pizzaseo.com. qtype=RRSIG rcode=NXDOMAIN
DEBU[0019] Answering question for domain                 domain=pizzaseo.com. qtype=RRSIG rcode=NXDOMAIN
DEBU[0020] Answering question for domain                 domain=pizzaseo.com. qtype=RRSIG rcode=NXDOMAIN
DEBU[0020] Answering question for domain                 domain=pizzaseo.com. qtype=RRSIG rcode=NXDOMAIN
DEBU[0022] Answering question for domain                 domain=pizzaseo.com. qtype=RRSIG rcode=NXDOMAIN

Would it be possible to log the request IP so it's possible to create a fail2ban rule to automatically block spammers like this?

@lan10rd
Copy link

lan10rd commented Jul 19, 2021

this is happening to me too! I am trying to put nginx reverse stream up front to at least have something in control but i am about to just run a different service bound to 53 and monitor incoming dns requests before sending them down to acme dns, this is so annoying

This was referenced Jul 19, 2021
@Yannik
Copy link
Contributor Author

Yannik commented Jul 19, 2021

@lanl0rd I have implemented logging of ips in my branch log-ips.
I have opened a PR for this at #271.

I have also merged this into my fork branch, which contains several PRs that have not been reviewed/merged by @joohoi. Builds of this fork are available on docker at yanniksembritzki/acme-dns:fork.
If @joohoi picks up this project again, I will gladly get rid of this fork. I have only created it out of necessity because @joohoi sadly doesn't really maintain acme-dns anymore.

@Yannik
Copy link
Contributor Author

Yannik commented Jul 26, 2021

For now I have blocked pizzaseo using iptables on the docker host:

iptables -I FORWARD 1 -p udp --dport 53 -m string --algo kmp --string "pizzaseo" -j DROP

I'm still looking to implement this as a fail2ban rule to catch all abusers.

@lan10rd
Copy link

lan10rd commented Jul 28, 2021

thanks @Yannik , i used that, i am still pursuing passing the dns traffic through my nginx container, then to acme-dns container. I would super curious if you could pull of using fail2ban, i hadn't heard of it before now, im supposing its one of the better auto ddos protection services that you can run?

@nogaff
Copy link

nogaff commented Jul 30, 2021

Maybe a bit off-topic for the feature request, but as far as blocking unwanted DNS queries with iptables is concerned, it would seem preferable to simply block all queries that don't match the domain your acme-dns instance is serving, rather than trying to block specific spam queries like "pizzaseo".

I found that this works pretty nicely for a single domain, where n.n.n.n is my Docker host's IP address:

iptables -A PREROUTING -t raw -d n.n.n.n -p udp --dport 53 -m u32 --u32 "28 & 0xF8 = 0" -m string --algo bm --icase --from 40 ! --hex-string "|08|mydomain|03|com" -j DROP

I had to use the PREROUTING chain and a destination IP address, because adding this rule to the FORWARD chain without a destination IP address prevents containers being able to resolve external domains, and by the time the packet hits the FORWARD chain, the destination IP address could be anything. Using the PREROUTING chain catches the packet as soon as it hits the Docker host.

The "u32" part ensures that the packet is actually a DNS query (source), the "icase" makes the string matching case insensitive, the "from" part just skips ahead to the earliest part of the packet where the string could actually be matched, and then finally we're dropping the packet if the string does NOT match "mydomain.com". We have to use "hex-string" because the packet doesn't actually contain any real dots. The hex-string notation uses "|nn|" in place of the dots, where nn is the length of the substring that follows the dot.

If you're serving multiple acme-dns domains then I think you'd have to start messing around with user-defined chains to "AND" your multiple "NOT" rules together. In pseudocode, that would be something like:

if not match "mydomain1.com" jump to MYCHAIN1
MYCHAIN1:
if not match "mydomain2.com" jump to MYCHAIN2
MYCHAIN2:
if not match "mydomain3.com" DROP

@Yannik
Copy link
Contributor Author

Yannik commented Jul 30, 2021

Hey @nogaff, good idea, and thanks for providing this detailed explanation of your iptables command!

Based on your idea I thought that (as a clean solution), dropping all requests that are not targeted at the acme-dns zone or against any of the predefined records could also be implemented within acme-dns, so that no modifications on the docker host are necessary.

@Yannik
Copy link
Contributor Author

Yannik commented Jul 30, 2021

@nogaff Why are you using the PREROUTING chain in the raw table instead of the mangle table?

@nogaff
Copy link

nogaff commented Jul 30, 2021

@nogaff Why are you using the PREROUTING chain in the raw table instead of the mangle table?

@Yannik No particular reason, but since the goal is to simply drop the packets we don't want acme-dns to respond to, I figured why waste CPU cycles sending them through conntrack and mangle?

I basically just cobbled that rule together from bits of info I found here and there, so by all means, use mangle if you prefer. 😄

@Yannik
Copy link
Contributor Author

Yannik commented Aug 2, 2021

@lanl0rd Thanks to @sebres, we now also have a working fail2ban filter! :-)

[Definition]
datepattern = ^time="%%Y-%%m-%%dT%%H:%%M:%%S(?:%%z)?"\s+

_acmedns_domain = acme-dns\.mydomain\.org

__prefix_journal = \s*(?:\S+ \S+\[\d+\]: (?:time=\S+)?)?

failregex = ^%(__prefix_journal)s\s*level=debug msg="Answering question for domain" domain=\S+ qtype=\S+ rcode=NXDOMAIN remoteaddr="<HOST>:\d+"$
ignoreregex = ^%(__prefix_journal)s\s*level=debug msg="Answering question for domain" domain=\S+\.%(_acmedns_domain)s\. qtype=\S+ rcode=NXDOMAIN remoteaddr="<HOST>:\d+"$

journalmatch = CONTAINER_NAME=acme-dns

@sebres
Copy link

sebres commented Aug 2, 2021

By the way, you don't need ignoreregex to ignore some domains... Just specify it as a negative lookahead directly in failregex, for example:

failregex = ^%(__prefix_journal)s\s*level=debug msg="Answering question for domain" domain=(?!\S+\.%(_acmedns_domain)s\. )\S+ qtype=\S+ rcode=NXDOMAIN remoteaddr="<HOST>:\d+"$

@lan10rd
Copy link

lan10rd commented Aug 2, 2021

incredible, @sebres and @Yannik yall rock, really helping out the folks who are newbs at dns/networking and literally just wanted wildcard certs lol, quite a rabbit hole you saved me from going down.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants