Alternative Synology installation method

Hello,

Just sharing this, maybe people find it useful, in which case I can elaborate or turn it into a proper howto. Basically the idea is to lift part of the complexities involved from the NAS to the router, so this strategy requires a decent router/firewall (I use a cheap Ubiquiti Edgerouter X, but DD-WRT and the like, or pfSense and other *nix/BSD boxes, or enterprise grade routers will obviously do the trick as well).

I recently installed pi-hole on my Synology DS916+ in an alternative manner. Although the Synology howto will probably work out great, I don’t like the amount of changes it made to the base Synology install and I am quite fond of isolating stuff in docker containers.

There is a great docker image for pi-hole, thanks to @diginc.

Some challenges to overcome:

  1. If running in a default bridged docker container, all network traffic is masqueraded, so the dashboard wont show which host made which request
  2. Synology’s DSM really loves port 80, even more with webstation installed - and my goal was to keep the NAS as close to baseline as possible
  3. Blocked https traffic requires a custom firewall rule to prevent long time outs, which I also didn’t want to do on the NAS itself as pi hole will run in a container

So the solutions are:

Edit: the latest dinc/pi-hole image has build in functionality to run the http deamon on a non-standard port, simplifying affairs a lot.

  1. To make sure that the client stats have the actual client’s IP address: run the docker container with “host network” network enabled (this will remove the ability to remap ports, but see #2 how to deal with that)

  2. To remap the http port, the nginx.conf needs to be changed. I did it by mapping the file from outside the container, and changing the listening port from 80 to something else (37337).
    The http port is remapped via the WEB_PORT envvar.
    By giving pi hole an IP address on a different (non-existing) subnet via the ServerIP envvar (this means pi-hole will hand out that IP address for hosts that should be blocked) we enable the router to apply DNAT to rewrite traffic destined for the non-existing ip address at port 80 to the actual Synology host at port 37337 (if the Synology is on the same subnet, you probably also want SNAT I learned BTW).
    Last step is to modify nginx’s config server_name directive to tell it its actual IP, else some security check will kick in
    We need one extra envvar, to allow the admin interface to be contacted on an address that differs from the pi-hole address (as we just configured pi-hole to use a non-existent address). For this the VIRTUAL_SERVER envvar is used.

  3. Is easy, now that we have the pi hole on its own non-existing subnet from the routers point of view, an empty firewall chain with a default of reject is sufficient.

Hope this might actually help somebody one day.

Thanks for the great stuff that is pi hole (and for the great docker image of it).

Edit: it seems wiki and posts are interchangeable on this board, I will start typing a guide piece by piece below in this post.

0. Prerequisites

  1. The method described in this guide will only work if you have a router/gateway on which you can configure IP tables or equivalent. Consumer routers running stock firmware will not work. Consumer routers running DD WRT or other open firmware will do for this guide, as will Ubiquiti, Mikrotik and other advanced routers. The router part of this guide will initially be based on Ubiquiti’s Edgerouter X.
  2. This guide is based on a Synology DS916+ running the latest firmware at the time of writing (DSM 6.1.4-15217 Update 1).
  3. It is assumed you have some basic command line skills when following this guide (ssh into your Synology, editing a file with nano or vi and adding iptable commands to your router if applicable).
  4. One needs the knowledge to apply firewall/NAT rules on the router. As it is very make/model specific this guide cannot cover all possible ways of applying the necessary configuration. (An attempt will be made to provide the required iptable commands though, as that is probably most commonly used).

The following network configuration is used, but this can obviously be changed to match any other network scheme:

The subnet pi-hole will serve is 192.168.178.0/24. All clients connect to this subnet.
The router/gateway can be reached on 192.168.178.1.
The Synology has 192.168.178.50 as address on the subnet.
The subnet 192.168.0.0/24 is not in use elsewhere on the network.
192.168.0.2 will be used as the IP address pi-hole is handing out for blocked domains.
TCP port 37337 is used as pi-hole’s http listening port.

1. Installing the docker image

  1. In the Synology web interface navigate to the Docker “app”. In there search for “hole” in the registry. Select @diginc’s pi-hole image and download it:
  2. Be sure to read the info page and keep it at hand - in order to not duplicate data that will become outdated this guide will only list deviations from the default installation as per the info page.

2. Configuring the docker container

  1. Select the image and start creating a container from it (using the mis-named launch button).
  2. Enter the “Advanced Settings” menu of the wizard.
  3. In the advanced settings tab enable auto-restart
  4. In the Volume tab map the two directories as per the image info page.
  5. In the network tab enable “Use the same network as the Docker host”
  6. In the environment tab fill in the environment variables as per the image info page, with the following exceptions:
  • set “ServerIP” to “192.168.0.2” (or whatever fake address you chose)
  • set “VIRTUAL_SERVER” to “192.168.178.50” (or whatever IP address the Synology has)
  • Set “WEB_PORT” to “37337” (or whatever port you want to assign to pi-hole’s http server)

At this point the container can be started, and the administration interface should be available on http://192.168.178.50:37337/admin/ (or whatever IP the Synology has and whatever port you chose previously).
Do not use it for DNS just yet, as any request to a blocked domain will result in a slow timeout.

3. Router firewall and NAT configuration

In case you want to get more information (on how to do this on a specific router): In general what we are establishing here is refered to as loopback NAT or hairpin NAT or NAT reflection, so search for those terms.

  1. Create a DNAT rule such that TCP traffic destined for 192.168.0.2:80 gets translated to 192.168.178.50:37337.

  1. Create an SNAT rule (or masquerading) from your router to the Synology for port 37337 (we need to force return traffic through the router, else the above DNAT rule won’t work). So translate source to 192.168.178.1 if destination is 192.168.178.50:37337.

For DD WRT I am not sure on what can be accomplished in the GUI. Probably you want to use iptable commands anyway. See this example on the necessary iptable commands (do not blindly type in every rule stated there, the first couple of rules are to enable outside internet access, which definitively should not be done in this case - and on DD WRT the lan device is probably br0).

  1. Create a firewall rule to REJECT all traffic from 192.168.178.0/24 to 192.168.0.0/24 (make sure to use REJECT, not DROP, to prevent long https timeouts). Note that DNAT will happen before any firewall rule is applied, so we can safely REJECT all and still reach the pi-hole.

Finally set 192.168.178.50 (or whatever address the Synology has) as DNS server in your DHCP server of choice.

Summary

Create a docker container from the diginc/pi-hole image
Configure it with host networking and some additional environment variables (given the network layout used as example above):
set “ServerIP” to “192.168.0.2”
set “VIRTUAL_SERVER” to “192.168.178.50”
Set “WEB_PORT” to “37337”

On your router configure:
DNAT from 192.168.0.2:80 to 192.168.178.50:37337
SNAT from 192.168.178.0/24 to 192.168.178.1 for traffic destined to 192.168.178.50:37337
Reject packets for 192.168.0.2

Start the container.
Set the clients to use 192.168.178.50 as DNS server.

Enjoy a safer and ad free internet!

3 Likes

Cool setup! Glad you figure out a cleaner way of running my image. The volume mounting of nginx.conf to overwrite the stock config is a docker trick I wouldn't expect most people to think of, don't know why I never recommended that to the synology users before. Very tech savvy setup between that and the DNAT forward, awesome job.

I'd like to see your solution turned into a guide of some sort to help more people get started with my docker image without as much overhead or steps. I think I can help make the instructions a little shorter though. I'm going to get the default port customization request done ASAP, I keep putting it off and it's relatively simple (honestly surprised no one else PR'd this yet with all the synology users). That'll simplify your setup instructions some.

I assume you run a linux router or some sorts to add a that DNAT forward? Might be hard to implement that on a basic commercial router firmware.

Ok, I will make a simply guide of this.
The Synology Docker part will just use the GUI (as that is the “Synology way” of handling docker).
For the router configuration I will probably just put my router’s GUI and ask for some kind to soul to translate that into iptable commands (I have a good grasp on what should be in those lines, but not sure on the exact syntax).

On the router part that you asked: as mentioned :slight_smile: I use an Edgerouter-X. It is Vayatta (Debian Linux) based. But as you said, it probably wont be possible on a consumer router with stock firmware. DD-WRT is obviously a good choice in that case (although I am hard pressed to recommend DD-WRT on an internet facing router due to the lack of updates on older hardware).

Nice updates. I got a pull request to add in custom web port https://github.com/diginc/docker-pi-hole/pull/186 - I'll merge it after the tests finish running.

Edit: merged and uploaded to the alpine_dev / debian_dev amd64 tags.

1 Like

Just gave alpine_dev a swing. Works like a charm. The makes running it on Synology so much easier .Somehow I overlooked VIRTUAL_HOST previously, together with the new WEB_PORT it enables configuration via only the Synology Docker GUI (and no need to change any *.conf that is part of th image anymore).

Cheers, will update the guide above accordingly.
Edit: Done!

2 Likes

I could really use some help from somebody with a DD WRT router on how to accomplish the NAT reflection :innocent:. Probably the 3 required iptable commands and where to put them would suffice. Or a screenshot if it can be done via the GUI.

Cheers

I gave it a shot on my slightly order DD-WRT router but wasn't able to get the DNAT PREROUTE working even after moving it to the very top of the rule list so the stock NAT catchall rules didn't effect it..

I tried: iptables -t nat -I PREROUTING 2 -p tcp --source 192.168.41.0/24 --destination 192.168.9.6 --dport 80 -j DNAT --to 192.168.41.55:32773

$ iptables -t nat -L -v --line-numbers

Chain PREROUTING (policy ACCEPT 4774 packets, 327K bytes)
num   pkts bytes target     prot opt in     out     source               destination      
...
2       17   980 DNAT       tcp  --  any    any     192.168.41.0/24      192.168.9.6         tcp dpt:www to:192.168.41.55:32773 

I can see the rule getting hit (note the pkt/byte count) when I try to curl 192.168.9.6 but I'm not getting results, just timeouts, for some reason - initial connection must be made to router to iterate the iptables counter but return or further traffic is failing?

Curling the container on 192.168.41.55:32773 returns pi-hole block page fine. I'm not exactly sure what IPTables magic is needed here, open to suggestions. It maybe this long standing NAT hairpin/reflection issue mtioned there, I do not have 'Filter WAN NAT Redirection' enabled so I think this should work.

Edit: I'll try the combo of rules on this port 80 intercept script for squid later

These are the iptables commands I used on my router (running asus-merlin) that seem to be implement the behavior described in this post. Hopefully it can help someone else out that cannot do it via their router GUI.

iptables -t nat -A PREROUTING -i br0 -s 192.168.178.0/24 -d 192.168.0.2/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.178.50:37337
iptables -t nat -A POSTROUTING -o br0 -s 192.168.178.0/24 -d 192.168.178.50/32 -p tcp -m tcp --dport 37337 -j SNAT --to-source 192.168.178.1:80
iptables -A OUTPUT -s 192.168.178.0/24 -j REJECT -d 192.168.0.2/32

I am the least sure about that last line, as it didn't have a direct equivalent in the linked example.

The biggest problem I have now is that https requests do not get routed (since they are for port 443), and end up hanging the request, leading to some very slow web page loads (try https://slickdeals.net with these settings enabled).
I tried to come up with an iptables rule to add to the router to REJECT requests to port 443 on the "fake" address (192.168.0.2), but nothing I came up with would stop the hanging. But I barely understand iptables, so hopefully someone can provide the necessary command.

Thanks very much. it helpful.

Great tut, thanks!

Is there a solution regarding the IP Tables?

Do you recommend this method for Synology users or this one using Apache and Web Server?

Another option is to mount the Ubuntu VM with Pi-Hole in DSM.

Trying to get an idea what is my best option since I am also wanting to run Organizr.

Thanks!

Does anyone have instructions for setting up the Hairpin NAT and the SNAT settings on a UniFi Firewall?

It appears VIRTUAL_SERVER doesn't exist anymore. When trying to launch the container :
2018-08-01 13:03:26: (network.c.464) can't bind to port: 192.168.0.2 37337 Cannot assign requested address (it seems to use the ServerIP instead of VIRTUAL_SERVER env variable).
Any idea ? :slight_smile:

Here the same issue and the same error message like jbpaux wrote:

2018-08-13 17:24:27: (network.c.464) can't bind to port: 192.168.244.2 8201 Cannot assign requested address

using pihole/pihole:latest (version 4.0)
Seems like envvar VIRTUAL_SERVER has gone

I got 4.0 working with the following docker run and corresponding proxy (Synology's "Application Portal") settings. I had the SNAT and DNAT working with Pi-hole 3.3 but assume they're not needed any more given that we can't bind to a fake IP via VIRTUAL_SERVER.

docker run --name pihole \
--volume=/volume1/docker/pihole/etc/pihole:/etc/pihole/ \
--volume=/volume1/docker/pihole/etc/dnsmasq.d:/etc/dnsmasq.d/ \
--network=host \
--publish=50314:80 \
--publish=50315:443 \
--env WEB_PORT=50314 \
--env ServerIP=192.168.1.9 \
--env VIRTUAL_HOST=pihole.your.domain \
--env TZ=America/New_York \
--env WEBPASSWORD=admin123 \
--env DNS1=1.1.1.1 \
--env DNS2=1.0.0.1 \
--log-driver=json-file \
--restart=unless-stopped \
--detach=true \
pihole/pihole:latest

ServerIP = Synology IP
VIRTUAL_HOST = FQDN that also resolves to Synology IP

I tried many permutations and I'm sure there's other ways to do this, but hopefully this helps someone else.

1 Like

Hey @musicsnob - so how does this work? If PiHole only answers DNS queries, how would the host (loading the ads) use the 'pihole.your.domain' header to make use of the Reverse Proxy feature?

I think I am missing the point :slight_smile:

Hi @Mathius, no worries! There's two parts: the DNS server, and the web server. The reverse proxy allows allows multiple web sites / servers to flow through the same IP. The proxy maps each request to the correct server based on the host name.

Pi-hole's web server provides the "block page", the Pi-hole graphic and text indicating something was blocked. (pihole.your.domain). Pi-hole's web server also provides the admin interface as well. (pihole.your.domain/admin/)

Given that Synology's web UI already grabs port 80, Pi-hole must use a different port: 50314 in my example. So the Synology web UI "synology.your.domain" continues to go to 192.168.1.9:80, and now "pihole.your.domain" to 192.168.1.9:50314.

When your browser asks Pi-hole's DNS for "some.adserver.com", Pi-hole responds with 192.168.1.9. Now, here's where it gets a little messy because of the ports. You won't see Pi-hole block page when adds are blocked, because the browser is requesting 192.168.1.9:80 or :443, which is the Synology UI. Ideally, Synology would let us remap :80 and :443 to Pi-hole, but that's not an option in DSM as far as I know.

I'm not an expert but hopefully this is somewhat helpful...? :exploding_head: :grin:

Hi - thanks for the explanation, it is exactly as I understand it. I thought you had a solution to correctly reroute traffic to the pihole server.

I think we can add a new subnet and IP to Synology - I have done this for running the proxy software Squid. If pihole can bind to that, then ad (and Admin) traffic can reach pihole, staying on port 80/443 all the time.

I will look for my old config for Squid.

sadly you must use CLI on Unifi to set up the SNAT and DNAT if you do not have an Edge device