Hosten Sie Ihren eigenen KI-Agenten mit OpenClaw - kostenlose 1-Klick-Installation!

Gitea-Server absichern: die Hardening-Checkliste

Gitea-Server absichern: die Hardening-Checkliste (Titelbild)

Kurz gesagt. Eine Gitea-Standardinstallation lässt den Web-Installer offen, erlaubt jedem die Registrierung und macht Repositories standardmäßig öffentlich. Diese Checkliste deckt die fünf app.ini-Einstellungen ab, die diese Lücken schließen (INSTALL_LOCK, DISABLE_REGISTRATION, REQUIRE_SIGNIN_VIEW, RUN_USER, Secret Keys), dazu erzwungene 2FA (ab Gitea 1.24), Scoping von Access Tokens, private Repositories per Voreinstellung, Branch Protection und eine abgesicherte Nginx-Reverse-Proxy-Konfiguration mit den Headern, die Gitea braucht.

Bevor du startest: die VPS-Sicherheitsbasis

Gitea-Hardening baut auf einem abgesicherten Host auf. Bevor du die folgenden Schritte durchgehst, stelle sicher, dass dein VPS bereits Folgendes hat:

Jeder Punkt ist ein Einstieg in einem Satz; die verlinkten Tutorials decken das komplette Vorgehen ab. Überspringe diese Basis nicht: Gitea-spezifisches Hardening bringt nichts gegen einen Angreifer, der über eine schwache Host-Konfiguration bereits SSH-Zugang hat.

Gitea-spezifisches Hardening (der Teil, den nichts anderes abdeckt)

Diese Punkte sind einzigartig für Gitea. Sie tauchen in keiner OS-Anleitung auf, und eine Gitea-Standardinstallation setzt die meisten davon nicht um. Arbeite jeden Abschnitt durch; alle Änderungen kommen in die app.ini, die bei den meisten Linux-Paketinstallationen unter /etc/gitea/app.ini liegt oder bei einer Binary-Installation unter custom/conf/app.ini.

Die app.ini-Konfiguration absichern

Fünf Einstellungen in der app.ini sind einzigartig für Gitea und werden von keiner OS-Hardening-Anleitung abgedeckt. Setze sie alle, bevor du deine Instanz ans Netzwerk hängst:

  • INSTALL_LOCK = true schließt den /install-Web-Endpoint. Der Web-Installer setzt das nach Abschluss automatisch, aber prüfe vor dem Go-live, ob es in der app.ini steht – fehlt es oder steht es aus irgendeinem Grund auf false, kann jeder, der den Endpoint erreicht, deine Instanz komplett neu konfigurieren.
  • REQUIRE_SIGNIN_VIEW = true erzwingt ein Login, bevor jemand ein Repository, ein Issue oder ein Nutzerprofil ansehen kann. Für private Team-Instanzen unverzichtbar.
  • RUN_USER = git weist Gitea an, unter einem dedizierten Nicht-Root-Systemkonto zu laufen, nicht als root. Prüfe deine systemd-Unit und korrigiere das zuerst, falls es falsch ist.
  • SECRET_KEY und INTERNAL_TOKEN werden beide vom Installer automatisch generiert. Prüfe, ob jeder Wert ein langer, zufälliger String ist und kein leerer oder ein Platzhalter. Rotiere sie, wenn du dir über ihre Herkunft nicht sicher bist.

Nach jeder Änderung an der app.ini neu starten und prüfen:

sudo systemctl restart gitea && sudo systemctl status gitea

Offene Registrierung deaktivieren und Sign-up kontrollieren

Setze DISABLE_REGISTRATION = true im Abschnitt [service] der app.ini. Damit kann niemand mehr sein eigenes Konto anlegen; neue Nutzer müssen von einem Administrator angelegt werden, entweder über Site Administration > Users oder per CLI-Befehl gitea admin user create. Wenn du externes Sign-in per OAuth oder LDAP brauchst, kannst du diese Quellen weiterhin für bestehende Konten konfigurieren: lege den Nutzer zuerst in Gitea an und verknüpfe dann die OAuth-Identität über sein Profil.

2FA und starke Authentifizierung erzwingen

Gitea unterstützt TOTP-basierte 2FA nativ. Nutzer richten sie unter Settings > Security > Two-Factor Authentication mit einer beliebigen RFC-6238-kompatiblen App ein (Google Authenticator, Authy, 1Password und Ähnliche).

Um 2FA für alle Nutzer zu erzwingen, füge Folgendes in den Abschnitt [service] der app.ini ein (erfordert Gitea 1.24 oder neuer):

[service]
TWO_FACTOR_AUTH = enforced

Nach dem Neustart wird jeder Nutzer ohne eingerichtete 2FA beim nächsten Login auf die Einrichtungsseite umgeleitet und bleibt von den Repositories ausgesperrt, bis er sie abschließt. Gitea Enterprise hat dafür ein eigenes Flag (ENFORCE_TWO_FACTOR_AUTH in [security]), aber beim Community-Gitea ab 1.24 deckt TWO_FACTOR_AUTH = enforced denselben Bereich ab.

Hygiene bei Access Tokens und Deploy Keys

Gib jedem Personal Access Token nur die minimalen Berechtigungen, die er braucht. Gitea unterstützt fein abgestufte Token-Scopes (zum Beispiel read:repository oder write:package) unter Settings > Applications. Setze für jeden Token ein Ablaufdatum; Tokens, die nie ablaufen, sammeln sich unbemerkt an. Für Nur-Lese-Zugriff aus CI/CD erstelle einen dedizierten Token, der ausschließlich auf read:repository beschränkt ist, statt einen Personal Token mit Vollzugriff zu nutzen. Rotiere jeden Token bei Verdacht auf eine Kompromittierung sofort.

Zugriffskontrolle für Repositories und Organisationen

Damit alle neuen Repositories standardmäßig privat sind, setze Folgendes im Abschnitt [repository] der app.ini:

[repository]
DEFAULT_PRIVATE = true

Damit ist die Checkbox „Private“ schon angehakt, sobald ein Nutzer ein neues Repository anlegt. Wenn du weiter gehen und verhindern willst, dass Repositories jemals öffentlich werden, setze stattdessen FORCE_PRIVATE = true – Administratoren können die Sichtbarkeit weiterhin ändern, normale Nutzer nicht. Für die Zugriffskontrolle nutze Organisationen und Teams statt direkter Berechtigungen pro Nutzer und vergib nur die minimal nötige Rolle: Reader, Writer oder Owner. Aktiviere Branch Protection auf Repositories, die in Produktion deployen: gehe zu Repository Settings > Branches, lege eine Regel für deinen Default-Branch an, verlange mindestens ein Review-Approval und deaktiviere Force-Push.

Reverse-Proxy- und TLS-Hardening für Gitea

Gitea kann TLS direkt verarbeiten, empfohlen ist aber, einen Reverse Proxy davorzusetzen und TLS dort zu terminieren. Dieser Abschnitt behandelt Nginx. In diesem Setup muss Gitea nichts von TLS wissen; es muss nur seine öffentliche URL kennen.

app.ini – erforderliche Einstellungen für jedes Reverse-Proxy-Setup

Setze diese in den Abschnitten [server] und [session], bevor du den Proxy hochziehst:

[server]
ROOT_URL = https://git.example.com/

[session]
COOKIE_SECURE = true

ROOT_URL muss exakt der öffentlichen HTTPS-URL entsprechen. Eine Abweichung zerschießt Clone-URLs, OAuth-Redirects und Webhook-Payloads. COOKIE_SECURE = true markiert Session-Cookies als Secure, sodass Browser sie nur über HTTPS senden; ohne die Einstellung lassen sich Cookies, die über einen HTTPS-Proxy ausgestellt wurden, über HTTP wiederholen.

Nginx-Server-Block

Die Proxy-Header unten sind von Gitea erforderlich, nicht optional. Ohne X-Forwarded-Proto und X-Real-IP sieht Gitea weder die echte Client-IP noch das Request-Schema, was IP-basiertes Rate Limiting, Audit-Logs und OAuth-Flows zerschießt.

server {
    listen 443 ssl;
    server_name git.example.com;

    ssl_certificate     /etc/letsencrypt/live/git.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/git.example.com/privkey.pem;

    # Security headers
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    add_header Referrer-Policy strict-origin-when-cross-origin;

    # Hide Gitea version
    proxy_hide_header X-Gitea-Version;

    # Rate-limit the login and sign-up routes
    limit_req zone=gitea_auth burst=5 nodelay;

    location / {
        client_max_body_size 512M;
        proxy_pass http://127.0.0.1:3000;

        # Required headers — do not remove
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Connection        $http_connection;
        proxy_set_header Upgrade           $http_upgrade;
    }
}

server {
    listen 80;
    server_name git.example.com;
    return 301 https://$host$request_uri;
}

Definiere die Rate-Limit-Zone im http-Block der nginx.conf (außerhalb des server-Blocks):

limit_req_zone $binary_remote_addr zone=gitea_auth:10m rate=10r/m;

client_max_body_size 512M entspricht dem Wert aus der Gitea-Doku. Ohne diese Einstellung geben Repository-Pushes und Datei-Uploads oberhalb des Nginx-Standardlimits (1 MB) einen 413-Fehler zurück.

Stelle TLS-Zertifikate über Let’s Encrypt mit Certbot aus; das komplette Vorgehen findest du in der Anleitung Wie installiere ich ein SSL-Zertifikat auf meinem Linux-Server mit Let’s Encrypt?.

Nach jeder Nginx-Änderung reload statt restart, um aktive Verbindungen nicht zu unterbrechen:

sudo nginx -t && sudo systemctl reload nginx

Backups und Update-Disziplin

Gitea sichern

Gitea bringt einen dump-Subbefehl mit, der Repositories, Datenbank, Konfiguration und Attachments in eine einzige Zip-Datei archiviert:

sudo systemctl stop gitea
sudo -u git gitea dump -c /etc/gitea/app.ini --file /backups/gitea-$(date +%Y%m%d).zip
sudo systemctl start gitea

Der Dump enthält die SQLite-Datenbank oder einen einfachen SQL-Export. Für PostgreSQL oder MySQL nimm einen separaten Datenbank-Dump, bevor du gitea dump ausführst:

# PostgreSQL
pg_dump -U gitea gitea > /backups/gitea-db-$(date +%Y%m%d).sql

# MySQL / MariaDB
mysqldump -u gitea -p gitea > /backups/gitea-db-$(date +%Y%m%d).sql

Bewahre Backups außerhalb des Servers auf. Contabo bietet zwei sich ergänzende Optionen:

  • Auto Backup Add-On – ein kostenpflichtiges Add-On für Cloud VPS, das täglich automatische Backups deiner kompletten VPS-Disk erstellt und diese bis zu 10 Tage lang auf einem separaten Contabo-Speichersystem außerhalb des Servers ablegt. Das gibt dir einen vollständigen Wiederherstellungspunkt, praktisch, um ein fehlgeschlagenes Gitea-Upgrade zurückzurollen oder einen kompromittierten Host wiederherzustellen.
  • Manuelles Auslagern per rclone – das gitea dump-Archiv ist ein portabler, Gitea-spezifischer Export unter deiner Kontrolle. Synchronisiere ihn per rclone in den Contabo Object Storage, um ihn über die 10 Tage hinaus langfristig aufzubewahren. Das komplette Vorgehen zeigt dir die rclone-Backup-Anleitung.

Für eine produktive Gitea-Instanz lohnen sich beide: Auto Backup für die schnelle Wiederherstellung des kompletten Systems, gitea dump plus Object Storage für langfristige Archivierung und Portabilität.

Aktuell bleiben

Gitea veröffentlicht häufig Patch-Versionen, und Security-Fixes kommen in Patch-Releases. Abonniere den Gitea-Release-Feed unter https://github.com/go-gitea/gitea/releases.atom, damit du benachrichtigt wirst. Nimm vor jedem Update einen vollständigen gitea dump und einen Datenbank-Snapshot; die Datenbank-Migrationen von Gitea laufen beim Start automatisch und sind ohne Backup nicht rückgängig zu machen. Wenn du ein Team-Deployment betreibst, teste Updates zuerst auf einer Staging-Instanz. Eine einzelne Patch-Version hinterher zu sein ist okay; mehrere Minor-Versionen hinterher zu sein nicht.

Eine Kompromittierung erkennen

Gitea erzeugt Access-Logs, Push-Logs und Admin-Audit-Logs. Das ist die erste Stelle, an der du nachsiehst, wenn du unbefugten Zugriff vermutest. Eine vollständige Liste der Signale, auf die du achten solltest, und der Reaktionsschritte findest du in der Anleitung zu den Anzeichen einer Kompromittierung. Als Grundregel: achte auf unerwartete neue Nutzerkonten, auf SSH-Keys, die zu bestehenden Konten hinzugefügt wurden, und auf Repositories, die privat waren und jetzt öffentlich sind.

Warum ein abgesichertes Gitea auf Contabo läuft

Wenn du Gitea auf einem Contabo VPS hostest, hast du reichlich Ressourcen. Deine CI-Runner, deine Git-Prozesse und deine Datenbank bekommen alle die CPU und den RAM, für die du bezahlt hast, ohne dass andere Tenants sie dir streitig machen.

Für Teams, die der DSGVO oder internen Data-Sovereignty-Richtlinien unterliegen, zählt die EU-Datenhaltung. Ein EU-Rechenzentrum hält Quellcode und Commit-History in EU-Jurisdiktion, auf einer Infrastruktur, die zu 100 % DSGVO-konform ist.

Snapshots verdienen im Kontext von Gitea-Updates besondere Erwähnung. Nimm vor einem großen Upgrade einen Snapshot des kompletten VPS über das Customer Control Panel. Schlägt die Migration fehl oder bringt sie eine Regression, rollst du in Minuten zurück, statt aus einem Datenbank-Backup wiederherzustellen.

VPS-Einstiegspläne beginnen bei unter 7 Euro pro Monat und geben dir genug Spielraum, um Gitea neben einer PostgreSQL-Datenbank und einem Nginx-Proxy zu betreiben, ohne dass die Ressourcen knapp werden.

FAQ: Gitea-Sicherheit

Wie deaktiviere ich die öffentliche Registrierung in Gitea?

Füge DISABLE_REGISTRATION = true unter [service] in der app.ini ein und starte Gitea neu. Neue Konten müssen dann von einem Administrator angelegt werden, über Site Administration > Users oder per gitea admin user create. Beachte, dass das auch die OAuth-basierte Kontoerstellung blockiert – lege Nutzer erst manuell an und verknüpfe die OAuth-Identitäten über ihr Profil.

Unterstützt Gitea Zwei-Faktor-Authentifizierung?

Ja. Nutzer richten sie unter Settings > Security > Two-Factor Authentication mit einer beliebigen TOTP-App ein. Um sie site-weit zu erzwingen, füge TWO_FACTOR_AUTH = enforced unter [service] in der app.ini ein und starte neu (erfordert Gitea 1.24+). Gitea Enterprise hat dafür ein eigenes Flag. API-Zugriff per Personal Token ist unabhängig von der Edition nicht durch 2FA geschützt.

Wie stelle ich Gitea hinter HTTPS?

Setze ROOT_URL = https://git.example.com/ in [server] und COOKIE_SECURE = true in [session] in der app.ini. Richte Nginx auf http://127.0.0.1:3000 und übergib die erforderlichen Header (Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto). Stelle ein Zertifikat per Certbot aus – das komplette Vorgehen findest du in der Anleitung Wie installiere ich ein SSL-Zertifikat auf meinem Linux-Server mit Let’s Encrypt?.

Wie sichere ich einen Gitea-Server?

Führe gitea dump -c /etc/gitea/app.ini aus, um ein Zip-Archiv aus Repositories, Konfiguration, Datenbank und Attachments zu erzeugen. Für PostgreSQL oder MySQL nimm vorher einen separaten pg_dump oder mysqldump. Bewahre Archive außerhalb des Servers auf – Contabo Auto Backup deckt tägliche Full-Disk-Snapshots ab, oder synchronisiere den Dump per rclone in den Object Storage für die langfristige Aufbewahrung.

Nach oben scrollen