Gitea CVE-2026-20896: the Docker image that let anyone in as admin
A critical flaw (CVSS 9.8) in Gitea's Docker images let attackers impersonate any user, admin included, with a single HTTP header. Active probing is already hitting ~6,200 exposed instances. Here's what it is, whether it affects you, and what to do today.
A default that never should have shipped
If you run your own Gitea in Docker to host your company's code —more and more small businesses do, to avoid leaning on GitHub— this one lands close to home. The official Gitea image shipped with a default setting that boiled down to this: any client on the internet could tell the server "I'm the administrator" and the server would believe it.
It's been assigned CVE-2026-20896, with a CVSS score of 9.8 out of 10. It's been patched since version 1.26.3, but what set off the alarms this week is something else: Sysdig has spotted the first real probes against internet-facing Gitea servers. Still recon-only for now, but already with a name attached.
In one sentence
Gitea can authenticate users through a reverse proxy: the proxy validates the user and hands Gitea an X-WEBAUTH-USER header with the account name. The trouble is that the Docker image shipped with REVERSE_PROXY_TRUSTED_PROXIES=* — meaning it trusted that header no matter where it came from.
In plain terms: if you had reverse-proxy authentication enabled, an attacker could fire a request straight at Gitea with X-WEBAUTH-USER: admin and log in as the administrator with no password, no token, nothing.
Who's affected
- Versions: Gitea Docker images up to and including 1.26.2. From 1.26.3 on, the
*wildcard is gone and proxy authentication becomes an explicit opt-in. - The key condition: you're affected if you deploy Gitea via the Docker image and have reverse-proxy authentication enabled (
ENABLE_REVERSE_PROXY_AUTHENTICATION). If you don't use that login mode, the direct vector doesn't apply — but you should still update and check your config, because a*value has no business being there. - Attack surface: roughly 6,200 Gitea instances are exposed directly to the internet. Not all with reverse-proxy auth turned on, but plenty to make brute scanning worthwhile. And that's exactly what's happening now.
What's actually being seen: Sysdig traced the first attempt to a ProtonVPN exit node (159.26.98[.]241). So far it's automated reconnaissance —scanning HTTP(S) ports, fingerprinting Gitea, and trying the header bypass— with no full exploitation yet. They caught it early. But once a 9.8 flaw starts getting probed, the window to patch calmly closes fast.
An honest note on the dates
Don't let anyone sell you smoke on this: the flaw was fixed in 1.26.3 in late June and the advisory went public in early July. What's new this week isn't the vulnerability, it's that it's now being actively probed. That distinction matters: if you updated in June, you're covered and this is just a nudge to verify. If your Gitea hasn't been touched in months, it's urgent.
How the flaw works
Reverse-proxy authentication is a legitimate, useful feature: you put a proxy in front (nginx, Traefik, Authelia…), the proxy validates the user against your SSO, and tells Gitea "the person coming through is john" via the X-WEBAUTH-USER header. Gitea trusts the proxy and doesn't ask for a password again.
The whole model rests on one premise: only your trusted proxy can set that header. That's what REVERSE_PROXY_TRUSTED_PROXIES is for — it should hold your proxy's IP (or range) and nothing else.
The Docker image shipped it as *. With the wildcard, Gitea accepted the header from any source IP. The attacker doesn't even need to go through your proxy: they talk to Gitea directly and make the header up. X-WEBAUTH-USER: admin and they're in.
What to do today
No fluff. This is what we've been checking these past few days on client boxes running Gitea.
1. Check your version
docker exec <gitea_container_name> gitea --version
If you're below 1.26.3, keep reading.
2. Update the image
Edit your docker-compose.yml to pin the patched version (or 1-latest if you'd rather track the 1.x branch) and recreate the container:
# docker-compose.yml -> image: gitea/gitea:1.26.3
docker compose pull
docker compose up -d
Confirm afterwards with the same gitea --version that you're now on 1.26.3 or later.
3. Review your trusted-proxy config
Updating the image removes the dangerous default, but make sure your app.ini hasn't set it to * on its own. Look under the [security] section:
[security]
REVERSE_PROXY_TRUSTED_PROXIES = 127.0.0.1, 172.18.0.0/16 ; ONLY your real proxy's IP/range
If you had it as *, replace it with your reverse proxy's specific IP. If you don't use proxy authentication, make sure ENABLE_REVERSE_PROXY_AUTHENTICATION is set to false.
4. Harden it at the reverse proxy (even after updating)
This is the belt-and-braces we always apply: have your own proxy strip or rewrite the header on every inbound request, so a client can never inject it. In nginx:
location / {
# The client does NOT get to say who they are: clear the header; the proxy sets it if needed
proxy_set_header X-WEBAUTH-USER "";
proxy_pass http://127.0.0.1:3000;
}
If you genuinely use proxy authentication, your auth module overwrites that line with the already-validated user. If you don't, you leave it empty and close the vector at the root. Cheap and permanent.
5. Audit past access
If your Gitea sat exposed with the vulnerable config, assume it could have been touched. Check:
- The Gitea log (
gitea.logor the container output) for reverse-proxy logins from users or IPs that don't add up. - Unexpected admin actions: new users, freshly created access tokens, added SSH or deploy keys, new webhooks.
- If anything looks off, assume compromise: rotate tokens, deploy keys and any CI/CD secrets that lived in those repos.
What we do
At Atenea Systems, when we set up a Gitea (or any self-hosted service behind a proxy), we start from two rules that would have neutralised this flaw before it ever existed:
- No wildcards in trust lists.
TRUSTED_PROXIES,allow,X-Forwarded-*… always specific IPs or ranges. A*is convenient on deployment day and a time bomb the rest of the year. - The proxy scrubs sensitive headers on the way in. The client doesn't dictate identity. That header is set by whoever should set it, never by whoever shows up from outside.
With those two habits, the image could ship however it liked: the attacker sending X-WEBAUTH-USER: admin runs into a proxy that wipes it before Gitea ever sees it.
The usual lesson
An image's defaults are not your security configuration. A convenient docker pull can hand you a wildcard you never set, leaving a door wide open. Whenever you stand up any service, it's worth spending ten minutes reading what it ships by default around trust and authentication — headers and proxies especially.
If you've had a Gitea (or any self-hosted service) running for a while and can't remember the last time you reviewed its config, now's the moment. Want us to take a look for you? Let's talk.
References
Want to talk about your case?
Tell us what you need and we will get back to you within 24 hours with a clear proposal.
Get a quote