Automatically Create and Reconcile Nexus Connector Resources with Tekton
When a new tenant, project, or namespace is created in Alauda Container Platform, administrators often need to complete the same Nexus onboarding steps again and again:
- Create one or more Nexus repositories, or attach access to existing shared ones.
- Create a Nexus local user and role scoped to those repositories.
- Create the Connector authentication Secret and the Nexus Connector in Kubernetes.
The repository set and the granted action scope evolve over time — projects pick up new repositories, drop old ones, or widen and narrow what a scoped user is allowed to do. Re-running the same automation handles those scope changes. Credential rotation is a separate, security-driven cycle: even when the repository set and action scope have not changed, the scoped Nexus user password should be rotated periodically (or in response to an incident) so that long-lived credentials do not accumulate in downstream consumers. The Task drives both flows from the same TaskRun, but the two intents are independent.
This guide focuses on that operational problem. The implementation mechanism used here is the nexus-connector-automatic-creation Tekton Task. You can run it once for initial provisioning, run it again later to reconcile repository configuration and scope, and run it on a schedule to rotate the scoped user password.
TOC
What Problem This SolvesBefore You BeginPrerequisitesSecurity considerationsCompatibilityScenario indexPrepare Credentials and WorkspacesNexus credentials fornexus-secretUse a basic-auth Secret for nexus-secretUse a Nexus Connector for nexus-secretKubernetes credentials for kube-configGrant the apply credential Kubernetes RBACUse a Kubernetes Secret for kube-configUse a Kubernetes Connector for kube-configHow the Task Solves the ProblemRepository lifecycle: provision versus referenceScoped user, role, and credential lifecycleThe nexusRepositories Schemaprovision: entriesCommon fields (all formats and types)hosted repositoriesproxy repositoriesgroup repositoriesImmutable fields (full list)reference: entriesactions referenceWhat the Task forwards verbatim vs defaultsExample: Provision Project-private Nexus RepositoriesExample: Attach Access to Existing Shared RepositoriesExample: Mixed Provision and ReferenceRe-run to Add Repositories or Rotate CredentialsVerify the ResultTroubleshootingnexusRepositories cannot be parsedprovision: entry is missing format or typereference: entry does not exist in NexusNexus returns 404 for the repository endpointprovision: entry already exists in NexusNexus user already exists but is not a member of the scoped roleNexus role already exists but its description does not start with OWNER=connectors-operatorNexus permissions are not sufficientKubernetes permissions are not sufficientnexus-secret workspace is missing filesnexusUrl is missing or emptyNexus anonymous access is enabledNexus returns 401 UnauthorizedNexus returns 403 ForbiddenTLS handshake failure against NexusWhat Problem This Solves
Use this approach when you need to automate Nexus onboarding and credential refresh for Alauda Container Platform tenants and namespaces.
Common scenarios include:
- Project-level Nexus repositories: create one or more Nexus repositories (hosted, proxy, or group) and one scoped user for an Alauda Container Platform project, then materialise the Connector and Secret in the project namespace.
- Mixed provision and reference: provision project-private repositories and at the same time attach the same scoped user to shared upstream repositories (for example, a built-in
maven-centralproxy) that already exist in Nexus. - Reference-only: skip provisioning entirely and only attach the scoped user to existing repositories, when repositories are administered through another channel.
- Credential refresh and reconciliation: re-run the same automation to update repository configuration, refresh the Nexus user password, and update the generated Connector Secret.
This guide is about solving the Nexus initialization and credential lifecycle problem. The Tekton Task is the solution used to implement that workflow.
Before You Begin
Prerequisites
- Tekton Pipelines is installed.
- The Connectors system, the Nexus Connector component, and the Kubernetes Connector component are installed. (The Kubernetes Connector is needed when you provide the
kube-configworkspace through a Kubernetes Connector — see Use a Kubernetes Connector forkube-config.) - The
nexus-connector-automatic-creationTask is installed in the namespace where you will run the TaskRun. - The Nexus runtime image used by the Task is available from your internal registry.
- (Optional) The TaskRun ServiceAccount can reach the target Kubernetes apiserver to apply the generated Connector and Secret. This is only required when the
kube-configworkspace is unbound and the Task falls back to the in-cluster ServiceAccount. When you bind thekube-configworkspace to a Kubernetes Connector pointing at akubeconfig-style credential, the Task targets that cluster via the Connector, and the TaskRun ServiceAccount does not need apiserver permissions on the local cluster.
Security considerations
- Use a private management namespace to store the privileged Nexus credential, the Kubernetes credential, and the TaskRun that performs provisioning or credential refresh.
- Restrict who can trigger the TaskRun, because that action can rotate the scoped Nexus user password and overwrite Connector Secrets.
- If a strict deployment is required, disable Nexus anonymous access in the Nexus console (Settings → Security → Anonymous Access) before running this Task. The Task only emits a warning when anonymous access is enabled; it does not change cluster-level Nexus configuration.
Compatibility
Tested on Nexus Repository Manager 3.76 OSS.
Other Nexus 3.x versions may work but are not covered by the test suite.
Scenario index
Use this matrix to pick the right starting example for your situation:
Prepare Credentials and Workspaces
The solution uses two workspaces:
Nexus credentials for nexus-secret
This workspace provides the Nexus credential that the automation uses to create or update Nexus repositories, create the scoped user and role, and link standard repository-view privileges to that role.
The Nexus credential used here must be able to:
- Create and update Nexus repositories.
- Create and update Nexus local users.
- Create and update Nexus roles.
- Read existing
nx-repository-view-<format>-<repo>-<action>privileges and attach them to a role. - Reset the password of the scoped Nexus user the Task creates.
The Nexus built-in nx-admin role satisfies all of the above. If you prefer least-privilege provisioning, create a dedicated Nexus role with the following privileges and assign it to the provisioning user:
You can provide this workspace in either of two ways:
- Basic-auth Secret — straightforward when you already have Nexus credentials and want to use them directly.
- Mounted Nexus Connector — recommended for production. Pod-side credentials stay in the Connectors proxy, and the Pod only sees proxy environment variables.
Use a basic-auth Secret for nexus-secret
Create a kubernetes.io/basic-auth Secret in the management namespace:
Bind it like this:
In this mode the Task reads username and password from the mounted files and connects directly to the URL given by the nexusUrl Task parameter.
Use a Nexus Connector for nexus-secret
This mode is the recommended choice when you want to keep the Nexus credential off the Pod — the Pod talks to Nexus through the Connectors proxy, and the proxy injects the real basic-auth header on the outgoing request. The Connector can live in the same cluster as the TaskRun or off-cluster; both shapes are valid choices.
First, create a Nexus Connector in the management namespace. The credential behind this Connector should satisfy the same Nexus permissions listed above.
Then bind the TaskRun nexus-secret workspace to the Connector through the Connectors CSI driver:
For the exact file layout the Connectors CSI driver mounts into the workspace, see the Connectors CSI driver documentation. For a step-by-step walkthrough of creating a Nexus Connector that you can mount here, see the Nexus Connector quick start.
The referenced Connector must use connectorClassName: nexus. The Task rejects other ConnectorClasses to keep proxy authentication routing consistent. Make sure the TaskRun ServiceAccount is allowed to use this Nexus Connector.
Kubernetes credentials for kube-config
This workspace provides the Kubernetes credential that the automation uses to create or update the Connector Secret and the Nexus Connector in the connector namespace.
The Kubernetes credential used here must be able to:
- Create and update Secrets in the connector namespace.
- Create and update Nexus Connector resources in the connector namespace.
When the kube-config workspace is unbound, the Task uses the in-cluster ServiceAccount of the TaskRun Pod. Bind it only when you need to apply the Connector and Secret with a kubeconfig that is different from the TaskRun ServiceAccount, for example when targeting an external cluster.
Grant the apply credential Kubernetes RBAC
Whether you use the in-cluster ServiceAccount or a bound kubeconfig, the credential must be allowed to get, create, and patch Secrets and Nexus Connector resources (connectors.connectors.alauda.io, group connectors.alauda.io) in the connector namespace. The Task uses Server-Side Apply, which the apiserver authorises as the patch verb, so create alone is not enough.
A minimal Role + RoleBinding in the connector namespace, bound to the TaskRun ServiceAccount in the management namespace:
When kube-config is bound, the same permissions must be held by the user identity inside that kubeconfig in the target namespace and cluster.
Use a Kubernetes Secret for kube-config
Bind it like this:
Use a Kubernetes Connector for kube-config
You can also provide kube-config through the Connectors CSI driver with a Kubernetes Connector that renders the kubeconfig configuration:
For a step-by-step walkthrough of creating that Kubernetes Connector, see the Kubernetes Connector quick start. Background: Using Kubernetes Connector in Tekton Task and Kubernetes Connector.
How the Task Solves the Problem
The nexus-connector-automatic-creation Task is used in two ways:
- Initial provisioning: create the listed Nexus repositories, create the scoped user and role, attach the standard repository-view privileges, then create the Connector Secret and the Nexus Connector in the connector namespace.
- Repeated reconciliation: run the same Task again with the intended repository set, formats, and
actionsto refresh updatable fields on existing repositories, re-link the privilege set, rotate the scoped user password, and update the generated Secret.
Repository lifecycle: provision versus reference
The nexusRepositories parameter is a YAML map with two top-level lists that make the lifecycle intent explicit:
provision:entries are owned by the Task. The Task creates them when they do not exist and refreshes the updatable fields on every run. If an immutable field (for examplestorage.blobStoreName,storage.writePolicy, ormaven.versionPolicy) would change on an existing repository, the Task fails the run. The surfaced error may either name the specific diff'd field or pass through Nexus's raw rejection (often the umbrella"You cannot change a repository's blob store through the API"even when the diff is on a different immutable field) — see the Immutable fields full list for the complete set to cross-reference against. Resolve by deleting the Nexus repository and rerunning, or by moving the entry underreference:so the Task only attaches privileges without touching the definition.reference:entries are not owned by the Task. They must already exist in Nexus. The Task only attaches the scoped user's privileges to them. If areference:entry does not exist, the Task fails fast with a hint to move it underprovision:withformatandtype.
Either list may be omitted, but at least one must be present.
Scoped user, role, and credential lifecycle
For each TaskRun the Task ensures one role and one user inside Nexus:
- The role is named
<connector-namespace>-<connector-name>-roleand is owned by the Task. Re-running the Task always refreshes the role's privilege list to match the currentnexusRepositoriesandactions. - The user is named
connector-<connector-namespace>-<connector-name>by default, or the value ofnexusUserwhen set. Every successful run rotates the user's password and writes it into the generated Connector Secret. Old passwords stop working after the run.
If you re-run the Task with the same nexusRepositories and actions, the privilege set on the role does not change but the user password still rotates. Use this behaviour to refresh the Connector Secret on a schedule, or after a Nexus user incident.
For the full parameter reference and the exact Task definition, see the nexus-connector-automatic-creation Task reference.
The nexusRepositories Schema
The nexusRepositories Task parameter carries a single YAML string with two top-level lists:
Either list may be omitted, but at least one must be present. Top-level keys other than provision: and reference: are rejected, so typos like provison: or references: are caught up front.
provision: entries
A provision: entry mirrors the body that Nexus accepts on POST /service/rest/v1/repositories/{format}/{type}. The Task forwards each entry verbatim to Nexus after merging in sensible defaults for fields the user omitted; anything not listed below is forwarded as-is and is accepted or rejected by Nexus itself.
Common fields (all formats and types)
hosted repositories
In addition to the common fields, hosted repositories accept format-specific configuration. For maven2 specifically:
Reference: Maven Repositories — Sonatype Help.
proxy repositories
Proxy repositories cache an upstream registry on demand.
For format-specific proxy fields (e.g. maven.versionPolicy on a maven2 proxy), see the corresponding format reference under Nexus Repository Formats. Authoritative request bodies: Nexus Repositories REST API.
group repositories
Group repositories aggregate read-only access across a list of member repositories of the same format.
For Maven groups specifically you may also want to set maven.versionPolicy: MIXED so the group can front a mix of release and snapshot member repos.
Immutable fields (full list)
The Task fails the run if a provision: entry would change any of the following on an existing repository — Nexus does not accept updates to these fields, and silently keeping the old value would let the requested config drift from reality:
nameformattypestorage.blobStoreNamestorage.writePolicymaven.versionPolicy
The user-visible error may take either of two forms, depending on which Nexus REST call surfaces the rejection:
- An "
<field> is IMMUTABLE in Nexus" message naming the specific diff'd field (when the Task's pre-PUT check fires). - The raw Nexus HTTP 400 body, often the umbrella message
"You cannot change a repository's blob store through the API", even when the actual diff is on a different immutable field (when Nexus's own validator rejects the PUT first). Cross-reference the diff against the list above; the message text alone is not always enough to identify the offending field.
Resolve an immutable diff by deleting the Nexus repository and rerunning, or by moving the entry under reference: so the Task only attaches privileges without touching the definition.
reference: entries
Used when the repository already exists in Nexus (administered through another channel) and you only want the scoped user to gain access to it.
The Task does not modify the repository definition itself in reference: mode — only the role's privilege grant for the scoped user.
actions reference
The actions field on either a provision: or a reference: entry takes any subset of the five standard Nexus repository-view actions:
Each entry resolves to the corresponding nx-repository-view-<format>-<repo>-<action> privileges Nexus generates when the repository is created, attached to the scoped role.
What the Task forwards verbatim vs defaults
- Verbatim: every field you set under a
provision:entry is forwarded to Nexus as-is on create (POST) and on idempotent refresh (PUT). The Task does not silently rewrite values you supplied. - Defaulted only when omitted: the fields listed with defaults in the tables above are filled in when the user omits them, then forwarded to Nexus. Explicit user values — including
falseon boolean fields — are preserved.
Example: Provision Project-private Nexus Repositories
Use this pattern for the most common case — one project owns one or more dedicated Nexus repositories, and only the project consumes them.
See the schema reference for the full set of fields each provision: entry accepts.
Replace these placeholders before you run the example:
<management-namespace>: the private namespace that contains the Task and the credential Secrets.<project-namespace>: the namespace where the Connector and Secret should land.
Then apply this TaskRun:
After the TaskRun succeeds:
- Nexus contains the two requested repositories with sensible defaults applied for unset fields (
storage.blobStoreName=default,storage.writePolicy=ALLOWon hosted repositories,maven.versionPolicy=RELEASEonmaven2). - Nexus contains the scoped role and user with full repository-view actions on both repositories.
- The project namespace contains the generated Connector Secret and the Nexus Connector pointing at the address you supplied in
nexusUrl.
Example: Attach Access to Existing Shared Repositories
Use this pattern when the repositories already exist in Nexus, administered through another channel, and you only need to give an Alauda Container Platform project a scoped credential against them.
See the schema reference for the reference: entry contract.
After the TaskRun succeeds:
- Nexus contains the scoped role and user with
read+browseactions onmaven-publicandnpm-public. The Task does not modify the repositories themselves. - The team namespace contains the generated Connector Secret and the Nexus Connector. Workloads in that namespace can resolve dependencies from the shared repositories through the Connector proxy without holding direct Nexus credentials.
Example: Mixed Provision and Reference
Use this pattern when one scoped user must both publish into project-private repositories and pull from shared upstream repositories. The provisioned set follows Maven best practice — separate releases and snapshots hosted repositories fronted by a single group repository, so consumers resolve through one URL.
See the schema reference for field semantics, defaults, and the immutable-field list.
After the TaskRun succeeds:
- Two
maven2hosted repositories — one pinned toRELEASE, one pinned toSNAPSHOT— plus agrouprepository that fronts both, are created with the requested configuration. - The scoped user can publish release artifacts into
project-a-maven-releases, publish snapshot artifacts intoproject-a-maven-snapshots, and resolve dependencies throughproject-a-maven2(the group) using a single URL. - The scoped user can additionally read from the existing
maven-centralreference repository.
The actions lists on each entry decide the privileges granted to the scoped user per repository. Repositories not listed in either provision: or reference: are not accessible to the scoped user.
Re-run to Add Repositories or Rotate Credentials
The Task is idempotent on re-run. Use the same TaskRun (or the Pipeline / Trigger that fabricates it) for two distinct purposes:
Scope changes — add a repository, remove one, or change the actions granted on an existing one:
- Edit the same TaskRun and append, remove, or modify entries under
provision:orreference:. Leave the other entries in place — they will be recognised as already-existing and refreshed in place. - Re-apply the TaskRun. The Task creates new repositories, refreshes the privileges granted to the scoped user, and rotates the scoped user's password (see below). The new password is written into the same Connector Secret in the project namespace, so consumers that read the Secret pick up the new password on their next reconcile.
Credential rotation — refresh the scoped Nexus user password without changing the repository set:
- Re-apply the same TaskRun unchanged. Every successful run rotates the scoped user's password and rewrites it into the same Connector Secret. This is the recommended mechanism for periodic rotation and for incident-driven rotation.
The following stay unchanged across re-runs (unless you explicitly override them via the corresponding Task parameter): the Connector resource name (<connector-name> part of the connector param), the Secret name (<connector-name>-secret unless overridden via the secret param), and the Nexus role name (<connector-namespace>-<connector-name>-role). The Nexus user name also stays unchanged unless overridden via nexusUser.
When removing a repository — drop the entry from nexusRepositories and re-apply. The Task does not delete the Nexus repository itself (so historical artifacts are not lost); it only revokes the scoped user's privileges against it.
Verify the Result
Check that the TaskRun succeeded:
Check the generated Connector and Secret in the connector namespace (the Secret is <connector-name>-secret unless overridden via the secret param):
Wait for the Connector to reach Ready, then confirm:
Ready=True confirms that the Connector controller successfully reconciled the target Connector with the generated Secret.
Read the Task results to confirm the exact repository set and user identity that were reconciled:
To verify that the generated Connector can actually consume Nexus, mount it into a workload in the project namespace and run a dependency resolution against the proxy. See Nexus Connector for the configuration mounts (settings.xml, .npmrc, pip.conf) provided by the Connectors CSI driver.
Troubleshooting
Tip: set the Task parameter
verbose: "true"on the failing TaskRun to enable shell tracing on non-secret steps. Secret values stay suppressed even when verbose tracing is on. The traced commands make 401 / 403 / SSL / yq-parse errors easier to localise.
nexusRepositories cannot be parsed
The parameter must be a valid YAML map with at least one of the top-level keys provision: or reference:. A bare list at the top level is rejected. The Task surfaces the underlying yq error in step 2 logs.
provision: entry is missing format or type
Every provision: entry requires name, format, and type. The Task fails fast and points at the entry that is missing fields. Add the missing field, or move the entry under reference: if the repository already exists in Nexus.
reference: entry does not exist in Nexus
The Task fails with a message of the form reference mode: repo '<name>' does not exist; move the entry under provision: (with format + type) to create it. Either create the repository in Nexus first, or move the entry under provision: with format and type.
Nexus returns 404 for the repository endpoint
Nexus returns 404 on POST /service/rest/v1/repositories/<slug>/<type> when the requested format is not supported by this Nexus instance. The Task surfaces the format name and reminds you that the only non-1:1
slug mapping is maven2 → maven — all other format names equal their endpoint slug.
provision: entry already exists in Nexus
The Task refreshes the fields that Nexus allows to be updated (for example online, storage.strictContentTypeValidation, cleanup.policyNames). If the supplied value of an immutable field (name, format, type, storage.blobStoreName, storage.writePolicy, or maven.versionPolicy) differs from the existing value, the Task fails the run — Nexus does not accept updates to these fields, so the requested configuration cannot be applied without dropping the existing repository first. The surfaced error may either name the specific diff'd field ("<field> is IMMUTABLE in Nexus") or pass through Nexus's raw HTTP 400 body, which often reads "You cannot change a repository's blob store through the API" even when the actual diff is on a different immutable field — cross-reference the diff against the immutable fields list rather than relying on the message text. Resolve by deleting the existing Nexus repository and rerunning, or by moving the entry under reference: so the Task only attaches privileges without touching the definition.
Nexus user already exists but is not a member of the scoped role
The Task refuses to refresh a foreign user — one whose userId matches the Task-derived name but whose roles list does not include the Task-managed role. The step fails with Refusing to refresh foreign user (would replace existing roles and rotate the password). Override the user name via the nexusUser Task param if a different identity is needed. Resolve by setting an explicit nexusUser value so the Task owns its own user identity, or by manually adding the Task-managed role to the existing user's roles list in Nexus before the next run. The Task deliberately does NOT take over an existing user, to avoid replacing roles set by another system and rotating a credential another integration depends on.
Nexus role already exists but its description does not start with OWNER=connectors-operator
This indicates a name collision with a role owned by something other than the Task — for example a manually created Nexus role with the same name. The Task fails with a squatter error. Either delete or rename the conflicting role, or choose a different connector name so the derived role name does not collide.
Nexus permissions are not sufficient
The Nexus credential used by nexus-secret must satisfy the permission set listed in Nexus credentials for nexus-secret above. Check the role assigned to the provisioning user in the Nexus console (Security → Roles).
If you use a Nexus Connector for nexus-secret, check both:
- The Nexus user behind the Connector has the listed permissions.
- The TaskRun ServiceAccount is allowed to use that Connector.
Kubernetes permissions are not sufficient
The Task runs a kubectl auth can-i preflight before applying the Connector and Secret. The preflight is advisory: if a verb is denied, the Task logs a warning naming the missing verb but does NOT fail fast — the subsequent Server-Side-Apply call will surface the same missing-permission error directly from the Kubernetes apiserver. When debugging RBAC, inspect both log lines: the advisory kubectl auth can-i ... reports forbidden warning from the preflight, and the apiserver error from the SSA call.
The Kubernetes credential used by kube-config (or the in-cluster ServiceAccount when kube-config is unbound) must be allowed to get, create, and patch Secrets and Nexus Connector resources (group connectors.alauda.io) in the connector namespace. The Task uses Server-Side Apply, which the apiserver authorises as the patch verb, so create alone is not enough. See Grant the apply credential Kubernetes RBAC for a copy-pasteable Role and RoleBinding.
nexus-secret workspace is missing files
In basic-auth Secret mode, the workspace must contain username and password files. The Task fails fast with a hint when either file is missing. In Nexus Connector mode, the workspace must contain the Connectors CSI driver's mount output. The Task auto-detects the mode by inspecting the workspace, and basic-auth files take precedence: if both username/password files AND the Connectors CSI mount are present at once, the Task picks basic-auth and does NOT emit a warning. Mount only one shape at a time so the auth mode you intend is unambiguous.
nexusUrl is missing or empty
The nexusUrl parameter is required in both workspace modes. The Task never derives the URL from the Secret or Connector — it always uses nexusUrl as the sole target. Set the parameter to the Nexus base URL (no trailing slash), for example https://nexus.example.com or an in-cluster Service URL such as http://nexus.nexus-system.svc.
Nexus anonymous access is enabled
The Task emits a warning in step 1 when Nexus anonymous access is enabled, but it does not fail and does not modify cluster-level Nexus settings. Disable anonymous access in the Nexus console (Settings → Security → Anonymous Access) if a strict deployment is required.
Nexus returns 401 Unauthorized
Step logs will contain HTTP_CODE=401 from one of the Nexus REST calls. Common causes:
- The bound Secret's
username/passwordfiles are wrong, empty, or have a stray trailing newline. Recreate the basic-auth Secret withkubectl create secret generic ... --from-literal=(which omits the newline that--from-file=of an editor-saved file inserts). - The
nexusUrlhost does not match the Nexus instance the credential was created against (for example ahttps://nexus.example.comURL pointing at a different Nexus 3.x deployment). - In Nexus Connector mode (CSI mount), the workspace did not bind correctly so the Task fell back to basic-auth mode and tried to read
username/passwordfrom the mount. Check the step 1 mode-detect log line to confirm which mode (A or B) was selected.
Nexus returns 403 Forbidden
Step logs will contain HTTP_CODE=403. The credential authenticated successfully but the Nexus role behind it lacks one of the privileges needed for the failing operation. Cross-reference the Nexus credentials for nexus-secret privilege table and the specific REST endpoint named in the log line — for example a 403 on POST /v1/repositories/maven/hosted means the role is missing nx-repository-admin-maven-*-create.
TLS handshake failure against Nexus
Step logs will contain SSL_ERROR or curl: (60) SSL certificate problem. The nexusUrl is HTTPS but the Task runtime image does not trust the certificate chain that Nexus is serving. If you front Nexus with a custom CA, mount the CA bundle through the Nexus Connector workspace (the Connectors CSI driver injects the CA from the Connector's caBundle); if you bind a basic-auth Secret directly, recreate the Secret type as kubernetes.io/tls so the Task image picks up the CA from the standard path, or arrange for the CA to be present in the image's trust store at build time.