Skip to content

Commit fdbffbf

Browse files
committed
fix: close stale ControlMaster ssh connection for machine init/add
1 parent 102421d commit fdbffbf

3 files changed

Lines changed: 43 additions & 13 deletions

File tree

internal/cli/cli.go

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -547,26 +547,35 @@ func provisionOrConnectRemoteMachine(
547547
}
548548

549549
// Use the system 'ssh' command (default).
550-
exec := sshexec.NewSSHCLIRemote(
551-
remoteMachine.User,
552-
remoteMachine.Host,
553-
remoteMachine.Port,
554-
remoteMachine.KeyPath,
555-
)
550+
sshConfig := &connector.SSHConnectorConfig{
551+
User: remoteMachine.User,
552+
Host: remoteMachine.Host,
553+
Port: remoteMachine.Port,
554+
KeyPath: remoteMachine.KeyPath,
555+
}
556+
conn := connector.NewSSHCLIConnector(sshConfig)
556557

557558
if !skipInstall {
559+
exec := sshexec.NewSSHCLIRemote(
560+
remoteMachine.User,
561+
remoteMachine.Host,
562+
remoteMachine.Port,
563+
remoteMachine.KeyPath,
564+
)
558565
if err := provisionMachine(ctx, exec, version); err != nil {
559566
return nil, fmt.Errorf("provision machine: %w", err)
560567
}
561-
}
562568

563-
sshConfig := &connector.SSHConnectorConfig{
564-
User: remoteMachine.User,
565-
Host: remoteMachine.Host,
566-
Port: remoteMachine.Port,
567-
KeyPath: remoteMachine.KeyPath,
569+
if remoteMachine.User != rootUser {
570+
// provisionMachine has just added the user to the uncloud group. Any SSH ControlMaster left over from
571+
// a previous uc invocation (e.g. a failed uc command against the uninitialised machine) still holds
572+
// the old user groups and would deny access to /run/uncloud/uncloud.sock. Close the current session
573+
// if it exists so the next session picks up the new groups.
574+
conn.CloseControlMaster(ctx)
575+
}
568576
}
569-
machineClient, err := client.New(ctx, connector.NewSSHCLIConnector(sshConfig))
577+
578+
machineClient, err := client.New(ctx, conn)
570579
if err != nil {
571580
return nil, fmt.Errorf("connect to remote machine: %w", err)
572581
}

internal/cli/machine.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,17 @@ func provisionMachine(ctx context.Context, exec sshexec.Executor, version string
7272
" echo '%[1]s ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/%[1]s",
7373
user)
7474
}
75+
if strings.Contains(err.Error(), "not in the sudoers file") {
76+
return fmt.Errorf(
77+
"user '%[1]s' is not in the sudo group or sudoers file so cannot use sudo, but Uncloud needs "+
78+
"passwordless sudo or root access to install and configure the uncloudd daemon on the remote "+
79+
"machine.\n\n"+
80+
"Possible solutions:\n"+
81+
"1. Use root user or a user with passwordless sudo instead.\n"+
82+
"2. Grant passwordless sudo to the user '%[1]s' by running on the remote machine as root:\n"+
83+
" echo '%[1]s ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/%[1]s",
84+
user)
85+
}
7586
return fmt.Errorf("sudo command failed for user '%s': %w. "+
7687
"Please ensure the user has sudo privileges or use root user instead", user, err)
7788
}

pkg/client/connector/sshcli.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,16 @@ func (c *SSHCLIConnector) CheckTCPForwarding(ctx context.Context) error {
207207
return c.fwdCheckErr
208208
}
209209

210+
// CloseControlMaster terminates the SSH ControlMaster process for this destination so the next connection starts
211+
// a fresh SSH session. No-op if no master is running or the control socket is not configured. Errors are ignored.
212+
func (c *SSHCLIConnector) CloseControlMaster(ctx context.Context) {
213+
if c.controlSockPath == "" {
214+
return
215+
}
216+
args := append(c.buildSSHArgs(), "-O", "exit")
217+
_ = exec.CommandContext(ctx, "ssh", args...).Run()
218+
}
219+
210220
func (c *SSHCLIConnector) Close() error {
211221
// Individual connections are managed by gRPC and closed when the gRPC connection closes.
212222
// The SSH control socket may persist for connection reuse across CLI invocations.

0 commit comments

Comments
 (0)