Configuring Firewalld with Ansible

We’ll be using Ansible to change and maintain our firewall settings on a server.

The playbook will do the following.

  1. Set the default zone to drop (Drops all external traffic to server)
  2. Set a zone for internal access
  3. Allow access from RFC1918 addresses to internal zone (Any local IP address will be able to access the server)
  4. Enable the services and ports specified in the vars section
  5. Disable the services listed in firewall_disable_services variable

Modify the variables as needed for your server(s). You can also add or move the variables to the inventory or host_vars files.

If you need to create an inventory file, refer to the first part of this post

BE CAREFUL CHANGING FIREWALL SETTINGS!!! IMPROPER SETTINGS COULD RENDER THE SERVER INACCESSIBLE!!!

Playbook for firewalld

Change the variables under the vars section

---
- name: Configure firewalld
  hosts: rhel
  gather_facts: yes
  become: yes

  vars: 
    firewall_allowed_ips:
      - 10.0.0.0/8
      - 172.16.0.0/12
      - 192.168.0.0/16
    firewall_allowed_services:
      - ssh
      - https
      - snmp
    firewall_allowed_ports:
      - "2222/tcp"
    firewall_disable_services:
      - cockpit
      - dhcpv6-client
      - mdns
      - samba-client

  tasks: 
  - name: Set default zone to drop
    ansible.builtin.command: firewall-cmd --set-default-zone=drop
    register: default_zone_set
    changed_when:
      - '"ZONE_ALREADY_SET" not in default_zone_set.stderr'

  - name: Enable and allow access to internal zone from RFC1918 addresses
    ansible.posix.firewalld:
      source: "{{ item }}"
      zone: internal
      permanent: true
      immediate: true
      state: enabled
    with_items: "{{ firewall_allowed_ips }}"

  - name: Disable unused services for internal zone
    ansible.posix.firewalld:
      service: "{{ item }}"
      zone: internal
      permanent: true
      immediate: true
      state: disabled
    with_items: "{{ firewall_disable_services }}"


  - name: Set services for internal zone
    ansible.posix.firewalld:
      service: "{{ item }}"
      zone: internal
      permanent: true
      immediate: true
      state: enabled
    with_items: "{{ firewall_allowed_services }}"

  - name: Set custom ports for internal zone
    ansible.posix.firewalld:
      port: "{{ item }}"
      zone: internal
      permanent: true
      immediate: true
      state: enabled
    with_items: "{{ firewall_allowed_ports }}"

Helpful links

https://docs.ansible.com/ansible/latest/collections/ansible/posix/firewalld_module.html#parameter-source

https://stackoverflow.com/questions/51563643/how-to-change-firewalld-zone-using-ansible

https://www.middlewareinventory.com/blog/ansible-firewalld/

Hardening Mikrotik RouterOS

https://wiki.mikrotik.com/wiki/Manual:Securing_Your_Router

Things to harden

  • Delete default admin user
  • Disable unused services and whitelist IP’s
  • Secure SSH
  • DNS

Delete default admin user

Before deleting the default admin user, create your own user account.

/user/add name=MyUsername group=full password=mylongsecurepassword

Note: running /user/add will prompt you for the rest of the options.

Delete the default admin user with

/user remove admin

We want to delete the default admin user for two reasons. 1. There is no default password for this user. 2. It is a default username which means it will be targeted for brute force attacks.

Consider using the /users/groups for more granular control.

Disable unused services

In the following, we disabled all services except SSH and Winbox. We also limit access to those services only from private “RFC 1918” IP addresses. Customize as needed.

/ip service
set telnet disabled=yes
set ftp disabled=yes
set www disabled=yes
set www-ssl tls-version=only-1.2
set ssh address="set winbox address="192.168.0.0/16,172.16.0.0/12,10.0.0.0/8"
set api disabled=yes
set winbox address="set winbox address="192.168.0.0/16,172.16.0.0/12,10.0.0.0/8"
set api-ssl disabled=yes tls-version=only-1.2

for www-ssl and api-ssl, tls-version is not a required argument, but you may consider using it if you need the API or Webfig.

Secure SSH

/ip/ssh/set strong-crypto=yes allow-none-crypto=no always-allow-password-login=no host-key-size=4096

And regenerate the SSH host key. It will prompt for a [y/N], hit y to regenerate.

/ip/ssh/regenerate-host-key 

DNS

Unless your device is being used as a DNS resolver, it is best to disable the “Allow Remote Request”

ip dns/set allow-remote-requests=no

If you do need it enabled, then be sure to add some firewall rules to keep your router from being used in amplification attacks.

add action=drop chain=input dst-port=53 in-interface-list=WAN protocol=udp

You can configure interface lists in /interface/list or Interface -> Interface List in the gui

Or you can change to in-interface and specify the WAN interface directly. You could also set it to !LAN if you have a LAN interface list set up.

Dual Zones in Firewalld (Public/Private or External/Internal)

In Firewalld we can use multiple zones for different types of traffic. For instance, we can setup an “internal” zone with our local IP addresses that are trusted, and then setup the public facing interface to the “drop” or “block” zone to block everything not from our internal network.

  1. Setup trusted IP addresses in the “internal” zone
  2. Configure services/ports that should be allowed on our “internal” zone
  3. Set “drop” zone as the default for all other traffic
  4. Reload firewall

1. Setup trusted IP addresses in “internal” zone

Add all of our trusted IP addresses to the internal zone. The following example adds all of the private IP addresses “RFC 1918” to the internal zone. Change as needed.

firewall-cmd --zone=internal --add-source=192.168.0.0/16 --add-source=172.16.0.0/12 --add-source=10.0.0.0/8 --permanent

2. Configure services/ports that should be allowed on our “internal” zone

Next we need to specify which services or ports should be accessible in our trusted zone.

Here is an example to allow https, ssh, and cockpit services

firewall-cmd --zone=internal --add-service=https --add-service=ssh --add-service=cockpit --permanent 

Here is an example to allow port 8080 tcp

firewall-cmd --zone=internal --add-port=8080/tcp --permanent

3. Set “drop” zone as the default for all other traffic

The final configuration piece we need to do is set the default zone. Anything not specified in other zones will get processed by the default zone.

firewall-cmd --set-default-zone=drop

The drop zone drops everything.

4. Reload firewall

Reload the firewall with

firewall-cmd --reload


Verifying changes

Let’s verify the changes with the firewall-cmd –get-active-zones command

# firewall-cmd --get-active-zones
drop
  interfaces: en0
internal
  sources: 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8

You can also use

firewall-cmd --list-all-zones

to list all the zones. Active zones show (active) next to them.

You can verify that your changes worked by doing an internal and external nmap scan.

If you have issues with services still being accessible from the outside, try disabling Network Manager for that specific interface

You can edit the ifcfg-eth0 file and add

NM_CONTROLLED=no

Enable Logging for firewalld

Enabling logging on firewall rules can be beneficial for tracking why a certain rule is not behaving as you intended.

Enabling logging is relatively straight forward.

  • Enable Firewall Logging
  • Check Logs
  • Disable Firewall Logging (Optional)

Enable Firewall Logging

Quickest way to enable logging is to run

sudo firewall-cmd --set-log-denied=all

This changes the options in the /etc/firewalld/firewalld.conf config file. Options include all, unicast, broadcast, multicast, and off

Enable Log option for firewalld

The command also reloads the firewall so manually restarting the firewall is necessary.

Checking Logs

You can use dmesg to view the failed attempts or you can follow the messages log and filter to just show the rejects

sudo tail -f /var/log/messages | grep -i REJECT

You can now try to access the server or run a test to trigger a log event. In my case I tried initiating a SSH connection.

Oct  1 16:32:10 localhost kernel: FINAL_REJECT: IN=eno1 OUT= MAC=f8:ab:98:12:fe:11:a1:ec:a6:00:67:3e:97:00 SRC=192.168.1.1 DST=192.168.88.2 LEN=60 TOS=0x08 PREC=0x40 TTL=59 ID=43080 DF PROTO=TCP SPT=38192 DPT=22 WINDOW=52240 RES=0x00 SYN URGP=0

Interesting bits are bolded. Our destination port it 22 “ssh” and our source address is 192.168.1.1. If I want this IP to access the server, I’ll need to add the 192.168.1.1 IP range in the allowed IP ranges.

Disable Logging (Optional)

After you have finished troubleshooting your problem, you may want to turn the logging feature off so you don’t fill up the logs with failed entries.

You can turn it off with

sudo firewall-cmd --set-log-denied=off

We can verify that logging is off by running

sudo firewall-cmd --get-log-denied 

If the firewall logging option is off it will return “off”

The following site has some more information and alternative ways

https://www.cyberciti.biz/faq/enable-firewalld-logging-for-denied-packets-on-linux/

UFW Allow ICMP (ping) Traffic

https://askubuntu.com/questions/6995/how-to-enable-ufw-firewall-to-allow-icmp-response

Open up the UFW before.rules config file

vi /etc/ufw/before.rules 

And make sure you have these rules in it

# ok icmp codes for INPUT
-A ufw-before-input -p icmp --icmp-type destination-unreachable -j ACCEPT
-A ufw-before-input -p icmp --icmp-type time-exceeded -j ACCEPT
-A ufw-before-input -p icmp --icmp-type parameter-problem -j ACCEPT
-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT
# ok icmp code for FORWARD
-A ufw-before-forward -p icmp --icmp-type destination-unreachable -j ACCEPT
-A ufw-before-forward -p icmp --icmp-type time-exceeded -j ACCEPT
-A ufw-before-forward -p icmp --icmp-type parameter-problem -j ACCEPT
-A ufw-before-forward -p icmp --icmp-type echo-request -j ACCEPT

Configure UFW Firewall on Ubuntu

UFW Firewall Status

Below are some simple commands around working with UFW. UFW is included in Ubuntu. However it may need to be enable.

Show status

sudo ufw status

Disable UFW Service

sudo systemctl stop ufw && sudo systemctl disable ufw

Stop UFW Service

sudo systemctl stop ufw

Start UFW service

sudo systemctl stop ufw

Enable UFW

sudo ufw enable

Allow SSH

sudo ufw allow 22/tcp

Show status

sudo ufw status numbered

Example output

sudo ufw status numbered
Status: active
To            Action   From 
--            ------   ----
[1] 3478/udp  ALLOW IN  Anywhere
[2] 5514/udp  ALLOW IN  Anywhere
[3] 8080/tcp  ALLOW IN  Anywhere
[4] 8443/tcp  ALLOW IN  Anywhere
[5] 8880/tcp  ALLOW IN  Anywhere
[6] 8843/tcp  ALLOW IN  Anywhere
[7] 6789/tcp  ALLOW IN  Anywhere
[8] 27117/tcp ALLOW IN  Anywhere
[9] 22/tcp    ALLOW IN  Anywhere

Delete rule

You need to know the number of the rule you want to delete. Replace number with the number of the rule from the status command

sudo ufw delete number

Reset rules

sudo ufw reset

Allow access to port from specific IP address

Example command allows access to SSH (port 22) from the 172.16.0.0/12 ip range.

sudo ufw allow proto tcp from 172.16.0.0/12 to any port 22

One note: It appears that you need to run the rule with every IP range you want to allow.

Allow access to port from all private IP ranges (RFC 1918)

If we wanted to allow SSH (port 22) from all local IP addresses, we would need to run the following three commands.

sudo ufw allow proto tcp from 10.0.0.0/8 to any port 22
sudo ufw allow proto tcp from 172.16.0.0/12 to any port 22
sudo ufw allow proto tcp from 192.168.0.0/16 to any port 22

The following link has more information regarding UFW firewall and subnets.
https://www.cyberciti.biz/faq/ufw-allow-incoming-ssh-connections-from-a-specific-ip-address-subnet-on-ubuntu-debian/

WHM/cPanel Firewall

Had a weird issue where a certain address was unable to access the cpanel server, but it was intermittent with it working some times, but failing at other times.

Ended up being the firewall on the WHM server blocking that particular IP address due to failed log in attempts.

Check the status of LFD (Login Failure Daemon)

systemctl status lfd

How do I know which IP’s are being blocked?

Check the logs, dmesg or tail /var/log/messages

[1122639.674605] Firewall: UDP_IN Blocked IN=eth0 OUT= MAC=8e:23:f5:16:a6:b1:cc:51:54:6a:2e:ea:14:00 SRC=72.211.105.113 DST=192.168.1.12 LEN=64 TOS=0x00 PREC=0x00 TTL=246 ID=40014 PROTO=UDP SPT=9307 DPT=161 LEN=44
[1122646.728510] Firewall: TCP_IN Blocked IN=eth0 OUT= MAC=8e:23:f5:16:a6:b1:cc:51:54:6a:2e:ea:14:00 SRC=198.199.98.83 DST=192.168.1.12 LEN=40 TOS=0x00 PREC=0x00 TTL=244 ID=54321 PROTO=TCP SPT=57522 DPT=15672 WINDOW=65535 RES=0x00 SYN URGP=0

CSF keeps a file with addresses to deny in “/etc/csf/csf.deny” Also nice that if gives you a little bit of info on why it was blocked.

# grep -r "192.168.1.21" /etc/csf/csf.deny
192.168.1.21 # lfd: (pop3d) Failed POP3 login from 192.168.1.21 (US/United States/-): 10 in the last 3600 secs - Tue Jun 20 11:36:15 2020

You can also dump all of the rule in iptables with

iptables --list | egrep "192.168.1.21"

Change 192.168.1.21 with the IP you are looking for

Whitelist IP Addresses

Open up /etc/csf/csf.allow with a text editor and add the IP to the bottom of the file.

or add the IP address to the end of the file with the following command. Replace 192.168.1.21 with the IP address you want to whitelist.

echo "192.168.1.21" >> /etc/csf/csf.allow

You can also do all of this from the WHM web interface “Plugins -> ConfigServer Security & Firewall”

Further reading

https://documentation.cpanel.net/display/CKB/How+to+Configure+Your+Firewall+for+cPanel+Services

Mikrotik RouterOS – “drop all from WAN not DSTNATed”

The default DSTNATed firewall rule keeps traffic from the WAN accessing LAN side IP addresses.

More info here

Printing the rules on a router with the default config should show the following.

;;; defconf: drop all from WAN not DSTNATed
chain=forward action=drop connection-nat-state=!dstnat in-interface=ether1

If you are wanting to add the rule to a router, you can copy and past the following command. Replace in-interface=ether with your in interface.

/ip firewall add action=drop chain=forward comment=\ "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat in-interface=ether1

Check if Mikrotik is an Open DNS Resolver

https://www.openresolver.com

You can test if a router is acting as an open DNS resolver by running the following command from a Linux terminal. If you need to install dig, refer to here for Debian/Ubuntu and here for RPM/CentOS/Fedora Distros.

Replace 192.168.88.1 with the host you want to test against.

dig +short test.openresolver.com TXT @192.168.88.1

If you receive the following

"open-resolver-detected"

The router is acting as an open resolver.

If you get

;; connection timed out; no servers could be reached

Then you are unable to use that router to resolve DNS.

Example running the command against a Mikrotik router with Remote DNS turned on Then adding a firewall rule to block unwanted request.

bob@localhost:~$ dig +short test.openresolver.com TXT @192.168.88.1
"open-resolver-detected"
bob@localhost:~$ 
<<-- Put firewall rule on router -->>
bob@localhost:~$ dig +short test.openresolver.com TXT @192.168.88.1
;; connection timed out; no servers could be reached  
bob@localhost:~$ 

Extra notes

If you have firewall rules allowing your IP address to use the router for DNS, then the above command to test will show it as an Open Resolver. Ideally you would want a connection from the outside to test. Or you can use this link and test it from the website. https://www.openresolver.com

FreeBSD 7 Allow IP range to SSH to server – IPF

Edit IPF config

vi /etc/ipf.rules

Hit “i” to enter insert mode and add the following to allow SSH from the 192.168.0.0/24 ip range. Change range if needed.

pass    in     quick on bge0 proto tcp from 192.168.0.0/24 to any port = 22 flags S keep state

Save and exit the file by hitting “Esc” then typing “:wq” followed by enter.

And start IPF with new rules

ipf -Fa -f /etc/ipf.rules