mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 19:13:34 +00:00
fix: close stale ControlMaster ssh connection for machine init/add
This commit is contained in:
+23
-14
@@ -547,26 +547,35 @@ func provisionOrConnectRemoteMachine(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use the system 'ssh' command (default).
|
// Use the system 'ssh' command (default).
|
||||||
exec := sshexec.NewSSHCLIRemote(
|
|
||||||
remoteMachine.User,
|
|
||||||
remoteMachine.Host,
|
|
||||||
remoteMachine.Port,
|
|
||||||
remoteMachine.KeyPath,
|
|
||||||
)
|
|
||||||
|
|
||||||
if !skipInstall {
|
|
||||||
if err := provisionMachine(ctx, exec, version); err != nil {
|
|
||||||
return nil, fmt.Errorf("provision machine: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sshConfig := &connector.SSHConnectorConfig{
|
sshConfig := &connector.SSHConnectorConfig{
|
||||||
User: remoteMachine.User,
|
User: remoteMachine.User,
|
||||||
Host: remoteMachine.Host,
|
Host: remoteMachine.Host,
|
||||||
Port: remoteMachine.Port,
|
Port: remoteMachine.Port,
|
||||||
KeyPath: remoteMachine.KeyPath,
|
KeyPath: remoteMachine.KeyPath,
|
||||||
}
|
}
|
||||||
machineClient, err := client.New(ctx, connector.NewSSHCLIConnector(sshConfig))
|
conn := connector.NewSSHCLIConnector(sshConfig)
|
||||||
|
|
||||||
|
if !skipInstall {
|
||||||
|
exec := sshexec.NewSSHCLIRemote(
|
||||||
|
remoteMachine.User,
|
||||||
|
remoteMachine.Host,
|
||||||
|
remoteMachine.Port,
|
||||||
|
remoteMachine.KeyPath,
|
||||||
|
)
|
||||||
|
if err := provisionMachine(ctx, exec, version); err != nil {
|
||||||
|
return nil, fmt.Errorf("provision machine: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if remoteMachine.User != rootUser {
|
||||||
|
// provisionMachine has just added the user to the uncloud group. Any SSH ControlMaster left over from
|
||||||
|
// a previous uc invocation (e.g. a failed uc command against the uninitialised machine) still holds
|
||||||
|
// the old user groups and would deny access to /run/uncloud/uncloud.sock. Close the current session
|
||||||
|
// if it exists so the next session picks up the new groups.
|
||||||
|
conn.CloseControlMaster(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
machineClient, err := client.New(ctx, conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("connect to remote machine: %w", err)
|
return nil, fmt.Errorf("connect to remote machine: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,17 @@ func provisionMachine(ctx context.Context, exec sshexec.Executor, version string
|
|||||||
" echo '%[1]s ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/%[1]s",
|
" echo '%[1]s ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/%[1]s",
|
||||||
user)
|
user)
|
||||||
}
|
}
|
||||||
|
if strings.Contains(err.Error(), "not in the sudoers file") {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"user '%[1]s' is not in the sudo group or sudoers file so cannot use sudo, but Uncloud needs "+
|
||||||
|
"passwordless sudo or root access to install and configure the uncloudd daemon on the remote "+
|
||||||
|
"machine.\n\n"+
|
||||||
|
"Possible solutions:\n"+
|
||||||
|
"1. Use root user or a user with passwordless sudo instead.\n"+
|
||||||
|
"2. Grant passwordless sudo to the user '%[1]s' by running on the remote machine as root:\n"+
|
||||||
|
" echo '%[1]s ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/%[1]s",
|
||||||
|
user)
|
||||||
|
}
|
||||||
return fmt.Errorf("sudo command failed for user '%s': %w. "+
|
return fmt.Errorf("sudo command failed for user '%s': %w. "+
|
||||||
"Please ensure the user has sudo privileges or use root user instead", user, err)
|
"Please ensure the user has sudo privileges or use root user instead", user, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,6 +207,16 @@ func (c *SSHCLIConnector) CheckTCPForwarding(ctx context.Context) error {
|
|||||||
return c.fwdCheckErr
|
return c.fwdCheckErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CloseControlMaster terminates the SSH ControlMaster process for this destination so the next connection starts
|
||||||
|
// a fresh SSH session. No-op if no master is running or the control socket is not configured. Errors are ignored.
|
||||||
|
func (c *SSHCLIConnector) CloseControlMaster(ctx context.Context) {
|
||||||
|
if c.controlSockPath == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
args := append(c.buildSSHArgs(), "-O", "exit")
|
||||||
|
_ = exec.CommandContext(ctx, "ssh", args...).Run()
|
||||||
|
}
|
||||||
|
|
||||||
func (c *SSHCLIConnector) Close() error {
|
func (c *SSHCLIConnector) Close() error {
|
||||||
// Individual connections are managed by gRPC and closed when the gRPC connection closes.
|
// Individual connections are managed by gRPC and closed when the gRPC connection closes.
|
||||||
// The SSH control socket may persist for connection reuse across CLI invocations.
|
// The SSH control socket may persist for connection reuse across CLI invocations.
|
||||||
|
|||||||
Reference in New Issue
Block a user