diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..913effa --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,85 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Cos'è questo repo + +Non è il codice sorgente di Gitea: è lo **stack Docker di deploy** (Gitea + Caddy) di un server Git self-hosted. Contiene solo `docker-compose.yml`, `Caddyfile`, `README.md` (requisiti, porte, troubleshooting) e `SETUP.md` (wizard iniziale). Non ci sono build, lint o test. Documentazione in italiano; rispondere all'utente in italiano. + +Riferimenti dettagliati in [docs/](docs/README.md): config-reference, cli, backup-restore, reverse-proxy-caddy, ssh, actions-runner, email, authentication, security-hardening, troubleshooting. Leggere il file pertinente prima di rispondere a domande di configurazione. + +Fonti ufficiali per risolvere dubbi di configurazione (consultarle con WebFetch prima di rispondere a memoria): https://docs.gitea.com (sito prodotto: https://about.gitea.com). + +## Comandi + +```bash +docker compose up -d # avvio +docker compose pull && docker compose up -d # aggiornamento (image gitea/gitea:latest, non pinned) +docker compose logs -f gitea # log +docker compose restart gitea # necessario dopo modifiche a app.ini +docker exec -u git gitea gitea # CLI Gitea nel container (utente git, non root) +ss -tlnp | grep '2222\|3000\|80\|443' # verifica porte occupate +``` + +## Architettura + +``` +Internet ─→ Caddy :80/:443 ─→ gitea:3000 (rete interna compose; sull'host solo 127.0.0.1:3000) + ─→ host :2222 ─→ gitea:22 (SSH Git, nessun proxy) +``` + +- **Caddy** (`caddy:2`) fa TLS automatico Let's Encrypt e `reverse_proxy gitea:3000`. Il dominio nel `Caddyfile` è il placeholder `your-domain.com`; va allineato a `ROOT_URL` di Gitea. Lo SSH **non** passa da Caddy. +- **Gitea** salva tutto in `./gitea-data` (→ `/data`): config in `gitea-data/gitea/conf/app.ini`, repo in `gitea-data/git/repositories`, SQLite in `gitea-data/gitea/gitea.db`. DB scelto al wizard: SQLite3. +- `gitea-data/`, `caddy/data/`, `caddy/config/` sono runtime e in `.gitignore`: mai committarli (contengono secrets e chiavi host SSH). +- Il wizard (SETUP.md) scrive `app.ini`; la porta SSH mostrata negli URL (`SSH_PORT`) deve coincidere con la porta **sinistra** del mapping compose (`2222`). Cambiando il mapping, aggiornare anche `SSH_PORT` (e `SSH_LISTEN_PORT` resta 22). + +## Schema configurazione Gitea (riferimento rapido) + +Qualunque chiave di `app.ini` si può impostare/sovrascrivere nel servizio `gitea` di compose con `environment:` nella forma `GITEA__sezione__CHIAVE=valore` (applicata a ogni avvio; variante `..._FILE` per leggere da file). Per UID/GID: `USER_UID`/`USER_GID` (default 1000) devono combaciare col proprietario di `./gitea-data`. + +Sezioni e chiavi principali (default da docs.gitea.com/administration/config-cheat-sheet): + +| Sezione | Chiavi chiave (default) | +|---|---| +| (root) | `APP_NAME`, `RUN_USER`, `RUN_MODE` (`prod`) | +| `[server]` | `ROOT_URL` (obbligatorio dietro proxy: `https://dominio/`), `DOMAIN`, `HTTP_PORT` (3000), `SSH_DOMAIN`, `SSH_PORT` (22; porta mostrata), `SSH_LISTEN_PORT`, `DISABLE_SSH`, `START_SSH_SERVER` (false), `LFS_START_SERVER` (false), `OFFLINE_MODE`, `APP_DATA_PATH` | +| `[database]` | `DB_TYPE` (`sqlite3`/`postgres`/`mysql`/`mssql`), `PATH` (sqlite), `HOST`, `NAME`, `USER`, `PASSWD`, `AUTO_MIGRATION` (true) | +| `[security]` | `INSTALL_LOCK` (true dopo wizard), `SECRET_KEY` (**mai cambiare** dopo l'install: rende illeggibili 2FA ecc.), `INTERNAL_TOKEN`, `REVERSE_PROXY_TRUSTED_PROXIES`, `PASSWORD_HASH_ALGO` (pbkdf2), `MIN_PASSWORD_LENGTH` (8), `DISABLE_GIT_HOOKS` (true) | +| `[service]` | `DISABLE_REGISTRATION`, `REQUIRE_SIGNIN_VIEW`, `REGISTER_EMAIL_CONFIRM`, `ENABLE_CAPTCHA`, `DEFAULT_USER_VISIBILITY`, `ENABLE_NOTIFY_MAIL` | +| `[mailer]` | `ENABLED`, `PROTOCOL` (`smtp`/`smtps`/`smtp+starttls`/`sendmail`), `SMTP_ADDR`, `SMTP_PORT`, `FROM`, `USER`, `PASSWD` | +| `[repository]` | `ROOT`, `DEFAULT_BRANCH` (main), `DEFAULT_PRIVATE` (last), `DISABLE_HTTP_GIT`, `DISABLED_REPO_UNITS`, `MAX_CREATION_LIMIT` (-1) | +| `[ssh.minimum_key_sizes]` | `RSA` 3071, `ED25519` 256, `ECDSA` 256, `DSA` -1 | +| `[actions]` | `ENABLED` (true); retention log 365g / artifact 90g; richiede un `act_runner` separato (token: `gitea actions generate-runner-token`) — non presente in questo stack | +| `[session]` | `PROVIDER` (file), `COOKIE_NAME`, `SESSION_LIFE_TIME` (86400) | +| `[log]` | `MODE` (console), `LEVEL` (Info) | +| `[cron]` / `[cron.*]` | task pianificati (mirror `@every 10m`, health check `@midnight`, update checker `@every 168h`) | +| `[attachment]` | `MAX_SIZE` (100 MB), `MAX_FILES` (5) | +| `[lfs]` / `[packages]` | storage `local`; limiti pacchetti `-1` (illimitati) | +| `[oauth2]`, `[cache]`, `[api]`, `[other]` | token lifetime 3600s, adapter `memory`, `MAX_RESPONSE_ITEMS` 50 | + +Per chiavi non elencate (indexer, ui, openid, webhook, storage S3/minio, ecc.) consultare il cheat sheet completo. + +## CLI utili (dentro il container come utente `git`) + +- `gitea admin user create --username U --password P --email E [--admin] [--must-change-password]` +- `gitea admin user list|delete|change-password|generate-access-token` +- `gitea admin regenerate hooks|keys` (obbligatorio dopo restore o spostamenti di directory) +- `gitea admin auth add-oauth|add-ldap|add-smtp|list` +- `gitea doctor check [--all] [--fix]`, `gitea doctor recreate-table` +- `gitea generate secret SECRET_KEY|INTERNAL_TOKEN|JWT_SECRET` +- `gitea manager shutdown|restart|flush-queues|processes` +- `gitea migrate`, `gitea dump`, `gitea actions generate-runner-token` +- Passare `-c /data/gitea/conf/app.ini` se la config non viene trovata. + +## Backup / restore + +- `gitea dump` richiede Gitea **fermo** per essere consistente. In Docker: `docker exec -u git -w /tmp gitea /usr/local/bin/gitea dump -c /data/gitea/conf/app.ini` (cwd scrivibile per evitare errori di permessi sullo zip; `RUN_USER` in app.ini deve essere `git`). Alternativa pragmatica: fermare lo stack e copiare `./gitea-data`. +- Il dump produce zip con `app.ini`, `custom/`, `data/`, `repos/`, `gitea-db.sql`. +- Restore manuale: estrarre `data/*` in `/data/gitea`, `repos/*` in `/data/git/repositories`, importare il DB, `chown -R git:git /data`, poi `gitea admin regenerate hooks`. + +## Note operative + +- Immagine `gitea/gitea:latest` non fissata: gli aggiornamenti possono includere migrazioni DB automatiche (`AUTO_MIGRATION`); fare backup prima di `pull`. +- Modifiche a `Caddyfile`: `docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile`. +- Se la 2222 è occupata, cambiare solo la porta host nel compose e aggiornare `SSH_PORT` in Gitea (vedi README). +- Cambiare dominio ⇒ aggiornare insieme `Caddyfile`, `ROOT_URL`, `DOMAIN`, `SSH_DOMAIN` e `~/.ssh/config` dei client. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..973f0d9 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,16 @@ +# Indice documentazione + +Note di riferimento per questo stack (Gitea + Caddy). Fonte: https://docs.gitea.com (verificare lì per versioni recenti). + +| File | Contenuto | +|---|---| +| [config-reference.md](config-reference.md) | Sezioni e chiavi di `app.ini`, variabili d'ambiente `GITEA__...` | +| [cli.md](cli.md) | Comandi `gitea` (admin, doctor, dump, manager...) | +| [backup-restore.md](backup-restore.md) | Backup, restore, aggiornamenti | +| [reverse-proxy-caddy.md](reverse-proxy-caddy.md) | Caddy, ROOT_URL, WebSocket, sub-path, IP reali | +| [ssh.md](ssh.md) | Porta SSH, chiavi, client config, problemi comuni | +| [actions-runner.md](actions-runner.md) | Gitea Actions e runner (non presente nello stack) | +| [email.md](email.md) | Configurazione `[mailer]` | +| [authentication.md](authentication.md) | OAuth2, LDAP, SMTP, proxy header, sicurezza account | +| [security-hardening.md](security-hardening.md) | Fail2ban, registrazione, impostazioni sicure | +| [troubleshooting.md](troubleshooting.md) | Problemi tipici e diagnosi | diff --git a/docs/actions-runner.md b/docs/actions-runner.md new file mode 100644 index 0000000..8ef3346 --- /dev/null +++ b/docs/actions-runner.md @@ -0,0 +1,34 @@ +# Gitea Actions e runner + +Gitea Actions (compatibile con workflow GitHub Actions) è abilitato di default da 1.21 (`[actions] ENABLED=true`). **Lo stack attuale non include un runner**: senza di esso i job restano in coda. + +## Passi +1. Abilitare Actions nel repo: Impostazioni → "Abilita Actions del repository" (disattivo per default). +2. Ottenere un token di registrazione: + - istanza: `/-/admin/actions/runners` + - organizzazione: `//settings/actions/runners` + - repo: `///settings/actions/runners` + - CLI: `gitea actions generate-runner-token [--scope owner/repo]` +3. Registrare e avviare il runner (binario "Gitea Runner", prima `act_runner`): +```bash +gitea-runner register --no-interactive --instance --token +gitea-runner daemon +``` + L'istanza deve essere raggiungibile da runner e container dei job: **non** `localhost`/`127.0.0.1`; usare l'URL pubblico o un indirizzo LAN. Il file `.runner` creato non va modificato a mano. +4. Il runner usa Docker per isolare i job (daemon Docker necessario; montare `/var/run/docker.sock` se containerizzato — implica privilegi elevati sull'host). +5. Workflow in `.gitea/workflows/*.yaml` (anche `.github/workflows`): +```yaml +name: demo +on: [push] +jobs: + build: + runs-on: ubuntu-latest + steps: + - run: echo "ciao" +``` +`runs-on` deve corrispondere a un'etichetta (label) registrata dal runner. + +## Config server +`[actions]`: DEFAULT_ACTIONS_URL (github), log retention 365g, artifact 90g, timeout task zombie 10m / endless 3h / abandoned 24h. Segreti e variabili: Impostazioni repo/org → Actions. + +Non verificati qui (consultare docs.gitea.com/usage/actions): compose del runner, variabili d'ambiente, formato label. diff --git a/docs/authentication.md b/docs/authentication.md new file mode 100644 index 0000000..2dccb83 --- /dev/null +++ b/docs/authentication.md @@ -0,0 +1,12 @@ +# Autenticazione + +Gestione da Amministrazione sito → Origini di autenticazione, o CLI `gitea admin auth ...`. + +- **OAuth2 / OpenID Connect** (Keycloak, GitHub, Google...): bottone "Accedi con". Il redirect URI deve combaciare esattamente (`https://your-domain.com/user/oauth2//callback`). Auto-registrazione e linking account in `[oauth2_client]`. +- **LDAP** (bind DN o template DN, sync utenti, filtro admin, gruppi) e **FreeIPA**. +- **SMTP**: valida login contro un mail server (PLAIN/LOGIN). +- **PAM**: richiede build custom, non nei binari ufficiali. +- **SPNEGO/SSPI**: SSO Windows/Kerberos. +- **Header del reverse proxy** (`X-WEBAUTH-USER`): Gitea deve essere raggiungibile *solo* dal proxy (qui la 3000 è già su 127.0.0.1, ma attenzione se si espone altrimenti). +- Account locali: 2FA (TOTP/WebAuthn) dalle impostazioni utente; token di accesso con scope; chiavi SSH e GPG. (Dettagli 2FA/token/GPG non verificati: docs.gitea.com/usage/authentication e pagine collegate.) +- `SECRET_KEY` cifra i segreti 2FA: non cambiarla. diff --git a/docs/backup-restore.md b/docs/backup-restore.md new file mode 100644 index 0000000..40edd47 --- /dev/null +++ b/docs/backup-restore.md @@ -0,0 +1,38 @@ +# Backup, restore, aggiornamenti + +## Cosa salvare +Tutto lo stato è in `./gitea-data` (repo, SQLite, app.ini con SECRET_KEY, chiavi host SSH) e `./caddy/data` (certificati TLS, riemettibili). Nessuno dei due è versionato. + +## Metodo semplice (consigliato per SQLite) +```bash +docker compose stop gitea +tar czf gitea-backup-$(date +%F).tgz gitea-data +docker compose start gitea +``` +Conservare il backup fuori dall'host e **insieme** a `app.ini` (senza SECRET_KEY i dati cifrati/2FA sono persi). + +## gitea dump +Richiede Gitea fermo per consistenza. In Docker (RUN_USER=git in app.ini): +```bash +docker exec -u git -w /tmp gitea /usr/local/bin/gitea dump -c /data/gitea/conf/app.ini +docker cp gitea:/tmp/gitea-dump-*.zip . +``` +Lo zip contiene `app.ini`, `custom/`, `data/`, `repos/`, `gitea-db.sql`, `log/` (non necessario). Opzioni: `--file`, `--tempdir`, `--type`, `--database`, `--skip-repository`, `--skip-log`, ecc. Per MySQL/Postgres preferire `mysqldump`/`pg_dump`. + +## Restore (manuale, non esiste comando) +1. Estrarre lo zip. +2. `data/*` → `/data/gitea`, `repos/*` → `/data/git/repositories`, `app.ini` → `/data/gitea/conf/`. +3. Importare `gitea-db.sql` (con SQLite basta ripristinare `gitea.db`). +4. `chown -R git:git /data` (nel container). +5. `gitea admin regenerate hooks`; se problemi `gitea doctor check --all --fix`. + +## Aggiornamento +```bash +# 1) backup 2) poi: +docker compose pull +docker compose up -d +``` +L'immagine è `gitea/gitea:latest`: per aggiornamenti controllati fissare un tag (es. `gitea/gitea:1.x`) e leggere le note di rilascio. Le migrazioni DB partono in automatico (`AUTO_MIGRATION`) e non sono reversibili: il backup pre-update è l'unico rollback. + +## Cambio tipo di database +`gitea dump --database postgres` genera SQL nel formato di destinazione; poco testato, scegliere il DB giusto dall'inizio. Prima: `gitea doctor check --all --fix` e `doctor recreate-table`. diff --git a/docs/cli.md b/docs/cli.md new file mode 100644 index 0000000..7a37952 --- /dev/null +++ b/docs/cli.md @@ -0,0 +1,42 @@ +# CLI di Gitea + +Eseguire nel container come utente `git` (mai root): + +```bash +docker exec -u git gitea gitea +# se non trova la config: +docker exec -u git gitea gitea -c /data/gitea/conf/app.ini +``` +Opzioni globali: `--config/-c`, `--custom-path/-C`, `--work-path/-w`. + +## Utenti +```bash +gitea admin user create --username U --password P --email E [--admin] [--must-change-password] [--random-password] [--access-token] +gitea admin user list [--admin] +gitea admin user change-password -u U -p NUOVA [--must-change-password] +gitea admin user delete --id N | --username U | --email E +gitea admin user generate-access-token -u U -t NOME --scopes ... +``` +## Manutenzione +```bash +gitea admin regenerate hooks # dopo restore/spostamento directory +gitea admin regenerate keys # rigenera authorized_keys +gitea doctor check --list # elenco controlli +gitea doctor check --all [--fix] # diagnosi/riparazione +gitea doctor recreate-table # backup DB prima! +gitea doctor convert # utf8 → utf8mb4 (MySQL) +gitea migrate # migrazioni DB (idempotente) +gitea generate secret SECRET_KEY|INTERNAL_TOKEN|JWT_SECRET +gitea cert --host your-domain.com +``` +## Autenticazione esterna +`gitea admin auth list|delete --id|add-oauth|add-smtp|add-ldap|add-ldap-simple|update-*` + +## Server in esecuzione +`gitea manager shutdown|restart|flush-queues|logging|processes [--stacktraces --cancel PID]` + +## Backup +`gitea dump` (vedi [backup-restore.md](backup-restore.md)), `dump-repo`/`restore-repo` per singoli repo. + +## Actions +`gitea actions generate-runner-token [--scope owner[/repo]]` diff --git a/docs/config-reference.md b/docs/config-reference.md new file mode 100644 index 0000000..dd168b2 --- /dev/null +++ b/docs/config-reference.md @@ -0,0 +1,112 @@ +# Riferimento configurazione (app.ini) + +Percorso nel container: `/data/gitea/conf/app.ini` (host: `./gitea-data/gitea/conf/app.ini`). Riavviare Gitea dopo ogni modifica: `docker compose restart gitea`. + +## Override via variabili d'ambiente (compose) + +Forma: `GITEA____=valore`, applicata a ogni avvio del container. +Per leggere da file: `GITEA______FILE=/percorso`. + +```yaml +services: + gitea: + environment: + - GITEA__server__ROOT_URL=https://your-domain.com/ + - GITEA__service__DISABLE_REGISTRATION=true + - USER_UID=1000 + - USER_GID=1000 +``` +`USER_UID/USER_GID` devono combaciare col proprietario di `./gitea-data`. +Sezioni con punto: `[ssh.minimum_key_sizes]` → `GITEA__ssh.minimum_key_sizes__RSA`. + +## Root +| Chiave | Default | Note | +|---|---|---| +| APP_NAME | "Gitea: Git with a cup of tea" | titolo pagine | +| RUN_USER | utente corrente | nel container `git`; deve combaciare con l'utente OS | +| RUN_MODE | prod | `dev` solo debug | + +## [server] +| Chiave | Default | Note | +|---|---|---| +| PROTOCOL | http | anche https, fcgi, http+unix | +| DOMAIN | localhost | host mostrato negli URL | +| HTTP_ADDR / HTTP_PORT | 0.0.0.0 / 3000 | | +| ROOT_URL | http://localhost:3000/ | **deve essere l'URL pubblico** (https://dominio/) | +| LOCAL_ROOT_URL | | da impostare se la porta web interna non è 3000 | +| SSH_DOMAIN | = DOMAIN | host SSH negli URL clone | +| SSH_PORT | 22 | porta **mostrata** negli URL (qui 2222) | +| SSH_LISTEN_PORT | = SSH_PORT | porta su cui ascolta il server SSH built-in (qui 22) | +| DISABLE_SSH | false | | +| START_SSH_SERVER | false | SSH built-in (nell'immagine Docker è già gestito da sshd) | +| LFS_START_SERVER | false | abilita Git LFS | +| OFFLINE_MODE | true | disabilita CDN/Gravatar | +| ENABLE_ACME | false | non serve: TLS lo fa Caddy | +| APP_DATA_PATH | data | | + +## [database] +| Chiave | Default | Note | +|---|---|---| +| DB_TYPE | mysql | sqlite3 / postgres / mysql / mssql (qui sqlite3) | +| PATH | data/gitea.db | solo sqlite | +| HOST, NAME, USER, PASSWD | 127.0.0.1:3306, gitea, root, vuoto | | +| SSL_MODE | disable | | +| MAX_OPEN_CONNS / MAX_IDLE_CONNS | 0 / 2 | | +| AUTO_MIGRATION | true | migrazioni all'avvio | + +## [security] +| Chiave | Default | Note | +|---|---|---| +| INSTALL_LOCK | false | true dopo il wizard (blocca /install) | +| SECRET_KEY | casuale | **non cambiare mai** dopo l'install (2FA e dati cifrati) | +| INTERNAL_TOKEN | casuale | comunicazione interna | +| LOGIN_REMEMBER_DAYS | 31 | | +| PASSWORD_HASH_ALGO | pbkdf2 | argon2, scrypt, bcrypt | +| MIN_PASSWORD_LENGTH / PASSWORD_COMPLEXITY | 8 / off | | +| REVERSE_PROXY_LIMIT | 1 | | +| REVERSE_PROXY_TRUSTED_PROXIES | 127.0.0.0/8,::1/128 | in Docker aggiungere la subnet compose (es. 172.16.0.0/12) | +| DISABLE_GIT_HOOKS | true | hook custom = esecuzione codice sull'host | +| EGRESS_MODE | lax | `strict` richiede allow-list | + +## [service] +| Chiave | Default | Note | +|---|---|---| +| DISABLE_REGISTRATION | false (ma il wizard lo propone) | true = solo admin crea utenti | +| REQUIRE_SIGNIN_VIEW | false | true = server privato | +| REGISTER_EMAIL_CONFIRM | false | richiede mailer | +| ENABLE_BASIC_AUTHENTICATION | true | | +| ENABLE_CAPTCHA | false | | +| DEFAULT_USER_VISIBILITY / DEFAULT_ORG_VISIBILITY | public | | +| ENABLE_NOTIFY_MAIL | false | | +| ENABLE_TIMETRACKING | true | | + +## [repository] +ROOT (`{APP_DATA_PATH}/gitea-repositories`; nel Docker `/data/git/repositories`), DEFAULT_BRANCH (main), DEFAULT_PRIVATE (last|private|public), DEFAULT_OBJECT_FORMAT (sha1|sha256), DISABLE_HTTP_GIT, DISABLE_MIGRATIONS, DISABLE_STARS, MAX_CREATION_LIMIT (-1), DEFAULT_REPO_UNITS, DISABLED_REPO_UNITS (disabilita funzioni globalmente, es. `repo.actions,repo.packages`). + +## [ssh.minimum_key_sizes] +ED25519 256, ECDSA 256, RSA 3071, DSA -1 (disabilitato). + +## [mailer] +Vedi [email.md](email.md). + +## [actions] +ENABLED true (da 1.21); default action host `github`; retention log 365g, artifact 90g, run 400g; zombie task timeout 10m; endless task 3h; abandoned job 24h. Vedi [actions-runner.md](actions-runner.md). + +## [session] +PROVIDER file, PROVIDER_CONFIG data/sessions, COOKIE_NAME i_like_gitea, SESSION_LIFE_TIME 86400, SAME_SITE lax. Aggiungere `COOKIE_SECURE=true` dietro HTTPS. + +## [log] +MODE console, LEVEL Info. Modalità file: `gitea.log`, rotazione 7 giorni. Necessario su file per fail2ban. + +## [cron] / [cron.*] +`[cron] ENABLED=false` (globale); task: archive cleanup `@midnight`, update_mirrors `@every 10m`, repo health check `@midnight`, stop_zombie_tasks `@every 5m`, stop_endless_tasks `@every 30m`, cancel_abandoned_jobs `@every 6h`, update_checker `@every 168h`. + +## Altre sezioni +- **[attachment]**: ENABLED, MAX_SIZE 100 (MB), MAX_FILES 5, STORAGE_TYPE local, PATH attachments. +- **[lfs]**: PATH ./data/lfs; batch 20, concurrency 8. +- **[packages]**: ENABLED true; limiti per tipo -1; CHUNKED_UPLOAD_PATH tmp/package-upload. Registry container richiede `/v2` alla radice del dominio. +- **[oauth2]**: ENABLED, ACCESS_TOKEN_EXPIRATION_TIME 3600, REFRESH_TOKEN_EXPIRATION_TIME 730h, JWT_SIGNING_ALGORITHM RS256. +- **[cache]**: ADAPTER memory, INTERVAL 60, ITEM_TTL 16h. +- **[api]**: ENABLE_SWAGGER true, MAX_RESPONSE_ITEMS 50, DEFAULT_PAGING_NUM 30. +- **[other]**: SHOW_FOOTER_VERSION, SHOW_FOOTER_POWERED_BY, ENABLE_SITEMAP, ENABLE_FEED. +- **[indexer], [ui], [openid], [webhook], [storage]**: non verificate in questo riferimento; consultare https://docs.gitea.com/administration/config-cheat-sheet. diff --git a/docs/email.md b/docs/email.md new file mode 100644 index 0000000..089c930 --- /dev/null +++ b/docs/email.md @@ -0,0 +1,21 @@ +# Email + +Abilita notifiche, reset password, conferma registrazione. Aggiungere a `app.ini` (o `GITEA__mailer__...`) e riavviare. + +```ini +[service] +ENABLE_NOTIFY_MAIL = true + +[mailer] +ENABLED = true +FROM = gitea@your-domain.com +PROTOCOL = smtps ; smtps (465) | smtp+starttls (587) | smtp | sendmail +SMTP_ADDR = mail.your-domain.com +SMTP_PORT = 465 +USER = gitea@your-domain.com +PASSWD = *** ; meglio GITEA__mailer__PASSWD__FILE +``` +- Gmail: `smtp.gmail.com`, 465 smtps (o 587 smtp+starttls), serve una *app password* (richiede 2FA Google); OAuth non supportato. +- L'autenticazione richiede TLS (o HOST=localhost). +- `sendmail` non funziona con l'immagine Docker ufficiale: usare SMTP. +- Test: Amministrazione sito → Configurazione → Riepilogo → Configurazione mailer → invia email di prova. diff --git a/docs/reverse-proxy-caddy.md b/docs/reverse-proxy-caddy.md new file mode 100644 index 0000000..949d30e --- /dev/null +++ b/docs/reverse-proxy-caddy.md @@ -0,0 +1,41 @@ +# Reverse proxy Caddy + +`Caddyfile` attuale: +``` +your-domain.com { + reverse_proxy gitea:3000 +} +``` +Caddy gestisce da solo certificato Let's Encrypt, rinnovi, redirect HTTP→HTTPS e WebSocket (`/-/ws`, aggiornamenti in tempo reale) con `reverse_proxy`. Passa `Host` e `X-Forwarded-Proto` correttamente. + +## Requisiti in Gitea +```ini +[server] +ROOT_URL = https://your-domain.com/ +DOMAIN = your-domain.com +[security] +REVERSE_PROXY_LIMIT = 1 +REVERSE_PROXY_TRUSTED_PROXIES = 172.16.0.0/12 ; subnet rete docker compose (verificare) +``` +Senza trusted proxies corretto Gitea (e fail2ban/log) vede l'IP del container Caddy invece del client. + +## Sub-path (sconsigliato) +``` +git.example.com { + route /git/* { + uri strip_prefix /git + reverse_proxy gitea:3000 + } +} +``` +`ROOT_URL = https://git.example.com/git/`. Il proxy non deve decodificare `%2F`. Il registry container richiede `/v2` alla radice del dominio. + +## Comandi +```bash +docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile +docker compose logs caddy +``` +Il certificato non viene emesso se 80/443 non sono raggiungibili o il DNS non punta all'IP pubblico. Per test senza rate limit, usare in `Caddyfile` la direttiva globale `{ acme_ca https://acme-staging-v02.api.letsencrypt.org/directory }`. + +## Limiti upload +Caddy non limita il body di default; i limiti sono in Gitea (`[attachment] MAX_SIZE`, `[repository.upload]`, `[packages] LIMIT_SIZE_*`). diff --git a/docs/security-hardening.md b/docs/security-hardening.md new file mode 100644 index 0000000..f46690b --- /dev/null +++ b/docs/security-hardening.md @@ -0,0 +1,44 @@ +# Sicurezza + +## Impostazioni consigliate +```ini +[service] +DISABLE_REGISTRATION = true ; server personale +REQUIRE_SIGNIN_VIEW = true ; opzionale: tutto privato +[session] +COOKIE_SECURE = true +[security] +INSTALL_LOCK = true +DISABLE_GIT_HOOKS = true +``` +- Esporre fuori solo 80, 443, 2222; 3000 resta su 127.0.0.1. +- Backup di `app.ini` (SECRET_KEY/INTERNAL_TOKEN) separato e protetto. +- Aggiornare regolarmente l'immagine (vedi [backup-restore.md](backup-restore.md)). +- Preferire chiavi ED25519; abilitare 2FA per gli admin. + +## Fail2ban (sull'host) +1. Log su file in `app.ini`: +```ini +[log] +MODE = console, file +LEVEL = Info +``` +2. `/etc/fail2ban/filter.d/gitea.local`: +```ini +[Definition] +failregex = .*(Failed authentication attempt|invalid credentials|Attempted access of unknown user).* from +ignoreregex = +``` +3. `/etc/fail2ban/jail.d/gitea.local` (path del log: `/gitea-data/gitea/log/gitea.log`): +```ini +[gitea] +enabled = true +filter = gitea +logpath = /home/davide3011/gitea/gitea/gitea-data/gitea/log/gitea.log +maxretry = 10 +findtime = 3600 +bantime = 900 +action = iptables-allports +``` +4. Con Docker aggiungere un jail con `action = iptables-allports[chain="FORWARD"]`. +5. Per vedere l'IP reale impostare `REVERSE_PROXY_LIMIT = 1` e `REVERSE_PROXY_TRUSTED_PROXIES` con la subnet docker (172.16.0.0/12 di norma). Verificare i path/permessi del log prima di fidarsi. diff --git a/docs/ssh.md b/docs/ssh.md new file mode 100644 index 0000000..e07b79d --- /dev/null +++ b/docs/ssh.md @@ -0,0 +1,30 @@ +# SSH + +Mapping compose: `2222:22` → host 2222 → sshd built-in dell'immagine nel container (porta 22). Lo SSH non passa da Caddy. + +## Impostazioni coerenti +```ini +[server] +SSH_DOMAIN = your-domain.com +SSH_PORT = 2222 ; mostrata negli URL +SSH_LISTEN_PORT = 22 ; porta nel container +``` +URL clone: `ssh://git@your-domain.com:2222/utente/repo.git`. + +## Client (`~/.ssh/config`) +``` +Host your-domain.com + Port 2222 + User git +``` +Poi `git clone git@your-domain.com:utente/repo.git`. + +## Chiavi +Aggiungere la chiave pubblica in Impostazioni → Chiavi SSH/GPG. Dimensioni minime: ED25519 256, ECDSA 256, RSA 3071, DSA vietato (`[ssh.minimum_key_sizes]`). Test: `ssh -T -p 2222 git@your-domain.com`. + +## Problemi +- Porta occupata: cambiare solo la porta host nel compose e `SSH_PORT`. +- `Permission denied (publickey)`: chiave non registrata o agent sbagliato (`ssh -vT ...`). +- Dopo restore: `gitea admin regenerate keys` e `hooks`. +- Le chiavi host SSH del server sono in `gitea-data/ssh`: conservarle per evitare avvisi "host key changed". +- Dietro NAT: port forwarding anche per 2222. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..6ceb046 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,22 @@ +# Troubleshooting + +| Sintomo | Causa / rimedio | +|---|---| +| Certificato non emesso | DNS non punta all'IP pubblico, 80/443 chiuse o in uso; `docker compose logs caddy` | +| Link/clone con URL sbagliato | `ROOT_URL`, `SSH_DOMAIN`, `SSH_PORT` non coerenti | +| Porta 2222 occupata | `ss -tlnp \| grep 2222`, cambiare porta host e `SSH_PORT` | +| `Permission denied (publickey)` | chiave non registrata; `ssh -vT -p 2222 git@dominio` | +| Push rifiutato dopo restore/spostamento | `gitea admin regenerate hooks` | +| Errori permessi su `/data` | `USER_UID/GID` ≠ proprietario di `./gitea-data`; `chown` | +| Login ok ma IP client = 172.x | `REVERSE_PROXY_TRUSTED_PROXIES` mancante | +| Aggiornamenti live assenti | WebSocket `/-/ws` bloccato dal proxy | +| Job Actions in coda | nessun runner registrato / label non corrisponde | +| Pagina /install riappare | `INSTALL_LOCK` non true o `app.ini` non persistito | +| Schema DB anomalo | `gitea doctor check --all --fix` (backup prima) | + +Diagnosi base: +```bash +docker compose ps +docker compose logs --tail=100 gitea caddy +docker exec -u git gitea gitea doctor check --all +```