Run Caddy as a rootless Podman Quadlet
Replaces the docker_container role with Quadlet systemd user units under ~/.config/containers/systemd, and moves state from /var/lib/caddy to ~/podman-data/caddy. Caddy is now the base unit and no longer lists its upstream networks. Each service registers itself by writing its own <name>.network unit plus a drop-in into caddy.container.d/, so adding or removing a service never means editing the Caddy playbook. Drop-ins are used rather than blockinfile because this playbook re-templates caddy.container on every run, which would wipe appended blocks. The play runs as the connecting user instead of using become: the rootless units belong to it, so the connection's PAM session is what makes systemctl --user work without setting XDG_RUNTIME_DIR. podman_user and the data directory are derived from facts, so the play fails loudly if that resolves to root. Two things that bit us and are worth keeping: - The privileged-port sysctl is applied inline with sysctl -w, not via a handler. Handlers only run at the end of the play, by which point Caddy has already failed to bind 80/443. - The Caddyfile is seeded with a minimal valid config. Caddy exits 1 with a bare "EOF" on an empty config file, so touching it into existence made the service crash-loop.
This commit is contained in:
1 parent
21f848340e
commit
060173db5e
8 files changed
+708
-79
No files matched your search
@@ -0,0 +1,80 @@
|
||||
---
|
||||
# Rootless Podman prerequisites. Shared by every container playbook.
|
||||
#
|
||||
# This playbook runs AS the container user; it does not use become to reach it.
|
||||
# Root-only steps elevate individually. Keeping the connection's PAM session
|
||||
# intact is what lets `systemctl --user` work without setting XDG_RUNTIME_DIR.
|
||||
|
||||
- name: Fail if the connecting user resolved to root
|
||||
ansible.builtin.assert:
|
||||
that: podman_user != 'root'
|
||||
fail_msg: >-
|
||||
podman_user resolved to root, but this playbook configures *rootless*
|
||||
Podman and writes Quadlet units into the user's home. Connect as the
|
||||
unprivileged user that should own the containers, e.g.
|
||||
`ansible-playbook -u tanshu ...`. If this appeared because become was
|
||||
re-enabled at play level, drop it: the root-only tasks below elevate
|
||||
individually.
|
||||
|
||||
- name: Install Podman and the rootless runtime pieces
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- podman
|
||||
# Everything below is only a Recommends, not a Depends, of podman or
|
||||
# netavark. apt installs Recommends by default, so these would normally
|
||||
# arrive on their own; listing them makes the requirement explicit and
|
||||
# keeps it true under --no-install-recommends.
|
||||
# - aardvark-dns # container-name DNS: Caddy resolves every upstream by name
|
||||
# - nftables # netavark's firewall backend; Podman 6 dropped iptables
|
||||
# - passt # pasta, the rootless network backend
|
||||
# - uidmap # newuidmap/newgidmap, required for rootless user namespaces
|
||||
state: present
|
||||
update_cache: true
|
||||
cache_valid_time: 3600
|
||||
become: true
|
||||
|
||||
# Without lingering, the user's systemd manager is torn down when the last
|
||||
# session ends: Quadlet units stop, and they never start at boot.
|
||||
- name: Enable systemd lingering for {{ podman_user }}
|
||||
ansible.builtin.command: "loginctl enable-linger {{ podman_user }}"
|
||||
args:
|
||||
creates: "/var/lib/systemd/linger/{{ podman_user }}"
|
||||
become: true
|
||||
|
||||
# Rootless containers cannot bind host ports below 1024 unless the kernel
|
||||
# floor is lowered. Caddy publishes 80/443.
|
||||
#
|
||||
# This is applied inline rather than via a handler on purpose: handlers only
|
||||
# run at the end of the play, by which point Caddy has already tried (and
|
||||
# failed) to bind port 80. sysctl -w touches the running kernel; the file
|
||||
# makes it survive a reboot.
|
||||
- name: Read the current unprivileged port floor
|
||||
ansible.builtin.command: sysctl -n net.ipv4.ip_unprivileged_port_start
|
||||
register: podman_port_floor
|
||||
changed_when: false
|
||||
|
||||
- name: Persist the unprivileged port floor
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/sysctl.d/99-podman.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
content: |
|
||||
# Managed by Ansible (podman role).
|
||||
net.ipv4.ip_unprivileged_port_start = {{ podman_unprivileged_port_start }}
|
||||
become: true
|
||||
|
||||
- name: Apply the unprivileged port floor
|
||||
ansible.builtin.command: >-
|
||||
sysctl -w net.ipv4.ip_unprivileged_port_start={{ podman_unprivileged_port_start }}
|
||||
become: true
|
||||
when: podman_port_floor.stdout | int > podman_unprivileged_port_start
|
||||
changed_when: true
|
||||
|
||||
- name: Ensure the Quadlet unit directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ quadlet_dir }}"
|
||||
state: directory
|
||||
owner: "{{ podman_user }}"
|
||||
group: "{{ podman_user }}"
|
||||
mode: "0755"
|
||||
Reference in new issue
Block a user