k3s-io/k3s · error

not authorized

Error message

not authorized

What it means

Thrown by doAuth in pkg/server/auth/auth.go when the HasRole/IsLocalOrHasRole middleware runs but the *config.Control pointer passed to the router is nil. k3s refuses to authorize the request because there is no server configuration at all, and returns HTTP 401 with body 'not authorized'. This is a wiring/startup-order defect, not a credential problem.

Source

Thrown at pkg/server/auth/auth.go:43

)

func hasRole(mustRoles []string, roles []string) bool {
	for _, check := range roles {
		for _, role := range mustRoles {
			if role == check {
				return true
			}
		}
	}
	return false
}

// doAuth calls the cluster's authenticator to validate that the client has at least one of the listed roles
func doAuth(roles []string, serverConfig *config.Control, next http.Handler, rw http.ResponseWriter, req *http.Request) {
	switch {
	case serverConfig == nil:
		logrus.Errorf("Authenticate not initialized: serverConfig is nil")
		util.SendError(errors.New("not authorized"), rw, req, http.StatusUnauthorized)
		return
	case serverConfig.Runtime.Authenticator == nil:
		logrus.Errorf("Authenticate not initialized: serverConfig.Runtime.Authenticator is nil")
		util.SendError(errors.New("not authorized"), rw, req, http.StatusUnauthorized)
		return
	}

	resp, ok, err := serverConfig.Runtime.Authenticator.AuthenticateRequest(req)
	if err != nil {
		logrus.Errorf("Failed to authenticate request from %s: %v", req.RemoteAddr, err)
		util.SendError(errors.New("not authorized"), rw, req, http.StatusUnauthorized)
		return
	}

	if !ok || !hasRole(roles, resp.User.GetGroups()) {
		util.SendError(errors.New("forbidden"), rw, req, http.StatusForbidden)
		return
	}

View on GitHub (pinned to 6ba341e396)

Solutions

  1. Check the k3s server log for 'Authenticate not initialized: serverConfig is nil' - it identifies this exact case.
  2. If you embed or fork k3s, make sure the value passed to auth.HasRole(...) / handlers.Register is a non-nil *config.Control before the mux starts serving.
  3. If you are a normal client, wait for the server to fully start (poll /v1-k3s/readyz or the readyz endpoint) before calling protected endpoints.
  4. Restart the server process if it was started with a broken/absent configuration.

Example fix

// before (fork/embedding): router with nil config
mux.Handle("/v1-k3s/runtime", auth.HasRole(nil, "system:masters")(h))

// after: pass the initialized control config
mux.Handle("/v1-k3s/runtime", auth.HasRole(control, "system:masters")(h))
Defensive patterns

Strategy: validation

Validate before calling

// Before serving, ensure the control config is wired (embedding/tests)
func routerReady(control *config.Control) error {
    if control == nil {
        return errors.New("control config is nil; do not mount auth.HasRole routes")
    }
    return nil
}

Prevention

When it happens

Trigger: An HTTP request reaches a role-protected supervisor endpoint (routes built with auth.HasRole / auth.IsLocalOrHasRole, e.g. the /v1-k3s runtime endpoints) while the serverConfig handed to handlers.Register is nil. Happens in custom builds that mount the k3s router with a nil control config, in unit tests that exercise the middleware without config, or if a request slips in before server setup assigns the config.

Common situations: Embedding k3s handlers in a test harness or fork; racing a request against server startup; refactors that construct routers before config.Control exists.

Related errors


AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15). Data as JSON: /api/errors/5faa47133f6bf2aa. Report an issue: GitHub.