chenhg5/cc-connect · critical

project %q: passwordless sudo to user %q is not configured.

Error message

project %q: passwordless sudo to user %q is not configured. Add a sudoers rule such as:
  %s ALL=(%s) NOPASSWD: ALL
then restart cc-connect. Underlying error: %w

What it means

PreflightRunAsUser (core/runas_check.go:96) performs a doctor/startup check per project: it runs 'sudo -n -iu <user> -- /usr/bin/true' and, on failure, records a fatal result explaining that passwordless sudo to the run-as user is not configured, including a ready-to-paste sudoers line and the underlying sudo error. Subsequent checks are skipped since they'd all fail.

Source

Thrown at core/runas_check.go:96

//     help the operator find the offending rule.
//  3. Target user can read AND write the work_dir root (fatal if not),
//     plus a best-effort descendant walk producing warnings for paths
//     the target user cannot access.
func PreflightRunAsUser(ctx context.Context, cfg PreflightConfig) PreflightResult {
	result := PreflightResult{Project: cfg.Project, RunAsUser: cfg.RunAsUser}
	if cfg.RunAsUser == "" {
		result.Fatal = append(result.Fatal, errors.New("PreflightRunAsUser: RunAsUser is empty"))
		return result
	}
	if cfg.Runner == nil {
		cfg.Runner = ExecSudoRunner{}
	}
	if cfg.ScanConfig.MaxReport == 0 {
		cfg.ScanConfig = DefaultDescendantScanConfig
	}

	if _, err := cfg.Runner.Run(ctx, "-n", "-iu", cfg.RunAsUser, "--", "/usr/bin/true"); err != nil {
		result.Fatal = append(result.Fatal, fmt.Errorf(
			"project %q: passwordless sudo to user %q is not configured. Add a sudoers rule such as:\n  %s ALL=(%s) NOPASSWD: ALL\nthen restart cc-connect. Underlying error: %w",
			cfg.Project, cfg.RunAsUser, currentUsernameOr("<supervisor>"), cfg.RunAsUser, err))
		return result // subsequent checks are pointless
	}

	if _, err := cfg.Runner.Run(ctx, "-n", "-iu", cfg.RunAsUser, "--", "sudo", "-n", "/usr/bin/true"); err == nil {
		// Escalation succeeded — collect sudo -l from the target's
		// context to help the operator find the offending rule.
		if out, listErr := cfg.Runner.Run(ctx, "-n", "-iu", cfg.RunAsUser, "--", "sudo", "-n", "-l"); listErr == nil {
			result.SudoListOutput = strings.TrimSpace(string(out))
		}
		msg := fmt.Sprintf(
			"project %q: target user %q can run passwordless sudo. The run_as_user sandbox provides no isolation if the spawned agent can escalate non-interactively. Remove NOPASSWD sudo access for this user before starting cc-connect.",
			cfg.Project, cfg.RunAsUser)
		if result.SudoListOutput != "" {
			msg += "\n\n`sudo -n -l` as " + cfg.RunAsUser + ":\n" + indent(result.SudoListOutput, "  ")
		}
		result.Fatal = append(result.Fatal, errors.New(msg))

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Add the exact sudoers rule shown in the error to /etc/sudoers.d/ (use visudo -f), substituting the real supervisor and target users
  2. Reload/verify sudoers with visudo -c
  3. Confirm the daemon actually runs as the supervisor user named in the rule (systemd User=)
  4. Test manually: sudo -n -iu <run_as_user> -- /usr/bin/true
  5. Re-run cc-connect doctor to confirm the fatal is cleared

Example fix

// before: error at startup
// project "myproj": passwordless sudo to user "agent1" is not configured.
// after: /etc/sudoers.d/cc-connect-myproj
supervisor ALL=(agent1) NOPASSWD: ALL
Defensive patterns

Strategy: validation

Validate before calling

func preflightSudo(project, supervisor, target string) error {
	out, err := exec.Command("sudo", "-n", "-iu", target, "--", "/usr/bin/true").CombinedOutput()
	if err != nil {
		return fmt.Errorf("%s: add '%s ALL=(%s) NOPASSWD: ALL' to /etc/sudoers.d/: %w: %s", project, supervisor, target, err, out)
	}
	return nil
}
// run in deploy smoke tests before cc-connect starts

Try / catch

result := core.PreflightRunAsUser(ctx, cfg)
for _, f := range result.Fatal {
	if strings.Contains(f.Error(), "passwordless sudo") {
		fmt.Fprintf(os.Stderr, "FATAL (fix sudoers, skipping further checks): %v\n", f)
		os.Exit(1)
	}
}

Prevention

When it happens

Trigger: Running the doctor (runDoctorOne) or an anonymous preflight when cfg.Runner.Run(ctx, '-n', '-iu', cfg.RunAsUser, '--', '/usr/bin/true') errors: no sudoers rule, rule requires a password, target user missing, or sudo policy blocks non-interactive use.

Common situations: New project added to config.toml with run_as_user but the matching sudoers entry was never created; sudoers rule created for the wrong supervisor username (e.g. root vs the daemon's service user); provisioning drifted after a VM rebuild.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/756bf578be1b2e58. Report an issue: GitHub.