# Firewall for multi-purpose server?

## The_Great_Sephiroth

I have a new server at a client location that is running great. It hosts, DHCP, DNS, NTP, OpenVPN, SMB/Samba, and it accepts SSH access for maintenance. Now I need to secure it. Since this is shell only I plan to write a shell-script which will setup iptables and then do iptables-save so it gets restored at boot. The issue is that it has been so long I cannot remember what I need to do. Below is what I have right now.

```

#!/bin/bash

iptables -F

iptables -X

iptables -t nat -F

iptables -t nat -X

iptables -t mangle -F

iptables -t mangle -X

iptables -t raw -F

iptables -t raw -X

iptables -t security -F

iptables -t security -X

iptables -P INPUT ACCEPT

iptables -P FORWARD ACCEPT

iptables -P OUTPUT ACCEPT

iptables -t nat -A POSTROUTING -s 192.168.110.0/24 -o enp3s0 -j MASQUERADE

```

This clears all rules and then allows NAT between the OpenVPN tunnel network (192.168.110.0/24) and the LAN (192.168.111.0/24). I know I need to drop by default everything in all three tables (INPUT, OUTPUT, FORWARD) but I no longer remember how to allow data back out on the OUTPUT table. Can somebody help point me back in the right direction here? I also know that I need to allow everything in and out of "lo" for the system to run.

----------

## pietinger

Can this help a little bit ? -> https://forums.gentoo.org/viewtopic-t-1114432-highlight-.html

----------

## The_Great_Sephiroth

Will read it now, thank you for sharing!

----------

## The_Great_Sephiroth

OK, I read it but I believe it is a tad more than what I need. This specific server hosts DHCP, DNS, OpenVPN server (UDP), SMB file shares, SSH, and I want it to host NTP for the Windows 10 workstations at some point. Below is what I believe I need as far as a script goes.

```

#!/bin/bash

# Clear out everything

iptables -F

iptables -X

iptables -t nat -F

iptables -t nat -X

iptables -t mangle -F

iptables -t mangle -X

iptables -t raw -F

iptables -t raw -X

iptables -t security -F

iptables -t security -X

iptables -P INPUT ACCEPT

iptables -P FORWARD ACCEPT

iptables -P OUTPUT ACCEPT

# Always allow lo unless you want to murder Linux

iptables -A INPUT -i lo -j ACCEPT

iptables -A FORWARD -i lo -j ACCEPT

iptables -A OUTPUT -o lo -j ACCEPT

# Setup the input table

iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

iptables -A INPUT -i enp3s0 -p TCP --dport 22 -j ACCEPT

iptables -A INPUT -i enp3s0 -p UDP --dport 1194 -j ACCEPT

iptables -A INPUT -i enp3s0 -p UDP --dport 67:68 -j ACCEPT

iptables -A INPUT -i enp3s0 -p UDP --dport 53 -j ACCEPT

# Setup the forward table

iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Setup the output table

# Setup the postrouting table

iptables -t nat -A POSTROUTING -s 192.168.110.0/24 -o enp3s0 -j MASQUERADE

# Now lock it down

iptables -P INPUT DROP

iptables -P FORWARD DROP

iptables -P OUTPUT ACCEPT

```

Is this secure enough and functional for a small office setting? I did not include ports for SMB, NTP, or others. The above should allow SSH, OpenVPN, DHCP, and DNS. How does it look to an iptables guru though?

I am thinking of changing output to drop and only allowing output for things like the above services. Will do that AFTER the thing is locked down.

----------

## pietinger

 *The_Great_Sephiroth wrote:*   

> How does it look to an iptables guru though?

 

Ooh ... I am not an iptables guru ... maybe an experienced amateuer. Anyway, thanks a lot.

 *The_Great_Sephiroth wrote:*   

> Is this secure enough and functional for a small office setting?

 

I cant answer this question, because I dont know your network design. Especially I dont know if there is another firewall protecting your LAN, and if Clients are allowed to go directly into the internet (I dont hope so).

I think your VPN directs to your headquarter and there will be done more network security. If this is true, then you can simply allow more. Does your headquarter needs access to your server ? Does your headquarter needs access to the clients ? If all yes, look at (1), (2) and (3) below.

You can also simply allow incoming traffic for your server without specifying your input device (-i enp3s0). Then you can delete the line (1) below:

```
iptables -A INPUT -i tun0 -j ACCEPT
```

 *The_Great_Sephiroth wrote:*   

> I am thinking of changing output to drop and only allowing output for things like the above services. Will do that AFTER the thing is locked down.

 

I highly recommend to change the output to drop, but then you will need some more accepts. But one thing first: Please delete this line:

```
iptables -A FORWARD -i lo -j ACCEPT
```

In this script, I am enabling pings, so you can search if you have some problems. Later you can/should delete it.

```

#!/bin/bash

# Clear out everything

iptables -F

iptables -X

iptables -t nat -F

iptables -t nat -X

iptables -t mangle -F

iptables -t mangle -X

iptables -t raw -F

iptables -t raw -X

iptables -t security -F

iptables -t security -X

iptables -P INPUT DROP

iptables -P FORWARD DROP

iptables -P OUTPUT DROP

# Always allow lo unless you want to murder Linux

iptables -A INPUT -i lo -j ACCEPT

iptables -A OUTPUT -o lo -j ACCEPT

# Setup the postrouting table

iptables -t nat -A POSTROUTING -s 192.168.110.0/24 -o enp3s0 -j MASQUERADE

# Setup the input table

iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# *** (1) ***

# iptables -A INPUT -i tun0 -j ACCEPT

iptables -A INPUT -i enp3s0 -p TCP --dport 22 -j ACCEPT

iptables -A INPUT -i enp3s0 -p UDP --dport 1194 -j ACCEPT

iptables -A INPUT -i enp3s0 -p UDP --dport 67:68 -j ACCEPT

iptables -A INPUT -i enp3s0 -p UDP --dport 53 -j ACCEPT

# If you are afraid against brute force attacks you can add this

# iptables -A INPUT -m limit --limit 3/min -j LOG --log-prefix "!!! LIM IN "

# iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable

# Setup the output table

iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# *** (2) ***

# iptables -A OUTPUT -o tun0 -j ACCEPT

# if you want to ssh from this server to clients

# iptables -A OUTPUT -p TCP --dport 22 -j ACCEPT

# Setup the forward table

iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# *** (3) ***

# iptables -A FORWARD -s 192.168.110.0/24 -i tun0 -o enps3s0 -j ACCEPT

# If you are afraid against brute force attacks you can add this

# iptables -A FORWARD -m limit --limit 3/min -j LOG --log-prefix "!!! LIM FWD "

# iptables -A FORWARD -j REJECT --reject-with icmp-port-unreachable

# Allow PINGs in any direction

# iptables -A INPUT -p icmp --icmp-type 8/0 -j ACCEPT

# iptables -A OUTPUT -p icmp --icmp-type 8/0 -j ACCEPT

# iptables -A FORWARD -p icmp --icmp-type 8/0 -j ACCEPT

# Do some logging for all tables

# iptables -A INPUT -j LOG --log-prefix "!!! IN "

# iptables -A OUTPUT -j LOG --log-prefix "!!! OUT "

# iptables -A FORWARD -j LOG --log-prefix "!!! FWD "
```

P.S.: I commented out some of my extensions to make it more visible.

P.P.S.: Always work with "iptables -L -v -n" and watch the increasing of the number of packets for each rule ...

----------

## Hu

As an initial review:pietinger is correct that you do not need a FORWARD rule for lo.  You are correct to have them for INPUT/OUTPUT.I understand the appeal, but I must discourage use of a shell script to load rules.  As written, if a rule fails to load, the shell will blunder on, loading what rules it can.  The resulting partial firewall may behave differently than the one you intended to load.  You should use iptables-restore which can load all the rules as a unit, and leave the state unchanged if it fails.  (If you do this, you should start by loading a very simple state, either fail-secure or fail-open, depending on your design requirements.  Then, use iptables-restore to load the production-ready rules.)Your rules are generally consistent with your stated goals.I discourage allowing ssh from anywhere, and prefer that you allow ssh only from subnets where you expect legitimate users to be.Your rule will allow the OpenVPN data packets to arrive and be processed by the openvpn process.  That process will likely write decrypted packets to a virtual Ethernet device for the kernel to route internally.  You have no rules to allow those decrypted packets, so traffic arriving from the VPN will probably be decrypted, then DROP'd.You mention some services you intend to host, but have no rules to allow them.

----------

## pietinger

Short remarks:

 *Hu wrote:*   

> I understand the appeal, but I must discourage use of a shell script to load rules. [...]

 

I didnt recommend to use a shell script for loading rules all the time; therefore we have "/etc/init.d/iptables". But I recommend a shell script for the initial setting of rules. It is not insecure, when you first set the default action for all tables to "DROP". And I recommend it for any changes later. You always have to save it then with "/etc/init.d/iptables save".

 *Hu wrote:*   

> I discourage allowing ssh from anywhere, and prefer that you allow ssh only from subnets where you expect legitimate users to be.

 

Yes, therefore I asked for the headquarter.

 *Hu wrote:*   

> Your rule will allow the OpenVPN data packets to arrive and be processed by the openvpn process.  That process will likely write decrypted packets to a virtual Ethernet device for the kernel to route internally.  You have no rules to allow those decrypted packets, so traffic arriving from the VPN will probably be decrypted, then DROP'd.You mention some services you intend to host, but have no rules to allow them.

 

You have seen my extensions, you did ?

----------

## Hu

I saw that you offered them, but I did not review them, because I don't know how much, if any, of it the original poster will adopt.  I only reviewed what the original poster had provided.  Aside from the acknowledgment of your suggestion, everything I wrote was based off the original poster's post and directed toward him.

----------

## pietinger

 *Hu wrote:*   

> [...]I only reviewed what the original poster had provided.  Aside from the acknowledgment of your suggestion, everything I wrote was based off the original poster's post and directed toward him.

 

Sorry, for my misunderstanding.    :Embarassed: 

----------

## The_Great_Sephiroth

I am only using a script to show off my intention. I do use iptables-save and -restore. The VPN allows users to work on their PCs from home, not provide an uplink. SSH is used by me either via the VPN or on the LAN itself if I am present there. Should I allow only from the LAN subnets? This thing does NOT face the Internet. You would need to be on the LAN or VPN anyway, so filtering for that seems redundant.

I have not setup NTP yet and SMB is going but I could not think of the ports off the top of my head. It's late here now and I am home. I will work on my stuff and add my missing services and the like soon. I am also trying to wrap my head around nftables, but it is a hot mess. However, if I am able to grasp nftables this stuff may go away anyway.

----------

## pietinger

The_Great_Sephiroth,

when you emerge "nftables" you should have "iptables-translate" and "iptables-restore-translate" for an automtic translation of your iptables-rules into nftables-rules. I never tried it, because the syntax of nftables is not very much different from iptables. The difference is inside the kernel. Nftables uses the berkerly packet filter (BPF) inside the network stack. I will do my switch when all needed options are pure nftables.

I dont know which machines (and how) in you LAN have a connection to the internet. But - be aware of IPv6 and its automatic configuration "service" ...

Good luck and have a good time,

Peter

----------

## Hu

You can use symbolic port names, if the port is known to /etc/services.  From your additional description, further restricting ssh is probably not necessary.

I have not looked at nftables, so I cannot comment on it.

----------

## The_Great_Sephiroth

Pietinger mentioned something that raises a question. Does nftables not support doing everything that iptables does yet? If so, why were we forced to nftables last year? This laptop is now using nftables and I seem to be doing fine, but since it is a workstation with Plasma I configure it via the firewalld GUI.

I am at the client location now and will be working on this while two old workstations are wiped (badblocks -wsv /dev/sda) and loaded with Windows 10 to be given to a school. Might have an updated post soon.

----------

## The_Great_Sephiroth

OK, I have some more questions. It seems the deeper I dig the more I wonder what is correct and what is erroneous. I list all of the listening ports on my server.

```

netstat -tulpn | grep LISTEN

tcp        0      0 0.0.0.0:53              0.0.0.0:*               LISTEN      2105/dnsmasq        

tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      2310/sshd: /usr/bin 

tcp        0      0 0.0.0.0:445             0.0.0.0:*               LISTEN      2269/smbd           

tcp        0      0 0.0.0.0:3551            0.0.0.0:*               LISTEN      2101/apcupsd        

tcp        0      0 0.0.0.0:139             0.0.0.0:*               LISTEN      2269/smbd           

tcp6       0      0 :::53                   :::*                    LISTEN      2105/dnsmasq        

tcp6       0      0 :::22                   :::*                    LISTEN      2310/sshd: /usr/bin 

tcp6       0      0 :::445                  :::*                    LISTEN      2269/smbd           

tcp6       0      0 :::139                  :::*                    LISTEN      2269/smbd

```

As you can see, DNS is on TCP 53, SSH is TCP 22, and SMBD/NMBD are on TCP 445 and 139. I am also hosting DHCP but IIRC that is on broadcast and I should not have to do anything beyond allowing UDP 67 and 68 in and out on the NIC.

The confusion comes in when viewing /etc/services. It lists DNS as "domain" and shows it on TCP and UDP 53. Why? Many others are listed this way as well, but I only ever see them being used one way or the other.

*UPDATE*

For now I tried what I have and it appears to work DNS gets out to resolve external names like google.com, the VPN continues to operate, and file shares are working.

```

#!/bin/bash

# Clear out everything

iptables -F

iptables -X

iptables -t nat -F

iptables -t nat -X

iptables -t mangle -F

iptables -t mangle -X

iptables -t raw -F

iptables -t raw -X

iptables -t security -F

iptables -t security -X

iptables -P INPUT DROP

iptables -P FORWARD DROP

iptables -P OUTPUT DROP

# Always allow lo unless you want to murder Linux

iptables -A INPUT -i lo -j ACCEPT

iptables -A OUTPUT -o lo -j ACCEPT

# Setup the postrouting table

iptables -t nat -A POSTROUTING -s 192.168.110.0/24 -o enp3s0 -j MASQUERADE

# Setup the input table

iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

iptables -A INPUT -i tun0 -j ACCEPT

iptables -A INPUT -i enp3s0 -p tcp --dport ssh -j ACCEPT

iptables -A INPUT -i enp3s0 -p udp --dport openvpn -j ACCEPT

iptables -A INPUT -i enp3s0 -p udp --dport bootps:bootpc -j ACCEPT

iptables -A INPUT -i enp3s0 -p udp --dport domain -j ACCEPT

iptables -A INPUT -i enp3s0 -p tcp --dport microsoft-ds -j ACCEPT

iptables -A INPUT -i enp3s0 -p tcp --dport netbios-ssn -j ACCEPT

iptables -A INPUT -p icmp --icmp-type 8/0 -j ACCEPT

# Setup the output table

iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

iptables -A OUTPUT -o tun0 -j ACCEPT

iptables -A OUTPUT -o enp3s0 -p udp --dport domain -j ACCEPT

iptables -A OUTPUT -o enp3s0 -p udp --dport bootps:bootpc -j ACCEPT

iptables -A OUTPUT -o enp3s0 -p tcp --dport microsoft-ds -j ACCEPT

iptables -A OUTPUT -o enp3s0 -p tcp --dport netbios-ssn -j ACCEPT

iptables -A OUTPUT -o enp3s0 -p tcp --dport rsync -j ACCEPT

iptables -A OUTPUT -p icmp --icmp-type 8/0 -j ACCEPT

# Setup the forward table

iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

iptables -A FORWARD -s 192.168.110.0/24 -i tun0 -o enps3s0 -j ACCEPT

iptables -A FORWARD -p icmp --icmp-type 8/0 -j ACCEPT

```

I will do the logging after I know all is good.

----------

## Hu

 *The_Great_Sephiroth wrote:*   

> The confusion comes in when viewing /etc/services. It lists DNS as "domain" and shows it on TCP and UDP 53. Why? Many others are listed this way as well, but I only ever see them being used one way or the other.

 For historical reasons, it was popular to assign the same number port to a service on both TCP and UDP, even if it seemed unlikely that the service would ever be offered on both.  Compounding the confusion, DNS actually is offered over both, in some cases.  DNS/UDP is preferred for most routine operations as it is simple and fast.  DNS/TCP is used for certain large operations that would otherwise require reinventing TCP inside UDP due to the volume of data, ordering constraints, and so on.

----------

## The_Great_Sephiroth

OK, I gotcha'. From what i understand now, with a fully locked-down firewall I need to add rules for incoming and outgoing ports and protocols, such as allowing 'domain' in on the NIC so LAN PCs can get DNS info from the server, but also out on the NIC where the destination port is 53 so the server can send queries upstream to Internet servers. My only area of confusion remaining is involving VPN stuff, but I can make a thread about that later. We will see how this firewall does for a while before I go to the next step.

Here is the firewall as it stands. Runs fine at this point!

```

[sv01 ~]# iptables -L

Chain INPUT (policy DROP)

target     prot opt source               destination         

ACCEPT     all  --  anywhere             anywhere            

ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED

ACCEPT     all  --  anywhere             anywhere            

ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:ssh

ACCEPT     udp  --  anywhere             anywhere             udp dpt:openvpn

ACCEPT     udp  --  anywhere             anywhere             udp dpts:bootps:bootpc

ACCEPT     udp  --  anywhere             anywhere             udp dpt:domain

ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:microsoft-ds

ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:netbios-ssn

ACCEPT     icmp --  anywhere             anywhere             icmptype 8 code 0

Chain FORWARD (policy DROP)

target     prot opt source               destination         

ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED

ACCEPT     all  --  192.168.110.0/24     anywhere            

ACCEPT     icmp --  anywhere             anywhere             icmptype 8 code 0

Chain OUTPUT (policy DROP)

target     prot opt source               destination         

ACCEPT     all  --  anywhere             anywhere            

ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED

ACCEPT     all  --  anywhere             anywhere            

ACCEPT     udp  --  anywhere             anywhere             udp dpt:domain

ACCEPT     udp  --  anywhere             anywhere             udp dpts:bootps:bootpc

ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:microsoft-ds

ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:netbios-ssn

ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:rsync

ACCEPT     icmp --  anywhere             anywhere             icmptype 8 code 0

```

I just realized I need to allow NTP requests out...

----------

## pietinger

Oh, many questions ...

1. iptables <> nftables

With nftables you can configure ARP-filtering, Bridging, IPv4 and IPv6 with ONE system: nftables. So you dont need arptables, ebtables, iptables, ip6tables ! And - nftables is the future !

But iptables is older and more stable (when I look onto the kernel patches). And the syntax is easier. Many people say the syntax of nftables is easier ... maybe if you understand you have to define the chains and tables and used hooks, because there are no predefined ones. I understand it, but I would say its more difficult, especially if you want some simple mangle definitions; e.g. allow only one UID (of a proxy) to go out to 443 and 80. Another point is: You are not free with your definitions anymore. You cant seperate your LOOPBACK-allows, CONNTRACK-rules and LOG-rules. With nft you have to put all together in one chain- and table-definition. So, for private using (if you have only IPv4) I recommend iptables.

(The same with IPv6. It is the future, but until you really need it stay at IPv4. There are some problems with your privacy and I am watching the kernel patches for it also. IPv4 is older and more stable.)

 *The_Great_Sephiroth wrote:*   

> I will do the logging after I know all is good.

 

2. You should do it now. Dont worry, you dont log all, you are "only" logging if netfilter denies a packet.

 *The_Great_Sephiroth wrote:*   

> [...] I need to add rules for incoming and outgoing ports and protocols, [...]

 

3. Let me quote myself from this thread: https://forums.gentoo.org/viewtopic-t-1112806.html

 *Quote:*   

> Stateful Inspection
> 
> I cant remember how long ago it is. Yes there was a time (I think until kernel 2.0) you had to explicitly allow an outgoing ping-request and the incoming ping-response. And the same for every protocol or target host, or target net. But then we had a (great) upcoming with a new kernel (2.2 I believe): the STATEFUL inspection ! What does this mean ?
> 
> Every communication between two computers begins with sending out the FIRST packet to the target, e.g. saying "hello, its me, I want to talk with your web-server". The answer from this web-server and all other packets related to this session, must be allowed also in the firewall, because the kernel filters EVERY packet. With the new kernel you was able to allow all RELATED packets for this session automatically. So, TODAY, when we configure a firewall, we simply allow only the INITIATING of a session (or just the first packet when using a session-less protocol like UDP) and the kernel checks by itself what is a packet belonging to this. This is why you see in every Firewall-script at minimum always these two lines:
> ...

 

Please dont forget you have two directions:

1) If you want ssh FROM your host to another machine, you have to allow the "FIRST packet" in the table OUTPUT and ALL other packets, coming back and sending and coming back and sending ..... will be handled from the two conntrack-rules in INPUT and OUTPUT.

2) If you want allow incoming ssh to your waiting sshd, you have to allow the "FIRST packet" in the table INTPUT and ALL other packets, coming back and sending and coming back and sending ..... will be handled from the two conntrack-rules in INPUT and OUTPUT.

You will see this behavior when you ask the number of packets with "iptables -L -v -n". You will see ONE packet in your rule where you allow ssh (in or out) and all the other packets in your conntrack-rules.

----------

## The_Great_Sephiroth

Now I see why you did the matching of a port and the state of "NEW". Makes sense. I need to modify my firewall...

----------

