removed stoat service user

This commit is contained in:
2026-09-13 14:00:45 -06:00
parent f70e88f6d5
commit 0ee978142a
8 changed files with 55 additions and 79 deletions

View File

@@ -2,7 +2,7 @@
# stoat role - task order matters:
# system -> update + baseline packages (git needed by stoat tasks)
# harden -> firewall, ssh, fail2ban, automatic updates, logrotate
# podman -> rootless podman + service user
# podman -> podman + compose + SELinux container policy
# stoat -> clone repo, generate config, systemd-managed compose stack
- name: System update and base packages
ansible.builtin.import_tasks: system.yml
@@ -10,7 +10,7 @@
- name: Harden the host
ansible.builtin.import_tasks: harden.yml
- name: Install rootless podman
- name: Install podman
ansible.builtin.import_tasks: podman.yml
- name: Deploy Stoat

View File

@@ -1,37 +1,12 @@
---
# ------------------------------------------------------------
# Podman - rootless engine, compose support, unprivileged user
# Podman - engine, compose support (runs as root, no service user)
# ------------------------------------------------------------
- name: Install podman stack
ansible.builtin.dnf:
name: "{{ podman_packages }}"
state: present
# Rootless podman cannot bind host ports < 1024 without this.
# Caddy (Stoat's TLS reverse proxy) needs 80/443.
- name: Allow rootless containers to bind privileged ports
ansible.builtin.sysctl:
name: net.ipv4.ip_unprivileged_port_start
value: "{{ podman_unprivileged_port_start }}"
sysctl_file: /etc/sysctl.d/99-podman.conf
state: present
reload: true
- name: Create dedicated service user for rootless podman
ansible.builtin.user:
name: "{{ stoat_service_user }}"
system: true
shell: /bin/bash
create_home: true
comment: "Stoat rootless podman service user"
register: stoat_system_user
# Keeps /run/user/<uid> alive and lets rootless containers start
# on boot without an interactive login.
- name: Enable lingering for the service user
ansible.builtin.command: "loginctl enable-linger {{ stoat_service_user }}"
changed_when: false
# ------------------------------------------------------------
# SELinux - keep enforcing, ensure the container policy and
# diagnostic tools are present for resolving any denials.

View File

@@ -1,12 +1,17 @@
---
# ------------------------------------------------------------
# Stoat deployment - rootless podman-compose, managed by systemd
# Stoat deployment - podman-compose (as root), managed by systemd
# ------------------------------------------------------------
# These tasks run as root (play-level become), NOT as the stoat
# user: becoming an unprivileged user trips an ACL bug on Linux
# ("chmod A+user:..."). Ownership is handed to the service user
# afterwards via chown, and the systemd unit runs podman-compose
# as the stoat user via User=.
# The whole stack runs as root (play-level become): the repo and
# config are generated as root and the systemd unit runs
# podman-compose without dropping privileges.
# The deployment dir may already be owned by another user from
# earlier runs; register it so git-as-root accepts it.
- name: Allow git to operate on the deployment directory
ansible.builtin.command:
cmd: "git config --global --add safe.directory {{ stoat_install_dir }}"
changed_when: false
- name: Clone Stoat self-hosted repository
ansible.builtin.git:
repo: "{{ stoat_repo }}"
@@ -20,13 +25,11 @@
mode: "0755"
# The compose file bind-mounts these directories; create them up
# front so rootless podman does not fail on missing sources.
# front so podman-compose does not fail on missing sources.
- name: Create data directories referenced by compose bind mounts
ansible.builtin.file:
path: "{{ stoat_install_dir }}/data/{{ item }}"
state: directory
owner: "{{ stoat_service_user }}"
group: "{{ stoat_service_user }}"
mode: "0755"
loop:
- db
@@ -48,16 +51,27 @@
chdir: "{{ stoat_install_dir }}"
creates: "{{ stoat_install_dir }}/Revolt.toml"
# The generator and clone run as root; hand the whole deployment
# tree to the service user so rootless podman can read/write it.
- name: Hand ownership of the deployment tree to the service user
# Rootful podman containers run as container_t; on enforcing
# SELinux they cannot read/write bind mounts labeled usr_t, so
# register the deployment tree as container_file_t (persistent).
- name: Ensure the deployment tree is root-owned
ansible.builtin.file:
path: "{{ stoat_install_dir }}"
state: directory
owner: "{{ stoat_service_user }}"
group: "{{ stoat_service_user }}"
owner: root
group: root
recurse: true
- name: Register the deployment directory as a container file context
community.general.sefcontext:
target: "{{ stoat_install_dir }}(/.*)?"
setype: container_file_t
state: present
- name: Relabel the deployment tree with restorecon
ansible.builtin.command: "restorecon -RF {{ stoat_install_dir }}"
changed_when: false
- name: Deploy podman-compose systemd unit
ansible.builtin.template:
src: podman-compose-stoat.service.j2

View File

@@ -6,15 +6,10 @@ Wants=network-online.target
[Service]
# oneshot + RemainAfterExit: the unit tracks compose state while
# the containers themselves run detached under rootless podman.
# the containers themselves run detached under the root podman.
Type=oneshot
RemainAfterExit=yes
User={{ stoat_service_user }}
Group={{ stoat_service_user }}
WorkingDirectory={{ stoat_install_dir }}
# Rootless podman needs the per-user runtime directory; %U expands
# to the UID of `User=`. Lingering (enable-linger) keeps it alive.
Environment=XDG_RUNTIME_DIR=/run/user/%U
ExecStart=/usr/bin/podman-compose up -d --remove-orphans
ExecStop=/usr/bin/podman-compose down
# First start pulls ~2 GB of images; be generous.