Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/agent/workspace/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (cmd *UpCmd) Run(ctx context.Context) error {

tunnelClient, logger, credentialsDir, err := initWorkspace(cancelCtx, cancel, workspaceInfo, cmd.Debug, !workspaceInfo.CLIOptions.Platform.Enabled && !workspaceInfo.CLIOptions.DisableDaemon)
if err != nil {
err1 := clientimplementation.DeleteWorkspaceFolder(workspaceInfo.Workspace.Context, workspaceInfo.Workspace.ID, workspaceInfo.Workspace.SSHConfigPath, logger)
err1 := clientimplementation.DeleteWorkspaceFolder(workspaceInfo.Workspace.Context, workspaceInfo.Workspace.ID, workspaceInfo.Workspace.SSHConfigPath, workspaceInfo.Workspace.SSHConfigIncludePath, logger)
if err1 != nil {
return fmt.Errorf("%s %w", err1.Error(), err)
}
Expand Down
1 change: 1 addition & 0 deletions cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func NewBuildCmd(flags *flags.GlobalFlags) *cobra.Command {
cmd.DevContainerImage,
cmd.DevContainerPath,
sshConfigPath,
"",
nil,
cmd.UID,
false,
Expand Down
2 changes: 1 addition & 1 deletion cmd/pro/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func cleanupLocalWorkspaces(ctx context.Context, devPodConfig *config.Config, wo
return
}
// delete workspace folder
err = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, client.Workspace(), client.WorkspaceConfig().SSHConfigPath, log)
err = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, client.Workspace(), client.WorkspaceConfig().SSHConfigPath, client.WorkspaceConfig().SSHConfigIncludePath, log)
if err != nil {
log.WithFields(logrus.Fields{
"workspaceId": w.ID,
Expand Down
2 changes: 1 addition & 1 deletion cmd/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func (cmd *SSHCmd) Run(
// get user
if cmd.User == "" {
var err error
cmd.User, err = devssh.GetUser(client.WorkspaceConfig().ID, client.WorkspaceConfig().SSHConfigPath)
cmd.User, err = devssh.GetUser(client.WorkspaceConfig().ID, client.WorkspaceConfig().SSHConfigPath, client.WorkspaceConfig().SSHConfigIncludePath)
if err != nil {
return err
}
Expand Down
24 changes: 18 additions & 6 deletions cmd/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,9 @@ func (cmd *UpCmd) Run(
devPodHome = envDevPodHome
}
setupGPGAgentForwarding := cmd.GPGAgentForwarding || devPodConfig.ContextOption(config.ContextOptionGPGAgentForwarding) == "true"
sshConfigIncludePath := devPodConfig.ContextOption(config.ContextOptionSSHConfigIncludePath)

err = configureSSH(client, cmd.SSHConfigPath, user, workdir, setupGPGAgentForwarding, devPodHome)
err = configureSSH(client, cmd.SSHConfigPath, sshConfigIncludePath, user, workdir, setupGPGAgentForwarding, devPodHome)
if err != nil {
return err
}
Expand Down Expand Up @@ -850,7 +851,7 @@ func setupBackhaul(client client2.BaseWorkspaceClient, authSockId string, log lo
return err
}

remoteUser, err := devssh.GetUser(client.WorkspaceConfig().ID, client.WorkspaceConfig().SSHConfigPath)
remoteUser, err := devssh.GetUser(client.WorkspaceConfig().ID, client.WorkspaceConfig().SSHConfigPath, client.WorkspaceConfig().SSHConfigIncludePath)
if err != nil {
remoteUser = "root"
}
Expand Down Expand Up @@ -1002,15 +1003,24 @@ func startBrowserTunnel(
return nil
}

func configureSSH(client client2.BaseWorkspaceClient, sshConfigPath, user, workdir string, gpgagent bool, devPodHome string) error {
func configureSSH(client client2.BaseWorkspaceClient, sshConfigPath, sshConfigIncludePath, user, workdir string, gpgagent bool, devPodHome string) error {
path, err := devssh.ResolveSSHConfigPath(sshConfigPath)
if err != nil {
return fmt.Errorf("invalid ssh config path %w", err)
}
sshConfigPath = path

if sshConfigIncludePath != "" {
includePath, err := devssh.ResolveSSHConfigPath(sshConfigIncludePath)
if err != nil {
return fmt.Errorf("invalid ssh config include path %w", err)
}
sshConfigIncludePath = includePath
}

err = devssh.ConfigureSSHConfig(
sshConfigPath,
sshConfigIncludePath,
client.Context(),
client.Workspace(),
user,
Expand Down Expand Up @@ -1195,7 +1205,7 @@ func buildDotCmd(devPodConfig *config.Config, dotfilesRepo, dotfilesScript strin
sshCmd = append(sshCmd, "--send-env", envKey)
}

remoteUser, err := devssh.GetUser(client.WorkspaceConfig().ID, client.WorkspaceConfig().SSHConfigPath)
remoteUser, err := devssh.GetUser(client.WorkspaceConfig().ID, client.WorkspaceConfig().SSHConfigPath, client.WorkspaceConfig().SSHConfigIncludePath)
if err != nil {
remoteUser = "root"
}
Expand Down Expand Up @@ -1254,7 +1264,7 @@ func setupGitSSHSignature(signingKey string, client client2.BaseWorkspaceClient,
return err
}

remoteUser, err := devssh.GetUser(client.WorkspaceConfig().ID, client.WorkspaceConfig().SSHConfigPath)
remoteUser, err := devssh.GetUser(client.WorkspaceConfig().ID, client.WorkspaceConfig().SSHConfigPath, client.WorkspaceConfig().SSHConfigIncludePath)
if err != nil {
remoteUser = "root"
}
Expand Down Expand Up @@ -1288,7 +1298,7 @@ func performGpgForwarding(
return err
}

remoteUser, err := devssh.GetUser(client.WorkspaceConfig().ID, client.WorkspaceConfig().SSHConfigPath)
remoteUser, err := devssh.GetUser(client.WorkspaceConfig().ID, client.WorkspaceConfig().SSHConfigPath, client.WorkspaceConfig().SSHConfigIncludePath)
if err != nil {
remoteUser = "root"
}
Expand Down Expand Up @@ -1435,6 +1445,7 @@ func (cmd *UpCmd) prepareClient(ctx context.Context, devPodConfig *config.Config
if cmd.SSHConfigPath == "" {
cmd.SSHConfigPath = devPodConfig.ContextOption(config.ContextOptionSSHConfigPath)
}
sshConfigIncludePath := devPodConfig.ContextOption(config.ContextOptionSSHConfigIncludePath)

client, err := workspace2.Resolve(
ctx,
Expand All @@ -1449,6 +1460,7 @@ func (cmd *UpCmd) prepareClient(ctx context.Context, devPodConfig *config.Config
cmd.DevContainerImage,
cmd.DevContainerPath,
cmd.SSHConfigPath,
sshConfigIncludePath,
source,
cmd.UID,
true,
Expand Down
4 changes: 2 additions & 2 deletions pkg/client/clientimplementation/daemonclient/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (c *client) Delete(ctx context.Context, opt clientpkg.DeleteOptions) error
return err
} else if workspace == nil {
// delete the workspace folder
err = clientimplementation.DeleteWorkspaceFolder(c.workspace.Context, c.workspace.ID, c.workspace.SSHConfigPath, c.log)
err = clientimplementation.DeleteWorkspaceFolder(c.workspace.Context, c.workspace.ID, c.workspace.SSHConfigPath, c.workspace.SSHConfigIncludePath, c.log)
if err != nil {
return err
}
Expand Down Expand Up @@ -67,7 +67,7 @@ func (c *client) Delete(ctx context.Context, opt clientpkg.DeleteOptions) error
}

// delete the workspace folder
err = clientimplementation.DeleteWorkspaceFolder(c.workspace.Context, c.workspace.ID, c.workspace.SSHConfigPath, c.log)
err = clientimplementation.DeleteWorkspaceFolder(c.workspace.Context, c.workspace.ID, c.workspace.SSHConfigPath, c.workspace.SSHConfigIncludePath, c.log)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/client/clientimplementation/proxy_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ func (s *proxyClient) Delete(ctx context.Context, opt client.DeleteOptions) erro
s.log.Errorf("Error deleting workspace: %v", err)
}

return DeleteWorkspaceFolder(s.workspace.Context, s.workspace.ID, s.workspace.SSHConfigPath, s.log)
return DeleteWorkspaceFolder(s.workspace.Context, s.workspace.ID, s.workspace.SSHConfigPath, s.workspace.SSHConfigIncludePath, s.log)
}

func (s *proxyClient) Stop(ctx context.Context, opt client.StopOptions) error {
Expand Down
14 changes: 11 additions & 3 deletions pkg/client/clientimplementation/workspace_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ func (s *workspaceClient) Delete(ctx context.Context, opt client.DeleteOptions)
}
}

return DeleteWorkspaceFolder(s.workspace.Context, s.workspace.ID, s.workspace.SSHConfigPath, s.log)
return DeleteWorkspaceFolder(s.workspace.Context, s.workspace.ID, s.workspace.SSHConfigPath, s.workspace.SSHConfigIncludePath, s.log)
}

func (s *workspaceClient) isMachineRunning(ctx context.Context) (bool, error) {
Expand Down Expand Up @@ -643,14 +643,22 @@ func DeleteMachineFolder(context, machineID string) error {
return nil
}

func DeleteWorkspaceFolder(context string, workspaceID string, sshConfigPath string, log log.Logger) error {
func DeleteWorkspaceFolder(context string, workspaceID string, sshConfigPath string, sshConfigIncludePath string, log log.Logger) error {
path, err := ssh.ResolveSSHConfigPath(sshConfigPath)
if err != nil {
return err
}
sshConfigPath = path

err = ssh.RemoveFromConfig(workspaceID, sshConfigPath, log)
if sshConfigIncludePath != "" {
includePath, err := ssh.ResolveSSHConfigPath(sshConfigIncludePath)
if err != nil {
return err
}
sshConfigIncludePath = includePath
}

err = ssh.RemoveFromConfig(workspaceID, sshConfigPath, sshConfigIncludePath, log)
if err != nil {
log.Errorf("Remove workspace '%s' from ssh config: %v", workspaceID, err)
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/config/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
ContextOptionDotfilesScript = "DOTFILES_SCRIPT"
ContextOptionSSHAgentForwarding = "SSH_AGENT_FORWARDING"
ContextOptionSSHConfigPath = "SSH_CONFIG_PATH"
ContextOptionSSHConfigIncludePath = "SSH_CONFIG_INCLUDE_PATH"
ContextOptionAgentInjectTimeout = "AGENT_INJECT_TIMEOUT"
ContextOptionRegistryCache = "REGISTRY_CACHE"
ContextOptionSSHStrictHostKeyChecking = "SSH_STRICT_HOST_KEY_CHECKING"
Expand Down Expand Up @@ -89,6 +90,10 @@ var ContextOptions = []ContextOption{
Name: ContextOptionSSHConfigPath,
Description: "Specifies the path where the ssh config should be written to",
},
{
Name: ContextOptionSSHConfigIncludePath,
Description: "Specifies an alternate path where DevPod host entries should be written. Use this when your main SSH config is read-only (e.g., managed by Nix). Your main SSH config should have an Include directive pointing to this file.",
},
{
Name: ContextOptionAgentInjectTimeout,
Description: "Specifies the timeout to inject the agent",
Expand Down
3 changes: 3 additions & 0 deletions pkg/provider/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ type Workspace struct {

// Path to the file where the SSH config to access the workspace is stored
SSHConfigPath string `json:"sshConfigPath,omitempty"`

// Path to an alternate file where DevPod entries are written (for read-only SSH configs)
SSHConfigIncludePath string `json:"sshConfigIncludePath,omitempty"`
}

type ProMetadata struct {
Expand Down
39 changes: 29 additions & 10 deletions pkg/ssh/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,25 @@ var (
MarkerEndPrefix = "# DevPod End "
)

func ConfigureSSHConfig(sshConfigPath, context, workspace, user, workdir string, gpgagent bool, devPodHome string, log log.Logger) error {
return configureSSHConfigSameFile(sshConfigPath, context, workspace, user, workdir, "", gpgagent, devPodHome, log)
func ConfigureSSHConfig(sshConfigPath, sshConfigIncludePath, context, workspace, user, workdir string, gpgagent bool, devPodHome string, log log.Logger) error {
return configureSSHConfigSameFile(sshConfigPath, sshConfigIncludePath, context, workspace, user, workdir, "", gpgagent, devPodHome, log)
}

func configureSSHConfigSameFile(sshConfigPath, context, workspace, user, workdir, command string, gpgagent bool, devPodHome string, log log.Logger) error {
func configureSSHConfigSameFile(sshConfigPath, sshConfigIncludePath, context, workspace, user, workdir, command string, gpgagent bool, devPodHome string, log log.Logger) error {
configLock.Lock()
defer configLock.Unlock()

newFile, err := addHost(sshConfigPath, workspace+"."+"devpod", user, context, workspace, workdir, command, gpgagent, devPodHome)
targetPath := sshConfigPath
if sshConfigIncludePath != "" {
targetPath = sshConfigIncludePath
}

newFile, err := addHost(targetPath, workspace+"."+"devpod", user, context, workspace, workdir, command, gpgagent, devPodHome)
if err != nil {
return fmt.Errorf("parse ssh config %w", err)
}

return writeSSHConfig(sshConfigPath, newFile, log)
return writeSSHConfig(targetPath, newFile, log)
}

type DevPodSSHEntry struct {
Expand Down Expand Up @@ -135,15 +140,24 @@ func addHostSection(config, execPath, host, user, context, workspace, workdir, c
return strings.Join(lines, newLineSep), nil
}

func GetUser(workspaceID string, sshConfigPath string) (string, error) {
func GetUser(workspaceID string, sshConfigPath string, sshConfigIncludePath string) (string, error) {
path, err := ResolveSSHConfigPath(sshConfigPath)
if err != nil {
return "", fmt.Errorf("invalid ssh config path %w", err)
}
sshConfigPath = path

targetPath := sshConfigPath
if sshConfigIncludePath != "" {
includePath, err := ResolveSSHConfigPath(sshConfigIncludePath)
if err != nil {
return "", fmt.Errorf("invalid ssh config include path %w", err)
}
targetPath = includePath
}

user := "root"
_, err = transformHostSection(sshConfigPath, workspaceID+"."+"devpod", func(line string) string {
_, err = transformHostSection(targetPath, workspaceID+"."+"devpod", func(line string) string {
splitted := strings.Split(strings.ToLower(strings.TrimSpace(line)), " ")
if len(splitted) == 2 && splitted[0] == "user" {
user = strings.Trim(splitted[1], "\"")
Expand All @@ -158,16 +172,21 @@ func GetUser(workspaceID string, sshConfigPath string) (string, error) {
return user, nil
}

func RemoveFromConfig(workspaceID string, sshConfigPath string, log log.Logger) error {
func RemoveFromConfig(workspaceID string, sshConfigPath string, sshConfigIncludePath string, log log.Logger) error {
configLock.Lock()
defer configLock.Unlock()

newFile, err := removeFromConfig(sshConfigPath, workspaceID+"."+"devpod")
targetPath := sshConfigPath
if sshConfigIncludePath != "" {
targetPath = sshConfigIncludePath
}

newFile, err := removeFromConfig(targetPath, workspaceID+"."+"devpod")
if err != nil {
return fmt.Errorf("parse ssh config %w", err)
}

return writeSSHConfig(sshConfigPath, newFile, log)
return writeSSHConfig(targetPath, newFile, log)
}

func writeSSHConfig(path, content string, log log.Logger) error {
Expand Down
6 changes: 3 additions & 3 deletions pkg/workspace/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func Delete(ctx context.Context, devPodConfig *config.Config, args []string, ign
log.Errorf("Error retrieving workspace: %v", err)

// delete workspace folder
err = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, workspaceID, "", log)
err = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, workspaceID, "", "", log)
if err != nil {
return "", err
}
Expand All @@ -52,7 +52,7 @@ func Delete(ctx context.Context, devPodConfig *config.Config, args []string, ign
workspaceConfig := client.WorkspaceConfig()
if !force && workspaceConfig.Imported {
// delete workspace folder
err = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, client.Workspace(), workspaceConfig.SSHConfigPath, log)
err = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, client.Workspace(), workspaceConfig.SSHConfigPath, workspaceConfig.SSHConfigIncludePath, log)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -138,7 +138,7 @@ func deleteSingleMachine(ctx context.Context, client client2.BaseWorkspaceClient
}

// delete workspace folder
err = clientimplementation.DeleteWorkspaceFolder(client.Context(), client.Workspace(), client.WorkspaceConfig().SSHConfigPath, log)
err = clientimplementation.DeleteWorkspaceFolder(client.Context(), client.Workspace(), client.WorkspaceConfig().SSHConfigPath, client.WorkspaceConfig().SSHConfigIncludePath, log)
if err != nil {
return false, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/workspace/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func List(ctx context.Context, devPodConfig *config.Config, skipPro bool, owner
for _, localWorkspace := range localWorkspaces {
if localWorkspace.IsPro() {
if shouldDeleteLocalWorkspace(ctx, localWorkspace, proWorkspaceResults) {
err = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, localWorkspace.ID, "", log)
err = clientimplementation.DeleteWorkspaceFolder(devPodConfig.DefaultContext, localWorkspace.ID, localWorkspace.SSHConfigPath, localWorkspace.SSHConfigIncludePath, log)
if err != nil {
log.Debugf("failed to delete local workspace %s: %v", localWorkspace.ID, err)
}
Expand Down
Loading
Loading