This how-to assumes basic familiarity with the Linux distribution of choice and basic IPv4 networking. It assumes the operating system is installed. Steps for (Red Hat Enterprise Linux) RHEL 7 based, RHEL 6 based, and Debian based distributions are included. If there are requests for other distributions, post a comment or use the contact page. I will refer to RHEL, CentOS, Oracle Linux, Scientific Linux, and any other RHEL based distribution as RHEL. Ubuntu, Mint, and other Debian based distributions will be referred to as Debian. I am assuming you are using a recent systemd based release. If you haven’t decided on a distribution, I recommend CentOS 7 because it is fairly easy to setup, it is free, and security updates will be available until June 30, 2024. Throughout this guide I will occasionally use the word netfilter. Netfilter is the firewall software that is built into the Linux kernel.
NOTE: All commands in this guide should be run as root.
What you need:
- Linux computer with at least two network interfaces.
- Modem that you have verified can connect to your ISP
- Ethernet switch and/or wireless access point for clients to connect to
Netfilter Mini Intro
Netfilter uses concepts called tables, rules, and chains. A rule tells netfilter what to do with a network packet it processes. A chain is a list of rules. A table is a group of chains for a specific purpose. For instance, the filter table has a set of chains that have rules that tell netfilter whether or not a packet is allowed or not. Chains and rules are easy for users to configure, where tables are not. Tables are predefined and require kernel modification to change. Each table comes with a few special, predefined chains. It is possible to create your own chains, but isn’t necessary for this guide. We will only be using the nat and filter tables with their special predefined chains.
Required Steps
Step 1: Verify your connections
Your setup should look something like this:
Step 2: Configure your interfaces
Determine which interface is connected to your modem and which interface is connected to your network. If your ISP is using DHCP, attempt to lease an address on one of your interfaces. If it doesn’t work, try the other. If they have provided you with a static IP address or static IP range, assign an address to your interface manually.
Test your configuration by pinging one of Google’s DNS servers.
ping 8.8.8.8
Step 3: Install packages
You may need to install a few packages to get things working.
Run the relevant command below to verify that everything you need is installed.
Distribution | Command |
---|---|
RHEL 6 | yum install iptables iptables-ipv6 |
RHEL 7 | yum install iptables iptables-utils iptables-services |
Debian | apt-get install iptables iptables-persistent netfilter-persistent |
If you are using RHEL 7, remove firewalld. For a simple router for home or even a small business, I think it adds an unnecessary layer of abstraction and complexity.
yum remove firewalld
Step 4: Configure netfilter to route traffic
Edit your firewall rules file, following the example below. Lines beginning with # are comments that will be ignored by your system. Feel free to leave them out. They are there to explain what each line of the file means. In the example, eth0 and eth1 are the interface connected to the modem and internal network, respectively. Substitute these with the interfaces on your system if necessary.
Distribution | Rules File |
---|---|
RHEL 6/7 | /etc/sysconfig/iptables |
Debian | /etc/iptables/rules.v4 |
*nat
# Tells iptables-restore that the following lines apply to the nat table
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
# The lines above are the chain default policies. Accept means they allow by default
# and reject or drop packets only when rules explicitly do so. We want to allow all
# packets entering the network address translation (NAT) table. Masquerading is a
# form of source NAT used so multiple internal devices can share a single public IP address.
-A POSTROUTING -o eth0 -j MASQUERADE
# This line does three things. It tells netfilter to substitute the source address
# of outgoing packets with the address of the named interface. It also tells
# netfilter to keep track of the internal address it originated. When packets
# come back, it substitutes the destination address with the address of the internal
# device the outgoing packet(s) came from.
COMMIT
# Commit just tells iptables-restore that you are done adding rules to the table
*filter
# Tells iptables-restore that the following lines apply to the filter table
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
# Tells netfilter that the default behavior is to accept all traffic on the
# FORWARD and OUTPUT chains and drop all traffic on the INPUT chain
-A INPUT -i eth1 -j ACCEPT
# Accept all incoming packets from the internal interface
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# Accept all packets from already established connections and any related packets
-A INPUT -p icmp -j ACCEPT
# Allow ICMP packets so things like ping and traceroute work. This is required because
# of the next line.
COMMIT
Step 5: Turn on IP forwarding
Routing is turned on by setting kernel parameters. Add the following lines to /etc/sysctl.conf. Some distributions may already have this line, but commented out:
# Enables IPv4 routing
net.ipv4.ip_forward=1
Run the command sysctl -p as root when you are done to apply the new settings.
Step 6: Apply the New Rules
Run the relevant command from the table to apply the rules.
Distribution | Command |
---|---|
RHEL 6 | service iptables reload |
RHEL 7 | systemctl reload iptables.service |
Debian | systemctl restart netfilter-persistent |
Step 7: Test your configuration
Using a device on the internal network, configure it to use the IP address of the internal interface of your system as its default gateway. Do the test from Step 2.
Step 8: Setup Netfilter to Load Rules During Boot
You probably want your router and firewall to come up automatically when you reboot your system. It is as simple as running the relevant commands from the table below:
Distribution | Command |
---|---|
RHEL 6 | chkconfig iptables on |
RHEL 7 | systemctl enable iptables.service |
Debian | systemctl enable netfilter-persistent |
Recommended Steps
Plan for IPv6
Now is the time to start planning for IPv6. IPv6 has such a huge address space, even residential users should get a fairly large block of public addresses rather than a single public address as is standard practice with IPv4. I recommend finding out when your provider plans on offering IPv6 service. Once available, I would go ahead and setup what is called a dual stack, which is running IPv4 and IPv6 in parallel.
Optional Steps
Optional 1: Configure port forwarding
If you have anything on the internal network you wish to reach from the internet, you can do so with port forwarding. Before doing this, make sure that the application you are trying to get to is properly configured and has the latest security patches. Malicious hackers use automated scanners to scan ranges of addresses for open ports and applications with known vulnerabilities.
Another thing to keep in mind is DHCP. I highly recommend statically assigning IP addresses to machines you are going to setup port forwarding on. If they use DHCP and lease a different address, the router won’t know and send traffic to the old address.
In your rules file, add lines to the nat table that look like the following. They should be placed between the chain default policy lines and the COMMIT below them in your rules file.
-A PREROUTING -i eth0 -p tcp --dport 1234 -j DNAT --to-destination 192.168.1.24:5000
Explanation:
Option | Meaning |
---|---|
-A PREROUTING | This rule applies to the PREROUTING chain. It is a built in chain in the nat table to alter packets as they come in. |
-i eth0 | This rule applies to traffic coming in on eth0. Replace eth0 with the interface connected to your modem. |
-p tcp | The protocol that this rule applies to. Replace it with udp (all lowercase) if applicable. |
–dport 1234 | The port you will connect to from the internet. Use something not in use. The command netstat -tuln will show you what is in use. The numbers after the : in the Local Address column are ports in use. |
-j DNAT | -j tells netfilter what to do when this rule is matched. In this case we want to DNAT, or change the destination to the IP address and port of the application we want to reach from the internet. |
–to-destination 192.168.1.55:5000 | This is where you want to change the destination to. In this case it will be sent to 192.168.1.55, port 5000 with the protocol specified by -p. |
Once you are done adding the DNAT rules to your nat table, add rules to the filter table to allow the traffic to pass. If you don’t do this and followed the example from Step 4, the packets will be dropped. If necessary, replace eth0 in the example below with the interface connected to your modem. These lines should go anywhere between the chain default policies and COMMIT.
-A INPUT -i eth0 -p tcp --dport 1234 -j ACCEPT
Finally, reload your rules to apply them.
Optional 2: Setup a DHCP server
Install the required packages:
Distribution | Command |
---|---|
RHEL 6 | yum install dhcp |
RHEL 7 | yum install dhcp |
Debian | apt-get install isc-dhcp-server |
Edit /etc/dhcp/dhcpd.conf and make it look similar to this:
# These are Google's DNS servers. If you have your own
# or want to use your ISP's, put in the appropriate IP
# Addresses
option domain-name-servers 8.8.8.8, 8.8.4.4;
# This prevents machines with leases from other DHCP servers from
# continuing to use the addresses obtained from these servers
authoritative;
# Unless you are running a DNS server that you want the DHCP server to
# dynamically update, this should be none.
ddns-update-style none;
# The example below should be self explanatory. The routers option
# defines what the client default gateway will be set to. It would be
# wise to leave a little room on either side of the range for devices
# you want to statically assign IPs to, such as access points and printers.
subnet 192.168.0.0 netmask 255.255.255.0 {
range 192.168.0.50 192.168.0.254;
option routers 192.168.0.1;
}
Optional 3: Sign up for a dynamic DNS service
Dynamic DNS allows DNS to be used with dynamic addressing systems, such as DHCP. If you have a dynamic address from your provider and wish to get to your network from the internet, this will be a lot easier than using the IP address. This will allow you to use a human friendly DNS name, such as tylersguides.com instead of an IP address to access your network. Choose a dynamic DNS provider and follow their instructions.
References
Netfilter Official Documentation
ISC DHCP Server Documentation