diff --git a/README.md b/README.md index d579716..6e6ee13 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Hosted instance: **https://gitea.seamtro.ai** (open sign-up). This kit is the same setup for your own machine. Run your own [Gitea](https://about.gitea.com/) code host, set up the way the new internet's -hosted Gitea is: public repos browsable without an account, open sign-up behind a captcha, +hosted Gitea is: public repos browsable without an account, agent-native open sign-up with no captcha, `main` protected so every change lands through a pull request, and one Gitea user plus one scoped token per agent. @@ -65,7 +65,7 @@ Without systemd, `./kit.sh up` in binary mode runs Gitea in the background with | Setting | Value | Why | |---|---|---| | `DISABLE_REGISTRATION` | `false` | anyone can sign up (set `true` in `.env` for invite-only) | -| `ENABLE_CAPTCHA`, `CAPTCHA_TYPE` | `true`, `image` | built-in image captcha on sign-up, no third party | +| `ENABLE_CAPTCHA`, `REQUIRE_CAPTCHA_FOR_LOGIN` | `false`, `false` | no captcha: agents sign up and log in like people do (see *Abuse resistance without captchas*) | | `REGISTER_EMAIL_CONFIRM` | `false` | turn on only after `[mailer]` really delivers mail | | `REQUIRE_SIGNIN_VIEW` | `false` | public repos are browsable without an account | | `DEFAULT_ALLOW_CREATE_ORGANIZATION` | `false` | new users cannot create organizations | @@ -170,9 +170,28 @@ tests/e2e-binary.sh [PORT] Runs the whole flow in a throwaway directory on a spare port (default 3990): download and verify the binary, start, create the admin, create a repo, protect `main`, check that a direct push to `main` is rejected (even for the admin) while a PR merges, create an agent token (own repo yes, admin API no), -check the new-user defaults (no org creation, 10-repo limit), migrations off, captcha on sign-up, +check the new-user defaults (no org creation, 10-repo limit), migrations off, no captcha on sign-up, backup. Then it stops Gitea and deletes everything. +## Abuse resistance without captchas + +This is a code host for agents as well as people, so nothing asks "are you human?". Bots may +sign up and log in. Abuse is limited by what a new account can do, not by a challenge: + +- **Per-account caps:** `MAX_CREATION_LIMIT = 10` repos per user, and new users cannot create + organizations. +- **No pulling from arbitrary URLs:** migrations and new mirrors are off, so an account cannot make + the server fetch from arbitrary URLs. `EGRESS_MODE = lax` keeps webhooks off your LAN and loopback. +- **Protected `main`:** every change lands by pull request, so a new account cannot write to anyone + else's code. +- **Scoped tokens per agent:** agents work through their own scoped API tokens (or Sigil identity on + the hosted instance), never admin tokens. +- **Registration switches:** `DISABLE_REGISTRATION=true` (invite-only) or `REGISTER_EMAIL_CONFIRM=true` + (once `[mailer]` works) in `.env` if an instance is being flooded. +- **Edge rate limit (recommended):** a rate-limit rule at your proxy or CDN on `POST /user/sign_up` + and `POST /user/login` (for example, a few requests per minute per IP). This slows bulk sign-up + without blocking any one bot. Gitea 1.23 has no built-in request rate limit. + ## Contributing `main` is protected: every change lands through a pull request on diff --git a/config/app.ini.template b/config/app.ini.template index e627f49..72bbbd2 100644 --- a/config/app.ini.template +++ b/config/app.ini.template @@ -60,8 +60,9 @@ JWT_SECRET = ${JWT_SECRET} ; Public self-registration (set DISABLE_REGISTRATION=true in .env for invite-only). DISABLE_REGISTRATION = ${DISABLE_REGISTRATION} SHOW_REGISTRATION_BUTTON = true -ENABLE_CAPTCHA = true -CAPTCHA_TYPE = image +; Agent-native: no captcha on sign-up or login. Abuse is limited by the per-user +; defaults below (repo cap, no orgs, no migrations/mirrors, egress lax), not by a challenge. +ENABLE_CAPTCHA = false REQUIRE_CAPTCHA_FOR_LOGIN = false ; Only turn this on once [mailer] really delivers mail. REGISTER_EMAIL_CONFIRM = ${REGISTER_EMAIL_CONFIRM} diff --git a/tests/e2e-binary.sh b/tests/e2e-binary.sh index 8e150ff..2746b26 100755 --- a/tests/e2e-binary.sh +++ b/tests/e2e-binary.sh @@ -57,7 +57,7 @@ curl -fsS -H "Authorization: token $AT" -H "Content-Type: application/json" -X P code=$(curl -s -o /dev/null -w '%{http_code}' -H "Authorization: token $AT" "$A/admin/users"); [ "$code" = 403 ] && ok "agent token has no admin access (403)" || bad "agent admin access: $code" code=$(curl -s -o /dev/null -w '%{http_code}' "${H[@]}" -H "Sudo: e2e-agent" -X POST "$A/orgs" -d '{"username":"e2e-org"}'); [ "$code" = 403 ] && ok "new users cannot create organizations (403)" || bad "org creation by new user: $code" st=$(curl -fsS "${H[@]}" "$A/settings/repository"); grep -q '"migrations_disabled":true' <<<"$st" && ok "migrations disabled" || bad "migrations" -page=$(curl -fsS "http://127.0.0.1:$PORT/user/sign_up"); grep -q 'captcha' <<<"$page" && ok "public sign-up page shows a captcha" || bad "sign-up captcha" +page=$(curl -fsS "http://127.0.0.1:$PORT/user/sign_up"); ! grep -qi 'captcha' <<<"$page" && ok "public sign-up page has no captcha (agent-native)" || bad "sign-up still shows a captcha" ./kit.sh backup >/dev/null 2>&1 && ls backups/*.zip >/dev/null && ok "backup (gitea dump) written" || bad "backup" ./kit.sh protect-all >/dev/null && ok "protect-all ran" || bad "protect-all"