Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/features/containers.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ A container runtime with a sandboxed-by-default configuration.

Installs `podman`, `podman-compose`, and the `crun` runtime from distro packages - no scripts, no extra repos.

Rootless prerequisites come along: `passt` (the `pasta` network backend) and `uidmap` on Debian/Ubuntu (`shadow-utils` on RHEL) for `newuidmap`/`newgidmap`.

`podman-auto-update.timer` is enabled system-wide.
When `users.manage` is on, the [managed user](users.md) also gets lingering enabled (`loginctl enable-linger`) and the same timer enabled in its user session, so rootless containers labelled `io.containers.autoupdate` are refreshed daily.

## Configuration (`features.containers`)

| Field | Default | Description |
Expand Down
75 changes: 73 additions & 2 deletions nullforge/runes/containers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""Containers deployment module."""

import re

from pyinfra.context import host
from pyinfra.facts.files import File
from pyinfra.facts.server import Arch, Which
from pyinfra.facts.files import File, FileContents
from pyinfra.facts.server import Arch, Users, Which
from pyinfra.operations import apt, files, server, systemd

from nullforge.models.containers import ContainersBackendType
Expand All @@ -13,6 +15,13 @@
from nullforge.smithy.versions import GPG_KEYS, STATIC_URLS


SUBID_MIN = 100000
"""First host id handed out to rootless users, below it live real accounts."""

SUBID_COUNT = 65536
"""Ids per rootless user, matches shadow's SUB_UID_COUNT default."""


def deploy_containers() -> None:
"""Deploy containers runtime and related tools."""

Expand All @@ -29,6 +38,9 @@ def deploy_containers() -> None:
case ContainersBackendType.PODMAN:
_install_crun()
_install_podman()
if user_opts.manage:
_ensure_subid_ranges(user_opts.name)
_enable_podman_autoupdate(user_opts)
case ContainersBackendType.CRIO:
raise ValueError("CRIO is not supported yet")

Expand Down Expand Up @@ -197,6 +209,65 @@ def _install_podman() -> None:
packages=[
"podman",
"podman-compose",
"passt",
"uidmap",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
],
_sudo=True,
)


def _ensure_subid_ranges(user: str) -> None:
"""Allocate subuid/subgid ranges for user when absent."""

user_exists = user in host.get_fact(Users)

for path in host.loop(("/etc/subuid", "/etc/subgid")):
entries = host.get_fact(FileContents, path)

if entries is not None and not user_exists:
host.noop(f"useradd allocates {path} range when creating {user}")
continue

entries = entries or []
if any(entry.startswith(f"{user}:") for entry in entries):
host.noop(f"{user} already has {path} range")
continue

files.line(
name=f"Allocate {path} range for {user}",
path=path,
line=f"^{re.escape(user)}:",
replace=f"{user}:{_next_subid_start(entries)}:{SUBID_COUNT}",
_sudo=True,
)


def _next_subid_start(entries: list[str]) -> int:
ends = [int(start) + int(count) for _, start, count in (e.split(":") for e in entries if e.count(":") == 2)]
return max([SUBID_MIN, *ends])


def _enable_podman_autoupdate(user_opts: UserMold) -> None:
"""Enable podman auto-update timer, rootful and rootless."""

systemd.service(
name="Enable podman auto-update timer",
service="podman-auto-update.timer",
running=True,
enabled=True,
_sudo=True,
)

if not user_opts.manage:
return

user = user_opts.name
server.shell(
name=f"Enable rootless podman auto-update timer for {user}",
commands=[
f"loginctl enable-linger {user}",
f"runuser -u {user} -- env XDG_RUNTIME_DIR=/run/user/$(id -u {user})"
" systemctl --user enable --now podman-auto-update.timer",
],
_sudo=True,
)
Expand Down
1 change: 1 addition & 0 deletions nullforge/smithy/packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"pkg-config": "pkgconfig",
"python3-dev": "python3-devel",
"software-properties-common": None,
"uidmap": "shadow-utils",
"ufw": "firewalld",
}
"""Package overrides for RHEL/CentOS/Fedora families
Expand Down
Loading