The audit logger is opened at startup (exit 2 on failure) and obtained before every call, which is refused with audit_unavailable otherwise. Audit records only take validated path and sudo values and use the request id as correlation id. The rate limiter evicts idle keys, caps tracked keys, audits the first excess per key and window and uses the settings passed to the app. FORWARDED_ALLOW_IPS is now a setting, and startup warns about a missing host allow-list and unavailable OAuth.
4.1 KiB
4.1 KiB
Hardening checklist
This server is full access by design (ADR 0001): whoever can call it has the power of the configured Gitea token, which for an admin token means everything. The server keeps a few invariants (audit, secret redaction, bounded output, validated input, fail-closed HTTP authentication), but it does not decide what is safe. This checklist is how you do that.
Decide what to expose
- Create the Gitea token with the narrowest scopes that do the job. Full access needs a
site-admin token with
all; a read-only assistant needs only read scopes. The token is the hard limit, because Gitea enforces it. - Prefer per-user OAuth to a shared admin token whenever more than one person connects: each user then acts under their own Gitea permissions and admin tools fail for non-admins.
- Narrow what is listed:
READ_ONLY=true,TOOLSETS=...,DISABLED_TOOLS=.... Hide what the assistant never needs:admin,actions(secrets),hook,user(tokens, keys) and the delete tools are the sensitive ones.gitea_api_requestcan reach any endpoint, so on a restricted profile dropmiscor disable it. - Keep
OAUTH_SCOPESas narrow as the use case allows. - Do not use Sudo-capable tokens for clients that do not need to impersonate users.
Treat the credentials as keys to the instance
GITEA_TOKENandMCP_BEARER_TOKENare equivalent in power on HTTP. Generate the bearer token withopenssl rand -hex 32, store it in a secret manager or a mode 0600 env file, rotate it when someone leaves, and never put it in a URL (it only goes in theAuthorizationheader).TOKEN_ENCRYPTION_KEYprotects the stored per-user tokens. Keep it separate from the data volume backups.GITEA_OAUTH_CLIENT_SECRETlikewise.- Use
setup, which writes the env file with mode 0600 and never prints secrets. - Do not paste tokens into chat with the assistant, and do not commit
.env.
Network and transport
- Keep the default bind
127.0.0.1. Publish the container port on127.0.0.1and put a reverse proxy with TLS in front. A non-loopback bind refuses to start without authentication; do not work around it. - Terminate TLS with a valid certificate.
PUBLIC_BASE_URLmust be https. - Set
ALLOWED_HOSTSto exactly the host names you serve. Requests with otherHostvalues are rejected. - Restrict who can reach the endpoint at the proxy or firewall too (VPN, allow list), as a second layer in front of bearer authentication.
- Set
FORWARDED_ALLOW_IPSto the proxy address only (not*), so a client cannot spoof its IP. - Leave
RATE_LIMIT_PER_MINUTEon. Put request-size and connection limits at the proxy. - Use https for
GITEA_URL. Plain http sends the token unencrypted (setup warns about it).
Container and host
- Run the image as the non-root user,
read_only: truewith atmpfsfor/tmp,cap_drop: ALLandno-new-privileges, as the compose snippet in deployment.md does. - Mount only the data volume. Give it to the service account alone (mode 0700).
- Pin the image by digest in production, and update regularly.
- Do not run the stdio server with a token that is broader than the user's own needs on a shared machine: any process of that user can read the client configuration.
Audit and monitoring
- Ship the audit log off the host and validate the chain regularly (audit-logging.md).
- Alert on repeated
forbiddenorunauthorizedoutcomes and on calls to admin, secret and delete tools. - Keep
LOG_LEVEL=INFOandEXPOSE_ERROR_DETAILS=falsein production. - Keep
SECRET_DETECTION_MODE=mask(orblock). Secret detection is a safety net with regular expressions, not a guarantee.
Untrusted content
- Keep tool-call confirmation enabled in the client for destructive tools. Prompt injection in issues, pull requests, comments and files is the realistic way a model is tricked into using the access it has.
- Do not connect a write-capable, broad-scope profile to an assistant that also reads untrusted content from other sources.
- Prefer separate profiles: a read-only one for browsing and triage, a write-capable one that you use deliberately.