argoproj/argo-workflows · error

Forbidden

Error message

Forbidden

What it means

Before uploading, the server checks RBAC with auth.CanI for verb 'get' on workflowtemplates in the target namespace for the named template. If the caller's credentials (client token, server SA, or SSO claims) lack that permission, the handler responds 403 Forbidden. This enforces least-privilege: you may only create workflows from templates you can read.

Source

Thrown at server/artifacts/artifact_server.go:119

		a.unauthorizedError(w)
		return
	}

	a.logger.WithFields(logging.Fields{
		"namespace":            namespace,
		"workflowTemplateName": workflowTemplateName,
		"artifactName":         artifactName,
	}).Info(ctx, "Upload artifact")

	// Authorize before reading the request body, so an unprivileged caller
	// cannot force the server to buffer a large upload just to be rejected.
	allowed, err := auth.CanI(ctx, "get", "workflowtemplates", namespace, workflowTemplateName)
	if err != nil {
		a.serverInternalError(ctx, err, w)
		return
	}
	if !allowed {
		http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
		return
	}
	allowed, err = auth.CanI(ctx, "create", "workflows", namespace, "")
	if err != nil {
		a.serverInternalError(ctx, err, w)
		return
	}
	if !allowed {
		http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
		return
	}

	// Get WorkflowTemplate to find artifact configuration
	wfClient := auth.GetWfClient(ctx)
	wfTemplate, err := wfClient.ArgoprojV1alpha1().WorkflowTemplates(namespace).Get(ctx, workflowTemplateName, metav1.GetOptions{})
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to get WorkflowTemplate %s/%s: %v", namespace, workflowTemplateName, err), http.StatusNotFound)
		return

View on GitHub (pinned to 35bff19146)

Solutions

  1. Grant the caller's Role/ClusterRole a rule: apiGroups: [argoproj.io], resources: [workflowtemplates], verbs: [get] in that namespace
  2. Bind that role to the user/service account making the request
  3. Verify the namespace in the URL matches the namespace where RBAC was granted
  4. Check SSO RBAC label mapping if using SSO auth mode

Example fix

# before (Role without workflowtemplates)
rules:
- apiGroups: [argoproj.io]
  resources: [workflows]
  verbs: [create]
# after
rules:
- apiGroups: [argoproj.io]
  resources: [workflows]
  verbs: [create]
- apiGroups: [argoproj.io]
  resources: [workflowtemplates]
  verbs: [get]
Defensive patterns

Strategy: validation

Validate before calling

kubectl auth can-i get workflowtemplates -n <namespace> --as=<user>   # must be yes
kubectl auth can-i create workflows -n <namespace> --as=<user>        # must be yes

Try / catch

if resp.StatusCode == http.StatusForbidden {
    return fmt.Errorf("caller lacks RBAC (workflowtemplates/get or workflows/create) in %s", namespace)
}

Prevention

When it happens

Trigger: Submitting an upload with a token whose RBAC role has no argoproj.io/workflowtemplates get permission in the target namespace; SSO user not mapped to a service account with that role.

Common situations: CI tokens scoped to workflows only; SSO RBAC mapping missing the workflowtemplates rule; wrong namespace used in the URL (permissions are namespace-scoped).

Understand the failure class

Related errors


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/04617c7e83d37b6a. Report an issue: GitHub.