--- # --------------------------------------------------------------------------- # Rootless Podman / Quadlet # --------------------------------------------------------------------------- # Duplicated on purpose from caddy/vars/default.yml: a play cannot include that # file wholesale, because it also sets `host_directory` — to the Caddy data # directory — and every playbook defines that for itself. Keep these three in # sync with caddy/vars/default.yml. (Its fourth, podman_unprivileged_port_start, # belongs to the podman tasks, which this play does not run: Gitea publishes # nothing host-side.) # # podman_user is derived from the connection: this play carries no become at # play level, so the Quadlet units belong to whoever Ansible connects as. # Requires gather_facts (set in playbook.yml). podman_user: "{{ ansible_facts['user_id'] }}" podman_data_dir: "{{ ansible_facts['user_dir'] }}/podman-data" quadlet_dir: "{{ ansible_facts['user_dir'] }}/.config/containers/systemd" # --------------------------------------------------------------------------- # Gitea # --------------------------------------------------------------------------- host: git.tanshu.com container_name: gitea unit_name: "{{ container_name }}.service" # Fully qualified on purpose: rootless Podman resolves short names against # unqualified-search-registries, and there is no TTY to answer a prompt when # the systemd unit starts. image: docker.io/gitea/gitea:latest # The Quadlet file is .network. caddy/roles/register pins the # Podman network name to _net (so: gitea.network, gitea_net). network_name: "{{ container_name }}" host_directory: "{{ podman_data_dir }}/{{ container_name }}" # Port inside the container that Caddy proxies to — app.ini's HTTP_PORT. Not # published on the host: Caddy reaches it by container name. container_port: 3000 # The uid/gid of the `git` account the image creates for itself # (git:x:1003:1003, verified in the image), and therefore the uid Gitea runs as. # The number is load-bearing three times over: # # * UserNS in the Quadlet unit maps it onto podman_user, which is what keeps # every file Gitea writes owned by podman_user on the host — plain tar and # rsync backups, no subuids; # * USER_UID/USER_GID force the entrypoint to keep it, so the file layout and # the mapping cannot drift apart; # * roles/ssh execs into the container as exactly this uid. # # Change one and the other two have to change with it. gitea_uid: 1003 gitea_gid: 1003 # Where the image keeps its data *inside* the container, and therefore the # GITEA_CUSTOM the image's own environment already sets. gitea_custom: /data/gitea # Gitea's own unauthenticated health endpoint. The image ships no HEALTHCHECK, # so the unit defines one from this and the play gates on it. health_cmd: "curl -fsS -o /dev/null http://127.0.0.1:{{ container_port }}/api/healthz" # Only log timestamps are affected: Gitea stores each user's own timezone. The # Docker-era container bind-mounted the host's /etc/localtime and /etc/timezone # instead; TZ is the same intent without depending on either file existing. timezone: Asia/Kolkata # A scratch directory for the sshd *inside* the image, which this deployment # does not use but which still has to write host keys somewhere. It is kept # outside the data tree on purpose: the process that runs it is the container's # root, which under keep-id is a subuid, and a subuid-owned file inside # {{ host_directory }} would break both a plain tar backup and the play's # "nothing in here belongs to another user" check. See readme.md. container_sshd_hostkeys: "{{ podman_data_dir }}/{{ container_name }}-sshd" # --------------------------------------------------------------------------- # The git-over-SSH entry point on the host # --------------------------------------------------------------------------- # Clients keep using git@git.tanshu.com:user/repo.git on port 22, served by the # *host's* sshd, exactly as they did on monoco. No sshd runs inside the # container: under keep-id the container's root is a subuid that cannot read the # authorized_keys Gitea writes as podman_user, so the second hop is a `podman # exec` instead of an ssh. See readme.md, "How git over SSH works now". # # `git_home` only has to exist — sshd runs the forced command from it — and its # uid is deliberately left to the system: nothing in this design pairs the host # account with the container's uid. See roles/ssh/tasks/main.yaml. git_user: git git_home: /home/git # The command Gitea writes into every authorized_keys line it maintains. This is # a contract, not a knob: it is already baked into the lines sitting in the file # copied from monoco, and renaming it locks every user out of git-over-SSH until # Gitea rewrites them. See roles/ssh/templates/gitea.j2. shim_path: /usr/local/bin/gitea # Runs as podman_user, via the sudoers drop-in, and does the podman exec. podman_exec_wrapper: /usr/local/bin/gitea-container-exec # What sshd calls to obtain the git user's authorized_keys. authorized_keys_command: /usr/local/bin/gitea-authorized-keys sshd_dropin_path: /etc/ssh/sshd_config.d/60-gitea.conf sshd_service: ssh sudoers_path: /etc/sudoers.d/gitea-ssh-passthrough # --------------------------------------------------------------------------- # The shared Caddy unit and its Caddyfile, both owned by caddy/playbook.yml # --------------------------------------------------------------------------- caddy_name: caddy caddyfile_path: "{{ podman_data_dir }}/{{ caddy_name }}/conf/Caddyfile" snippet_block: | {{ host }} { reverse_proxy {{ container_name }}:{{ container_port }} }