Variables
+++++++++
-For the most part, each host will have its own unique variables. See the playbook comments for details.
+Defaults
+--------
+Override these in your ansible inventory ``host_vars`` file.
+
+``use_ufw: false`` specifies whether an Ubuntu host should use UFW_
+
+host_vars
+---------
+If required, define these in your ansible inventory ``host_vars`` file.
+
+``ufw_allowed_ports: []`` should be a list of ports you want UFW to allow traffic through. Port numbers must be double-quoted due to the way the task processes stdout of ``ufw status``. Example::
+
+ ufw_allowed_ports:
+ - "22"
+ - "80"
+ - "443"
+
+Common Tasks
+++++++++++++
+
+UFW
+---
+At the time of this writing, we only have one public-facing host that doesn't run Ubuntu -- the nameserver. Its firewall is managed in the ``nameserver`` role.
+
+Despite having network port ACLs defined for each host in our cloud provider's interface, enabling a firewall local to the system will allow us to block abusive IPs using fail2ban_.
+
+
+.. _UFW: https://wiki.ubuntu.com/UncomplicatedFirewall
+.. _fail2ban: http://www.fail2ban.org/wiki/index.php/Main_Page
---
+## Common tasks
+
+# Most of our public-facing hosts are running Ubuntu.
+# use_ufw defaults to false but is overridden in inventory host_vars
+- include: ufw.yml
+ when: use_ufw == true
+ tags:
+ - always
+
+## Individual host tasks
+
# local_action in the task after this causes 'ansible_host' to change to 'localhost'
# we set a temporary variable here to search for in the local_action task
- set_fact:
--- /dev/null
+---
+- name: Make sure iptables-persistent is not installed
+ apt:
+ name: iptables-persistent
+ state: absent
+
+- name: Install or update ufw
+ apt:
+ name: ufw
+ state: latest
+
+- name: Get current ufw status
+ shell: ufw status | grep 'Status' | cut -d ' ' -f2
+ register: ufw_status
+
+# policy: allow makes sure we can still ssh if ufw is inactive.
+# We revert this at the end of the playbook
+- name: Enable ufw if inactive
+ ufw:
+ state: enabled
+ policy: allow
+ when: ufw_status.stdout == "inactive"
+
+# Instead of deleting all rules and re-opening ports with each playbook run,
+# we'll compare a list of ports we specify should be open with a list of currently open ports.
+- name: Get list of currently allowed ports
+ shell: ufw status | grep 'ALLOW' | grep -v v6 | grep -o '[0-9]*'
+ register: ufw_current_allowed_raw
+ # Don't fail if we don't get any output
+ failed_when: false
+
+- name: Determine ports to disable
+ set_fact:
+ ufw_ports_to_disable: "{{ ufw_current_allowed_raw.stdout_lines | difference(ufw_allowed_ports) }}"
+
+- name: Determine ports to enable
+ set_fact:
+ ufw_ports_to_enable: "{{ ufw_allowed_ports | difference(ufw_current_allowed_raw.stdout_lines) }}"
+
+- name: Disable any open ports that aren't specified in ufw_allowed_ports
+ ufw:
+ rule: allow
+ port: "{{ item }}"
+ delete: yes
+ with_items: "{{ ufw_ports_to_disable }}"
+
+- name: Enable any ports we're missing
+ ufw:
+ rule: allow
+ port: "{{ item }}"
+ with_items: "{{ ufw_ports_to_enable }}"
+
+# ufw_allowed_ports are excluded from the default policy
+- name: Set default policy to deny
+ ufw:
+ policy: deny