Simple web application firewall using .htaccess

Apache provides a simple web application firewall by a allowing for a “.htaccess” file with certain rules in it. This is a file you put in your document root and may restrict or allow access from certain specific IP addresses. NB: These commands may also be put directly in the virtual host configuration file in “/etc/apache2/sites-available/”.

Use Case #1: Test environment

Sometimes you may want to lock down a site and only grant access from a limited set of IP addresses. The following example (for Apache 2.2) only allows access from the IP address “127.0.0.1” and blocks any other request:

Order Allow,Deny
Deny from all
Allow from 127.0.0.1

In Apache 2.4 the syntax has slightly changed:

Require all denied
Require ip 127.0.0.1

You can find your IP address on: whatismyipaddress.com

Use Case #2: Application level firewall

If you run a production server and somebody is abusing your system with a lot of requests then you may want to block a specific IP address. The following example (for Apache 2.2) only blocks access from the IP address “172.28.255.2” and allows any other request:

Order deny,allow
Allow from all
Deny from 172.28.255.2

In Apache 2.4 the syntax has slightly changed:

Require all granted
Require not ip 172.28.255.2

If you want to block an entire range you may also specify CIDR notation:

Require all granted
Require not ip 10.0.0.0/8
Require not ip 172.16.0.0/12
Require not ip 192.168.0.0/16

NB: Not only IPv4, but also IPv6 addresses may be used.

Limit concurrent PHP requests using Memcache

When you run a website you may want to use nginx reverse proxy to cache some of your static assets and also to limit the amount of connections per client IP to each of your applications. Some good modules for nginx are:

Many people are not running a webfarm, but they still want to protect themselves against scrapers and hackers that may slow the website (or even make it unavailable). The following script allows you to protect your PHP application from too many concurrent connections per IP address. You need to have Memcache installed and you need to be running a PHP web application that uses a front controller.

Installing Memcache for PHP

Run the following command to install Memcache for PHP on a Debian based Linux machine (e.g. Ubuntu):

sudo apt-get install php5-memcache memcached

This is easy. You can flush your Memcache data by running:

telnet 0 11211
flush_all

You may have to restart apache for the Memcache extension to become active.

sudo service apache2 restart

Modifying your front controller

It is as simple as opening up your “index.php” or “app.php” (Symfony) and then pasting in the following code in the top of the file:

<?php
function firewall($concurrency,$spinLock,$interval,$cachePrefix,$reverseProxy)
{
  $start = microtime(true);
  if ($reverseProxy && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $ip = array_pop(explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']));
  }
  else {
    $ip = $_SERVER['REMOTE_ADDR'];
  }
  $memcache=new Memcache();
  $memcache->connect('127.0.0.1', 11211);
  $key=$cachePrefix.'_'.$ip;
  $memcache->add($key,0,false,$interval);
  register_shutdown_function(function() use ($memcache,$key){ $memcache->decrement($key); });
  while ($memcache->increment($key)>$concurrency) {
    $memcache->decrement($key);
    if (!$spinLock || microtime(true)-$start>$interval) {
      http_response_code(429);
      die('429: Too Many Requests');
    }
    usleep($spinLock*1000000);
  }
}
firewall(10,0.15,300,'fw_concurrency_',false);

Add these lines if you want to test the script in stand-alone mode:

session_start();
session_write_close();
usleep(3000000);

With the default setting you can protect a small WordPress blog as it limits your visitors to do 10 concurrent(!) requests per IP address. Note that this is a lot more than 10 visitors per IP address. A normal visitor does not do concurrent requests to PHP as your browser tends to send only one request at a time. Even multiple users may not do concurrent requests (if you are lucky). In case concurrent requests do happen they will be delayed for “x” times 150 ms until the concurrency level (from that specific IP) is below 10. Other IP addresses are not affected/slowed down.

If you use a reverse proxy you can configure this (to get the correct IP address from the “X-Forwarded-For” header). Also if you set “$spinLock” to “false” then you will serve “429: Too Many Requests” if there are too many concurrent requests instead of stalling the connection.

This functionality is included as the “Firewall” feature of the new MindaPHP framework and also as the firewall functionality in the LeaseWeb Memcache Bundle for Symfony. Let me know what you think about it using the comments below.

Using Powershell to update Windows Firewall rule IP’s

I like to be careful to expose any service to the public Internet. Especially when running a Windows server. On Windows 2012, the firewall can be managed by the “Windows Firewall with Advanced Security” application.

Windows Firewall defaults

The default settings in the Windows Firewall seem strange to me. By default, the server does not answer to ping requests, but has the WMI (remote management) service open to the world. Another questionable defaults are the RPC port that are open. I don’t want to go too deep into the details on how to properly setup the Windows Firewall. But as a rule of thumb I recommend to block all incoming requests, allow all outgoing and then only open up the ports you need for your applications to function. This policy blocks everything by default and forced you to make “allow” rules to open the Windows Firewall for specific applications.

Don’t lock yourself out (important!)

There is one port you always need to keep open: TCP 3389. If you close it you will lose remote (RDP, Terminal Service Client) access to the server. In my case, that would mean I have to go to the data center to connect a screen, keyboard, and mouse.

Updating a Windows firewall rule using PowerShell

Now for the problem and the solution. I am a bit careful with the Windows Firewall rules, since the server has a public IP address. Each application gets its own rule with a corresponding “scope”. The scope determines which IP addresses are allowed to connect. When you have several people that need to maintain the list of IP addresses that are allowed to connect you may want to make a system that administers the entries. This post proposes a system where this list is maintained in a file (with comments) that is updatable by multiple users on the system. Then the “Task Scheduler” will compare the date in the “Description” field of the Windows Firewall rule with the “Last Modified” date of the file. If the file is modified later than the reload, the Task Scheduler will update the Windows Firewall rule (and the date stored in the description field).

Task Scheduler configuration

The system works with four files that I all load from “firewall” directory on the “H:” partition (home directory):

  • firewall.bat: Batch file invoked by the Task Scheduler that calls the PowerShell script
  • firewall.ps1: PowerShell script that actually reconfigures the firewall rule scope
  • firewall.log: Log file that allows you to keep track of proper operation
  • firewall.allow: List of IP addresses and subnets that are allowed to connect

firewall_task_action
The Task Scheduler should be configured to run the “firewall.bat” file in the “Edit Action” screen.

firewall_task_trigger
You could trigger the task every five minutes by setting the following in the “Edit Trigger” screen.

H:\firewall\firewall.bat

powershell -executionpolicy remotesigned -File H:\firewall\firewall.ps1 >> H:\firewall\firewall.log

H:\firewall\firewall.allow

# This is the firewall configuration file. It is scanned every 5 minutes for updates.

# This file may contain comment lines using hash signs (like this one). Other lines
# must consist of the following: IP address or range in CIDR notation

# Localhost
127.0.0.1/8

H:\firewall\firewall.ps1

$name = "World Wide Web Services (HTTP Traffic-In)"
$rule = Get-NetFirewallRule -DisplayName $name
$ruleTime = [datetime]::ParseExact($rule.Description, 'yyyy-MM-dd HH:mm:ss',$null)
$file = 'H:\firewall\firewall.allow'
$fileTime = (Get-Item $file).LastWriteTime
$time = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
$tspan = New-TimeSpan $ruleTime $fileTime
$seconds = ($tspan).TotalSeconds
if ($seconds -lt 0) {
  echo "$time Firewall up-to-date, exitting"
  exit
}
echo "$time Loading new firewall rules"
$ips = New-Object Collections.Generic.List[String]
$lines = Get-Content $file
foreach ($line in $lines) {
  if ($line -match "^s*#") {
    continue
  } elseif ($line -match "^s*$") {
    continue
  } elseif ($line -match "^([0-9./]{7,18})s*(#.*)?$") {
    echo $matches[1]
    $ips.Add($line)
  } else {
    echo ("IGNORED: " + $line)
  }
}
# set new firewall rules
try {
  Set-NetFirewallRule -DisplayName $name -RemoteAddress $ips -Description $time
} catch {
  echo "ERROR: Firewall rules could not be loaded"
}

H:\firewall\firewall.log

2014-02-09 13:10:02 Firewall up-to-date, exitting
2014-02-09 13:15:02 Loading new firewall rules
127.0.0.1/8
2014-02-09 13:20:02 Firewall up-to-date, exitting
2014-02-09 13:25:02 Firewall up-to-date, exitting

Setup your own Linux router using iptables – Part 1

When using Linux on servers we all know that one basic tool to secure the setup is iptables. Iptables not only allows you to secure your setup, it will also allow you create a routing service in a very controlled and efficient way.router_hw_example

Every distribution has it’s how way to configure and deploy the desired set of rules, Ubuntu and it’s variants use ufw service, Redhat and it’s clones iptables service, OpenSuSE uses SuSEfirewall2 service, etc.

To be honest my preferred way of managing is the Redhat style. It’s cleaner, doesn’t have hidden features and it doesn’t try to make it easier for the user. Although some of those functions may seam good in the long run they may become limitations.

So, why not make your own custom iptables startup script and iptables rule set that will be compatible with most of Linux distributions? This blog post will focus in two distinct areas:

  1. Create an init script to enforce our iptables rule set
  2. Create a set of iptables rules that could be put in place on a Linux gateway

Assumptions:

  1. You have ip_forwarding enabled on your system (if not you may check this link)
  2. You have a mainstream Linux distro with iptables and it’s most used modules (like conntrack)
  3. You have root access to the Linux GW
  4. I’m using OpenSuSE 13.1 x86_64, nevertheless this guide should work on other distributions only with minor changes on configuration variables

Lets start by defining the scope of the rule set, the example scenario will be (the IPs were randomly selected) one Linux box serving as gateway for three networks):

  • eth1 172.17.31.0/29 – lets call it orange and use it as DMZ (we will have the server providing external services in this network)
  • eth2 192.168.9.0/24 – lets call it green and use it as users networks, having some servers on the subnet 192.168.9.0/9, we also want to give VPN users to this subnet
  • tun0 10.238.23.1/28 – lest call it blue and use it for VPN access

In the examples we use different network cards but it can also be done with VLANs and/or with a plain network interface having all the logic based on src/dst IPs, ports and protocols.

On the Internet access interface (eth0, we may call it red) we will have 6 usable IP addresses 21.32.181.1-6. These IPs will be exposed to the Internet.

On those six available IP addresses we will want different services also exposed to the Internet, the network address translation table will be:

  • 21.32.181.1 port 53 -> 172.17.31.1 port 53 (udp)
  • 21.32.181.1 port 53 -> 172.17.31.1 port 53 (tcp)
  • 21.32.181.2 port 80 -> 172.17.31.2 port 80 (tcp)
  • 21.32.181.3 port 443 -> 172.17.31.3 port 443 (tcp)

Apart from the NAT table we will also make sure that:

  • Access to other destination IP/port/protocol combination will be denied
  • The default gateway to access the wan will be 21.32.181.6

In this example we won’t talk about having more than one gateway, although it’s easily achievable.

Finally we won’t allow internal IP traffic to access the Internet freely (we all know how dangerous it is :)), so  we will limit the access to the following ports/protocols:

  • TCP: 25,80,110,144,443,465,993
  • UDP: 53,1194
  • ICMP: type 8

Please note that this doesn’t limit the access from our Linux GW to the Internet, it will be applied on the forward chain so the effect will only be visible on the clients using our Linux GW as their GW. The Linux GW will have complete access to all the networks, including unfiltered access to the Internet.

Bellow is a diagram about how the network should like:


        ------------         ---------------------------------------
        |          |         | Linux GW:                           |
        | Internet |---------| Ext net (eth0): 21.32.181.1/29  *   |
        |          |         | Int net (eth1): 172.17.31.14/28     |
        ------------         | Int net (eth2): 192.168.9.254/24    |
                             | VPN net (tun0): 10.238.23.14/28     |
                             ---------------------------------------
                              /               |                \
           output            /          input |    output       \  output
          (Internet/        /      everywhere |   (to Internet)  \(to orange / Internet)
            orange)        /      to services |                   \
                ----------------         ------------        ----------------
                   green                   orange                       blue
          192.168.9.0/24                172.17.31.0/28                  10.238.23.0/28
                                        172.17.31.1/28 - udp 53
                                        172.17.31.2/28 - tcp 80
                                        172.17.31.3/28 - tcp 443

* the external network includes all the IPs between 21.32.181.1 - 21.32.181.6

Lets get back to the first target on this blog post: Create an init script.

Please create a file named pmso-fw (in reality you can name it whatever you like) in /etc/init.d

#!/bin/bash
# PMSO fw initializer
#
# pmso-fw:   Pedro Oliveira FW builder
#
# chkconfig: 345 03 03
# description:  This is a daemon for managing firewall scripts \
#               config file should be located in \
#               /etc/sysconfig/iptables
#
# notes:        please set rc.status according to your OS / Distro
# processname: pmso-fw
# pidfile: /var/run/pmso-fw.pid
#

. /etc/rc.status
rc_reset

SERVICE="pmso-fw"
IPT4="/usr/sbin/iptables"
PROCESS_APPLY="/usr/sbin/iptables-apply"
CONFIG_FILE="/etc/sysconfig/iptables"
LIST_RULES="/usr/sbin/iptables-save"
PIDFILE="/var/run/pmso-fw.pid"

test -f $CONFIG_FILE

start() {
  stop
  sleep 3650d &
  echo $! > $PIDFILE
  echo -n $"Starting $SERVICE "
  `$PROCESS_RESTORE < $CONFIG_FILE`
  RETVAL=$?
  rc_status -v
}

stop() {
  echo -n $"Stopping $SERVICE "
  $IPT4 -F && \
  $IPT4 -X && \
  $IPT4 -t nat -F && \
  $IPT4 -t nat -X && \
  $IPT4 -t mangle -F && \
  $IPT4 -t mangle -X && \
  $IPT4 -P INPUT ACCEPT && \
  $IPT4 -P FORWARD ACCEPT && \
  $IPT4 -P OUTPUT ACCEPT
  RETVAL=$?
  rc_status -v
}

restart() {
  stop
  start
}

stat() {
  $LIST_RULES
  RETVAL=$?
  rc_status -v
}

# See how we were called.
case "$1" in
  start|stop|restart)
    $1
  ;;
  status)
    echo -n "Checking status of $SERVICE "
    rc_status -v
  ;;
  *)
    echo $"Usage: $0 {start|stop|status|restart}"
    rc_failed 2
    rc_exit
  ;;
esac
rc_exit

Lets describe what this script does:

  1. The first section look a regular comment section, nevertheless it’s in this zone that are configured the systemd options. Be careful if editing
  2. /etc/rc.status – this line means that the /etc/rc.status file will be imported, this file contains the systemd control functions
  3. General script configs and main utilities location section
  4. Script functionality functions:
  5. start() – The start function starts with stop, this may seam odd but if we are starting the FW service we want to guarantee that no rules will be duplicated. I also issue a sleep command, this is due to the fact that iptables isn’t a process and we need to keep the process open so we have a clean process status
  6. stop() – The stop function cleans all the tables
  7. restart() – No explanation needed
  8. stat() – Stat will use the systemd functions to return the status of the fw rules

We also need to make the script executable:

chmod u+x /etc/init.d/pmso-fw

Now you should create the systemd config file IF you use systemd. The file should be placed in: /etc/systemctl/system/pmso-fw.service and it will have the following content:


[Unit]
Description=PMSO-FW
After=network.target
Wants=network.service

[Service]
ExecStart=/etc/init.d/pmso-fw start
ExecStop=/etc/init.d/pmso-fw stop
RemainAfterExit=true
Type=oneshot

[Install]
WantedBy=multi-user.target

Finally lets setup the rule set.
The format of the this rule set is compatible with iptables-save and iptables-restore

The location of the file will be: /etc/sysconfig/iptables

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:LOG-INPUT - [0:0]
:LOG-FORWARD - [0:0]
:LOG-BLUE-TO-GREEN - [0:0]
:LOG-GREEN-TO-BLUE - [0:0]
:RED-TO-ORANGE - [0:0]
:BLUE-TO-ORANGE - [0:0]
:BLUE-TO-GREEN - [0:0]
:GREEN-TO-ORANGE - [0:0]
:GREEN-TO-BLUE - [0:0]
:TO-RED - [0:0]
:TO-GREEN - [0:0]
:TO-ORANGE - [0:0]
:T0-BLUE - [0:0]
:FROM-RED - [0:0]
:FROM-GREEN - [0:0]
:FROM-ORANGE - [0:0]
:FROM-BLUE - [0:0]
###

-A LOG-INPUT -j LOG --log-prefix "IPTables-reject-INPUT: "
-A LOG-FORWARD -j LOG --log-prefix "IPTables-reject-FORWARD: "
-A LOG-BLUE-TO-GREEN -j LOG --log-prefix "IPTables-reject-BLUE-TO-GREEN: "
-A LOG-GREEN-TO-BLUE -j LOG --log-prefix "IPTables-reject-GREEN-TO-BLUE: "
###

-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -m limit --limit 2/sec -j ACCEPT
-A INPUT -d 172.17.31.14 -p tcp -m tcp --dport 22 -j ACCEPT -m comment --comment "Access to the SSH interface only on the DMZ interface"
-A INPUT -m limit --limit 2/sec -j LOG-INPUT
-A INPUT -m limit --limit 2/sec -j LOG-INPUT
-A INPUT -j REJECT --reject-with icmp-port-unreachable
###

-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i eth1 -o eth0 -s 172.17.31.0/28 -j TO-RED
-A FORWARD -i eth2 -o eth0 -s 192.168.9.0/24 -j TO-RED
-A FORWARD -i tun0 -o eth0 -s 10.238.23.0/28 -j TO-RED

-A FORWARD -o eth1 -d 172.17.31.0/28 -j TO-ORANGE
-A FORWARD -o eth2 -d 192.168.9.0/24 -j TO-GREEN
#-A FORWARD -o tun0 -d 10.238.23.0/28 -j TO-BLUE

-A FORWARD -i eth1 -s 172.17.31.0/28 -j FROM-ORANGE
-A FORWARD -i eth2 -s 192.168.9.0/24 -j FROM-GREEN
-A FORWARD -i tun0 -s 10.238.23.0/28 -j FROM-BLUE

-A FORWARD -i tun0 -s 10.238.23.0/28 -o eth2 -d 192.168.9.0/24 -j BLUE-TO-GREEN
-A FORWARD -i tun0 -s 10.238.23.0/28 -o eth1 -d 172.17.31.0/28 -j BLUE-TO-ORANGE
-A FORWARD -i eth2 -s 192.168.9.0/24 -o eth1 -d 172.17.31.0/28 -j GREEN-TO-ORANGE

-A FORWARD -i eth1 -o eth1 -j ACCEPT
-A FORWARD -i eth2 -o eth2 -j ACCEPT
-A FORWARD -i tun0 -o tun0 -j ACCEPT

-A FORWARD -m limit --limit 2/sec -j LOG-FORWARD
-A FORWARD -j REJECT --reject-with icmp-port-unreachable
###

-A BLUE-TO-GREEN -d 192.168.9.0/29 -j ACCEPT
-A BLUE-TO-GREEN -m limit --limit 2/sec -j LOG-BLUE-TO-GREEN
-A BLUE-TO-GREEN -j REJECT --reject-with icmp-port-unreachable

-A BLUE-TO-ORANGE -j ACCEPT

-A GREEN-TO-ORANGE -j ACCEPT

-A GREEN-TO-BLUE -m limit --limit 2/sec -j LOG-GREEN-TO-BLUE
-A GREEN-TO-BLUE -j REJECT --reject-with icmp-port-unreachable

-A TO-RED -p icmp -m icmp --icmp-type 8  -j ACCEPT
-A TO-RED -p tcp -m multiport --dports 25,80,110,144,443,465,993 -j ACCEPT
-A TO-RED -p udp -m multiport --dports 53,1194 -j ACCEPT

COMMIT
###

*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

-A PREROUTING  -d 21.32.181.1 -p udp --dport 53 -j DNAT --to-destination 172.17.31.1:53
-A PREROUTING  -d 21.32.181.1 -p tcp --dport 53 -j DNAT --to-destination 172.17.31.1:53

-A PREROUTING  -d 21.32.181.2 -p udp --dport 53 -j DNAT --to-destination 172.17.31.1:53
-A PREROUTING  -d 21.32.181.2 -p tcp --dport 53 -j DNAT --to-destination 172.17.31.1:53

-A PREROUTING  -d 21.32.181.3 -p tcp --dport 80 -j DNAT --to-destination 172.17.31.2:80
-A PREROUTING  -d 21.32.181.3 -p tcp --dport 443 -j DNAT --to-destination 172.17.31.3:443

-A POSTROUTING -o eth0 -j MASQUERADE

COMMIT
###

*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT

Now that we have the files ready we need to setup our newly created installation script, this can be done with the following commands if you use systemv:

insserv /etc/init.d/pmso-fw (if you use systemv)
/etc/init.d/pmso-fw status

or if you use systemd

systemctl daemon-reload
systemctl enable pmso-fw # to make it permanent
systemctl start pmso-fw # to start it

you’ll be able to check the status of the fw with:

systemctl status pmso-fw

if you want to see the rules in use at any given moment you may use:

iptables-save

or

iptables -n -L --line-numbers
iptables -n -L -t NAT --line-numbers

After editing the rules you can also reload the service with:

systemctl restart pmso-fw

As you might have noticed we log the dropped packets, not all because if we have an exposed router the log will be immense but we limit the writes to two entries per second. This will allow us to debug something that isn’t right on our configuration but won’t fill our log file system or root partition (depending on how you partitioned your system).

In OpenSuSE 13.1 you may check your logs in “/var/log/firewall”.

On the next post we will get into more detail on the rules, detailing what’s done on the main blocks of the iptables rule set. I hope you have as much fun adapting this how to to your needs as I had making it.

Regards,
Pedro M. S. Oliveira

Simple firewall in Ubuntu with UFW

The default firewall configuration tool for Ubuntu is UFW. Developed to ease iptables firewall configuration, UFW provides a user friendly way to create an IPv4 or IPv6 host-based firewall. — Ubuntu community documentation

By default UFW is disabled. Although it might not be strictly needed to run a firewall in all cases, it is good advice for most users. UFW can be configured to allow all outgoing traffic and deny all incoming traffic. This is the “normal” operation mode for desktop PC’s. In general, it is advisable to run a firewall, so that if you are mis-configuring and opening up a port, your firewall will protect you. This is especially relevant when your machine is a laptop that you use on other people’s WiFi networks or when your network supports IPv6. If you run a web server and you want to open up ports to allow incoming traffic you can configure UFW using either a GUI (graphical user interface) or the CLI (command line interface).

Using the GUI to configure UFW

The Gufw GUI for UFW can be installed by executing the following simple command:

sudo apt-get install gufw

In the GUI you can go to “Edit” and “Preferences” to turn off or adjust the logging levels. The preferences pane also allows you to toggle the listening applications list. This is a nice overview, but not as powerful as the output of the commands “netstat -plant” and “ps aux”.

Gufw_1
Figure 1: A screenshot from the Gufw GUI for “ufw”

Configure UFW using the CLI

Normally, I advise desktop users to use a GUI for configuring the software, but the “ufw” CLI is so easy-to-use (or uncomplicated) that you might prefer it. The commands you typically have to type at the prompt are:

sudo ufw enable
sudo ufw logging off
sudo ufw status verbose

First we execute the “ufw enable” command to enable the firewall. Second we issue the “ufw logging off” command to prevent log lines in “/var/log/syslog” when connections are denied. Last we run the status command to check whether the firewall is running with the right configuration. Note that if you want to start all over again and wish to throw the configuration away you can run the “ufw reset” command. By default the enabled UFW will deny incoming and allow outgoing traffic.

ufw2
Figure 2: A sample of ufw log lines that show up in /var/log/syslog

Check firewall status

If you want to make sure the effective firewall rules are correct you can run the following command:

maurits@nuc:~$ sudo ufw status verbose
Status: active
Logging: off
Default: deny (incoming), allow (outgoing)
New profiles: skip
maurits@nuc:~$

Allow some (incoming) traffic

If you are running Apache (or Nginx) to serve HTTP (port 80) traffic from your box to your network or even the Internet, then allow it like this:

maurits@nuc:~$ sudo ufw allow 80
Rule added
Rule added (v6)
maurits@nuc:~$

Remove a rule

If you want to delete a rule, just prefix the rule with the word “delete” like this:

maurits@nuc:~$ sudo ufw delete allow 80
Rule deleted
Rule deleted (v6)
maurits@nuc:~$

Remove a rule by number

You can also identify and delete a rule using a (sequence) number. First use the “numbered” suffix on the “status” command to list the rules with their numbers, like this:

maurits@nuc:~$ sudo ufw status numbered
Status: active

To                         Action      From
--                         ------      ----
[ 1] 80                         ALLOW IN    Anywhere
[ 2] 80                         ALLOW IN    Anywhere (v6)

maurits@nuc:~$

Then execute the command for deletion, like this:

maurits@nuc:~$ sudo ufw delete 2
Deleting:
 allow 80
Proceed with operation (y|n)? y
Rule deleted (v6)
maurits@nuc:~$

Note that every time you delete a rule, all other sequence numbers might change.