Closed Tatsh closed 3 years ago
I'd like to see the instructions. If it's long maybe we make it a separate document that gets linked to from the README.
Posting a rough draft.
This setup is for those who have two NICs on their machine. One will take in the internet (WAN) connection and the other will provide routing, usually with switches. There will be no other devices connected.
Some ISPs provide modems that also function as routers. You need to switch this device into bridge mode before it will correctly give a non-private IP address, usually out of the first ethernet port if there is more than one. This is the case with Comcast Xfinity and Spectrum in the US.
Every command except the two Docker commands in this document require using the root user, or sudo
(easier if you use sudo su -
). This guide assumes a system booting with systemd.
You need to know which NIC will provide which service, either internet or routing. Use ifconfig -a
(manpage) to see all connections. Output will be similar to below:
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 0.0.0.0
ether 02:42:6f:02:85:a1 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
enp1s0f0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet xx.xx.xx.xx netmask 255.255.248.0 broadcast xx.xx.xx.xx
inet6 xx prefixlen 64 scopeid 0x20<link>
ether 00:1b:21:36:88:00 txqueuelen 1000 (Ethernet)
RX packets 81765649 bytes 108457262437 (101.0 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 86576734 bytes 94204952379 (87.7 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device memory 0xfdc20000-fdc3ffff
enp1s0f1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether 00:1b:21:36:88:01 txqueuelen 1000 (Ethernet)
RX packets 88680977 bytes 94356583993 (87.8 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 84016023 bytes 111055124531 (103.4 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device memory 0xfdc00000-fdc1ffff
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 57902 bytes 4144373 (3.9 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 57902 bytes 4144373 (3.9 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
We can ignore lo
and docker0
as these are always present. We can see two devices enp1s0f0
and enp1s0f1
. enp1s0f0
is connected to the internet and has an IP from the ISP. enp1s0f1
is connected to a switch but does not have an IP because we have not given it one yet.
Before we do that, we need to create a bridge interface so the internet can be shared between enp1s0f0
and enp1s0f1
.
On my machine I am using systemd's service to create a bridge and set up enp1s0f1
with a static IP. This has to be an IP you will not allow to be taken with DHCP, so it should out of range of the DHCP range. I am using 192.168.1.5
in my case which is below the DHCP starting IP I have set 192.168.1.100
. You need to create /etc/systemd/network/
if it does not exist (mkdir -p /etc/systemd/network/
).
Here is the configuration for the input from the ISP, located at /etc/systemd/network/20-isp.network
(replace enp1s0f0
with your interface name).
[Match]
Name=enp1s0f0
[Network]
DHCP=yes
We are saying to get an IP via DHCP from the ISP here. If your ISP assigns you a static IP you need to set this correctly here. Most ISPs assign with DHCP. yes
means get IPv4 and IPv6. You can write ipv4
and ipv6
if you only want those.
This is the configuration to set a static IP on the NIC providing LAN routing, located at /etc/systemd/network/21-lan-nic.network
:
[Match]
Name=enp1s0f1
[Network]
Address=192.168.1.5/24
This is the configuration to create a bridge between these devices. Three files in total: describe the device, set its static IP (this will be the pi.hole
IP), and set up which devices connect to this bridge. We are naming this bridge br0
. Note the extensions.
/etc/systemd/network/30-bridge.netdev
:
[NetDev]
Name=br0
Kind=bridge
/etc/systemd/network/31-bridge.network
:
[Match]
Name=br0
[Network]
Address=192.168.1.254/24
/etc/systemd/network/90-br0-devices.network
:
[Match]
; mask is used here to match any device starting with 'en'
Name=en*
[Network]
Bridge=br0
Once you have created these files, restart the network service: systemctl restart systemd-networkd.service
.
ifconfig br0
should show a device with the IP you set:
br0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.254 netmask 255.255.255.0 broadcast 192.168.1.255
...
You can also verify the LAN routing NIC got an IP as well with a similar command.
Now you can start the Pi-hole container with some very specific options. Note that --net=host
is being used, which means anything using port 80 or port 53 should be turned off. Note the --cap-add
, -e ServerIP
, -e DNSMASQ_LISTENING
and -e INTERFACE
arguments.
This is not done as root.
docker container rm -f 'pihole'
docker run \
--net=host \
--cap-add=NET_ADMIN \
-e ServerIP="192.168.1.254'" \
-e WEBPASSWORD='the-password' \
-e DNSMASQ_LISTENING=all \
-e INTERFACE=br0 \
--restart=always \
--name pihole \
-v '/etc/resolv.conf:/etc/resolv.conf:ro' \
-v '/home/tatsh/dev/hosts:/etc/hosts:ro' \
-v '/home/tatsh/dev/pihole-config/:/etc/pihole/' \
-v '/home/tatsh/dev/pihole-dnsmasq.d/:/etc/dnsmasq.d' \
-v /sys/fs/cgroup:/sys/fs/cgroup:ro \
-d \
diginc/pi-hole:debian
Verify this works by running docker logs -f pihole
to watch the logs. Pi-hole should be able to start normally.
Now you need another computer with a web browser and ethernet. Connect this computer to a switch, then connect that switch to the LAN ethernet port. Set the IP of the computer statically to something like 192.168.1.33
, with a netmask of 255.255.255.0
and the router set to 192.168.1.254
. Set the DNS to have primary as 192.168.1.254
.
Alternatively you can plug the client system directly into the LAN NIC but you might need a crossover cable as older NICs may not support using a patch cable for this purpose. Most newer NICs automatically adjust.
You should now be able to go to pi.hole/admin
in the web browser of the client computer. If this does not work, temporarily set pi.hole
in /etc/hosts
or similar to route to 192.168.1.254
. Log in and head over to settings and enable DHCP. Be sure to set the router gateway IP address to the correct one, in this case it is 192.168.1.254
. Click the Save button.
On the server, you need to make the following changes to the firewall. I do this with iptables
commands.
Do not run iptables -P INPUT DROP
until the very end, when you are certain you have not blocked yourself out. If you are using SSH to manage this server, you must enable access to the SSH port. If you find yourself locked out, you will have to use something else to get on the machine (keyboard and monitor, IPMI/AMT/etc, VNC/RDP (usually provided by a web host), etc).
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
# Allow SSH
iptables -A INPUT -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Needed to access the web interface and API
iptables -A INPUT -p tcp -m tcp --dport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Needed to correctly receive DHCP information from clients
iptables -A INPUT -p udp -m udp --dport 67 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Needed for DNS
iptables -A INPUT -p udp -m udp --dport 53 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# Needed to allow internet access (br0 should be last)
iptables -A FORWARD -i enp1s0f1 -j ACCEPT
iptables -A FORWARD -i enp1s0f0 -j ACCEPT
iptables -A FORWARD -i br0 -j ACCEPT
iptables -P FORWARD DROP
# Allow any output (you could also limit to 80 and 443 only if you wanted only HTTP web access on your network)
# If you decide to use DROP, you must allow UDP port 68 here for DHCP to work
iptables -P OUTPUT ACCEPT
# Disallow input except for what was listed above
iptables -P INPUT DROP
# Just in case it is not set
echo net.ipv4.ip_forward=1 >> /etc/sysctl.conf
sysctl -p /etc/sysctl.conf
On the client system you can now disable static routing and set the NIC to use DHCP. You should receive a new IP and internet should be working.
If you are replacing your router that has wi-fi, you can probably still use this router as wi-fi access point. Plug this router's 1st LAN port into a switch port connected to your new routing system. Disable DHCP and set the router in its configuration to simply be an access point (it may say something else like DHCP forwarder).
systemctl restart docker
(you may need to start the Pi-hole container again after this)tcpdump -i br0 'src net 192.168.1.0/24 and not port 22 and not src 192.168.1.254'
to watch traffic.ngrep -d br0 -q tcp
to watch traffic.journalctl _COMM=systemd-networkd
Would you accept a pull request to update the README on how to make the DHCP server work?
I got it working on a regular Gentoo install with systemd and only having to adjust iptables.