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/README.md b/README.md index eecffb1..241639d 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,4 @@ - [mount_device](roles/mount_device/README.md) - [alertmanager](roles/alertmanager/README.md) +- [wg_hub](roles/wg_hub/README.md) \ No newline at end of file diff --git a/galaxy.yml b/galaxy.yml index b17cb19..1c0944f 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: genlab name: common -version: 0.0.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 @@ +--- diff --git a/roles/wg_hub/README.md b/roles/wg_hub/README.md new file mode 100644 index 0000000..fe551c0 --- /dev/null +++ b/roles/wg_hub/README.md @@ -0,0 +1,43 @@ +template +========= + +Template for Ansible role monorepos. + +⚠️ Do not forget to update: + +- `meta/main.yml` +- Conda/Mamba manifests +- this README =) + +Requirements +------------ + +None + +Role Variables +-------------- + +None + +Dependencies +------------ + +None + +Example Playbook +---------------- + +```yaml +roles: + - role: genlab.template +``` + +License +------- + +BSD + +Author Information +------------------ + +corvus-migratorius@proton.me diff --git a/roles/wg_hub/defaults/main.yml b/roles/wg_hub/defaults/main.yml new file mode 100644 index 0000000..7bf9b28 --- /dev/null +++ b/roles/wg_hub/defaults/main.yml @@ -0,0 +1,7 @@ +--- +wg_hub_iface_name: wg0 +wg_hub_host_id: hub +wg_hub_ipv4_vpn_addr: 10.0.0.254 +wg_hub_ipv4_vpn_cidr: 24 +wg_hub_wg_port: 51820 +wg_hub_hide_secrets: true diff --git a/roles/wg_hub/handlers/main.yml b/roles/wg_hub/handlers/main.yml new file mode 100644 index 0000000..ff2cedb --- /dev/null +++ b/roles/wg_hub/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: "Run the Wireguard service" + ansible.builtin.systemd_service: + name: wg-quick@{{ wg_hub_iface_name | replace("-", "_") }} + state: restarted + enabled: true + daemon_reload: true diff --git a/roles/wg_hub/meta/main.yml b/roles/wg_hub/meta/main.yml new file mode 100644 index 0000000..e2da64a --- /dev/null +++ b/roles/wg_hub/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + role_name: "wg_hub" + namespace: genlab + author: "Alexander Gorelyshev" + company: "Genlab, LLC" + description: "" + license: "MIT" + min_ansible_version: "2.1" + + platforms: + - name: "Ubuntu" + versions: ["jammy", "noble"] + + galaxy_tags: [] + +dependencies: [] diff --git a/roles/wg_hub/molecule/default/converge.yml b/roles/wg_hub/molecule/default/converge.yml new file mode 100644 index 0000000..e807dae --- /dev/null +++ b/roles/wg_hub/molecule/default/converge.yml @@ -0,0 +1,24 @@ +--- +- name: Converge + hosts: all + vars: + wg_hub_iface_name: adm + wg_hub_ipv4_vpn_addr: 10.0.0.254 + wg_hub_ipv4_vpn_cidr: 24 + wg_hub_wg_port: 51820 + wg_hub_wg_pkey: gABk6e/n3UDgudEVlUrEVcVdn0tc0YVDeCASsI10QFA= + wg_hub_wg_pubkey: EvcoZ21/p0AHz5y95jwjVIR9puljc2kXqh/f3U1A4nk= + wg_hub_hide_secrets: false + # dns_server: 127.0.0.1:5300 # doesn't work in Github Actions environment + peers: + - wg_hub_host_id: alpha + wg_hub_ipv4_vpn_addr: 10.0.0.1 + wg_hub_wg_pubkey: LfEJgNiJ05nx4nWB0Pj3wS3WRyFq567fsdDh4XZqRF0= + wg_psk: i3JCrQOfptZhgpL+BTm/65MPW/ljJexCgiuWMIqZJYo= + - wg_hub_host_id: beta + wg_hub_ipv4_vpn_addr: 10.0.0.2 + wg_hub_wg_pubkey: oq3Fcwwfxsi5f5UHcZKtxMwQ2aSeHOUe3r35soUUYzU= + wg_psk: Z/z7Qo8hW97UcImYE/ZbCxpNmizfVhvl0dzygtvtYYg= + + roles: + - role: genlab.common.wg_hub diff --git a/roles/wg_hub/molecule/default/molecule.yml b/roles/wg_hub/molecule/default/molecule.yml new file mode 100644 index 0000000..fd2f06d --- /dev/null +++ b/roles/wg_hub/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/wg_hub/molecule/default/verify.yml b/roles/wg_hub/molecule/default/verify.yml new file mode 100644 index 0000000..38f93ff --- /dev/null +++ b/roles/wg_hub/molecule/default/verify.yml @@ -0,0 +1,13 @@ +--- +- name: Verify + hosts: all + gather_facts: false + any_errors_fatal: true + vars: + iface_name: adm + + tasks: + - name: "Check that the Wireguard service is running" + ansible.builtin.service: + name: wg-quick@{{ iface_name }} + state: started diff --git a/roles/wg_hub/tasks/main.yml b/roles/wg_hub/tasks/main.yml new file mode 100644 index 0000000..78551ea --- /dev/null +++ b/roles/wg_hub/tasks/main.yml @@ -0,0 +1,54 @@ +--- +- name: "Install wireguard system-wide" + ansible.builtin.apt: + name: wireguard + state: present + update_cache: true + cache_valid_time: 3600 + +- name: "Ensure no dashes in the interface name" + when: "'-' in wg_hub_iface_name" + ansible.builtin.fail: + msg: "The interface name must not contain dashes, got: '{{ wg_hub_iface_name }}'" + +- name: "Create the Hub configuration file" + no_log: "{{ wg_hub_hide_secrets }}" + notify: "Run the Wireguard service" + ansible.builtin.blockinfile: + path: "/etc/wireguard/{{ wg_hub_iface_name }}.conf" + create: true + owner: root + group: root + mode: "0600" + state: present + block: | + [Interface] + Address = {{ wg_hub_ipv4_vpn_addr }}/32 + ListenPort = {{ wg_hub_wg_port }} + PrivateKey = {{ wg_hub_wg_pkey }} + + PreUp = sysctl -w net.ipv4.ip_forward=1 + {% if dns_server is defined %} + PostUp = resolvectl dns %i {{ dns_server }}; resolvectl domain %i {{ wg_hub_iface_name }}.local + {% endif %} + PostDown = sysctl -w net.ipv4.ip_forward=0 + +- name: "Add [Peer] sections to the Hub configuration file" + no_log: "{{ wg_hub_hide_secrets }}" + notify: "Run the Wireguard service" + loop: "{{ peers }}" + vars: + domain_name: "{{ item.wg_hub_host_id }}.{{ wg_hub_iface_name }}.local" + ansible.builtin.blockinfile: + path: "/etc/wireguard/{{ wg_hub_iface_name }}.conf" + owner: root + group: root + mode: "0600" + marker: "# {mark} ANSIBLE MANAGED SPOKE BLOCK: {{ domain_name }}" + block: | + # + [Peer] # {{ domain_name }} + PublicKey = {{ item.wg_hub_wg_pubkey }} + PresharedKey = {{ item.wg_psk }} + AllowedIPs = {{ item.wg_hub_ipv4_vpn_addr }}/32 + # diff --git a/roles/wg_hub/vars/main.yml b/roles/wg_hub/vars/main.yml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/wg_hub/vars/main.yml @@ -0,0 +1 @@ +---