diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index b90a6ea..0aace81 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -26,6 +26,10 @@ jobs: # run: ansible-galaxy install -r requirements.yml # shell: micromamba-shell {0} + - name: "Install community.general collection" + run: ansible-galaxy collection install community.general + shell: micromamba-shell {0} + - name: Check workflow files themselves with ActionLint run: actionlint shell: micromamba-shell {0} diff --git a/.github/workflows/molecule.yml b/.github/workflows/molecule.yml index 2fde508..97885ee 100644 --- a/.github/workflows/molecule.yml +++ b/.github/workflows/molecule.yml @@ -78,6 +78,10 @@ jobs: run: ansible-galaxy install -r requirements.yml shell: micromamba-shell {0} + - name: "Install community.general collection" + run: ansible-galaxy collection install community.general + shell: micromamba-shell {0} + - name: "Run Molecule tests" if: ${{ matrix.role != '__no_role__' }} working-directory: ${{ matrix.role }} diff --git a/galaxy.yml b/galaxy.yml index beac011..1c0944f 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.3.1 +version: 0.4.0 readme: README.md authors: - Alexander Gorelyshev (corvus-migratorius@proton.me) diff --git a/roles/ufw/README.md b/roles/ufw/README.md new file mode 100644 index 0000000..7207f41 --- /dev/null +++ b/roles/ufw/README.md @@ -0,0 +1,37 @@ +template +========= + +Whitelist network ports with UFW + +Requirements +------------ + +None + +Role Variables +-------------- + +None + +Dependencies +------------ + +None + +Example Playbook +---------------- + +```yaml +roles: + - role: genlab.ufw +``` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/ufw/defaults/main.yml b/roles/ufw/defaults/main.yml new file mode 100644 index 0000000..702f638 --- /dev/null +++ b/roles/ufw/defaults/main.yml @@ -0,0 +1,4 @@ +--- +ufw_rules: [] +ufw_limit_ssh: false +ufw_openssh_port: 22 diff --git a/roles/ufw/handlers/main.yml b/roles/ufw/handlers/main.yml new file mode 100644 index 0000000..802d7ec --- /dev/null +++ b/roles/ufw/handlers/main.yml @@ -0,0 +1,4 @@ +--- +- name: "Reload-ufw" + community.general.ufw: + state: reloaded diff --git a/roles/ufw/meta/main.yml b/roles/ufw/meta/main.yml new file mode 100644 index 0000000..004334a --- /dev/null +++ b/roles/ufw/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: ufw + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "Whitelist network ports with UFW" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: ["focal", "jammy"] + + galaxy_tags: [] + +dependencies: [] diff --git a/roles/ufw/molecule/default/converge.yml b/roles/ufw/molecule/default/converge.yml new file mode 100644 index 0000000..ddd580c --- /dev/null +++ b/roles/ufw/molecule/default/converge.yml @@ -0,0 +1,17 @@ +--- +- name: Converge + hosts: all + vars: + custom_rules: + - port: 80 + - port: 9080 + src: "10.2.1.0/24" + - interface: eth0@if288 + direction: in + comment: "Allow all incoming traffic on eth0@if288" + + roles: + - role: genlab.common.ufw + disable_ipv6: true + ufw_limit_ssh: true + ufw_rules: "{{ custom_rules }}" diff --git a/roles/ufw/molecule/default/molecule.yml b/roles/ufw/molecule/default/molecule.yml new file mode 100644 index 0000000..fd2f06d --- /dev/null +++ b/roles/ufw/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy + +driver: + name: docker + +platforms: + - name: ubuntu + image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2204}-ansible:latest + pre_build_image: true + command: ${MOLECULE_DOCKER_COMMAND:-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host + privileged: true + +provisioner: + name: ansible + +verifier: + name: ansible + +lint: | + set -e + yamllint . + ansible-lint . diff --git a/roles/ufw/molecule/default/verify.yml b/roles/ufw/molecule/default/verify.yml new file mode 100644 index 0000000..3130f87 --- /dev/null +++ b/roles/ufw/molecule/default/verify.yml @@ -0,0 +1,28 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + + tasks: + - name: "Get the UFW status" + register: ufw_status + changed_when: false + ansible.builtin.command: + cmd: ufw status + + - name: "Verify expected UFW status" + vars: + expected: + - "Status: active" + - "" + - "To Action From" + - "-- ------ ----" + - "22/tcp LIMIT Anywhere " + - "80 ALLOW Anywhere " + - "9080 ALLOW 10.2.1.0/24 " + - "Anywhere on eth0@if288 ALLOW Anywhere # Allow all incoming traffic on eth0@if288" + ansible.builtin.assert: + that: ufw_status.stdout_lines == expected + success_msg: "UFW has the expected state" + fail_msg: "Unexpected UFW state (some rules may have not been applied correctly)" diff --git a/roles/ufw/tasks/main.yml b/roles/ufw/tasks/main.yml new file mode 100644 index 0000000..30885d3 --- /dev/null +++ b/roles/ufw/tasks/main.yml @@ -0,0 +1,54 @@ +--- +- name: "Ensure that ufw is installed" + ansible.builtin.apt: + name: ufw + update_cache: true + +- name: "Disable IPv6" + when: (disable_ipv6 is defined) and (disable_ipv6 is true) + ansible.builtin.lineinfile: + path: /etc/default/ufw + regexp: ^IPV6 + line: IPV6=no + +- name: "Deny incoming connections" + notify: Reload-ufw + community.general.ufw: + direction: incoming + proto: any + policy: deny + +- name: "Allow outgoing connections" + notify: Reload-ufw + community.general.ufw: + direction: outgoing + proto: any + policy: allow + +- name: "Allow SSH access" + notify: Reload-ufw + community.general.ufw: + rule: "{{ ufw_limit_ssh | ternary('limit', 'allow') }}" + port: "{{ ufw_openssh_port }}" + proto: tcp + +- name: "Set whitelist rules" + notify: Reload-ufw + loop: "{{ ufw_rules }}" + community.general.ufw: + rule: "{{ item.rule | default('allow') }}" + comment: "{{ item.comment | default(omit) }}" + port: "{{ item.port | default(omit) }}" + proto: "{{ item.proto | default('any') }}" + src: "{{ item.src | default('any') }}" + dest: "{{ item.dest | default(omit) }}" + interface: "{{ item.interface | default(omit) }}" + direction: "{{ item.direction | default(omit) }}" + route: "{{ item.route | default(false) }}" + +- name: "Enable the ufw service" + community.general.ufw: + state: enabled + +- name: "Flush handlers" + ansible.builtin.meta: flush_handlers diff --git a/roles/ufw/vars/main.yml b/roles/ufw/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/ufw/vars/main.yml @@ -0,0 +1 @@ +---