Skip to content

UFW and firewalld: Allow a Port Only From One IP

Updated 5 min read

Updated: 18 October 2026 · Applies to: UFW 0.36.2 on Ubuntu 24.04 and firewalld 2.4.3 on AlmaLinux 10.2, tested; the same commands work on Debian, Rocky Linux and RHEL

To open a port for one IP address only, allow that address and that port together, and make sure the port is not also open to everyone. With UFW (Ubuntu, Debian):

sudo ufw allow from 203.0.113.10 to any port 3306 proto tcp

With firewalld (AlmaLinux, Rocky Linux, RHEL), use a rich rule:

sudo firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="203.0.113.10" port port="3306" protocol="tcp" accept'
sudo firewall-cmd --reload

Replace 203.0.113.10 with the address that should get in (your office, another server) and 3306 with the port. Below are the variations, how to check and remove the rule, and the mistakes that leave a port open or lock you out.

1. UFW: allow a port from one IP

  1. Add the rule. The word order matters: from the address, to any port the port, then the protocol:
    sudo ufw allow from 203.0.113.10 to any port 3306 proto tcp
    UFW answers Rule added. (While UFW is still inactive it says Rules updated instead.)
  2. Check the result:
    sudo ufw status numbered
    Lab output on Ubuntu 24.04 after the examples below:
         To                         Action      From
         --                         ------      ----
    [ 1] 3306/tcp                   ALLOW IN    203.0.113.10
    [ 2] 22/tcp                     ALLOW IN    203.0.113.0/24
    [ 3] 80,443/tcp                 ALLOW IN    203.0.113.10
    [ 4] 3306/tcp                   ALLOW IN    2001:db8::10
  3. Make sure the port is not open to everyone by an older rule such as 3306/tcp ALLOW IN Anywhere. If it is, delete that rule (section 4).

Variations, all tested with UFW 0.36.2:

  • A whole network: sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp.
  • Several ports: sudo ufw allow proto tcp from 203.0.113.10 to any port 80,443. Without proto UFW refuses: ERROR: Must specify 'tcp' or 'udp' with multiple ports.
  • An IPv6 address works the same way and answers Rule added (v6).

Two mistakes to avoid:

  • sudo ufw allow from 203.0.113.20 port 3306 (without to any) is accepted, but port then means the source port of the visitor. The status shows Anywhere ALLOW IN 203.0.113.20 3306: all your ports, for traffic coming from port 3306. Delete such a rule.
  • UFW checks rules from the top and the first match wins. With 3306/tcp DENY IN Anywhere above your allow rule, our allowed test client still timed out. Put the allow rule first with sudo ufw insert 1 allow from 203.0.113.10 to any port 3306 proto tcp; after that the client got through.

UFW's default policy drops other traffic silently, so a blocked client waits until it times out instead of getting an error.

2. firewalld: rich rule for one IP and one port

  1. Add the rule permanently and load it:
    sudo firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="203.0.113.10" port port="3306" protocol="tcp" accept'
    sudo firewall-cmd --reload
    Both commands print success.
  2. Check it:
    sudo firewall-cmd --zone=public --list-rich-rules
    rule family="ipv4" source address="203.0.113.10" port port="3306" protocol="tcp" accept
    firewalld turned it into this nftables rule in our lab: ip saddr 203.0.113.10 tcp dport 3306 accept.
  3. Check that the port is not open to everyone as well: sudo firewall-cmd --zone=public --list-ports and --list-services must not show it. When we added 3306/tcp to the zone as a plain port, an address without a rich rule could connect again. Remove such an entry with sudo firewall-cmd --permanent --zone=public --remove-port=3306/tcp (or --remove-service= with the service name shown) and reload.

Variations, all tested on firewalld 2.4.3:

  • A whole network: source address="203.0.113.0/24".
  • A service instead of a port number: rule family="ipv4" source address="203.0.113.0/24" service name="ssh" accept.
  • An IPv6 address needs family="ipv6": rule family="ipv6" source address="2001:db8::10" port port="3306" protocol="tcp" accept.
  • Leaving out family fails with Error: MISSING_FAMILY, and leaving out protocol="tcp" fails with Error: INVALID_PROTOCOL.

In our lab, a client whose address was not in the rule got No route to host (firewalld rejects the packet), and the allowed client got Connection refused because no service was listening yet: the firewall let it through.

3. firewalld: a zone for trusted sources

For several addresses and ports, put the addresses in their own zone instead of writing many rich rules:

sudo firewall-cmd --permanent --new-zone=dbaccess
sudo firewall-cmd --reload
sudo firewall-cmd --permanent --zone=dbaccess --add-source=203.0.113.10/32
sudo firewall-cmd --permanent --zone=dbaccess --add-port=3306/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --zone=dbaccess --list-all

Add more addresses or networks with further --add-source lines. firewall-cmd --get-zone-of-source=203.0.113.10/32 then prints dbaccess.

Watch out: traffic from a source in a zone is handled by that zone only. In our test, the client in dbaccess could reach port 3306 but now got No route to host on SSH (port 22), which the public zone still allowed for everyone else. If you put your own admin address into such a zone, add SSH to it first (--zone=dbaccess --add-service=ssh) or you lock yourself out. The built-in trusted zone does the opposite: --zone=trusted --add-source=203.0.113.10 allows everything from that address.

4. Remove the access again

With UFW, repeat the rule after delete, or delete it by its number from ufw status numbered:

sudo ufw delete allow from 203.0.113.10 to any port 3306 proto tcp
sudo ufw delete 1

Both printed Rule deleted. A rule for Anywhere also has a (v6) twin: deleting by number removes only one line, deleting by the rule text removes both. With firewalld:

sudo firewall-cmd --permanent --zone=public --remove-rich-rule='rule family="ipv4" source address="203.0.113.10" port port="3306" protocol="tcp" accept'
sudo firewall-cmd --permanent --zone=dbaccess --remove-source=203.0.113.10/32
sudo firewall-cmd --permanent --delete-zone=dbaccess
sudo firewall-cmd --reload

5. Before you test

Official documentation: firewalld rich language and ufw manual (Ubuntu 24.04).

Related services

KVM VPS hosting

NVMe SSD and full root access, with Free Basic Managed Support.

Unmanaged dedicated servers

full root access and free IPMI KVM, with Free Basic Managed Support.

Linux server management

hardening, patching, monitoring and automation for Ubuntu, Debian, RHEL and AlmaLinux.

Was this answer helpful?

Still stuck? Open a ticket