Conditional DNS Resolution for Home Server Access from Local Network and VPN

Hello everyone,

I've set up Pi-hole on a VPS in the cloud. It runs Pi-hole, acting as a recursive DNS server with unbound, and also functions as a WireGuard VPN server. I followed the instructions provided in this video . I made a few changes to the VPN client configuration files so that only traffic for internal resources, such as DNS queries, is routed through the VPN, while the rest of the traffic bypasses the VPN. Specifically, I modified the AllowedIPs line, changing 0.0.0.0/0 to the VPN network address (e.g., 172.16.16.0/24).

Currently, I have a Nextcloud server running on an Orange PI at home. I access it only locally, but I would like to access it from outside my home network as well. Therefore, I connected the Nextcloud server to my Pi-hole VPN. This way, I don't need to open a port on my modem to access Nextcloud over the internet; I can simply use the VPN connection.

My devices are connected to the VPN 100% of the time, whether at home, on mobile data, on my company's Wi-Fi, or at my university. This allows me to access the Nextcloud server when I am away from home using its VPN IP (172.16.16.100). The problem is that when I am at home and access the Nextcloud server with this IP, the traffic is unnecessarily routed through the VPN. I could access the server using its local IP (192.168.1.100), but it is not practical to constantly switch this IP in the Nextcloud client app.

I would like to know if it is possible to create a domain name, for example, mynextcloudserver.local, so that when I am at home, this domain resolves to 192.168.1.100, and when I am away from home, it resolves to 172.16.16.100. I am looking for a solution that works primarily on my Android devices, which connect to various networks throughout the day, and also on my Linux desktop, which stays at home.

Thank you in advance for your help!

You are aspiring to set up a split-horizon DNS, providing different DNS replies based on the request's source IP.

As a tailored dnsmasq fork, pihole-FTL is limited in that regard, i.e you can't configure that for arbitrary domains.

But as you are running unbound as your Pi-hole's upstream, you could try to configure unbound's Tags and Views — Unbound 1.19.3 documentation

On a side note, don't use .local - that TLD is reserved for mDNS usage and should not be used with plain DNS.

I have a very similar setup to OP, except that instead of connecting over VPN vs Local, I have an IoT VLAN and a Home VLAN.

My PiHole instance is listening on two interfaces, one for each respective VLAN, and the authoritative DNS server is bound to this PiHole for the correct IP on the corresponding subnet (at least when IPs are leased via DHCP). Firewall rules further restrict access to other DNS servers.

In this situation, is it possible to provide different DNS replies based on the incoming listen interface (rather than the source IP), or would you still recommend using unbound's Tags and Views?

That is a very different setup.

What is your goal here?
Please give examples of domains and expected replies where applicable.

My home networking setup has an IoT VLAN (100) [10.10.10.0/24] as well as a Home LAN [192.168.1.0/24]. I have a single Jellyfin instance running inside a Proxmox LXC that has two interfaces with an IP on each of the above subnets (and correctly configured matching VLAN tag for IoT).

The goal is to have the single address https://jellyfin.example.com configured within my Google Chromecast, but when the Chromecast switches WiFi connection between the Home WiFi network and the IoT network it will continue to work without additional configuration.

Since I do not have a layer 3 switch, the most important aspect of this setup is that when the Chromecast is on the Home network, it does not attempt to stream from the IoT network (or vice versa), since this will force packets across my 100 MbE router and result in choppy video.

The ideal setup would be to just keep Chromecast on the IoT network always but for various reasons this is not possible.

In my current setup:

  • Proxmox VE handles the network setup and correctly routes the tagged traffic
  • Jellyfin listens on both 192.168.1.4 and 10.10.10.17
  • PiHole functions as the primary DNS and also listens on both 192.168.1.8 and 10.10.10.108 (but it's a single instance, same as Jellyfin, not bound to a specific address)
  • The DHCP service (running on my router, 192.168.1.1) advertises the IPs above as the primary DNS for each respective subnet, and firewall rules prevent the IoT VLAN from contacting any other DNS server.
  • I am running unbound as my Pi-Hole upstream, same as OP.

When devices on the IoT network/vlan/subnet resolve jellyfin.example.com it should direct them to 10.10.10.17, while everything else should resolve to 192.168.1.4

Caveat: I'm actually going to route these via a reverse proxy (Caddy), but the end result is the same since Caddy will be running in the same configuration as both Pi-Hole and Jellyfin. All traffic between Jellyfin and Chromecast needs to stay on the same subnet.

I've got an update here with some progress.

I previously had a mapping in /etc/dnsmasq.d/42-reverse-proxy.conf

That would just map the entire example.com address space to my reverse proxy:

address=/example.com/192.168.1.92

Then the reverse proxy would actually serve up jellyfin.

I've since deleted that in order to delegate the lookup to unbound via the following:

root@pihole:/etc/unbound/unbound.conf.d# cat jellyfin-vlan.conf 
view:
  name: "jellyfin-iot"
  local-zone: "jellyfin.example.com." static
  local-data: "jellyfin.example.com. A 10.10.10.17"

server:
  local-zone: "example.com" redirect
  local-data: "example.com A 192.168.1.92"
  access-control-view: 10.10.10.0/24 jellyfin-iot

But I'm pretty sure this is not working because unbound is receiving all its queries from Pi-hole on the loopback interface, and can't actually discern the client IP. Have tested this locally via dig and direct queries work as intended.

I have my upstream DNS server configured as 127.0.0.1#5335 and it's not clear how to delegate the query so that it will take the original client IP or interface into account.

I've also tried add-subnet=32 in my dnsmasq config but it's still not working.

My current results when looking up jellyfin.example.com from IoT subnet, first using Pi-Hole directly:

root@mdns100:~# dig @10.10.10.108 jellyfin.example.com

; <<>> DiG 9.18.24-1-Debian <<>> @10.10.10.108 jellyfin.example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 17297
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;jellyfin.example.com.          IN      A

;; ANSWER SECTION:
jellyfin.example.com.   3600    IN      A       192.168.1.92

;; Query time: 2 msec
;; SERVER: 10.10.10.108#53(10.10.10.108) (UDP)
;; WHEN: Mon Jun 17 23:16:00 UTC 2024
;; MSG SIZE  rcvd: 65

Note when I add the port to query unbound directly I get a different answer:

root@mdns100:~# dig -p 5335 @10.10.10.108 jellyfin.example.com

; <<>> DiG 9.18.24-1-Debian <<>> -p 5335 @10.10.10.108 jellyfin.example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 400
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;jellyfin.example.com.          IN      A

;; ANSWER SECTION:
jellyfin.example.com.   3600    IN      A       10.10.10.17

;; Query time: 0 msec
;; SERVER: 10.10.10.108#5335(10.10.10.108) (UDP)
;; WHEN: Mon Jun 17 23:16:09 UTC 2024
;; MSG SIZE  rcvd: 65

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.