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

Per client, per domain complex behavior #1208

Open
rpm5099 opened this issue Dec 19, 2024 · 2 comments
Open

Per client, per domain complex behavior #1208

rpm5099 opened this issue Dec 19, 2024 · 2 comments
Assignees

Comments

@rpm5099
Copy link

rpm5099 commented Dec 19, 2024

I am unable to configure per client per domain response handling following any method after reading the documentation thoroughly and extensively searching the issues and web examples for more complete explanations than provided in the documentation. Behavior would preferably be by record type, but just A/AAAA I could work with, not concerned about performance. This is the required behavior for my application:

  • Clients A and B resolve DOMAIN1 normally/transparently (use global zone)
  • Clients C and D get redirected when looking up DOMAIN1
  • ALL clients except for B and C are redirected when resolving DOMAIN2 - A and D (and all other clients) resolve transparently
  • DOMAIN3 is redirected for ALL hosts
  • DOMAIN4 is resolved transparently for ALL hosts

Are tags, views, both or neither necessary for this behavior? How many views would be required? How are views prioritized after largest subnet they are tagged with? Can you apply multiple views (in priority order) to a specific client IP to control responses for specific domain(s)?

More generally - does order matter in the config - if so which items and how? Does indentation change the scoping? Is the English language documentation missing information on these features? Is it possible to use a python script that dictates the exact behavior required by hooking on specific client IP's to allow arbitrary logic?

I found very little documentation or examples on python response handling implementation except for pfblockerng which does not do per host DNS actions.

I appreciate any help you can provide.

System:

  • Unbound version: 1.22.0
  • OS: pfsense plus 24.11-RELEASE (amd64) FreeBSD XXX 15.0-CURRENT FreeBSD 15.0-CURRENT #0 plus-RELENG_24_11-n256407-1bbb3194162: Fri Nov 22 05:08:46 UTC 2024
  • unbound -V output:
Version 1.22.0

Configure line: --with-libexpat=/usr/local --with-ssl=/usr --enable-dnscrypt --disable-dnstap --with-dynlibmodule --enable-ecdsa --disable-event-api --enable-gost --with-libevent --with-pythonmodule=yes --with-pyunbound=yes ac_cv_path_SWIG=/usr/local/bin/swig LDFLAGS=-L/usr/local/lib --disable-subnet --disable-tfo-client --disable-tfo-server --with-pthreads --prefix=/usr/local --localstatedir=/var --mandir=/usr/local/share/man --infodir=/usr/local/share/info/ --build=amd64-portbld-freebsd15.0
Linked libs: libevent 2.1.12-stable (it uses kqueue), OpenSSL 3.0.14 4 Jun 2024
Linked modules: dns64 python dynlib respip validator iterator
DNSCrypt feature available

BSD licensed, see LICENSE in source package for details.
Report bugs to [email protected] or https://github.com/NLnetLabs/unbound/issues
@gthess gthess self-assigned this Dec 21, 2024
@gthess
Copy link
Member

gthess commented Dec 21, 2024

You could accomplish this with both tags and views as per https://unbound.docs.nlnetlabs.nl/en/latest/topics/filtering/tags-views.html. If something is not clear on that page we can always improve the text.

I believe the instructions you gave for domain2 are confusing but the examples below still work one way or another.

Tag example:
server:
	# Only use configured zones while testing
	local-zone: . refuse

        # domain4 would be always_transparent, this is just for testing
	local-zone: domain4. transparent
	local-data: 'domain4. TXT "domain4 global"' # only used for testing

	local-zone: domain3. redirect
	local-data: 'domain3. TXT "domain3 global"'

        # domain2 would be always_transparent, this is just for testing
	local-zone: domain2. transparent
	local-data: 'domain2. TXT "domain2 global"' # only used for testing

        # domain1 would be always_transparent, this is just for testing
	local-zone: domain1. transparent
	local-data: 'domain1. TXT "domain1 global"' # only used for testing

	access-control: 127.0.0.0/8 allow

	define-tag: "domain1global domain1redirect domain2global domain2redirect"

	access-control-tag: 127.0.0.1/32 "domain1global domain2global"
	access-control-tag: 127.0.0.2/32 "domain1global domain2redirect"
	access-control-tag: 127.0.0.3/32 "domain1redirect domain2redirect"
	access-control-tag: 127.0.0.4/32 "domain1redirect domain2global"

	# domain3 and domain4 have no tags, they are global
	local-zone-tag: domain1. "domain1global domain1redirect"
	local-zone-tag: domain2. "domain2global domain2redirect"

	access-control-tag-action: 127.0.0.2/32 "domain2redirect" redirect
	access-control-tag-data: 127.0.0.2/32 "domain2redirect" 'TXT "domain2 - clientB"'

	access-control-tag-action: 127.0.0.3/32 "domain1redirect" redirect
	access-control-tag-data: 127.0.0.3/32 "domain1redirect" 'TXT "domain1 - clientC"'
	access-control-tag-action: 127.0.0.3/32 "domain2redirect" redirect
	access-control-tag-data: 127.0.0.3/32 "domain2redirect" 'TXT "domain2 - clientC"'

	access-control-tag-action: 127.0.0.4/32 "domain1redirect" redirect
	access-control-tag-data: 127.0.0.4/32 "domain1redirect" 'TXT "domain1 - clientD"'
View example:
server:
	# Only use configured zones while testing
	local-zone: . refuse

        # domain4 would be always_transparent, this is just for testing
	local-zone: domain4. transparent
	local-data: 'domain4. TXT "domain4 global"' # only used for testing

	local-zone: domain3. redirect
	local-data: 'domain3. TXT "domain3 global"'

        # domain2 would be always_transparent, this is just for testing
	local-zone: domain2. transparent
	local-data: 'domain2. TXT "domain2 global"' # only used for testing

        # domain1 would be always_transparent, this is just for testing
	local-zone: domain1. transparent
	local-data: 'domain1. TXT "domain1 global"' # only used for testing

	access-control: 127.0.0.0/8 allow

	access-control-view: 127.0.0.1/32 clientA
	access-control-view: 127.0.0.2/32 clientB
	access-control-view: 127.0.0.3/32 clientC
	access-control-view: 127.0.0.4/32 clientD

view:
	name: "clientA"
	# domain 1 fallbacks to global
	# domain 2 fallbacks to global
	# domain 3 fallbacks to global
	# domain 4 fallbacks to global
	view-first: yes

view:
	name: "clientB"
	# domain 1 fallbacks to global
	local-zone: domain2. redirect
	local-data: 'domain2. TXT "domain2 - clientB"'
	# domain 3 fallbacks to global
	# domain 4 fallbacks to global
	view-first: yes

view:
	name: "clientC"
	local-zone: domain1. redirect
	local-data: 'domain1. TXT "domain1 - clientC"'
	local-zone: domain2. redirect
	local-data: 'domain2. TXT "domain2 - clientC"'
	# domain 3 fallbacks to global
	# domain 4 fallbacks to global
	view-first: yes

view:
	name: "clientD"
	local-zone: domain1. redirect
	local-data: 'domain1. TXT "domain1 - clientD"'
	# domain 2 fallbacks to global
	# domain 3 fallbacks to global
	# domain 4 fallbacks to global
	view-first: yes

You can try the above examples locally with something like:

dig -b 127.0.0.X @<unbound> -t txt domainX

Can you apply multiple views (in priority order) to a specific client IP to control responses for specific domain(s)?

Per IP prefix you can only apply a single view (the first one configured IIRC), but multiple tags.

More generally - does order matter in the config - if so which items and how?

Generally no. access-control-tag-action describes when the tag order matters.

Is it possible to use a python script that dictates the exact behavior required by hooking on specific client IP's to allow arbitrary logic?

Yes , however it can be way more involved than the above, way less performant, but maybe more dynamic if the script talks to external sources. If you are interested in something like that I can see if I could give some quick pointers.

@rpm5099
Copy link
Author

rpm5099 commented Dec 22, 2024

@gthess Thank you very much for your response, it's exactly what I was looking for. To do what I wanted, which was to block a set of domains for a list of specific clients and at the same time allow specific domains for a list of specific clients - and those two lists of clients overlap - the config gets big and complicated fast, especially if adding additional domains with those type so requirements. I actually wrote a python script to generate the config using the views method. The documentation is relatively clear but it's lacking meaningful examples like the ones you provided.

I would be very interested in hearing more about per client/per query/domain handling using a python module. Ideally I would like to be able to "hook" by client IP/query criteria. For clients this would mean any client that does not have per-client criteria would be handled normally (and therefore not impact performance). For clients that are hooked it would be possible to send any response of any kind, up to and including arbitrary bytes replied to on the same socket. I can think of quite a few interesting doors that would open up. I am guessing this is mostly possible, I just was not able to figure out the API in the amount of time I had available a few years back when I looked into it.

On that note, the pfsense unbound package does not currently allow activation of more than one python module at a time, which I could pretty easily work around, but it would be great if at some point that changes and they could be stacked in priority order. I have not yet experimented with opnsense or other firewalls.

Thanks again!

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

No branches or pull requests

2 participants