diff --git a/roles/ufw_firewall/README.md b/roles/ufw_firewall/README.md index d708db2cc4..6a40380002 100644 --- a/roles/ufw_firewall/README.md +++ b/roles/ufw_firewall/README.md @@ -1,26 +1,98 @@ -# UFW Firewall Role +## Role Description -This Ansible role configures the Uncomplicated Firewall (UFW) on our Linux systems. It allows you to define allowed and denied networks and ports, making it easy to manage your firewall rules. Further descriptions of the networks will be on [IT-Handbook](https://github.com/pulibrary/pul-it-handbook) +The `ufw_firewall` Ansible role configures UFW (Uncomplicated Firewall) to allow or deny traffic to specified networks and ports based on dynamic, parameterized variables. It supports flexible definitions of allowed networks and ports, enabling easy customization for specific environments, such as campus networks or library systems. + +This role dynamically generates UFW rules, sets default policies, and ensures outgoing traffic is allowed while controlling incoming traffic as specified. + +*** + +## Features + +1. **Dynamic Rule Generation**: + + * Define firewall rules based on networks and ports using variables. + * Parametrize ports for different services (e.g., `ssh`, `web`) for easy reuse and customization. + +2. **Default Policies**: + + * Allows outgoing traffic by default. + * Drops all other incoming traffic unless explicitly permitted. + +3. **Template-Driven Configuration**: + + * Generates UFW rules dynamically using Jinja2 templates. + * Supports multiple network groups (e.g., `campus_and_vpn` and `libnet`). + +4. **Customizable Ports**: + + * Easily update port numbers for specific services like `SSH` or `HTTP` using variables. + +*** ## Requirements -- Ansible 2.9 or higher -- Supported Operating Systems: - - Rocky Linux (tested on 9) - - Ubuntu (tested on jammy) +* Ansible >= 2.9 +* Target system running a UFW-compatible operating system (e.g., Ubuntu) + +*** ## Role Variables -the examples below allow ssh, http, and redis to those CIDR subnets. For ssh make sure you use the [defaults/main.yml](defaults/main.yml) example or you will lose access to your VM +The role uses the following variables for customization: + +### 1. **Network Definitions** + +Define the networks [All defined here][../../group_vars/all/vars.yml] you want to allow traffic from, grouped logically by purpose: ```yaml -ufw_firewall_rules: - - protocol: tcp - source: "{{ ufw_campus_and_vpn }}" - port: 80 - action: ACCEPT - - protocol: tcp - source: "{{ ufw_libnet }}" - port: 22 - action: ACCEPT +ufw_campus_and_vpn: + - name: "Princeton Wired Private" + network: 10.249.64.0/18 + - name: "Princeton VPN Subnet 1" + network: 172.20.95.0/24 + +ufw_libnet: + - name: "PU Subnet - LibNet" + network: 128.112.200.0/21 ``` + +### 2. **Ports** + +Specify the ports for each service: + +```yaml +ufw_firewall_ports: + ssh: 22 + web: 5342 # Replace with the port for your web service +``` + + +## Usage + +### Example Playbook + +```yaml +--- + + - hosts: servers + roles: + - { role: roles/ufw_firewall } +``` + +*** + +## Verification + +To verify the configuration, you can: + +1. Check UFW status on endpoint: + + ```bash + sudo ufw status verbose + ``` + +2. Inspect iptables rules on endpoint: + + ```bash + sudo iptables -L -n -v + ``` diff --git a/roles/ufw_firewall/defaults/main.yml b/roles/ufw_firewall/defaults/main.yml index 4b083ef385..ff393e7632 100644 --- a/roles/ufw_firewall/defaults/main.yml +++ b/roles/ufw_firewall/defaults/main.yml @@ -4,6 +4,9 @@ ufw_firewall_rules: port: 22 protocol: tcp allowed_cidrs: "{{ ufw_campus_and_vpn }}" + +ufw_campus_and_vpn: [] +ufw_libnet: [] # example of http in your group_vars/project # - service: http # port: 80 diff --git a/roles/ufw_firewall/handlers/main.yml b/roles/ufw_firewall/handlers/main.yml index 7cd3381653..ff4f7d8fa1 100644 --- a/roles/ufw_firewall/handlers/main.yml +++ b/roles/ufw_firewall/handlers/main.yml @@ -2,3 +2,7 @@ # handlers file for roles/ufw_firewall - name: Reload UFW ansible.builtin.command: ufw reload + changed_when: false + +- name: Reload sysctl + ansible.builtin.command: sysctl -p diff --git a/roles/ufw_firewall/tasks/main.yml b/roles/ufw_firewall/tasks/main.yml index 59febde3c3..65c898cde9 100644 --- a/roles/ufw_firewall/tasks/main.yml +++ b/roles/ufw_firewall/tasks/main.yml @@ -1,13 +1,23 @@ --- # Main tasks for ufw_firewall role -- name: UFW | Install UFW +- name: UFW| Install UFW ansible.builtin.package: name: ufw state: present -- name: UFW | Enable UFW - ansible.builtin.command: ufw --force enable - changed_when: false +- name: UFW | Enable IP forwarding in sysctl + ansible.builtin.lineinfile: + path: /etc/sysctl.conf + regexp: '^net.ipv4.ip_forward=' + line: 'net.ipv4.ip_forward=1' + notify: Reload sysctl + +- name: UFW | Set default forward policy to ACCEPT + ansible.builtin.lineinfile: + path: /etc/default/ufw + regexp: '^DEFAULT_FORWARD_POLICY=' + line: 'DEFAULT_FORWARD_POLICY="ACCEPT"' + notify: Reload UFW - name: UFW | Deploy custom before.rules ansible.builtin.template: @@ -27,9 +37,14 @@ mode: "0644" notify: Reload UFW -- name: UFW | Ensure default forward policy - ansible.builtin.lineinfile: - path: /etc/default/ufw - regexp: '^DEFAULT_FORWARD_POLICY=' - line: 'DEFAULT_FORWARD_POLICY="ACCEPT"' - notify: Reload UFW +- name: UFW | Enable UFW + ansible.builtin.command: ufw --force enable + changed_when: false + +- name: UFW | Allow all outgoing traffic in UFW + ansible.builtin.command: ufw default allow outgoing + changed_when: false + +- name: UFW | Reload sysctl settings + ansible.builtin.command: sysctl -p + changed_when: false diff --git a/roles/ufw_firewall/templates/before.rules.j2 b/roles/ufw_firewall/templates/before.rules.j2 index 586f099230..58d10b7da0 100644 --- a/roles/ufw_firewall/templates/before.rules.j2 +++ b/roles/ufw_firewall/templates/before.rules.j2 @@ -3,6 +3,15 @@ :ufw-before-input - [0:0] :ufw-before-output - [0:0] :ufw-before-forward - [0:0] +:ufw-not-local - [0:0] + +# allow all on loopback +-A ufw-before-input -i lo -j ACCEPT +-A ufw-before-output -o lo -j ACCEPT +# quickly process packets for which we already have a connection +-A ufw-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT +-A ufw-before-output -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT +-A ufw-before-forward -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT ### RULES ### {% for item in ufw_firewall_rules %} diff --git a/roles/ufw_firewall/vars/main.yml b/roles/ufw_firewall/vars/main.yml index 99bf6f39ed..316375752f 100644 --- a/roles/ufw_firewall/vars/main.yml +++ b/roles/ufw_firewall/vars/main.yml @@ -1,8 +1,22 @@ --- # vars file for roles/ufw_firewall + ufw_firewall_rules: > {%- set rules = [] -%} {%- for network in ufw_campus_and_vpn -%} - {{ rules.append({'protocol': 'tcp', 'source': network, 'port': 22, 'action': 'ACCEPT'}) }} + {{ rules.append({'protocol': 'tcp', 'source': network, 'port': ufw_firewall_ports.ssh, 'action': 'ACCEPT'}) }} {%- endfor -%} {{ rules }} + +ufw_firewall_after_rules: [] + +ufw_firewall_ports: + ssh: 22 + +# example of opening ports for web template + # {%- for network in ufw_libnet -%} + # {{ rules.append({'protocol': 'tcp', 'source': network, 'port': ufw_firewall_ports.web, 'action': 'ACCEPT'}) }} + # {%- endfor -%} +# example of defining ports for web template +# ufw_firewall_ports: +# web: 80