Open Anuskuss opened 8 months ago
This isn't possible with the current implementation unfortunately. It's mostly a limitation of the configuration language chosen (TOML). This requires a rewrite of significant parts to support a more flexible way to configure it, possibly using a higher-level language for config. I've been considering doing that but not sure I'll have time for it in the foreseeable future.
These are mostly gimmicks so it's understandable that it's not a priority. Anyway, I have another example (for people that can't disable their routers' DNS rebind protection):
[routers.router]
routes = [
{ resolver = "dns-rebind-emulator", type = "A", name = '\.rebind\.$' },
{ resolver = "myresolver" }
]
[groups.dns-rebind-emulator]
type = "replace"
resolvers = [ "dns-rebind-emulator-helper" ]
replace = [
{ from = '^(\d+)-(\d+)-(\d+)-(\d+)\.rebind\.$', to = '${1}.${2}.${3}.${4}.' }
]
[groups.dns-rebind-emulator-helper]
type = "static-responder"
answer = [ "IN A $1" ]
Regarding the 2nd suggestion, do you think that's possible with current tools?
If I understand it correctly you need a responder that can build a response based on the query name, right?
This should be possible, and in fact there's a lot of potential here. Let's just make this even more generic. What if we had an element that would allow some simple scripting language to be defined, that could eval the input and either build the output or forward to another group? That'd be extremely powerful and could probably even do what you were asking in part 1 and more.
Just a mockup, but perhaps it could look like
[groups.my-script]
type = "script-modifier"
resolvers = [ "resolver1", "resolver2" ]
script = '
<some script language that can take the input, then either produce a response or forward to any resolver>
'
Would have to find a script interpreter that integrates well, perhaps https://github.com/Shopify/go-lua or https://github.com/yuin/gopher-lua (not used either one yet).
What does the resolver do in this example? The group takes an input, gives it to the script, the script returns something and that gets forwarded to the resolver? I don't think that's gonna be too useful. I guess preparing something for another group could be useful, but we already got replace and routers, what else could one want?
If scripting were to be introduced (which I don't think is a good idea anyway), I would make it a resolver. So emulating ANY
queries would look something like this:
[resolvers.myscript]
type = "script"
address = "script.sh"
[routers.router]
routes = [
{ resolver = "myscript", type = "ANY" },
{ resolver = "myresolver" }
]
#!/bin/bash
for t in A AAAA CAA HTTPS MX NS SOA TXT; do
dig +noall +answer $2 $t
done
But then again, I feel like this could almost be done exclusively with the tools already provided.
Maybe some of this can be done similar as with replace
, but then make it possible to modify the the data/answer instead only the query (or even combine)? The variables can be generated using parentheses (round brackets) in regex?
It's definitely possible to implement something like that, a group that takes the query-name, applies a regex, and generates the response IP for it. It's just that this would be very specific to one use-case with a lot of limitations:
With the scripting element I was thinking it'd add a low-level way to do custom modifications. It wouldn't be a shell script of course, but some other language that can inspect and modify queries and responses. In the example above the "resolvers" would be optional. The script would receive a query and could then decide to either answer it directly, or forward to one of the defined resolvers. It'd allow implementing custom routing, load-balancing, etc. Not sure how complex that'd be to implement yet. Perhaps a basic "derive-answer-from-query-name" element would be the right approach for now.
It's just that scripting is a can of worms. Or rather executing any external file is. Maybe inline scripting is enough, possible in a language that can't do much damage (e.g. Lua)?
Generally speaking, I think a query merger makes sense: Send your query to multiple resolvers (e.g. Cloudflare and Google), collect the answers, remove duplicates and return. That would be the first (technically last) step to emulate ANY
queries and would be useful just by itself. I guess changing the query type really isn't useful except for that single use case.
It'd definitely be inline Lua with a small set of pre-defined functions to manipulate DNS queries and responses, no sub-processes or shell access. With that one could then implement pretty much any behavior that may be missing from the standard elements (though a little slower). Implementing this requires a bit more time than I can currently spend.
In the mean time, perhaps I could implement what you asked like so:
[routers.router]
routes = [
{ resolver = "dns-rebind-emulator", type = "A", name = '\.rebind\.$' },
{ resolver = "myresolver" }
]
[groups.dns-rebind-emulator-helper]
type = "static-responder"
query-name-regex = '^(\d+)-(\d+)-(\d+)-(\d+)\.rebind\.$'
answer = [ "IN A ${1}.${2}.${3}.${4}." ]
This would allow you to use the content of the query name to build a static response. It should solve the original ask. Not a fan of that there being some dynamic behavior in a group named "static-responder" but it's not too bad. This is relatively easy to add. Any thoughts on doing this?
Doesn't win the beauty contest, but works. :-)
I think for the cases mentioned it should be fine and should be sufficient.
Looks okay but I don't really like query-name-regex
. How about format
or question
(question
being the counterpart to answer
)? Also would be nice to be able use this to implement the original request:
[groups.blocklist-resolver]
type = "static-responder"
question = "^(.+)\.$"
answer = [ "IN A 0.0.0.0" ]
edns0-ede = { code = 15, text = "IP ${1} is in the blocklist" }
Would you be able to try out the issue-366
branch? It should let you use a question
regex like this:
[groups.static]
type = "static-responder"
question = '^(\d+)-(\d+)-(\d+)-(\d+)\.rebind\.$'
answer = ["IN A $1.$2.$3.$4"]
[groups.static]
type = "static-responder"
question = '^(.+)\.$'
answer = [ "IN A 0.0.0.0" ]
edns0-ede = { code = 15, text = "IP $1 is in the blocklist" }
https://github.com/folbricht/routedns/pull/373 lets you specify an extended error message from the blocklist. And it lets you customize the whole message. Would you be able to try that out?
https://github.com/folbricht/routedns/pull/378 adds a new static-template
that should be able to handle the rebind protection. There's an example of it in the PR. These templates can also use a number of string-manipulation functions that might come in handy. The documentation has been updated.
I think this solution is better than using regexes, and it should be more powerful as it allows customizing all response records, not just the answers.
The base case works but it breaks when blocklist-resolver
is used in response-blocklist-ip
(but that may be by design).
Also it seems like there's still no ability to get the filtered IP address instead.
Not sure I understand what exactly fails. Do you have an example config I can try?
[groups.blocklist-resolver]
type = "static-responder"
answer = [ "IN A 0.0.0.0" ]
[groups.blocklist]
type = "blocklist-v2"
resolvers = [ "cloudflare" ]
#blocklist-resolver = "blocklist-resolver"
blocklist-format = "domain"
blocklist = [ "evil.com" ]
edns0-ede = { code = 15, text = "{{ .Question }}" }
This example will have EDE: 15 (Blocked): (evil.com.)
in the response. Uncommenting blocklist-resolver
results in no message.
Yes, that is actually correct/expected. edns0-ede
is only used when the blocklist actually blocks something itself. If blocklist-resolver
is used, it doesn't actually block but forward somewhere for a response.
Ok so the first (block reason in EDE text) and third (rebind emulation) suggestions are possible now. Do you think the second (ANY emulation) is something that's worth implementing? I think that it sounds interesting (mostly for getting A
and AAAA
in a single call) but I don't know how feasible this is. I'll close this if you think it's not worth the effort.
When a group calls another group it would be great to have access to the arguments, e.g.
And I would also like to propose the idea of a new
query
group which allows you to construct your own query (this also needs support for arguments). Could be useful for changing the query type or question, e.g.