From eee34f58bd4cac785dd6ef58eb8e1a4d80975a4a Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Sun, 16 Jun 2019 03:12:09 +1000 Subject: [PATCH] add ~/.ssh directory sanity check before starting Checks: - if it exists - if it's a directory (if not warn and disable reuse connection feature) - if it has safe permissions (not writable by anyone except the owner, if not then warn) --- sshcode.go | 46 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/sshcode.go b/sshcode.go index 41f6e23..92b7f3c 100644 --- a/sshcode.go +++ b/sshcode.go @@ -21,7 +21,9 @@ import ( ) const codeServerPath = "~/.cache/sshcode/sshcode-server" -const sshControlPath = "~/.ssh/control-%h-%p-%r" +const sshDirectory = "~/.ssh" +const sshDirectoryUnsafeModeMask = 0022 +const sshControlPath = sshDirectory + "/control-%h-%p-%r" type options struct { skipSync bool @@ -34,8 +36,6 @@ type options struct { } func sshCode(host, dir string, o options) error { - flog.Info("ensuring code-server is updated...") - host, extraSSHFlags, err := parseHost(host) if err != nil { return xerrors.Errorf("failed to parse host IP: %w", err) @@ -56,6 +56,28 @@ func sshCode(host, dir string, o options) error { return xerrors.Errorf("failed to find available remote port: %w", err) } + // Check the SSH directory's permissions and warn the user if it is not safe. + sshDirectoryMode, err := os.Lstat(expandPath(sshDirectory)) + if err != nil { + if !o.noReuseConnection { + flog.Info("failed to stat %v directory, disabling connection reuse feature: %v", sshDirectory, err) + o.noReuseConnection = true + } + } else { + if !sshDirectoryMode.IsDir() { + if !o.noReuseConnection { + flog.Info("%v is not a directory, disabling connection reuse feature", sshDirectory) + o.noReuseConnection = true + } else { + flog.Info("warning: %v is not a directory", sshDirectory) + } + } + if sshDirectoryMode.Mode().Perm()&sshDirectoryUnsafeModeMask != 0 { + flog.Info("warning: the %v directory has unsafe permissions, they should only be writable by "+ + "the owner (and files inside should be set to 0600)", sshDirectory) + } + } + // Start SSH master connection socket. This prevents multiple password prompts from appearing as authentication // only happens on the initial connection. if !o.noReuseConnection { @@ -100,6 +122,7 @@ func sshCode(host, dir string, o options) error { } } + flog.Info("ensuring code-server is updated...") dlScript := downloadScript(codeServerPath) // Downloads the latest code-server and allows it to be executed. @@ -214,6 +237,23 @@ func sshCode(host, dir string, o options) error { return nil } +// expandPath returns an expanded version of path. +func expandPath(path string) string { + path = filepath.Clean(os.ExpandEnv(path)) + + // Replace tilde notation in path with the home directory. + homedir := os.Getenv("HOME") + if homedir != "" { + if path == "~" { + path = homedir + } else if strings.HasPrefix(path, "~/") { + path = filepath.Join(homedir, path[2:]) + } + } + + return filepath.Clean(path) +} + func parseBindAddr(bindAddr string) (string, error) { if bindAddr == "" { bindAddr = ":"