Skip to content

Convert Podman Quadlet deployment from rootful to rootless (rebased) - #12

Open
pablomh wants to merge 43 commits into
masterfrom
rootless_rebase
Open

Convert Podman Quadlet deployment from rootful to rootless (rebased)#12
pablomh wants to merge 43 commits into
masterfrom
rootless_rebase

Conversation

@pablomh

@pablomh pablomh commented Mar 23, 2026

Copy link
Copy Markdown
Owner

Rebase of the rootless conversion commit on top of current origin/master.

@pablomh
pablomh force-pushed the rootless_rebase branch 21 times, most recently from 3db50fb to 0c156cf Compare March 26, 2026 12:28
@pablomh
pablomh force-pushed the rootless_rebase branch 2 times, most recently from bf89edc to cea2b0f Compare March 26, 2026 22:34
@pablomh
pablomh force-pushed the rootless_rebase branch 7 times, most recently from 4a98db9 to db2d934 Compare April 22, 2026 21:00
pablomh and others added 17 commits April 23, 2026 12:28
Adds helpers to conftest.py that delegate to `foremanctl service`,
making all test assertions go through the operational tool:

- service_is_running(server, name) — foremanctl service status
- service_is_enabled(server, name) — systemctl --user is-enabled
- service_exists(server, name)     — systemctl --user status (rc != 4)
- service_start/stop/restart(server, name)
- foremanctl_run(server, cmd)      — run as foremanctl user
- foremanctl_exec(server, ctr, cmd)— foremanctl service exec

Also adds foremanctl_service_test.py which tests the tool itself:
help output, status, brief mode, startup order, ps, unknown subcommand,
and exec.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Replaces server.service() and raw podman/systemctl calls with the new
test helpers that delegate to foremanctl service. Tests now exercise
the operational tool at the same time as verifying service state.

- service_is_running() replaces server.service().is_running
- service_is_enabled()/service_exists() for enable/existence checks
- service_start/stop/restart() in target_lifecycle_test
- foremanctl_exec() replaces raw podman exec invocations
- Port reachability tests kept where ports are published to loopback

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Creates the foremanctl system user that owns all containerized services
in rootless mode:

- Creates foremanctl group and user (home /var/lib/foremanctl, no login)
- Derives XDG_RUNTIME_DIR from the user's actual UID
- Writes subuid/subgid entries (100000:65536) for user namespace mapping
- Sets net.ipv4.ip_unprivileged_port_start=80 via sysctl
- Enables systemd lingering so services survive without an active session
- Verifies XDG_RUNTIME_DIR exists (fails fast if lingering didn't work
- Creates ~/.config/containers/systemd and ~/.config/systemd/user dirs

Exposes rootless_user_xdg_runtime_dir fact for the deploy playbook to
map into the foremanctl namespace.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
EOF
)
Reusable role that creates named Podman bridge networks in the
foremanctl user namespace. Called multiple times from the deploy
playbook with different parameters to create the tiered network
topology (foreman-db, foreman-cache, foreman-app, foreman-proxy-net).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Play 1 sets up the rootless foremanctl user and derives
foremanctl_xdg_runtime_dir before vars_files are loaded.
Play 2 deploys all services, now with four bridge networks replacing
host networking:

  foreman-db    (internal, isolated) — postgresql only
  foreman-cache (internal, isolated) — redis only
  foreman-app   (bridge)             — foreman, pulp, candlepin
  foreman-proxy-net (bridge)         — foreman-proxy, iop-gateway

base.yaml gains foremanctl_* vars and foremanctl_runtime_env.
database.yml changes database_host from localhost to the postgresql
container DNS name.
default_certificates.yml changes the cert directory from /root/certificates
to /var/lib/foremanctl/certificates.
pull-images.yaml follows the same two-play split so images are pulled
in the foremanctl Podman namespace.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Image updated to postgresql-16-c10s (PG16 defaults to scram-sha-256)
- All podman/systemd operations wrapped in become_user: foremanctl block
- Socket dir mounted at /tmp/socket to avoid sclorg entrypoint corruption
- unix_socket_directories configured post-startup for community.postgresql
- POSTGRESQL_LISTEN_ADDRESSES=* for bridge-networked TCP clients
- Network changed from host to foreman-db (internal, isolated)
- Handler updated with become_user and scope: user

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- All operations in a single become_user: foremanctl block including
  podman unshare chown for container UID 1001
- Network changed from host to foreman-cache (internal, isolated)
- Systemd operations use scope: user

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Keystore/truststore paths moved to /var/lib/foremanctl/
- certs.yml/artemis.yml: root ops remain root, podman_secret calls
  grouped into become_user blocks
- Network changed from host to foreman-app; hostname set to candlepin
- Port 23443 published to 127.0.0.1 for httpd proxy access
- Handler updated with become_user and scope: user

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- All operations in a single become_user: foremanctl block
- Networks: foreman-app, foreman-db, foreman-cache
- PULP_REDIS_URL changed to redis://redis (container DNS)
- Ports 24817/24816 published to 127.0.0.1 for httpd
- Worker symlinks and pulp-worker.target moved to foremanctl dirs
- Worker cleanup uses find on quadlet dir instead of service_facts
- Handlers updated with become_user and scope: user

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- All operations in a single become_user: foremanctl block
- Networks: foreman-app, foreman-db, foreman-cache, foreman-proxy-net
- Port 3000 published to 127.0.0.1 for httpd proxy
- DYNFLOW_REDIS_URL changed to redis://redis (container DNS)
- Dynflow symlinks and recurring timers moved to foremanctl dirs
- Handlers updated with become_user and scope: user

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Moves foreman.target from /etc/systemd/system/ to the foremanctl
user's ~/.config/systemd/user/ directory. All operations use
become_user: foremanctl with scope: user.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- All container/secret/systemd operations in become_user blocks
- Network changed from host to foreman-proxy-net
- Port 8443 published externally for managed host connectivity
- container.d drop-in path moved to foremanctl_quadlet_dir
- remote_execution_ssh: SSH keypair generated inside foremanctl block
- Handlers updated with become_user and scope: user

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Default cert directory changed from /root/certificates to
  /var/lib/foremanctl/certificates (also updated in default_certificates.yml)
- Directory creation tasks set owner/group to foremanctl
- Final recursive chown in main.yml covers all files generated by
  openssl commands that run as root

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The role now checks /etc/subuid and /etc/subgid for the foremanctl
user (set up by the rootless_user role) rather than the deploying
user. Adds defaults/main.yaml with rootless_user_name: foremanctl so
the variable is available when called from Play 2.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Adds acl package (required for Ansible become_user with unprivileged users)
- Adds fapolicyd rule when fapolicyd is active: allows Ansible become_user
  to copy Python modules to /var/tmp/ (RHEL-37912)
  Rule: allow perm=open exe=/usr/bin/python3 : dir=/var/tmp/ ftype=text/x-python

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Replace iop_network role with deploy_network (internal, isolated)
- Wrap all container/systemd operations in become_user: foremanctl blocks
- Add scope: user to all systemd tasks and handlers
- Gateway uses dedicated iop-core-gateway certificates instead of localhost
- Gateway connects via container DNS on foreman-proxy-net (no host port)
- IOP inventory frontend uses rootless podman for image pull and extraction
- Remove per-role _registry_auth_file vars (foremanctl_runtime_env handles it)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Convert all IOP test files to use rootless helpers (service_is_running,
  foremanctl_run, container_exec, SYSTEMCTL_USER)
- Update quadlet file paths to rootless user directory
- Fix frontend asset paths to match role defaults
- Fix timer paths to user systemd directory
- Test gateway port via bridge network (not published to localhost)
- Fix systemctl stop in upgrade workflow for rootless user scope

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@pablomh
pablomh force-pushed the rootless_rebase branch 4 times, most recently from 9aec640 to 9fa4f12 Compare April 26, 2026 16:44
Reverting foreman-proxy back to foreman-proxy-net to isolate whether
the network name collision with the container name causes REX failures
in rootless mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants