diff --git a/defaults/main.yml b/defaults/main.yml index ba30254..290a7e2 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -118,13 +118,21 @@ forgejo_systemd_hardening_file: '/etc/systemd/system/{{ forgejo_service_name }}. forgejo_systemd_read_write_paths: [] # Paths to hide from the service entirely (InaccessiblePaths=) forgejo_systemd_inaccessible_paths: [] -# ProtectHome= empties /home, /root and /run/user. It is safe ONLY while the -# builtin SSH server is in use: forgejo then never writes -# /.ssh/authorized_keys (models/asymkey returns immediately when -# START_SSH_SERVER is on) and only reads $HOME, which it never stats. +# ProtectHome= hides /home, /root and /run/user. Two things decide the value: +# +# * 'tmpfs' and NOT 'true', because the postgres driver looks for client +# certificates in $HOME/.postgresql/ on every TLS connection. With 'true' +# that stat returns EACCES and forgejo dies with +# "stat /home/git/.postgresql/postgresql.crt: permission denied", after ten +# connection attempts that say nothing about systemd. 'tmpfs' gives the +# same empty home and answers ENOENT, which the driver ignores. +# * hiding the home at all is safe ONLY while the builtin SSH server is in +# use: forgejo then never writes /.ssh/authorized_keys +# (models/asymkey returns immediately when START_SSH_SERVER is on). +# # ReadWritePaths= does NOT punch a hole through ProtectHome= (verified on # systemd 255), so with system sshd serving git this has to be false. -forgejo_systemd_protect_home: "{{ 'true' if (server_START_SSH_SERVER | default('true') | bool) else 'false' }}" +forgejo_systemd_protect_home: "{{ 'tmpfs' if (server_START_SSH_SERVER | default('true') | bool) else 'false' }}" # PrivateUsers= maps only root and the service user; every other uid becomes # nobody. Turn it off if forgejo has to reach a file owned by a third user, or # if the service user needs supplementary groups. diff --git a/templates/forgejo-hardening.conf.j2 b/templates/forgejo-hardening.conf.j2 index f583fb1..0b2887d 100644 --- a/templates/forgejo-hardening.conf.j2 +++ b/templates/forgejo-hardening.conf.j2 @@ -20,6 +20,9 @@ CapabilityBoundingSet= # The whole filesystem read only except the paths below. Every one of them must # exist, or systemd fails the namespace and forgejo never starts (226/NAMESPACE). ProtectSystem=strict +# 'tmpfs' rather than 'true': an empty home either way, but a missing file +# under it answers ENOENT instead of EACCES, and the postgres driver stats +# $HOME/.postgresql/postgresql.crt on every TLS connection. ProtectHome={{ forgejo_systemd_protect_home }} ReadWritePaths={{ ([forgejo_work_dir, forgejo_repository_data] + forgejo_systemd_read_write_paths) | unique | map('quote') | join(' ') }} {% if forgejo_systemd_inaccessible_paths | length > 0 %}