From 290a86e906b514244e9ed39e1d6bef11f070cf9e Mon Sep 17 00:00:00 2001 From: Philipp Rintz Date: Tue, 10 Nov 2020 21:17:11 +0100 Subject: [PATCH] Support merged firewall rules for multiple groups per host. - Multiple groups for a single server will now lead to all firewall rules being merged instead of overwritten. --- defaults/main.yml | 11 +++++++++ tasks/main.yml | 23 ++++++++++++++++--- templates/etc/nftables.conf.j2 | 12 ++++++---- templates/etc/nftables.d/defines.nft.j2 | 11 +++++---- templates/etc/nftables.d/filter-input.nft.j2 | 12 ++++++---- templates/etc/nftables.d/filter-output.nft.j2 | 12 ++++++---- .../etc/nftables.d/nat-postrouting.nft.j2 | 12 ++++++---- .../etc/nftables.d/nat-prerouting.nft.j2 | 12 ++++++---- templates/etc/nftables.d/sets.nft.j2 | 12 ++++++---- 9 files changed, 86 insertions(+), 31 deletions(-) diff --git a/defaults/main.yml b/defaults/main.yml index 86b73ef..96c8a76 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -74,6 +74,17 @@ nft_global_default_rules: # in the Ansible inventory. nft_global_rules: {} # ]]] +# .. envvar:: merged_groups [[[ +# +# Enable or disable the ability to merge multiple firewall group variables +merged_groups: false + # ]]] +# .. envvar:: merged_groups_dir [[[ +# +# The directory to read the group firewall rules from. +# Relative to the playbook directory. +merged_groups_dir: vars/ + # ]]] # .. envvar:: nft_global_group_rules [[[ # # List of global rules (applied on all tables) to configure for hosts in diff --git a/tasks/main.yml b/tasks/main.yml index 544725a..82199fc 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -2,16 +2,33 @@ # .. vim: foldmarker=[[[,]]]:foldmethod=marker # # tasks file for nftables +- name: Import nftables-variables if merged_groups is set + when: merged_groups + set_fact: + "{{ groupname }}": "{{ lookup('file',merged_groups_dir ~ groupname) | from_yaml }}" + loop: "{{ group_names }}" + loop_control: + loop_var: groupname + +- name: Combine Rules when merged_groups is set + when: merged_groups + set_fact: + nft_combined_rules: "{{ nft_combined_rules | default({}) | combine ( hostvars[inventory_hostname][groupname], recursive=True ) }}" + loop: "{{ group_names }}" + loop_control: + loop_var: groupname - name: Load specific OS vars for nftables - include_vars: "{{ item }}" + include_vars: "{{ osname }}" with_first_found: - "{{ ansible_distribution|lower }}-{{ ansible_distribution_version }}.yml" - "{{ ansible_distribution|lower }}.yml" - "{{ ansible_os_family|lower }}.yml" + loop_control: + loop_var: osname # Manage packages [[[1 -- name: Ensure Nftables packages are in there desired state +- name: Ensure Nftables packages are in their desired state package: name: '{{ nft_pkg_list | list }}' state: '{{ nft_pkg_state }}' @@ -19,7 +36,7 @@ until: pkg_install_result is success when: nft_enabled|bool -- name: Ensure old Iptables packages are in there desired state +- name: Ensure old Iptables packages are in their desired state apt: name: '{{ nft_old_pkg_list | list }}' state: '{{ nft_old_pkg_state }}' diff --git a/templates/etc/nftables.conf.j2 b/templates/etc/nftables.conf.j2 index 6dde3e7..2cd184d 100755 --- a/templates/etc/nftables.conf.j2 +++ b/templates/etc/nftables.conf.j2 @@ -1,8 +1,12 @@ +#jinja2: lstrip_blocks: "True", trim_blocks: "True" #!/usr/sbin/nft -f # {{ ansible_managed }} {% set globalmerged = nft_global_default_rules.copy() %} {% set _ = globalmerged.update(nft_global_rules) %} {% set _ = globalmerged.update(nft_global_group_rules) %} +{% if merged_groups and hostvars[inventory_hostname]['nft_combined_rules'].nft_global_group_rules is defined%} + {% set _ = globalmerged.update(hostvars[inventory_hostname]['nft_combined_rules'].nft_global_group_rules) %} +{% endif %} {% set _ = globalmerged.update(nft_global_host_rules) %} # clean @@ -14,12 +18,12 @@ table inet filter { chain global { {% for group, rules in globalmerged|dictsort %} # {{ group }} -{% if not rules %} + {% if not rules %} # (none) -{% endif %} -{% for rule in rules %} + {% endif %} + {% for rule in rules %} {{ rule }} -{% endfor %} + {% endfor %} {% endfor %} } include "{{ nft_set_conf_path }}" diff --git a/templates/etc/nftables.d/defines.nft.j2 b/templates/etc/nftables.d/defines.nft.j2 index 94516d6..89d8709 100644 --- a/templates/etc/nftables.d/defines.nft.j2 +++ b/templates/etc/nftables.d/defines.nft.j2 @@ -1,16 +1,19 @@ +#jinja2: lstrip_blocks: "True", trim_blocks: "True" # {{ ansible_managed }} {% set definemerged = nft_define_default.copy() %} {% set _ = definemerged.update(nft_define) %} {% set _ = definemerged.update(nft_define_group) %} +{% if merged_groups and hostvars[inventory_hostname]['nft_combined_rules'].nft_define_group is defined%} + {% set _ = definemerged.update(hostvars[inventory_hostname]['nft_combined_rules'].nft_define_group) %} +{% endif %} {% set _ = definemerged.update(nft_define_host) %} - {% for definition in definemerged.values() %} -{% if definition.desc is defined %} + {% if definition.desc is defined %} # {{ definition.desc }} -{% else %} + {% else %} # {{ definition.name }} -{% endif %} + {% endif %} define {{ definition.name }} = {{ definition.value }} {% endfor %} diff --git a/templates/etc/nftables.d/filter-input.nft.j2 b/templates/etc/nftables.d/filter-input.nft.j2 index a7ff44a..6d0b4b3 100644 --- a/templates/etc/nftables.d/filter-input.nft.j2 +++ b/templates/etc/nftables.d/filter-input.nft.j2 @@ -1,17 +1,21 @@ +#jinja2: lstrip_blocks: "True", trim_blocks: "True" # {{ ansible_managed }} {% set inputmerged = nft_input_default_rules.copy() %} {% set _ = inputmerged.update(nft_input_rules) %} {% set _ = inputmerged.update(nft_input_group_rules) %} +{% if merged_groups and hostvars[inventory_hostname]['nft_combined_rules'].nft_input_group_rules is defined %} + {% set _ = inputmerged.update(hostvars[inventory_hostname]['nft_combined_rules'].nft_input_group_rules) %} +{% endif %} {% set _ = inputmerged.update(nft_input_host_rules) %} chain input { {% for group, rules in inputmerged|dictsort %} # {{ group }} -{% if not rules %} + {% if not rules %} # (none) -{% endif %} -{% for rule in rules %} + {% endif %} + {% for rule in rules %} {{ rule }} -{% endfor %} + {% endfor %} {% endfor %} } diff --git a/templates/etc/nftables.d/filter-output.nft.j2 b/templates/etc/nftables.d/filter-output.nft.j2 index 269ac05..a4a7619 100644 --- a/templates/etc/nftables.d/filter-output.nft.j2 +++ b/templates/etc/nftables.d/filter-output.nft.j2 @@ -1,17 +1,21 @@ +#jinja2: lstrip_blocks: "True", trim_blocks: "True" # {{ ansible_managed }} {% set outputmerged = nft_output_default_rules.copy() %} {% set _ = outputmerged.update(nft_output_rules) %} {% set _ = outputmerged.update(nft_output_group_rules) %} +{% if merged_groups and hostvars[inventory_hostname]['nft_combined_rules'].nft_output_group_rules is defined %} + {% set _ = outputmerged.update(hostvars[inventory_hostname]['nft_combined_rules'].nft_output_group_rules) %} +{% endif %} {% set _ = outputmerged.update(nft_output_host_rules) %} chain output { {% for group, rules in outputmerged|dictsort %} # {{ group }} -{% if not rules %} + {% if not rules %} # (none) -{% endif %} -{% for rule in rules %} + {% endif %} + {% for rule in rules %} {{ rule }} -{% endfor %} + {% endfor %} {% endfor %} } diff --git a/templates/etc/nftables.d/nat-postrouting.nft.j2 b/templates/etc/nftables.d/nat-postrouting.nft.j2 index d4d91c3..1555098 100644 --- a/templates/etc/nftables.d/nat-postrouting.nft.j2 +++ b/templates/etc/nftables.d/nat-postrouting.nft.j2 @@ -1,17 +1,21 @@ +#jinja2: lstrip_blocks: "True", trim_blocks: "True" # {{ ansible_managed }} {% set postroutingmerged = nft__nat_default_postrouting_rules.copy() %} {% set _ = postroutingmerged.update(nft__nat_postrouting_rules) %} {% set _ = postroutingmerged.update(nft__nat_group_postrouting_rules) %} +{% if merged_groups and hostvars[inventory_hostname]['nft_combined_rules'].nft__nat_group_postrouting_rules is defined %} + {% set _ = postroutingmerged.update(hostvars[inventory_hostname]['nft_combined_rules'].nft__nat_group_postrouting_rules) %} +{% endif %} {% set _ = postroutingmerged.update(nft__nat_host_postrouting_rules) %} chain postrouting { {% for group, rules in postroutingmerged|dictsort %} # {{ group }} -{% if not rules %} + {% if not rules %} # (none) -{% endif %} -{% for rule in rules %} + {% endif %} + {% for rule in rules %} {{ rule }} -{% endfor %} + {% endfor %} {% endfor %} } diff --git a/templates/etc/nftables.d/nat-prerouting.nft.j2 b/templates/etc/nftables.d/nat-prerouting.nft.j2 index c14d0c4..3bb217e 100644 --- a/templates/etc/nftables.d/nat-prerouting.nft.j2 +++ b/templates/etc/nftables.d/nat-prerouting.nft.j2 @@ -1,17 +1,21 @@ +#jinja2: lstrip_blocks: "True", trim_blocks: "True" # {{ ansible_managed }} {% set preroutingmerged = nft__nat_default_prerouting_rules.copy() %} {% set _ = preroutingmerged.update(nft__nat_prerouting_rules) %} {% set _ = preroutingmerged.update(nft__nat_group_prerouting_rules) %} +{% if merged_groups and hostvars[inventory_hostname]['nft_combined_rules'].nft__nat_group_prerouting_rules is defined %} + {% set _ = preroutingmerged.update(hostvars[inventory_hostname]['nft_combined_rules'].nft__nat_group_prerouting_rules) %} +{% endif %} {% set _ = preroutingmerged.update(nft__nat_host_prerouting_rules) %} chain prerouting { {% for group, rules in preroutingmerged|dictsort %} # {{ group }} -{% if not rules %} + {% if not rules %} # (none) -{% endif %} -{% for rule in rules %} + {% endif %} + {% for rule in rules %} {{ rule }} -{% endfor %} + {% endfor %} {% endfor %} } diff --git a/templates/etc/nftables.d/sets.nft.j2 b/templates/etc/nftables.d/sets.nft.j2 index b7eba99..004db3c 100644 --- a/templates/etc/nftables.d/sets.nft.j2 +++ b/templates/etc/nftables.d/sets.nft.j2 @@ -1,16 +1,20 @@ +#jinja2: lstrip_blocks: "True", trim_blocks: "True" # {{ ansible_managed }} {% set setmerged = nft_set_default.copy() %} {% set _ = setmerged.update(nft_set) %} {% set _ = setmerged.update(nft_set_group) %} +{% if merged_groups and hostvars[inventory_hostname]['nft_combined_rules'].nft_set_group is defined %} + {% set _ = setmerged.update(hostvars[inventory_hostname]['nft_combined_rules'].nft_set_group) %} +{% endif %} {% set _ = setmerged.update(nft_set_host) %} {% for set, rules in setmerged|dictsort %} -{% if rules %} + {% if rules %} set {{ set }} { -{% for rule in rules %} + {% for rule in rules %} {{ rule }} -{% endfor %} + {% endfor %} } -{% endif %} + {% endif %} {% endfor %}