Five documentation fixes that do not depend on anything upstream. **PostgreSQL version in the docker guide** (closes #224) The compose example pinned `postgres:14`, the oldest version Gitea still tests against, and PostgreSQL does not upgrade its data directory on its own, so every new deployment that copies the example is one major upgrade behind from day one. The example now uses `postgres:18`, with a note about why the major version is pinned and a pointer to the supported range. **SELinux** (closes #211) `setcap 'cap_net_bind_service=+ep'` is what the docs suggest for binding to a port below 1024, but it is denied under SELinux depending on the label of the binary, and it is lost on every upgrade because the capability lives in an extended attribute of the file. A new page describes granting the capability through the systemd unit instead (`AmbientCapabilities`, plus `PrivateUsers=false` when the sandboxing gets in the way), the `semanage fcontext` / `restorecon` labels for the binary and the data directory, and how to read the denials with `ausearch` and `audit2allow`. It is linked from the binary and source installation pages and from the `gitea web` notes. **OAuth2 and OpenID Connect** (closes #106) The authentication page documented LDAP, PAM, SMTP, FreeIPA, SPNEGO and the reverse proxy, but not that Gitea can log users in against an external OAuth2 or OpenID Connect provider, which made it look like the only OAuth2 support is Gitea acting as the provider. The new section covers the fields of the authentication source, the callback URL to register at the provider, the group and claim mappings, and the `[oauth2_client]` settings, and separates it from the older OpenID 2.0 sign-in. **IIS reverse proxy** (closes #39) Application Request Routing rewrites the host of the `Location` header back to the internal one while "Reverse rewrite host in response headers" is on, which sends the browser to `127.0.0.1:3000` after a sign-in. The IIS section now says to turn it off and where the setting lives. **ACME in containers** (closes #292) `ACME_DIRECTORY` defaults to the relative path `https`, which is resolved against the working directory of the process. In the official image that is `/app/gitea`, outside the `/data` volume, so a recreated container asks the CA for a new certificate and runs into the Let's Encrypt limit of 5 certificates per week for the same domains. The HTTPS page now warns about it and shows an absolute path inside the volume; the cheat sheet entry points at the warning. The default itself is being changed in https://github.com/go-gitea/gitea/pull/35851, the note stays useful for every released version. Only `docs/` is touched, the released versions keep their content. <!-- cloudflare-preview --> Preview: https://pr-513.docs-gitea-com.pages.dev Reviewed-on: https://gitea.com/gitea/docs/pulls/513 Reviewed-by: bircni <[email protected]> Co-authored-by: Lunny Xiao <[email protected]>
3.9 KiB
date, slug, sidebar_position
| date | slug | sidebar_position |
|---|---|---|
| 2026-08-13T00:00:00+00:00 | selinux | 45 |
Running Gitea with SELinux
Distributions such as Fedora, RHEL, CentOS Stream, Rocky Linux and AlmaLinux ship SELinux in enforcing mode. Packages from a distribution repository come with a policy that fits them, but an installation from binary or from source puts the binary and the data in paths the policy knows nothing about, so the labels have to be set by hand. The same applies to any other distribution where SELinux is enabled.
Do not turn SELinux off to work around the problems below. setenforce 0 is
useful to confirm that a failure really comes from SELinux, but switch it back
on with setenforce 1 afterwards.
Binding to a port below 1024
Gitea should not run as root, and an unprivileged process may not bind to a
port below 1024. Granting the capability to the binary with
setcap 'cap_net_bind_service=+ep' /usr/local/bin/gitea is often suggested,
but it is the least robust option:
- file capabilities are stored in an extended attribute of the binary, so they are lost every time the binary is replaced, which happens on every upgrade.
- the SELinux policy has to allow the capability for the label of the binary,
and a binary in a path such as
/optusually does not carry a label that allows it.
Let systemd grant the capability to the process instead. It works with SELinux in enforcing mode and survives upgrades, the sample unit file has the two lines commented out:
[Service]
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
Reload and restart the service afterwards:
sudo systemctl daemon-reload
sudo systemctl restart gitea
If the capability still does not reach the process, add PrivateUsers=false to
the unit: the sandboxing runs the service in a user namespace where the
capability does not apply to the host.
Two alternatives avoid the privileged port altogether, and are what most installations end up doing:
- put a reverse proxy in front of Gitea
and let it own ports
80and443. - redirect the port in the firewall, for example with
firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=3000.
Labelling the binary and the data directory
If Gitea starts but cannot read its own files, or fails in ways that are not
explained by the file permissions, the labels are usually wrong. The tools come
from the policycoreutils-python-utils package.
Label the binary as an executable and the data directory as application state:
sudo semanage fcontext -a -t bin_t '/usr/local/bin/gitea'
sudo restorecon -v /usr/local/bin/gitea
sudo semanage fcontext -a -t var_lib_t '/var/lib/gitea(/.*)?'
sudo restorecon -Rv /var/lib/gitea
semanage fcontext records the rule, restorecon applies it to the files that
are already there. Use the two together rather than chcon, whose labels are
reverted by the next relabel of the file system.
Adjust the paths if the installation does not follow
installation from binary; the configuration in /etc/gitea
is read with the label distributions give to /etc, so it normally needs
nothing.
Finding out what was denied
SELinux denials are written to the audit log, not to the Gitea log, so a failure often looks like a permission problem without any further explanation:
sudo ausearch -m AVC -c gitea --start recent
If a denial shows up that the rules above do not cover, turn it into a local
policy module. Read what it allows before installing it, audit2allow writes
down whatever was denied, including the things that were denied for a good
reason:
sudo ausearch -c gitea --raw | audit2allow -M gitea-local
cat gitea-local.te
sudo semodule -i gitea-local.pp
If the denials point at a path Gitea should not be using at all, fix the path instead of allowing the access.