-
Notifications
You must be signed in to change notification settings - Fork 362
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Add --reuse-sock flag so browser IDEs can reuse another SSH connections SSH_AUTH_SOCK #1471
base: main
Are you sure you want to change the base?
Changes from all commits
b4cf40f
76d2fb0
aca4f46
39fe247
d7cba57
e9b620d
d2ae304
9e7f788
5983f55
c9fb98a
631f742
9e40c61
990cb47
b3810f1
2797aca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,6 +52,7 @@ type SSHCmd struct { | |
|
||
Stdio bool | ||
JumpContainer bool | ||
ReuseSSHAuthSock string | ||
AgentForwarding bool | ||
GPGAgentForwarding bool | ||
GitSSHSignatureForwarding bool | ||
|
@@ -110,6 +111,8 @@ func NewSSHCmd(f *flags.GlobalFlags) *cobra.Command { | |
sshCmd.Flags().StringVar(&cmd.WorkDir, "workdir", "", "The working directory in the container") | ||
sshCmd.Flags().BoolVar(&cmd.Proxy, "proxy", false, "If true will act as intermediate proxy for a proxy provider") | ||
sshCmd.Flags().BoolVar(&cmd.AgentForwarding, "agent-forwarding", true, "If true forward the local ssh keys to the remote machine") | ||
sshCmd.Flags().StringVar(&cmd.ReuseSSHAuthSock, "reuse-ssh-auth-sock", "", "If set, the SSH_AUTH_SOCK is expected to already be available in the workspace (under /tmp using the key provided) and the connection reuses this instead of creating a new one") | ||
_ = sshCmd.Flags().MarkHidden("reuse-ssh-auth-sock") | ||
sshCmd.Flags().BoolVar(&cmd.GPGAgentForwarding, "gpg-agent-forwarding", false, "If true forward the local gpg-agent to the remote machine") | ||
sshCmd.Flags().BoolVar(&cmd.Stdio, "stdio", false, "If true will tunnel connection through stdout and stdin") | ||
sshCmd.Flags().BoolVar(&cmd.StartServices, "start-services", true, "If false will not start any port-forwarding or git / docker credentials helper") | ||
|
@@ -430,6 +433,10 @@ func (cmd *SSHCmd) startTunnel(ctx context.Context, devPodConfig *config.Config, | |
|
||
log.Debugf("Run outer container tunnel") | ||
command := fmt.Sprintf("'%s' helper ssh-server --track-activity --stdio --workdir '%s'", agent.ContainerDevPodHelperLocation, workdir) | ||
if cmd.ReuseSSHAuthSock != "" { | ||
log.Info("Reusing SSH_AUTH_SOCK") | ||
command += fmt.Sprintf(" --reuse-ssh-auth-sock=%s", cmd.ReuseSSHAuthSock) | ||
} | ||
if cmd.Debug { | ||
command += " --debug" | ||
} | ||
|
@@ -484,7 +491,7 @@ func (cmd *SSHCmd) startServices( | |
log log.Logger, | ||
) { | ||
if cmd.User != "" { | ||
err := tunnel.RunInContainer( | ||
err := tunnel.RunServices( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you revert this naming change please? Makes it harder to backmerge and it's not really required in this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason I changed this function's name was because it runs a server locally, so the name is misleading IMO |
||
ctx, | ||
devPodConfig, | ||
containerClient, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ import ( | |
config2 "github.com/loft-sh/devpod/pkg/devcontainer/config" | ||
"github.com/loft-sh/devpod/pkg/devcontainer/sshtunnel" | ||
dpFlags "github.com/loft-sh/devpod/pkg/flags" | ||
"github.com/loft-sh/devpod/pkg/ide" | ||
"github.com/loft-sh/devpod/pkg/ide/fleet" | ||
"github.com/loft-sh/devpod/pkg/ide/jetbrains" | ||
"github.com/loft-sh/devpod/pkg/ide/jupyter" | ||
|
@@ -37,6 +38,7 @@ import ( | |
provider2 "github.com/loft-sh/devpod/pkg/provider" | ||
devssh "github.com/loft-sh/devpod/pkg/ssh" | ||
"github.com/loft-sh/devpod/pkg/tunnel" | ||
"github.com/loft-sh/devpod/pkg/util" | ||
"github.com/loft-sh/devpod/pkg/version" | ||
workspace2 "github.com/loft-sh/devpod/pkg/workspace" | ||
"github.com/loft-sh/log" | ||
|
@@ -151,6 +153,19 @@ func (cmd *UpCmd) Run( | |
cmd.Recreate = true | ||
} | ||
|
||
// check if we are a browser IDE and need to reuse the SSH_AUTH_SOCK | ||
targetIDE := client.WorkspaceConfig().IDE.Name | ||
// Check override | ||
if cmd.IDE != "" { | ||
targetIDE = cmd.IDE | ||
} | ||
if !cmd.Proxy && ide.ReusesAuthSock(targetIDE) { | ||
cmd.SSHAuthSockID = util.RandStringBytes(10) | ||
log.Debug("Reusing SSH_AUTH_SOCK", cmd.SSHAuthSockID) | ||
} else if cmd.Proxy && ide.ReusesAuthSock(targetIDE) { | ||
log.Info("Reusing SSH_AUTH_SOCK is not supported with proxy mode, consider launching the IDE from the platform UI") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can potentially be confusing if you don't know what |
||
} | ||
|
||
// run devpod agent up | ||
result, err := cmd.devPodUp(ctx, devPodConfig, client, log) | ||
if err != nil { | ||
|
@@ -274,6 +289,7 @@ func (cmd *UpCmd) Run( | |
ideConfig.Options, | ||
cmd.GitUsername, | ||
cmd.GitToken, | ||
cmd.SSHAuthSockID, | ||
log, | ||
) | ||
case string(config.IDERustRover): | ||
|
@@ -310,6 +326,7 @@ func (cmd *UpCmd) Run( | |
ideConfig.Options, | ||
cmd.GitUsername, | ||
cmd.GitToken, | ||
cmd.SSHAuthSockID, | ||
log, | ||
) | ||
case string(config.IDEJupyterDesktop): | ||
|
@@ -322,6 +339,7 @@ func (cmd *UpCmd) Run( | |
ideConfig.Options, | ||
cmd.GitUsername, | ||
cmd.GitToken, | ||
cmd.SSHAuthSockID, | ||
log) | ||
case string(config.IDEMarimo): | ||
return startMarimoInBrowser( | ||
|
@@ -333,6 +351,7 @@ func (cmd *UpCmd) Run( | |
ideConfig.Options, | ||
cmd.GitUsername, | ||
cmd.GitToken, | ||
cmd.SSHAuthSockID, | ||
log) | ||
} | ||
} | ||
|
@@ -559,7 +578,7 @@ func startMarimoInBrowser( | |
client client2.BaseWorkspaceClient, | ||
user string, | ||
ideOptions map[string]config.OptionValue, | ||
gitUsername, gitToken string, | ||
gitUsername, gitToken, authSockID string, | ||
logger log.Logger, | ||
) error { | ||
if forwardGpg { | ||
|
@@ -606,6 +625,7 @@ func startMarimoInBrowser( | |
extraPorts, | ||
gitUsername, | ||
gitToken, | ||
authSockID, | ||
logger, | ||
) | ||
} | ||
|
@@ -617,7 +637,7 @@ func startJupyterNotebookInBrowser( | |
client client2.BaseWorkspaceClient, | ||
user string, | ||
ideOptions map[string]config.OptionValue, | ||
gitUsername, gitToken string, | ||
gitUsername, gitToken, authSockID string, | ||
logger log.Logger, | ||
) error { | ||
if forwardGpg { | ||
|
@@ -664,6 +684,7 @@ func startJupyterNotebookInBrowser( | |
extraPorts, | ||
gitUsername, | ||
gitToken, | ||
authSockID, | ||
logger, | ||
) | ||
} | ||
|
@@ -675,7 +696,7 @@ func startJupyterDesktop( | |
client client2.BaseWorkspaceClient, | ||
user string, | ||
ideOptions map[string]config.OptionValue, | ||
gitUsername, gitToken string, | ||
gitUsername, gitToken, authSockID string, | ||
logger log.Logger, | ||
) error { | ||
if forwardGpg { | ||
|
@@ -719,6 +740,7 @@ func startJupyterDesktop( | |
extraPorts, | ||
gitUsername, | ||
gitToken, | ||
authSockID, | ||
logger, | ||
) | ||
} | ||
|
@@ -765,7 +787,7 @@ func startVSCodeInBrowser( | |
client client2.BaseWorkspaceClient, | ||
workspaceFolder, user string, | ||
ideOptions map[string]config.OptionValue, | ||
gitUsername, gitToken string, | ||
gitUsername, gitToken, authSockID string, | ||
logger log.Logger, | ||
) error { | ||
if forwardGpg { | ||
|
@@ -813,6 +835,7 @@ func startVSCodeInBrowser( | |
extraPorts, | ||
gitUsername, | ||
gitToken, | ||
authSockID, | ||
logger, | ||
) | ||
} | ||
|
@@ -848,16 +871,75 @@ func parseAddressAndPort(bindAddressOption string, defaultPort int) (string, int | |
return address, portName, nil | ||
} | ||
|
||
// setupBackhaul sets up a long running command in the container to ensure an SSH connection is kept alive | ||
func setupBackhaul(client client2.BaseWorkspaceClient, authSockId string, log log.Logger) error { | ||
execPath, err := os.Executable() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
remoteUser, err := devssh.GetUser(client.WorkspaceConfig().ID, client.WorkspaceConfig().SSHConfigPath) | ||
if err != nil { | ||
remoteUser = "root" | ||
} | ||
|
||
dotCmd := exec.Command( | ||
execPath, | ||
"ssh", | ||
"--agent-forwarding=true", | ||
fmt.Sprintf("--reuse-ssh-auth-sock=%s", authSockId), | ||
"--start-services=false", | ||
"--user", | ||
remoteUser, | ||
"--context", | ||
client.Context(), | ||
client.Workspace(), | ||
"--log-output=raw", | ||
"--command", | ||
"while true; do sleep 6000000; done", // sleep infinity is not available on all systems | ||
) | ||
|
||
if log.GetLevel() == logrus.DebugLevel { | ||
dotCmd.Args = append(dotCmd.Args, "--debug") | ||
} | ||
|
||
log.Info("Setting up backhaul SSH connection") | ||
|
||
writer := log.Writer(logrus.InfoLevel, false) | ||
|
||
dotCmd.Stdout = writer | ||
dotCmd.Stderr = writer | ||
|
||
err = dotCmd.Run() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
log.Infof("Done setting up backhaul") | ||
|
||
return nil | ||
} | ||
|
||
func startBrowserTunnel( | ||
ctx context.Context, | ||
devPodConfig *config.Config, | ||
client client2.BaseWorkspaceClient, | ||
user, targetURL string, | ||
forwardPorts bool, | ||
extraPorts []string, | ||
gitUsername, gitToken string, | ||
gitUsername, gitToken, authSockID string, | ||
logger log.Logger, | ||
) error { | ||
// Setup a backhaul SSH connection using the remote user so there is an AUTH SOCK to use | ||
// With normal IDEs this would be the SSH connection made by the IDE | ||
// authSockID is not set when in proxy mode since we cannot use the proxies ssh-agent | ||
if authSockID != "" { | ||
go func() { | ||
if err := setupBackhaul(client, authSockID, logger); err != nil { | ||
logger.Error("Failed to setup backhaul SSH connection: ", err) | ||
} | ||
}() | ||
} | ||
err := tunnel.NewTunnel( | ||
ctx, | ||
func(ctx context.Context, stdin io.Reader, stdout io.Writer) error { | ||
|
@@ -866,6 +948,7 @@ func startBrowserTunnel( | |
|
||
cmd, err := createSSHCommand(ctx, client, logger, []string{ | ||
"--log-output=raw", | ||
fmt.Sprintf("--reuse-ssh-auth-sock=%s", authSockID), | ||
"--stdio", | ||
}) | ||
if err != nil { | ||
|
@@ -887,7 +970,7 @@ func startBrowserTunnel( | |
} | ||
|
||
// run in container | ||
err := tunnel.RunInContainer( | ||
err := tunnel.RunServices( | ||
ctx, | ||
devPodConfig, | ||
containerClient, | ||
|
@@ -994,6 +1077,8 @@ func createSSHCommand( | |
} | ||
args = append(args, extraArgs...) | ||
|
||
logger.Debug("Connecting with SSH command ", execPath, args) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we still need this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought it would be a useful debug log so we have the flags the SSH connection made |
||
|
||
return exec.CommandContext(ctx, execPath, args...), nil | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: This will always be printing when we connect, can you make this log level
Debug
please?