AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

UFW Allow Port with Ansible: Complete Firewall Management Guide (Ubuntu/Debian)

By Luca Berton · Published 2024-01-01 · Category: installation

Manage UFW firewall rules with Ansible on Ubuntu and Debian. Allow ports, deny traffic, rate-limit SSH, configure web server and database firewalls, IPv6.

UFW Allow Port with Ansible: Complete Firewall Management Guide (Ubuntu/Debian)

How to open firewall ports in Debian-like systems with Ansible?

Ansible open firewall ports in Debian-like systems

Today we're talking about the Ansible module UFW. The full name is community.general.ufw, which means that is part of the collection supported by the Ansible community. This module requires Ansible 2.9+. It works in Debian-like systems so distributions like Debian, Ubuntu, and Mint with ufw firewall, the Uncomplicated Firewall. This module manages the firewall with UFW.

See also: Ansible on Debian 11 Bullseye: UFW Firewall Automation Complete Guide

Parameters

The parameter list is pretty wide but these are the most important options for our use case to open firewall ports. The first set of parameters controls UFW program and the second the single rules.

UFW program parameters

ParameterTypeOptionsDescription
default (policy)stringallow / deny / rejectChange default policy for incoming or outgoing traffic
loggingstringon / off / low / medium / high / fullToggle UFW logging (uses LOG_KERN syslog)
statestringenabled / disabled / reloaded / resetEnable, disable, reload, or reset firewall
directionstringin / incoming / out / outgoing / routedDirection for the default policy
The "state" parameter options:
  • enabled - reloads firewall and enables firewall on boot
  • disabled - unloads firewall and disables firewall on boot
  • reloaded - reloads firewall
  • reset - disables and resets firewall to installation defaults

Rule-specific parameters

ParameterTypeOptionsDescription
rulestringallow / deny / limit / rejectFirewall rule action
name (app)stringApplication nameUses profile in /etc/ufw/applications.d
port (to_port)stringPort number or rangeDestination port (e.g., 80 or 60000:61000)
protostringany / tcp / udp / ipv6 / esp / ah / gre / igmpProtocol
from_ipstringIP address or CIDRSource address (default: any)
to_ipstringIP address or CIDRDestination address (default: any)
interfacestringInterface nameNetwork interface for the rule
directionstringin / outDirection for interface rules
routebooleantrue / falseApply rule to routed/forwarded packets
commentstringTextAdd comment to the rule
insertintegerRule numberInsert rule at position instead of appending
insert_relative_tostringzero / first-ipv4 / last-ipv4 / first-ipv6 / last-ipv6Relative position for insert
deletebooleantrue / falseDelete matching rule
logbooleantrue / falseLog matched packets

Basic Playbook: Open Firewall Ports

---
- name: ufw module Playbook
  hosts: all
  become: true
  tasks:
    - name: nginx installed
      ansible.builtin.apt:
        name: "nginx"
        state: "present"
        update_cache: true

    - name: ufw enabled
      community.general.ufw:
        state: "enabled"
        policy: "deny"
        logging: "on"

    - name: ufw rules
      community.general.ufw:
        rule: "allow"
        port: "{{ item }}"
        proto: "tcp"
      loop:
        - "22"
        - "80"
        - "443"

Allow Port: Common Examples

Allow a Single Port

- name: Allow SSH
  community.general.ufw:
    rule: allow
    port: '22'
    proto: tcp

Allow Port from Specific IP

- name: Allow SSH from office network
  community.general.ufw:
    rule: allow
    port: '22'
    proto: tcp
    from_ip: '10.0.0.0/8'
    comment: 'SSH from office'

Allow Port Range

- name: Allow high ports for passive FTP
  community.general.ufw:
    rule: allow
    port: '60000:61000'
    proto: tcp
    comment: 'Passive FTP range'

Allow by Application Profile

- name: Allow Nginx Full profile
  community.general.ufw:
    rule: allow
    name: 'Nginx Full'
    comment: 'Web server HTTP and HTTPS'

To see available application profiles:

sudo ufw app list
# Available applications:
#   Nginx Full
#   Nginx HTTP
#   Nginx HTTPS
#   OpenSSH

Allow on Specific Interface

- name: Allow MySQL on internal interface only
  community.general.ufw:
    rule: allow
    port: '3306'
    proto: tcp
    interface: eth1
    direction: in
    comment: 'MySQL on private network'

See also: Ansible on Debian 12 Bookworm: UFW Firewall Automation Complete Guide

Deny and Reject Rules

- name: Deny incoming telnet
  community.general.ufw:
    rule: deny
    port: '23'
    proto: tcp

- name: Reject with ICMP unreachable
  community.general.ufw:
    rule: reject
    port: '25'
    proto: tcp
    comment: 'Reject SMTP - use relay instead'

The difference: deny silently drops packets (attacker gets no response). reject sends an ICMP unreachable back (faster failure for legitimate clients).

Rate Limiting with UFW

The limit rule allows only 6 connections per 30 seconds from a single IP. Essential for SSH brute-force protection:

- name: Rate limit SSH connections
  community.general.ufw:
    rule: limit
    port: '22'
    proto: tcp
    comment: 'Rate limit SSH - 6 conn/30s'

See also: Ansible on Debian 13 Trixie: UFW Firewall Automation Complete Guide

Delete Rules

- name: Remove old FTP rule
  community.general.ufw:
    rule: allow
    port: '21'
    proto: tcp
    delete: true

Complete Web Server Firewall Playbook

---
- name: Configure web server firewall
  hosts: webservers
  become: true

  vars:
    allowed_ssh_networks:
      - '10.0.0.0/8'
      - '172.16.0.0/12'
    web_ports:
      - { port: '80', comment: 'HTTP' }
      - { port: '443', comment: 'HTTPS' }
    monitoring_port: '9100'
    monitoring_server: '10.0.10.50'

  tasks:
    - name: Install UFW
      ansible.builtin.apt:
        name: ufw
        state: present
        update_cache: true

    - name: Set default deny incoming
      community.general.ufw:
        default: deny
        direction: incoming

    - name: Set default allow outgoing
      community.general.ufw:
        default: allow
        direction: outgoing

    - name: Allow SSH from trusted networks
      community.general.ufw:
        rule: limit
        port: '22'
        proto: tcp
        from_ip: "{{ item }}"
        comment: "SSH from {{ item }}"
      loop: "{{ allowed_ssh_networks }}"

    - name: Allow web traffic from anywhere
      community.general.ufw:
        rule: allow
        port: "{{ item.port }}"
        proto: tcp
        comment: "{{ item.comment }}"
      loop: "{{ web_ports }}"

    - name: Allow node-exporter from monitoring server
      community.general.ufw:
        rule: allow
        port: "{{ monitoring_port }}"
        proto: tcp
        from_ip: "{{ monitoring_server }}"
        comment: "Prometheus scrape from monitoring"

    - name: Enable UFW logging
      community.general.ufw:
        logging: 'low'

    - name: Enable UFW
      community.general.ufw:
        state: enabled

    - name: Verify UFW status
      ansible.builtin.command:
        cmd: ufw status verbose
      register: ufw_status
      changed_when: false

    - name: Show firewall rules
      ansible.builtin.debug:
        msg: "{{ ufw_status.stdout_lines }}"

Database Server Firewall

---
- name: Configure database firewall
  hosts: databases
  become: true

  vars:
    app_servers:
      - '10.0.1.10'
      - '10.0.1.11'
      - '10.0.1.12'
    db_port: '5432'

  tasks:
    - name: Default deny incoming
      community.general.ufw:
        default: deny
        direction: incoming

    - name: Allow PostgreSQL from app servers only
      community.general.ufw:
        rule: allow
        port: "{{ db_port }}"
        proto: tcp
        from_ip: "{{ item }}"
        comment: "PostgreSQL from {{ item }}"
      loop: "{{ app_servers }}"

    - name: Allow SSH from bastion
      community.general.ufw:
        rule: allow
        port: '22'
        proto: tcp
        from_ip: '10.0.0.5'
        comment: 'SSH from bastion host'

    - name: Deny all other PostgreSQL access
      community.general.ufw:
        rule: deny
        port: "{{ db_port }}"
        proto: tcp
        comment: 'Deny direct DB access'

    - name: Enable UFW
      community.general.ufw:
        state: enabled

Kubernetes Node Firewall

---
- name: Configure K8s node firewall
  hosts: k8s_nodes
  become: true

  tasks:
    - name: Default deny incoming
      community.general.ufw:
        default: deny
        direction: incoming

    - name: Allow Kubernetes API server
      community.general.ufw:
        rule: allow
        port: '6443'
        proto: tcp
        comment: 'K8s API server'

    - name: Allow kubelet API
      community.general.ufw:
        rule: allow
        port: '10250'
        proto: tcp
        comment: 'Kubelet API'

    - name: Allow NodePort range
      community.general.ufw:
        rule: allow
        port: '30000:32767'
        proto: tcp
        comment: 'K8s NodePort services'

    - name: Allow flannel VXLAN
      community.general.ufw:
        rule: allow
        port: '8472'
        proto: udp
        comment: 'Flannel VXLAN'

    - name: Allow etcd (control plane only)
      community.general.ufw:
        rule: allow
        port: '2379:2380'
        proto: tcp
        comment: 'etcd client and peer'
      when: "'control_plane' in group_names"

    - name: Enable UFW
      community.general.ufw:
        state: enabled

IPv6 Support

- name: Configure IPv6 firewall rules
  hosts: all
  become: true

  tasks:
    - name: Enable IPv6 in UFW config
      ansible.builtin.lineinfile:
        path: /etc/default/ufw
        regexp: '^IPV6='
        line: 'IPV6=yes'
      notify: reload ufw

    - name: Allow HTTP over IPv6
      community.general.ufw:
        rule: allow
        port: '80'
        proto: tcp
        from_ip: '::/0'
        comment: 'HTTP IPv6'

  handlers:
    - name: reload ufw
      community.general.ufw:
        state: reloaded

Firewall Reset

- name: Reset UFW to defaults
  community.general.ufw:
    state: reset

- name: Disable UFW completely
  community.general.ufw:
    state: disabled

Verification Commands

# Check UFW status
sudo ufw status
sudo ufw status verbose
sudo ufw status numbered

# Check specific app profiles
sudo ufw app list
sudo ufw app info 'Nginx Full'

# Check UFW logs
sudo tail -f /var/log/ufw.log

code with ❤️ in GitHub

FAQ

How do I allow a port with UFW in Ansible?

Use community.general.ufw with rule: allow and specify the port and proto parameters. For example: community.general.ufw: rule=allow port=80 proto=tcp. Add from_ip to restrict access to specific source networks.

What is the difference between deny and reject in UFW?

deny silently drops the packet - the sender gets no response and eventually times out. reject sends an ICMP unreachable message back. Use deny for external-facing rules (reveals less information to attackers) and reject for internal rules (faster failure for legitimate connections).

How do I rate-limit SSH with Ansible and UFW?

Use rule: limit instead of rule: allow. This automatically limits connections to 6 per 30 seconds from a single IP address: community.general.ufw: rule=limit port=22 proto=tcp. This is the simplest brute-force protection.

Can I use UFW with Docker?

Docker modifies iptables directly, bypassing UFW rules. Containers with published ports are accessible even if UFW blocks the port. Use DOCKER_OPTS="--iptables=false" or the ufw-docker utility to make Docker respect UFW rules.

How do I allow a port range with UFW in Ansible?

Specify the range with a colon: port: '60000:61000'. You must also specify proto: tcp or proto: udp - port ranges require an explicit protocol.

Conclusion

Now you know how to manage firewall rules on Debian-like systems with Ansible using UFW, the Uncomplicated Firewall. From basic port opening to complete web server, database, and Kubernetes firewall configurations - the community.general.ufw module handles it all with simple, declarative YAML.

Category: installation

Watch the video: UFW Allow Port with Ansible: Complete Firewall Management Guide (Ubuntu/Debian) — Video Tutorial

Browse all Ansible tutorials · AnsiblePilot Home