diff --git a/inventory/host_vars/ch01.balsillie.house/high_availability.yml b/inventory/host_vars/ch01.balsillie.house/high_availability.yml index ea96c80..ee7d366 100644 --- a/inventory/host_vars/ch01.balsillie.house/high_availability.yml +++ b/inventory/host_vars/ch01.balsillie.house/high_availability.yml @@ -1 +1,2 @@ ha_pair_value: '01' +subnet_suffix_value: '1' diff --git a/inventory/host_vars/ch01.balsillie.house/sudoers.yml b/inventory/host_vars/ch01.balsillie.house/sudoers.yml new file mode 100644 index 0000000..95c473b --- /dev/null +++ b/inventory/host_vars/ch01.balsillie.house/sudoers.yml @@ -0,0 +1 @@ +unprivileged_user: quadlet diff --git a/inventory/host_vars/ch02.balsillie.house/high_availability.yml b/inventory/host_vars/ch02.balsillie.house/high_availability.yml index 78cb262..c698281 100644 --- a/inventory/host_vars/ch02.balsillie.house/high_availability.yml +++ b/inventory/host_vars/ch02.balsillie.house/high_availability.yml @@ -1 +1,2 @@ ha_pair_value: '02' +subnet_suffix_value: '2' diff --git a/inventory/host_vars/ch02.balsillie.house/sudoers.yml b/inventory/host_vars/ch02.balsillie.house/sudoers.yml new file mode 100644 index 0000000..95c473b --- /dev/null +++ b/inventory/host_vars/ch02.balsillie.house/sudoers.yml @@ -0,0 +1 @@ +unprivileged_user: quadlet diff --git a/inventory/host_vars/dl01.balsillie.house/sudoers.yml b/inventory/host_vars/dl01.balsillie.house/sudoers.yml new file mode 100644 index 0000000..9023be0 --- /dev/null +++ b/inventory/host_vars/dl01.balsillie.house/sudoers.yml @@ -0,0 +1 @@ +unprivileged_user: download diff --git a/playbooks/archlinux-servers.yml b/playbooks/archlinux-servers.yml index df62eb7..c796a3d 100644 --- a/playbooks/archlinux-servers.yml +++ b/playbooks/archlinux-servers.yml @@ -3,7 +3,7 @@ - name: Configure ssh and firewall hosts: # - archlinux_servers - - ch01.balsillie.house + - ch02.balsillie.house gather_facts: false become: true pre_tasks: diff --git a/roles/podman/files/storage.conf b/roles/podman/files/storage.conf index 4e4c783..84ac7cc 100644 --- a/roles/podman/files/storage.conf +++ b/roles/podman/files/storage.conf @@ -1,6 +1,10 @@ [storage] driver = "overlay" +graphroot="$HOME/.local/share/containers/storage" [storage.options] +ignore_chown_errors = "true" additionalimagestores = [ "/var/containers/images" -] \ No newline at end of file +] +[storage.options.overlay] +mount_program = "/usr/bin/fuse-overlayfs" diff --git a/roles/podman/handlers/main.yml b/roles/podman/handlers/main.yml new file mode 100644 index 0000000..6e76d5f --- /dev/null +++ b/roles/podman/handlers/main.yml @@ -0,0 +1,13 @@ +# code: language=ansible + +- name: Quadlet daemon-reload # noqa: command-instead-of-module + ansible.builtin.command: + cmd: systemctl --user --machine=quadlet@.host daemon-reload + changed_when: false + +# - name: Quadlet daemon-reload # noqa: command-instead-of-module +# become: true +# become_user: quadlet +# ansible.builtin.systemd_service: +# daemon_reload: true +# scope: user diff --git a/roles/podman/tasks/main.yml b/roles/podman/tasks/main.yml index b9777a9..fe3655f 100644 --- a/roles/podman/tasks/main.yml +++ b/roles/podman/tasks/main.yml @@ -8,6 +8,7 @@ - passt - podlet - podman-compose + - fuse-overlayfs state: present update_cache: true @@ -22,28 +23,50 @@ - name: Create an unprivileged user to run quadlet files as ansible.builtin.user: comment: Quadlet services user - create_home: false - home: / + create_home: true + home: /home/quadlet name: quadlet password: '!' password_lock: true - shell: /usr/bin/nologin + shell: /usr/bin/bash state: present - system: true + system: false # Can't be a system user or subuid/subgid entries don't get created -- name: Ensure quadlet user parent directory exists +- name: Ensure graphroot location exists for unprivileged user ansible.builtin.file: - group: root - mode: '0755' - owner: root - path: /etc/containers/systemd/users + group: quadlet + mode: '0750' + owner: quadlet + path: "/home/quadlet/.local/share/containers/storage" state: directory -- name: Create quadlet user directory +- name: Enable linger for unprivileged user + ansible.builtin.command: + cmd: loginctl enable-linger quadlet + creates: /var/lib/systemd/linger/quadlet + register: podman_enable_linger_result + +- name: Reboot if enabling lingering caused a change # noqa: no-handler + when: + - podman_enable_linger_result is changed + ansible.builtin.reboot: + post_reboot_delay: 10 + test_command: whoami + +- name: Create directories for quadlet volumes ansible.builtin.file: group: quadlet mode: '0775' owner: quadlet + path: /quadlet/{{ item }} + state: directory + loop: "{{ podman_containers }}" + +- name: Create quadlet systemd user directory + ansible.builtin.file: + group: root + mode: '0775' + owner: root path: /etc/containers/systemd/users/quadlet state: directory @@ -79,3 +102,58 @@ mode: '0644' owner: root src: "{{ role_path }}/files/storage.conf" + +- name: Template out volume unit files + ansible.builtin.template: + dest: /etc/containers/systemd/users/quadlet/{{ item }}.volume + group: root + mode: '0644' + owner: root + src: "{{ role_path }}/templates/quadlet.volume.j2" + loop: "{{ podman_containers }}" + notify: + - Quadlet daemon-reload + +- name: Template out network unit file + ansible.builtin.template: + dest: /etc/containers/systemd/users/quadlet/dual-stack.network + group: root + mode: '0644' + owner: root + src: "{{ role_path }}/templates/dual-stack.network.j2" + notify: + - Quadlet daemon-reload + +- name: Template out container unit files + ansible.builtin.template: + dest: /etc/containers/systemd/users/quadlet/{{ item }}.container + group: root + mode: '0644' + owner: root + src: "{{ role_path }}/templates/{{ item }}.container.j2" + loop: + - technitium + notify: + - Quadlet daemon-reload + +- name: Flush handlers to trigger daemon-reload + ansible.builtin.meta: flush_handlers + +- name: Enable IP forwarding + ansible.posix.sysctl: + name: "{{ item }}" + reload: true + state: present + sysctl_set: true + value: 1 + loop: + - net.ipv4.ip_forward + - net.ipv6.conf.all.forwarding + +# Enable forwarding rules in UFW + +# Enable/start user service + +# Add to containers.conf +# [network] +# rootless_port_forwarder="pasta" diff --git a/roles/podman/templates/dual-stack.network.j2 b/roles/podman/templates/dual-stack.network.j2 new file mode 100644 index 0000000..5c1e51f --- /dev/null +++ b/roles/podman/templates/dual-stack.network.j2 @@ -0,0 +1,12 @@ +[Network] +NetworkDeleteOnStop=true +DisableDNS=no +Driver=bridge +Internal=no +IPv6=yes +Subnet={{ podman_subnet_prefix_ipv4 }}{{ subnet_suffix_value }}.0/24 +Subnet={{ podman_subnet_prefix_ipv6 }}{{ subnet_suffix_value }}::/64 +IPRange={{ podman_subnet_prefix_ipv4 }}{{ subnet_suffix_value }}.128/26 +IPRange={{ podman_subnet_prefix_ipv6 }}{{ subnet_suffix_value }}::0100/120 +Gateway={{ podman_subnet_prefix_ipv4 }}{{ subnet_suffix_value }}.254 +Gateway={{ podman_subnet_prefix_ipv6 }}{{ subnet_suffix_value }}::254 diff --git a/roles/podman/templates/quadlet.volume.j2 b/roles/podman/templates/quadlet.volume.j2 new file mode 100644 index 0000000..65557d9 --- /dev/null +++ b/roles/podman/templates/quadlet.volume.j2 @@ -0,0 +1,5 @@ +[Volume] +Device=/quadlet/{{ item }} +Group=quadlet +Type=bind +User=quadlet \ No newline at end of file diff --git a/roles/podman/templates/technitium.container.j2 b/roles/podman/templates/technitium.container.j2 new file mode 100644 index 0000000..54c277c --- /dev/null +++ b/roles/podman/templates/technitium.container.j2 @@ -0,0 +1,56 @@ +[Unit] +Description=Technitium DNS Server +After=network-online.target + +[Service] +Restart=on-failure + +[Install] +WantedBy=default.target + +[Container] +# Ref: https://manpages.opensuse.org/Tumbleweed/podman/quadlet.5.en.html#Container_units_%5BContainer%5D + +ContainerName=technitium +Image={{ podman_technitium_registry }}/{{ podman_technitium_repository }}:{{ podman_technitium_version }} +HostName=ns{{ ha_pair_value }} + +AutoUpdate=local + + +# Do not try to pull the image, use pre-populated local image store only +Pull=never + +# Set timezone inside container +Timezone=America/New_York + +# Volume mappings + +Volume=technitium.volume:/etc/dns + +# Specify custom podman network to use +Network=dual-stack.network + +# Set container addresses +IP={{ podman_subnet_prefix_ipv4 }}{{ subnet_suffix_value }}.153 +IP6={{ podman_subnet_prefix_ipv6 }}{{ subnet_suffix_value }}::0153 + +# Port mappings + +# HTTP Web UI IPv4/6 +PublishPort={{ ipv4_address }}:5380:5380/tcp +PublishPort=[{{ ipv6_address }}]:5380:5380/tcp + +# DNS TCP/UDP IPv4/6 +#PublishPort={{ ipv4_address }}:5353:53/tcp +#PublishPort=[{{ ipv6_address }}]:5353:53/udp +#PublishPort={{ ipv4_address }}:5353:53/udp +#PublishPort=[{{ ipv6_address }}]:5353:53/tcp + +# Environment Variables + +Environment=DNS_SERVER_DOMAIN=ns{{ ha_pair_value }}.{{ podman_local_domain }} + +# UID/GID Mappings + +UIDMap=0:0:1 diff --git a/roles/podman/templates/technitium.service.j2 b/roles/podman/templates/technitium.service.j2 deleted file mode 100644 index c4f086d..0000000 --- a/roles/podman/templates/technitium.service.j2 +++ /dev/null @@ -1,37 +0,0 @@ -[Unit] -Description=Technitium DNS Server - -[Container] -ContainerName=technitium -Image={{ podman_technitium_registry }}/{{ podman_technitium_repository }}:{{ podman_technitium_version }} - -# Enable auto-update container -AutoUpdate=registry - -Volume=/path/to/syncthing/config:/config -Volume=/path/to/data1:/data1 - -HostName=technitium{{ ha_pair_value }} -PublishPort=127.0.0.1:8384:8384/tcp -PublishPort=22000:22000/tcp - -Environment=PUID=1000 -Environment=PGID=1000 -Environment=TZ=Etc/UTC - -# UID mapping is needed to run linuxserver.io container as rootless podman. -# This will map UID=1000 inside the container to intermediate UID=0. -# For rootless podman intermediate UID=0 will be mapped to the UID of current user. -UIDMap=1000:0:1 -UIDMap=0:1:1000 -UIDMap=1001:1001:64536 - -[Service] -Restart=on-failure - -# Extend Timeout to allow time to pull the image -TimeoutStartSec=300 - -# The [Install] section allows enabling the generated service. -[Install] -WantedBy=default.target \ No newline at end of file diff --git a/roles/podman/vars/main.yml b/roles/podman/vars/main.yml index 8445cfb..bba1295 100644 --- a/roles/podman/vars/main.yml +++ b/roles/podman/vars/main.yml @@ -1,5 +1,12 @@ # code: language=ansible +podman_containers: + - technitium + - git-server + - distribution + +podman_local_domain: "balsillie.house" + podman_technitium_registry: "docker.io" podman_technitium_repository: "technitium/dns-server" podman_technitium_version: "15.4.0" @@ -11,3 +18,24 @@ podman_distribution_version: "3" podman_git_server_registry: "docker.io" podman_git_server_repository: "rockstorm/git-server" podman_git_server_version: "2.54" + +podman_subnet_prefix_ipv4: '10.96.1' +podman_subnet_prefix_ipv6: '2600:4040:593d:8b1' + +podman_ufw_rules: + - source: '10.96.30.0/24' + port: '5380' + proto: tcp + comment: 'Technitium Web UI LAN IPv4' + - source: '10.96.10.0/24' + port: '5380' + proto: tcp + comment: 'Technitium Web UI Servers IPv4' + - source: '2600:4040:593d:8b30::/64' + port: '5380' + proto: tcp + comment: 'Technitium Web UI LAN IPv6' + - source: '2600:4040:593d:8b10::/64' + port: '5380' + proto: tcp + comment: 'Technitium Web UI Servers IPv6' diff --git a/roles/sudoers/tasks/main.yml b/roles/sudoers/tasks/main.yml index 371251e..369ad82 100644 --- a/roles/sudoers/tasks/main.yml +++ b/roles/sudoers/tasks/main.yml @@ -46,7 +46,7 @@ name: "{{ sudoers_user }}" state: present -- name: Add sudoers entry +- name: Add sudoers entries community.general.sudoers: commands: - ALL @@ -54,12 +54,19 @@ - '!fqdn' # Ensures using shortname only when assessing hostname in sudo rule group: sudo host: "{{ inventory_hostname_short }}" - name: sudo_group - nopassword: false - runas: root + name: sudo_{{ item.name }} + nopassword: "{{ item.nopassword }}" + runas: "{{ item.name }}" state: present sudoers_path: "/etc/sudoers.d" validation: required + loop: + - name: root + nopassword: false + - name: "{{ unprivileged_user }}" + nopassword: true + loop_control: + label: "{{ item.name }}" - name: Remove default sudoers file ansible.builtin.file: