Locking Down Harbor CI/CD Pipelines with Scoped Robot Accounts and Immutable Tags
Use project-scoped Harbor robot accounts and immutable tag rules to stop CI credential leaks from becoming registry-wide incidents and prevent release tags from being silently replaced.
07 Jan 2026, 15:27 UTC

Sharing a Harbor admin account with your CI/CD pipeline turns a leaked build token into a registry-wide incident. The practical fix is to give each pipeline its own Harbor robot account scoped to a single project with only the actions it needs, and to enable immutable tag rules so promoted release tags cannot be silently replaced.
How Harbor Robot Accounts Work
A robot account is a non-human service identity in Harbor. It authenticates with a username prefixed by robot$ and a secret that functions as the Docker login password. Permissions are defined per project and resource—for example, pull-only access to repositories in a specific namespace. Harbor exchanges the robot secret for a short-lived JWT bearer token; the token authorizes only the scoped actions, so a leaked secret cannot alter other projects.
Worked Example: Pull-Only Robot for a CI Pipeline
Create the account from an admin workstation or CI job with Harbor system-admin or project-admin API access. The following curl command posts a new robot scoped to the library project with repository pull permission only. Replace harbor.example.com, admin:password, and the project namespace with your values.
curl -u 'admin:password' -X POST \
'https://harbor.example.com/api/v2.0/robots' \
-H 'Content-Type: application/json' \
-d '{
"name": "ci-pull",
"description": "Pull-only robot for production CI",
"duration": 30,
"level": "project",
"permissions": [
{
"kind": "project",
"namespace": "library",
"access": [
{
"resource": "repository",
"action": "pull"
}
]
}
]
}'
Harbor returns the robot's secret in the response body. This value is displayed only at creation, so store it in a secret manager immediately. The duration field sets the account lifetime in days; when the account expires, Harbor invalidates any tokens issued from that secret even if the secret itself has not changed.
Use the robot from your CI runner or local Docker host:
docker login harbor.example.com -u 'robot$ci-pull' -p '<secret>'
docker pull harbor.example.com/library/nginx:1.25
Expected check: the pull succeeds. Attempting to push to a different project or to modify tags in library is denied because the robot's permissions do not include those actions.
Immutable Tag Rules: Blocking Silent Overwrites
Immutable tag rules are configured per project. A rule matches a set of repositories and tags; when active, Harbor rejects any registry API push that would overwrite an existing tag in that set. This prevents a compromised or misconfigured pipeline from replacing a promoted release tag such as v1.2.3 with a different image after the fact.
Because the immutable-tag rule schema varies across Harbor 2.x versions, confirm the exact field names in your running version's API documentation or UI before automating the configuration. In the Harbor UI, navigate to the project, select Tag Immutability, and add a rule that matches your release repositories and tags.
To verify the rule works, push a tag, then attempt to re-push the same tag with a different image digest. Harbor should reject the second push. Note that immutable tags block same-tag re-push but do not prevent a pipeline from pushing a new tag; they are a guard against overwrite, not a replacement for image signature verification or admission control.
Limits and Version-Specific Behavior
- Secret visibility: Robot secrets cannot be re-read after creation. If the secret is lost, create a new robot account.
- Token lifetime: The JWT tokens Harbor issues are bounded by the robot account's
durationsetting. Plan rotation before expiry. - Immutable tag scope: Rules apply to Harbor's registry API. Direct writes to backend object storage bypass Harbor entirely.
- Disk usage: Immutable tags and robot permissions do not manage storage. Use retention rules and garbage collection to reclaim space from unreferenced blobs.
- Version differences: Harbor 2.x releases differ in robot API fields and immutable-tag rule schema. Verify against your running version.
Common Mistakes to Avoid
- Logging in as admin from CI: A shared admin credential gives every pipeline full control over every project.
- Granting system-level permissions: Robot accounts should be scoped to a single project unless a specific cross-project task requires otherwise.
- Omitting the
robot$prefix: Docker login fails if the username does not include the literalrobot$prefix. - Treating immutable tags as a signature substitute: Immutability prevents overwrite; it does not prove image provenance. Use signature verification and admission control for that.
Verifying the Configuration
Before wiring the robot into production CI, inspect its effective permissions with GET /api/v2.0/robots/{id} or the Harbor UI. Confirm that the permissions list contains only the intended project and actions. Then run a controlled test: log in with the robot secret, pull an allowed image, and confirm that a push to an unauthorized project is denied. For immutable tags, push a tag and attempt to overwrite it; the rejection confirms the rule is active.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.