Skip to content

Escalate on distinct rejected tokens per address, and make the auth budgets configurable #790

Description

@jmrplens

Follow-up to #619, which #789 closes by implementing its first point only. That pull request lets a request through when its credential is one the pool already holds, so a token sprayer behind a shared address no longer cuts off its legitimate neighbours. The two remaining points are additive hardening rather than the fix for that defect, and they were deliberately kept out so the security change stayed the smallest one that is clearly correct.

What is still owed

Count distinct rejected tokens per address over a longer window, beside the fast lockout, and escalate. Ten failures in a minute is a stuck client retrying one bad token as much as it is an attack, and a minute's block is the right answer to both. Fifty distinct invalid tokens from one address inside ten minutes is only an attack, and the answer is a block that lasts and grows: a minute, then ten, then an hour, reset by an hour of silence. The distinct count is also what keeps a legitimate neighbour from ever tripping it, because a person has one token.

Make both windows and both limits configurable, as flags with GITLAB_MCP_ environment variables, since a public deployment and a LAN one want different numbers. Record the block in telemetry as a counter keyed by the reason, never by the address.

Why this matters beyond the neighbours

Every distinct token that reaches verification is a request to GitLab from the deployment's own address. GitLab rate-limits failed authentication per source address, so a sprayer that gets past the per-address budget, for instance by rotating addresses, is not attacking this server: it is spending the deployment's standing with GitLab, and the block GitLab eventually applies lands on the server, for everyone. The distinct-token count is the earliest signal of that, earlier than the failure count, because it is the one thing a sprayer cannot avoid producing.

Where it belongs

test/e2e/http, which is where the existing budgets and the ordering in resolve are already covered, and where #789 put its two regressions. The cases are a distinct-token count that blocks while a same-token retry does not, and an escalation that lengthens on repetition and resets after silence.

docs/guides/http-server-mode.md and docs/concepts/security.md describe the current budgets and get the new ones.

What already exists to build on

  • The per-address failure lockout, ten failures in one minute, keyed by client IP or by --trusted-proxy-header when the connection came from a --trusted-proxies peer.
  • The per-transport-source budget of five hundred distinct failing keys per minute, transportFailureLimit.
  • oauth.RejectedTokens, so a token GitLab already refused is not verified twice.
  • ServerPool.Admitted, added by #789, which answers whether the next request for a credential would take the pool's fast path.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    v3.1.0Targeted at the 3.1.0 release

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions