CI authenticates to SonarQube as admin rather than a restricted analysis account #71

Closed
opened 2026-08-20 10:15:12 -05:00 by bermudalamb · 0 comments
Owner

Split out of #61, which raised this as a "Related" note. Kept separate so a permissions change is not buried inside a CI-config commit.

Full write-up: docs/ci/sonarqube-ci-identity.md.

Problem

Gitea Actions authenticates to SonarQube with a token belonging to the admin account.

An analysis job needs exactly one permission: submit an analysis report for one project. The admin token carries every permission the server has — creating and deleting projects, editing quality gates and profiles, managing users, and reading every project on the server including sql-utilities.

The related half of this is already fixed: #67 removed the -Dsonar.login= command-line argument, so the token now reaches the scanner only through the SONAR_TOKEN environment variable and no longer appears on a command line where it could reach a log. What remains is whose token it is.

Why it matters

Three ordinary situations, none of which need anyone to act badly:

  • A leaked token is a leaked server. CI secrets end up in more places than intended — a debug run with set -x, a third-party action, a fork's workflow. The blast radius of a restricted analysis token is one project's analysis history. The blast radius of this one is everything on the server.
  • A misconfigured scan can destroy history. sonar.projectKey is just a string in a properties file. A wrong value combined with admin rights silently creates projects, and other admin endpoints can delete them. A restricted token fails instead.
  • Rotation is painful today. Rotating admin's token means tracking down everywhere else that account is used. A dedicated account rotates on its own.

There is also a plainer reason: with CI showing up as admin in the analysis history, the audit trail cannot tell an automated scan from a person making a change.

Fix

  1. Create a SonarQube user gitea-ci with no global permissions.
  2. Grant it Execute Analysis on redefined-designs only.
  3. Generate a user token for it — not a project or global analysis token. This server is 9.9, where the distinction matters, and SonarSource tooling requires the user type.
  4. Update the SONAR_TOKEN secret in the repository's Actions settings.
  5. Run the SonarQube workflow and confirm it still passes.
  6. Revoke the admin token CI was using.

Step 6 is worth calling out: the workflow is already green after step 5, so it is easy to stop there and leave the old credential valid, which makes the whole change cosmetic.

Note for whoever does this

This server does not accept Authorization: Bearer — it is 9.9, and bearer auth arrived in SonarQube 10. A token goes in as the basic-auth username: curl -u "$TOKEN:" .... Any script verifying the new account's permissions with a bearer header will report a permissions problem that is really an auth-scheme problem.

Severity

Medium. Nothing is broken and nothing has gone wrong; the exposure is disproportionate to what the job actually needs, and the cost of fixing it is about ten minutes.

Split out of #61, which raised this as a "Related" note. Kept separate so a permissions change is not buried inside a CI-config commit. Full write-up: `docs/ci/sonarqube-ci-identity.md`. ## Problem Gitea Actions authenticates to SonarQube with a token belonging to the **`admin`** account. An analysis job needs exactly one permission: submit an analysis report for one project. The `admin` token carries every permission the server has — creating and deleting projects, editing quality gates and profiles, managing users, and reading every project on the server including `sql-utilities`. The related half of this is already fixed: #67 removed the `-Dsonar.login=` command-line argument, so the token now reaches the scanner only through the `SONAR_TOKEN` environment variable and no longer appears on a command line where it could reach a log. What remains is whose token it is. ## Why it matters Three ordinary situations, none of which need anyone to act badly: - **A leaked token is a leaked server.** CI secrets end up in more places than intended — a debug run with `set -x`, a third-party action, a fork's workflow. The blast radius of a restricted analysis token is one project's analysis history. The blast radius of this one is everything on the server. - **A misconfigured scan can destroy history.** `sonar.projectKey` is just a string in a properties file. A wrong value combined with admin rights silently creates projects, and other admin endpoints can delete them. A restricted token fails instead. - **Rotation is painful today.** Rotating `admin`'s token means tracking down everywhere else that account is used. A dedicated account rotates on its own. There is also a plainer reason: with CI showing up as `admin` in the analysis history, the audit trail cannot tell an automated scan from a person making a change. ## Fix 1. Create a SonarQube user `gitea-ci` with **no** global permissions. 2. Grant it **Execute Analysis** on `redefined-designs` only. 3. Generate a **user** token for it — not a project or global analysis token. This server is 9.9, where the distinction matters, and SonarSource tooling requires the user type. 4. Update the `SONAR_TOKEN` secret in the repository's Actions settings. 5. Run the SonarQube workflow and confirm it still passes. 6. **Revoke the `admin` token CI was using.** Step 6 is worth calling out: the workflow is already green after step 5, so it is easy to stop there and leave the old credential valid, which makes the whole change cosmetic. ## Note for whoever does this This server does not accept `Authorization: Bearer` — it is 9.9, and bearer auth arrived in SonarQube 10. A token goes in as the basic-auth username: `curl -u "$TOKEN:" ...`. Any script verifying the new account's permissions with a bearer header will report a permissions problem that is really an auth-scheme problem. ## Severity Medium. Nothing is broken and nothing has gone wrong; the exposure is disproportionate to what the job actually needs, and the cost of fixing it is about ten minutes.
bermudalamb self-assigned this 2026-08-20 15:23:06 -05:00
bermudalamb added this to the Code Quality and Hardening project 2026-08-20 15:23:11 -05:00
bermudalamb moved this to Ready for Release in Code Quality and Hardening on 2026-08-20 15:23:16 -05:00
bermudalamb moved this to Released in Code Quality and Hardening on 2026-08-21 12:12:16 -05:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bermudalamb/redefined-designs#71