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
- Add the exact sudoers rule shown in the error to /etc/sudoers.d/ (use visudo -f), substituting the real supervisor and target users
- Reload/verify sudoers with visudo -c
- Confirm the daemon actually runs as the supervisor user named in the rule (systemd User=)
- Test manually: sudo -n -iu <run_as_user> -- /usr/bin/true
- 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
- Ship the sudoers file per project via configuration management, keyed to the daemon's actual runtime user
- Run PreflightRunAsUser (doctor) in CI or on deploy before enabling run-as projects
- After OS upgrades or VM rebuilds, verify sudoers and target users still exist
- Keep one sudoers file per project in /etc/sudoers.d/ so removals are clean
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
- RunIsolationProbe: RunAsUser is empty
- PreflightRunAsUser: RunAsUser is empty
- project %q: target user %q can run passwordless sudo. The ru
- passwordless sudo to user %q failed (check that your sudoers
- project %q: target user %q cannot read AND write work_dir %q
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/756bf578be1b2e58.
Report an issue: GitHub.