From 1a3c3f2ffcea1561b7994030a19c87ed7e04acc0 Mon Sep 17 00:00:00 2001 From: Ali Rizvi-Santiago Date: Sun, 2 Dec 2018 17:30:01 -0600 Subject: [PATCH 1/9] Replaced all instances of mitchellh/go-homedir with an implementation based on os/user. --- builder/azure/common/devicelogin.go | 10 ++++++--- builder/oracle/oci/config.go | 21 ++++++++++++----- helper/communicator/config.go | 30 ++++++++++++++++++++----- helper/communicator/step_connect_ssh.go | 21 +++++++++++------ 4 files changed, 61 insertions(+), 21 deletions(-) diff --git a/builder/azure/common/devicelogin.go b/builder/azure/common/devicelogin.go index 8d053f802..ea177638a 100644 --- a/builder/azure/common/devicelogin.go +++ b/builder/azure/common/devicelogin.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "os" + "os/user" "path/filepath" "regexp" "strings" @@ -15,7 +16,6 @@ import ( "github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/to" "github.com/hashicorp/packer/helper/useragent" - "github.com/mitchellh/go-homedir" ) var ( @@ -148,9 +148,13 @@ func tokenFromDeviceFlow(say func(string), oauthCfg adal.OAuthConfig, clientID, // tokenCachePath returns the full path the OAuth 2.0 token should be saved at // for given tenant ID. func tokenCachePath(tenantID string) string { - dir, err := homedir.Dir() - if err != nil { + var dir string + + u, err := user.Current() + if err != nil || u.HomeDir == "" { dir, _ = filepath.Abs(os.Args[0]) + } else { + dir = u.HomeDir } return filepath.Join(dir, ".azure", "packer", fmt.Sprintf("oauth-%s.json", tenantID)) diff --git a/builder/oracle/oci/config.go b/builder/oracle/oci/config.go index b7db218db..948b2fc23 100644 --- a/builder/oracle/oci/config.go +++ b/builder/oracle/oci/config.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "log" "os" + "os/user" "path/filepath" "strings" @@ -16,8 +17,6 @@ import ( "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template/interpolate" ocicommon "github.com/oracle/oci-go-sdk/common" - - "github.com/mitchellh/go-homedir" ) type Config struct { @@ -95,11 +94,17 @@ func NewConfig(raws ...interface{}) (*Config, error) { var keyContent []byte if c.KeyFile != "" { - path, err := homedir.Expand(c.KeyFile) + u, err := user.Current() if err != nil { return nil, err } + if u.HomeDir == "" { + return nil, fmt.Errorf("Unable to determine the home directory for the current user.") + } + + path := filepath.Join(u.HomeDir, c.KeyFile) + // Read API signing key keyContent, err = ioutil.ReadFile(path) if err != nil { @@ -249,15 +254,19 @@ func NewConfig(raws ...interface{}) (*Config, error) { return c, nil } -// getDefaultOCISettingsPath uses mitchellh/go-homedir to compute the default +// getDefaultOCISettingsPath uses os/user to compute the default // config file location ($HOME/.oci/config). func getDefaultOCISettingsPath() (string, error) { - home, err := homedir.Dir() + u, err := user.Current() if err != nil { return "", err } - path := filepath.Join(home, ".oci", "config") + if u.HomeDir == "" { + return "", fmt.Errorf("Unable to determine the home directory for the current user.") + } + + path := filepath.Join(u.HomeDir, ".oci", "config") if _, err := os.Stat(path); err != nil { return "", err } diff --git a/helper/communicator/config.go b/helper/communicator/config.go index fa4e053c3..117602d33 100644 --- a/helper/communicator/config.go +++ b/helper/communicator/config.go @@ -6,6 +6,8 @@ import ( "io/ioutil" "net" "os" + "os/user" + "path/filepath" "time" packerssh "github.com/hashicorp/packer/communicator/ssh" @@ -13,7 +15,6 @@ import ( helperssh "github.com/hashicorp/packer/helper/ssh" "github.com/hashicorp/packer/template/interpolate" "github.com/masterzen/winrm" - "github.com/mitchellh/go-homedir" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" ) @@ -73,10 +74,16 @@ func (c *Config) ReadSSHPrivateKeyFile() ([]byte, error) { var privateKey []byte if c.SSHPrivateKeyFile != "" { - keyPath, err := homedir.Expand(c.SSHPrivateKeyFile) + u, err := user.Current() if err != nil { - return privateKey, fmt.Errorf("Error expanding path for SSH private key: %s", err) + return []byte{}, fmt.Errorf("Error trying to determine the current user for the SSH private key: %s", err) } + + if u.HomeDir == "" { + return []byte{}, fmt.Errorf("Error locating home directory for the SSH private key") + } + + keyPath := filepath.Join(u.HomeDir, c.SSHPrivateKeyFile) privateKey, err = ioutil.ReadFile(keyPath) if err != nil { return privateKey, fmt.Errorf("Error on reading SSH private key: %s", err) @@ -260,8 +267,19 @@ func (c *Config) prepareSSH(ctx *interpolate.Context) []error { errs = append(errs, errors.New("An ssh_username must be specified\n Note: some builders used to default ssh_username to \"root\".")) } + // Figure out the current user's home directory + u, err := user.Current() + if err != nil { + errs = append(errs, fmt.Errorf("Unable to determine the current user from : %s", err)) + } else if u.HomeDir == "" { + // If the home directory is empty, then set `err` so that later a failure will happen + err = fmt.Errorf("Unable to determine the home directory for the current user.") + } + if c.SSHPrivateKeyFile != "" { - path, err := homedir.Expand(c.SSHPrivateKeyFile) + path := filepath.Join(u.HomeDir, c.SSHPrivateKeyFile) + + // The `err` variable here comes from an empty home directory if err != nil { errs = append(errs, fmt.Errorf( "ssh_private_key_file is invalid: %s", err)) @@ -279,7 +297,9 @@ func (c *Config) prepareSSH(ctx *interpolate.Context) []error { errs = append(errs, errors.New( "ssh_bastion_password or ssh_bastion_private_key_file must be specified")) } else if c.SSHBastionPrivateKeyFile != "" { - path, err := homedir.Expand(c.SSHBastionPrivateKeyFile) + path := filepath.Join(u.HomeDir, c.SSHBastionPrivateKeyFile) + + // The `err` variable here comes from an empty home directory if err != nil { errs = append(errs, fmt.Errorf( "ssh_bastion_private_key_file is invalid: %s", err)) diff --git a/helper/communicator/step_connect_ssh.go b/helper/communicator/step_connect_ssh.go index 6517a04a7..b8b5ee3ce 100644 --- a/helper/communicator/step_connect_ssh.go +++ b/helper/communicator/step_connect_ssh.go @@ -7,6 +7,8 @@ import ( "log" "net" "os" + "os/user" + "path/filepath" "strings" "time" @@ -14,7 +16,6 @@ import ( "github.com/hashicorp/packer/helper/multistep" helperssh "github.com/hashicorp/packer/helper/ssh" "github.com/hashicorp/packer/packer" - "github.com/mitchellh/go-homedir" gossh "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" "golang.org/x/net/proxy" @@ -178,9 +179,9 @@ func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, cancel <-chan stru // Then we attempt to connect via SSH config := &ssh.Config{ - Connection: connFunc, - SSHConfig: sshConfig, - Pty: s.Config.SSHPty, + Connection: connFunc, + SSHConfig: sshConfig, + Pty: s.Config.SSHPty, DisableAgentForwarding: s.Config.SSHDisableAgentForwarding, UseSftp: s.Config.SSHFileTransferMethod == "sftp", KeepAliveInterval: s.Config.SSHKeepAliveInterval, @@ -227,11 +228,17 @@ func sshBastionConfig(config *Config) (*gossh.ClientConfig, error) { } if config.SSHBastionPrivateKeyFile != "" { - path, err := homedir.Expand(config.SSHBastionPrivateKeyFile) + u, err := user.Current() if err != nil { - return nil, fmt.Errorf( - "Error expanding path for SSH bastion private key: %s", err) + return nil, fmt.Errorf("Unable to determine the current user for the SSH bastion private key: %s", err) } + + if u.HomeDir == "" { + return nil, fmt.Errorf("Unable to determine the current user's home directory for the SSH bastion private key.") + } + + path := filepath.Join(u.HomeDir, config.SSHBastionPrivateKeyFile) + signer, err := helperssh.FileSigner(path) if err != nil { return nil, err From a4a42001f5effe36ce1633913617860361b4d1b6 Mon Sep 17 00:00:00 2001 From: Ali Rizvi-Santiago Date: Sun, 2 Dec 2018 17:34:10 -0600 Subject: [PATCH 2/9] Removed mitchellh/go-homedir from vendor packages. --- .../github.com/mitchellh/go-homedir/LICENSE | 21 --- .../github.com/mitchellh/go-homedir/README.md | 14 -- .../mitchellh/go-homedir/homedir.go | 132 ------------------ vendor/vendor.json | 17 +-- 4 files changed, 6 insertions(+), 178 deletions(-) delete mode 100644 vendor/github.com/mitchellh/go-homedir/LICENSE delete mode 100644 vendor/github.com/mitchellh/go-homedir/README.md delete mode 100644 vendor/github.com/mitchellh/go-homedir/homedir.go diff --git a/vendor/github.com/mitchellh/go-homedir/LICENSE b/vendor/github.com/mitchellh/go-homedir/LICENSE deleted file mode 100644 index f9c841a51..000000000 --- a/vendor/github.com/mitchellh/go-homedir/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Mitchell Hashimoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/github.com/mitchellh/go-homedir/README.md b/vendor/github.com/mitchellh/go-homedir/README.md deleted file mode 100644 index d70706d5b..000000000 --- a/vendor/github.com/mitchellh/go-homedir/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# go-homedir - -This is a Go library for detecting the user's home directory without -the use of cgo, so the library can be used in cross-compilation environments. - -Usage is incredibly simple, just call `homedir.Dir()` to get the home directory -for a user, and `homedir.Expand()` to expand the `~` in a path to the home -directory. - -**Why not just use `os/user`?** The built-in `os/user` package requires -cgo on Darwin systems. This means that any Go code that uses that package -cannot cross compile. But 99% of the time the use for `os/user` is just to -retrieve the home directory, which we can do for the current user without -cgo. This library does that, enabling cross-compilation. diff --git a/vendor/github.com/mitchellh/go-homedir/homedir.go b/vendor/github.com/mitchellh/go-homedir/homedir.go deleted file mode 100644 index 6944957d5..000000000 --- a/vendor/github.com/mitchellh/go-homedir/homedir.go +++ /dev/null @@ -1,132 +0,0 @@ -package homedir - -import ( - "bytes" - "errors" - "os" - "os/exec" - "path/filepath" - "runtime" - "strconv" - "strings" - "sync" -) - -// DisableCache will disable caching of the home directory. Caching is enabled -// by default. -var DisableCache bool - -var homedirCache string -var cacheLock sync.RWMutex - -// Dir returns the home directory for the executing user. -// -// This uses an OS-specific method for discovering the home directory. -// An error is returned if a home directory cannot be detected. -func Dir() (string, error) { - if !DisableCache { - cacheLock.RLock() - cached := homedirCache - cacheLock.RUnlock() - if cached != "" { - return cached, nil - } - } - - cacheLock.Lock() - defer cacheLock.Unlock() - - var result string - var err error - if runtime.GOOS == "windows" { - result, err = dirWindows() - } else { - // Unix-like system, so just assume Unix - result, err = dirUnix() - } - - if err != nil { - return "", err - } - homedirCache = result - return result, nil -} - -// Expand expands the path to include the home directory if the path -// is prefixed with `~`. If it isn't prefixed with `~`, the path is -// returned as-is. -func Expand(path string) (string, error) { - if len(path) == 0 { - return path, nil - } - - if path[0] != '~' { - return path, nil - } - - if len(path) > 1 && path[1] != '/' && path[1] != '\\' { - return "", errors.New("cannot expand user-specific home dir") - } - - dir, err := Dir() - if err != nil { - return "", err - } - - return filepath.Join(dir, path[1:]), nil -} - -func dirUnix() (string, error) { - // First prefer the HOME environmental variable - if home := os.Getenv("HOME"); home != "" { - return home, nil - } - - // If that fails, try getent - var stdout bytes.Buffer - cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid())) - cmd.Stdout = &stdout - if err := cmd.Run(); err != nil { - // If "getent" is missing, ignore it - if err != exec.ErrNotFound { - return "", err - } - } else { - if passwd := strings.TrimSpace(stdout.String()); passwd != "" { - // username:password:uid:gid:gecos:home:shell - passwdParts := strings.SplitN(passwd, ":", 7) - if len(passwdParts) > 5 { - return passwdParts[5], nil - } - } - } - - // If all else fails, try the shell - stdout.Reset() - cmd = exec.Command("sh", "-c", "cd && pwd") - cmd.Stdout = &stdout - if err := cmd.Run(); err != nil { - return "", err - } - - result := strings.TrimSpace(stdout.String()) - if result == "" { - return "", errors.New("blank output when reading home directory") - } - - return result, nil -} - -func dirWindows() (string, error) { - drive := os.Getenv("HOMEDRIVE") - path := os.Getenv("HOMEPATH") - home := drive + path - if drive == "" || path == "" { - home = os.Getenv("USERPROFILE") - } - if home == "" { - return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank") - } - - return home, nil -} diff --git a/vendor/vendor.json b/vendor/vendor.json index 17d9bde1f..85af9fb8f 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1314,11 +1314,6 @@ "revision": "7b48fa161ea73ce54566b233d6fba8750b2faf47", "revisionTime": "2018-04-02T23:40:41Z" }, - { - "checksumSHA1": "z235fRXw4+SW4xWgLTYc8SwkM2M=", - "path": "github.com/mitchellh/go-homedir", - "revision": "d682a8f0cf139663a984ff12528da460ca963de9" - }, { "checksumSHA1": "SJOpcDnBxxKHq146atZmumzST3A=", "path": "github.com/mitchellh/go-vnc", @@ -1487,18 +1482,18 @@ "revision": "2d205ac6ec17a839a94bdbfd16d2fa6c6dada2e0", "revisionTime": "2016-03-31T20:48:55Z" }, - { - "checksumSHA1": "6JP37UqrI0H80Gpk0Y2P+KXgn5M=", - "path": "github.com/ryanuber/go-glob", - "revision": "256dc444b735e061061cf46c809487313d5b0065", - "revisionTime": "2017-01-28T01:21:29Z" - }, { "checksumSHA1": "4gVuzkHbQoznf+lCSJhIJnvS5tc=", "path": "github.com/rwtodd/Go.Sed/sed", "revision": "d6d5d585814e4c3560c684f52e3d8aeed721313d", "revisionTime": "2017-05-07T04:53:31Z" }, + { + "checksumSHA1": "6JP37UqrI0H80Gpk0Y2P+KXgn5M=", + "path": "github.com/ryanuber/go-glob", + "revision": "256dc444b735e061061cf46c809487313d5b0065", + "revisionTime": "2017-01-28T01:21:29Z" + }, { "checksumSHA1": "zmC8/3V4ls53DJlNTKDZwPSC/dA=", "path": "github.com/satori/go.uuid", From e04986659af93fbed426fd9342e4e66841eabcdc Mon Sep 17 00:00:00 2001 From: Ali Rizvi-Santiago Date: Sun, 2 Dec 2018 18:06:14 -0600 Subject: [PATCH 3/9] Changed the logic for when to actually prefix a path with a u.HomeDir so that it's only done when a non-absolute path is specified. --- builder/oracle/oci/config.go | 6 +++++- helper/communicator/config.go | 16 +++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/builder/oracle/oci/config.go b/builder/oracle/oci/config.go index 948b2fc23..85de3b2cc 100644 --- a/builder/oracle/oci/config.go +++ b/builder/oracle/oci/config.go @@ -103,7 +103,11 @@ func NewConfig(raws ...interface{}) (*Config, error) { return nil, fmt.Errorf("Unable to determine the home directory for the current user.") } - path := filepath.Join(u.HomeDir, c.KeyFile) + // If c.KeyFile is not an absolute path, then it's relative so prefix it relative to the home directory + path := c.KeyFile + if !filepath.IsAbs(c.KeyFile) { + path = filepath.Join(u.HomeDir, c.KeyFile) + } // Read API signing key keyContent, err = ioutil.ReadFile(path) diff --git a/helper/communicator/config.go b/helper/communicator/config.go index 117602d33..0bd54a134 100644 --- a/helper/communicator/config.go +++ b/helper/communicator/config.go @@ -83,7 +83,12 @@ func (c *Config) ReadSSHPrivateKeyFile() ([]byte, error) { return []byte{}, fmt.Errorf("Error locating home directory for the SSH private key") } - keyPath := filepath.Join(u.HomeDir, c.SSHPrivateKeyFile) + // If c.SSHPrivateKeyFile is not an absolute path, then it's relative and should be prefixed with u.HomeDir + keyPath := c.SSHPrivateKeyFile + if !filepath.IsAbs(c.SSHPrivateKeyFile) { + keyPath = filepath.Join(u.HomeDir, c.SSHPrivateKeyFile) + } + privateKey, err = ioutil.ReadFile(keyPath) if err != nil { return privateKey, fmt.Errorf("Error on reading SSH private key: %s", err) @@ -277,9 +282,14 @@ func (c *Config) prepareSSH(ctx *interpolate.Context) []error { } if c.SSHPrivateKeyFile != "" { - path := filepath.Join(u.HomeDir, c.SSHPrivateKeyFile) + path := c.SSHPrivateKeyFile - // The `err` variable here comes from an empty home directory + // If c.SSHPrivateKeyFile is not an absolute path, then it's relative and should be prefixed with u.HomeDir + if err == nil && !filepath.IsAbs(c.SSHPrivateKeyFile) { + path = filepath.Join(u.HomeDir, c.SSHPrivateKeyFile) + } + + // The `err` variable here comes from the empty home directory above if err != nil { errs = append(errs, fmt.Errorf( "ssh_private_key_file is invalid: %s", err)) From 79b68fb89ce2ecb3fcbaa84eb21c4cb388c300cb Mon Sep 17 00:00:00 2001 From: Ali Rizvi-Santiago Date: Sun, 2 Dec 2018 18:32:20 -0600 Subject: [PATCH 4/9] Replaced the previous incorrect logic with an re-implementation of common.ExpandUser. --- builder/oracle/oci/config.go | 12 +------ common/config.go | 42 +++++++++++++++++++++++++ helper/communicator/config.go | 39 +++-------------------- helper/communicator/step_connect_ssh.go | 13 ++------ 4 files changed, 51 insertions(+), 55 deletions(-) diff --git a/builder/oracle/oci/config.go b/builder/oracle/oci/config.go index 85de3b2cc..dd2beaaba 100644 --- a/builder/oracle/oci/config.go +++ b/builder/oracle/oci/config.go @@ -94,21 +94,11 @@ func NewConfig(raws ...interface{}) (*Config, error) { var keyContent []byte if c.KeyFile != "" { - u, err := user.Current() + path, err := common.ExpandUser(c.KeyFile) if err != nil { return nil, err } - if u.HomeDir == "" { - return nil, fmt.Errorf("Unable to determine the home directory for the current user.") - } - - // If c.KeyFile is not an absolute path, then it's relative so prefix it relative to the home directory - path := c.KeyFile - if !filepath.IsAbs(c.KeyFile) { - path = filepath.Join(u.HomeDir, c.KeyFile) - } - // Read API signing key keyContent, err = ioutil.ReadFile(path) if err != nil { diff --git a/common/config.go b/common/config.go index 70ebc8452..9dffab23c 100644 --- a/common/config.go +++ b/common/config.go @@ -4,6 +4,7 @@ import ( "fmt" "net/url" "os" + "os/user" "path/filepath" "regexp" "runtime" @@ -206,3 +207,44 @@ func FileExistsLocally(original string) bool { } return true } + +func ExpandUser(path string) (string, error) { + var ( + u *user.User + err error + ) + + // refuse to do anything with a zero-length path + if len(path) == 0 { + return path + } + + // If no expansion was specified, then refuse that too + if path[0] != "~" { + return path, nil + } + + // Grab everything up to the first filepath.Separator + idx := strings.IndexAny(path, `/\`) + if idx == -1 { + idx = len(path) + } + + // Now we should be able to extract the username + username := path[:idx] + + // Check if the current user was requested + if username == "~" { + u, err = user.Current() + } else { + u, err = user.Lookup(username[1:]) + } + + // If we couldn't figure that out, then fail here + if err != nil { + return "", err + } + + // Now we can replace the path with u.HomeDir + return filepath.Join(u.HomeDir, path[idx:]) +} diff --git a/helper/communicator/config.go b/helper/communicator/config.go index 0bd54a134..6c0477bf5 100644 --- a/helper/communicator/config.go +++ b/helper/communicator/config.go @@ -6,10 +6,9 @@ import ( "io/ioutil" "net" "os" - "os/user" - "path/filepath" "time" + "github.com/hashicorp/packer/common" packerssh "github.com/hashicorp/packer/communicator/ssh" "github.com/hashicorp/packer/helper/multistep" helperssh "github.com/hashicorp/packer/helper/ssh" @@ -74,19 +73,9 @@ func (c *Config) ReadSSHPrivateKeyFile() ([]byte, error) { var privateKey []byte if c.SSHPrivateKeyFile != "" { - u, err := user.Current() + keyPath, err := common.ExpandUser(c.SSHPrivateKeyFile) if err != nil { - return []byte{}, fmt.Errorf("Error trying to determine the current user for the SSH private key: %s", err) - } - - if u.HomeDir == "" { - return []byte{}, fmt.Errorf("Error locating home directory for the SSH private key") - } - - // If c.SSHPrivateKeyFile is not an absolute path, then it's relative and should be prefixed with u.HomeDir - keyPath := c.SSHPrivateKeyFile - if !filepath.IsAbs(c.SSHPrivateKeyFile) { - keyPath = filepath.Join(u.HomeDir, c.SSHPrivateKeyFile) + return []byte{}, fmt.Errorf("Error expanding path for SSH private key: %s", err) } privateKey, err = ioutil.ReadFile(keyPath) @@ -272,24 +261,8 @@ func (c *Config) prepareSSH(ctx *interpolate.Context) []error { errs = append(errs, errors.New("An ssh_username must be specified\n Note: some builders used to default ssh_username to \"root\".")) } - // Figure out the current user's home directory - u, err := user.Current() - if err != nil { - errs = append(errs, fmt.Errorf("Unable to determine the current user from : %s", err)) - } else if u.HomeDir == "" { - // If the home directory is empty, then set `err` so that later a failure will happen - err = fmt.Errorf("Unable to determine the home directory for the current user.") - } - if c.SSHPrivateKeyFile != "" { - path := c.SSHPrivateKeyFile - - // If c.SSHPrivateKeyFile is not an absolute path, then it's relative and should be prefixed with u.HomeDir - if err == nil && !filepath.IsAbs(c.SSHPrivateKeyFile) { - path = filepath.Join(u.HomeDir, c.SSHPrivateKeyFile) - } - - // The `err` variable here comes from the empty home directory above + path, err := common.ExpandUser(c.SSHPrivateKeyFile) if err != nil { errs = append(errs, fmt.Errorf( "ssh_private_key_file is invalid: %s", err)) @@ -307,9 +280,7 @@ func (c *Config) prepareSSH(ctx *interpolate.Context) []error { errs = append(errs, errors.New( "ssh_bastion_password or ssh_bastion_private_key_file must be specified")) } else if c.SSHBastionPrivateKeyFile != "" { - path := filepath.Join(u.HomeDir, c.SSHBastionPrivateKeyFile) - - // The `err` variable here comes from an empty home directory + path, err := common.ExpandUser(c.SSHBastionPrivateKeyFile) if err != nil { errs = append(errs, fmt.Errorf( "ssh_bastion_private_key_file is invalid: %s", err)) diff --git a/helper/communicator/step_connect_ssh.go b/helper/communicator/step_connect_ssh.go index b8b5ee3ce..121a7c307 100644 --- a/helper/communicator/step_connect_ssh.go +++ b/helper/communicator/step_connect_ssh.go @@ -7,8 +7,6 @@ import ( "log" "net" "os" - "os/user" - "path/filepath" "strings" "time" @@ -228,17 +226,12 @@ func sshBastionConfig(config *Config) (*gossh.ClientConfig, error) { } if config.SSHBastionPrivateKeyFile != "" { - u, err := user.Current() + path, err := common.ExpandUser(config.SSHBastionPrivateKeyFile) if err != nil { - return nil, fmt.Errorf("Unable to determine the current user for the SSH bastion private key: %s", err) + return nil, fmt.Errorf( + "Error expanding path for SSH bastion private key: %s", err) } - if u.HomeDir == "" { - return nil, fmt.Errorf("Unable to determine the current user's home directory for the SSH bastion private key.") - } - - path := filepath.Join(u.HomeDir, config.SSHBastionPrivateKeyFile) - signer, err := helperssh.FileSigner(path) if err != nil { return nil, err From 040a33567e6e9043a56ba54bee33316d5ab1479d Mon Sep 17 00:00:00 2001 From: Ali Rizvi-Santiago Date: Sun, 2 Dec 2018 18:56:33 -0600 Subject: [PATCH 5/9] Trying again one more time to prevent import cycles.. Moved common.ExpandUser into packer.ExpandUser.. --- builder/oracle/oci/config.go | 2 +- common/config.go | 42 ----------------------- helper/communicator/config.go | 8 ++--- helper/communicator/step_connect_ssh.go | 2 +- packer/config_file.go | 44 +++++++++++++++++++++++++ 5 files changed, 50 insertions(+), 48 deletions(-) diff --git a/builder/oracle/oci/config.go b/builder/oracle/oci/config.go index dd2beaaba..d83fa6c15 100644 --- a/builder/oracle/oci/config.go +++ b/builder/oracle/oci/config.go @@ -94,7 +94,7 @@ func NewConfig(raws ...interface{}) (*Config, error) { var keyContent []byte if c.KeyFile != "" { - path, err := common.ExpandUser(c.KeyFile) + path, err := packer.ExpandUser(c.KeyFile) if err != nil { return nil, err } diff --git a/common/config.go b/common/config.go index 9dffab23c..70ebc8452 100644 --- a/common/config.go +++ b/common/config.go @@ -4,7 +4,6 @@ import ( "fmt" "net/url" "os" - "os/user" "path/filepath" "regexp" "runtime" @@ -207,44 +206,3 @@ func FileExistsLocally(original string) bool { } return true } - -func ExpandUser(path string) (string, error) { - var ( - u *user.User - err error - ) - - // refuse to do anything with a zero-length path - if len(path) == 0 { - return path - } - - // If no expansion was specified, then refuse that too - if path[0] != "~" { - return path, nil - } - - // Grab everything up to the first filepath.Separator - idx := strings.IndexAny(path, `/\`) - if idx == -1 { - idx = len(path) - } - - // Now we should be able to extract the username - username := path[:idx] - - // Check if the current user was requested - if username == "~" { - u, err = user.Current() - } else { - u, err = user.Lookup(username[1:]) - } - - // If we couldn't figure that out, then fail here - if err != nil { - return "", err - } - - // Now we can replace the path with u.HomeDir - return filepath.Join(u.HomeDir, path[idx:]) -} diff --git a/helper/communicator/config.go b/helper/communicator/config.go index 6c0477bf5..7d66254b5 100644 --- a/helper/communicator/config.go +++ b/helper/communicator/config.go @@ -8,10 +8,10 @@ import ( "os" "time" - "github.com/hashicorp/packer/common" packerssh "github.com/hashicorp/packer/communicator/ssh" "github.com/hashicorp/packer/helper/multistep" helperssh "github.com/hashicorp/packer/helper/ssh" + "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template/interpolate" "github.com/masterzen/winrm" "golang.org/x/crypto/ssh" @@ -73,7 +73,7 @@ func (c *Config) ReadSSHPrivateKeyFile() ([]byte, error) { var privateKey []byte if c.SSHPrivateKeyFile != "" { - keyPath, err := common.ExpandUser(c.SSHPrivateKeyFile) + keyPath, err := packer.ExpandUser(c.SSHPrivateKeyFile) if err != nil { return []byte{}, fmt.Errorf("Error expanding path for SSH private key: %s", err) } @@ -262,7 +262,7 @@ func (c *Config) prepareSSH(ctx *interpolate.Context) []error { } if c.SSHPrivateKeyFile != "" { - path, err := common.ExpandUser(c.SSHPrivateKeyFile) + path, err := packer.ExpandUser(c.SSHPrivateKeyFile) if err != nil { errs = append(errs, fmt.Errorf( "ssh_private_key_file is invalid: %s", err)) @@ -280,7 +280,7 @@ func (c *Config) prepareSSH(ctx *interpolate.Context) []error { errs = append(errs, errors.New( "ssh_bastion_password or ssh_bastion_private_key_file must be specified")) } else if c.SSHBastionPrivateKeyFile != "" { - path, err := common.ExpandUser(c.SSHBastionPrivateKeyFile) + path, err := packer.ExpandUser(c.SSHBastionPrivateKeyFile) if err != nil { errs = append(errs, fmt.Errorf( "ssh_bastion_private_key_file is invalid: %s", err)) diff --git a/helper/communicator/step_connect_ssh.go b/helper/communicator/step_connect_ssh.go index 121a7c307..0a2a8c757 100644 --- a/helper/communicator/step_connect_ssh.go +++ b/helper/communicator/step_connect_ssh.go @@ -226,7 +226,7 @@ func sshBastionConfig(config *Config) (*gossh.ClientConfig, error) { } if config.SSHBastionPrivateKeyFile != "" { - path, err := common.ExpandUser(config.SSHBastionPrivateKeyFile) + path, err := packer.ExpandUser(config.SSHBastionPrivateKeyFile) if err != nil { return nil, fmt.Errorf( "Error expanding path for SSH bastion private key: %s", err) diff --git a/packer/config_file.go b/packer/config_file.go index 043b40ae8..3bb9c8e51 100644 --- a/packer/config_file.go +++ b/packer/config_file.go @@ -5,6 +5,7 @@ import ( "os" "os/user" "path/filepath" + "strings" ) // ConfigFile returns the default path to the configuration file. On @@ -84,3 +85,46 @@ func configDir() (string, error) { return filepath.Join(dir, defaultConfigDir), nil } + +// Given a path, check to see if it's using ~ to reference a user directory. +// If so, then replace that component with the requrested user's directory. +func ExpandUser(path string) (string, error) { + var ( + u *user.User + err error + ) + + // refuse to do anything with a zero-length path + if len(path) == 0 { + return path, nil + } + + // If no expansion was specified, then refuse that too + if path[0] != '~' { + return path, nil + } + + // Grab everything up to the first filepath.Separator + idx := strings.IndexAny(path, `/\`) + if idx == -1 { + idx = len(path) + } + + // Now we should be able to extract the username + username := path[:idx] + + // Check if the current user was requested + if username == "~" { + u, err = user.Current() + } else { + u, err = user.Lookup(username[1:]) + } + + // If we couldn't figure that out, then fail here + if err != nil { + return "", err + } + + // Now we can replace the path with u.HomeDir + return filepath.Join(u.HomeDir, path[idx:]), nil +} From b0af406e0812ab5cd1ea6780f4cf7ae257818593 Mon Sep 17 00:00:00 2001 From: Ali Rizvi-Santiago Date: Sun, 2 Dec 2018 19:42:34 -0600 Subject: [PATCH 6/9] gofmt using v1.11.2 instead of disro's outdated v1.10.5. --- helper/communicator/step_connect_ssh.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/helper/communicator/step_connect_ssh.go b/helper/communicator/step_connect_ssh.go index 0a2a8c757..37db32eae 100644 --- a/helper/communicator/step_connect_ssh.go +++ b/helper/communicator/step_connect_ssh.go @@ -177,9 +177,9 @@ func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, cancel <-chan stru // Then we attempt to connect via SSH config := &ssh.Config{ - Connection: connFunc, - SSHConfig: sshConfig, - Pty: s.Config.SSHPty, + Connection: connFunc, + SSHConfig: sshConfig, + Pty: s.Config.SSHPty, DisableAgentForwarding: s.Config.SSHDisableAgentForwarding, UseSftp: s.Config.SSHFileTransferMethod == "sftp", KeepAliveInterval: s.Config.SSHKeepAliveInterval, From 194f31aba1a52a58ba61c1ac3b094e04e5f26d71 Mon Sep 17 00:00:00 2001 From: Ali Rizvi-Santiago Date: Sun, 2 Dec 2018 19:54:00 -0600 Subject: [PATCH 7/9] Revert "Removed mitchellh/go-homedir from vendor packages." This reverts commit f156f00ccad3f7b16ece894109d1de4968ddfa88. --- .../github.com/mitchellh/go-homedir/LICENSE | 21 +++ .../github.com/mitchellh/go-homedir/README.md | 14 ++ .../mitchellh/go-homedir/homedir.go | 132 ++++++++++++++++++ vendor/vendor.json | 17 ++- 4 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 vendor/github.com/mitchellh/go-homedir/LICENSE create mode 100644 vendor/github.com/mitchellh/go-homedir/README.md create mode 100644 vendor/github.com/mitchellh/go-homedir/homedir.go diff --git a/vendor/github.com/mitchellh/go-homedir/LICENSE b/vendor/github.com/mitchellh/go-homedir/LICENSE new file mode 100644 index 000000000..f9c841a51 --- /dev/null +++ b/vendor/github.com/mitchellh/go-homedir/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Mitchell Hashimoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/mitchellh/go-homedir/README.md b/vendor/github.com/mitchellh/go-homedir/README.md new file mode 100644 index 000000000..d70706d5b --- /dev/null +++ b/vendor/github.com/mitchellh/go-homedir/README.md @@ -0,0 +1,14 @@ +# go-homedir + +This is a Go library for detecting the user's home directory without +the use of cgo, so the library can be used in cross-compilation environments. + +Usage is incredibly simple, just call `homedir.Dir()` to get the home directory +for a user, and `homedir.Expand()` to expand the `~` in a path to the home +directory. + +**Why not just use `os/user`?** The built-in `os/user` package requires +cgo on Darwin systems. This means that any Go code that uses that package +cannot cross compile. But 99% of the time the use for `os/user` is just to +retrieve the home directory, which we can do for the current user without +cgo. This library does that, enabling cross-compilation. diff --git a/vendor/github.com/mitchellh/go-homedir/homedir.go b/vendor/github.com/mitchellh/go-homedir/homedir.go new file mode 100644 index 000000000..6944957d5 --- /dev/null +++ b/vendor/github.com/mitchellh/go-homedir/homedir.go @@ -0,0 +1,132 @@ +package homedir + +import ( + "bytes" + "errors" + "os" + "os/exec" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync" +) + +// DisableCache will disable caching of the home directory. Caching is enabled +// by default. +var DisableCache bool + +var homedirCache string +var cacheLock sync.RWMutex + +// Dir returns the home directory for the executing user. +// +// This uses an OS-specific method for discovering the home directory. +// An error is returned if a home directory cannot be detected. +func Dir() (string, error) { + if !DisableCache { + cacheLock.RLock() + cached := homedirCache + cacheLock.RUnlock() + if cached != "" { + return cached, nil + } + } + + cacheLock.Lock() + defer cacheLock.Unlock() + + var result string + var err error + if runtime.GOOS == "windows" { + result, err = dirWindows() + } else { + // Unix-like system, so just assume Unix + result, err = dirUnix() + } + + if err != nil { + return "", err + } + homedirCache = result + return result, nil +} + +// Expand expands the path to include the home directory if the path +// is prefixed with `~`. If it isn't prefixed with `~`, the path is +// returned as-is. +func Expand(path string) (string, error) { + if len(path) == 0 { + return path, nil + } + + if path[0] != '~' { + return path, nil + } + + if len(path) > 1 && path[1] != '/' && path[1] != '\\' { + return "", errors.New("cannot expand user-specific home dir") + } + + dir, err := Dir() + if err != nil { + return "", err + } + + return filepath.Join(dir, path[1:]), nil +} + +func dirUnix() (string, error) { + // First prefer the HOME environmental variable + if home := os.Getenv("HOME"); home != "" { + return home, nil + } + + // If that fails, try getent + var stdout bytes.Buffer + cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid())) + cmd.Stdout = &stdout + if err := cmd.Run(); err != nil { + // If "getent" is missing, ignore it + if err != exec.ErrNotFound { + return "", err + } + } else { + if passwd := strings.TrimSpace(stdout.String()); passwd != "" { + // username:password:uid:gid:gecos:home:shell + passwdParts := strings.SplitN(passwd, ":", 7) + if len(passwdParts) > 5 { + return passwdParts[5], nil + } + } + } + + // If all else fails, try the shell + stdout.Reset() + cmd = exec.Command("sh", "-c", "cd && pwd") + cmd.Stdout = &stdout + if err := cmd.Run(); err != nil { + return "", err + } + + result := strings.TrimSpace(stdout.String()) + if result == "" { + return "", errors.New("blank output when reading home directory") + } + + return result, nil +} + +func dirWindows() (string, error) { + drive := os.Getenv("HOMEDRIVE") + path := os.Getenv("HOMEPATH") + home := drive + path + if drive == "" || path == "" { + home = os.Getenv("USERPROFILE") + } + if home == "" { + return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank") + } + + return home, nil +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 85af9fb8f..17d9bde1f 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1314,6 +1314,11 @@ "revision": "7b48fa161ea73ce54566b233d6fba8750b2faf47", "revisionTime": "2018-04-02T23:40:41Z" }, + { + "checksumSHA1": "z235fRXw4+SW4xWgLTYc8SwkM2M=", + "path": "github.com/mitchellh/go-homedir", + "revision": "d682a8f0cf139663a984ff12528da460ca963de9" + }, { "checksumSHA1": "SJOpcDnBxxKHq146atZmumzST3A=", "path": "github.com/mitchellh/go-vnc", @@ -1482,18 +1487,18 @@ "revision": "2d205ac6ec17a839a94bdbfd16d2fa6c6dada2e0", "revisionTime": "2016-03-31T20:48:55Z" }, - { - "checksumSHA1": "4gVuzkHbQoznf+lCSJhIJnvS5tc=", - "path": "github.com/rwtodd/Go.Sed/sed", - "revision": "d6d5d585814e4c3560c684f52e3d8aeed721313d", - "revisionTime": "2017-05-07T04:53:31Z" - }, { "checksumSHA1": "6JP37UqrI0H80Gpk0Y2P+KXgn5M=", "path": "github.com/ryanuber/go-glob", "revision": "256dc444b735e061061cf46c809487313d5b0065", "revisionTime": "2017-01-28T01:21:29Z" }, + { + "checksumSHA1": "4gVuzkHbQoznf+lCSJhIJnvS5tc=", + "path": "github.com/rwtodd/Go.Sed/sed", + "revision": "d6d5d585814e4c3560c684f52e3d8aeed721313d", + "revisionTime": "2017-05-07T04:53:31Z" + }, { "checksumSHA1": "zmC8/3V4ls53DJlNTKDZwPSC/dA=", "path": "github.com/satori/go.uuid", From ecf45d47404d0ac106da7ee90f7aa18d4c8f3c32 Mon Sep 17 00:00:00 2001 From: Ali Rizvi-Santiago Date: Mon, 3 Dec 2018 21:11:40 -0600 Subject: [PATCH 8/9] Added tests for packer.ExpandUser in packer/config_file_test.go. --- packer/config_file_test.go | 161 +++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 packer/config_file_test.go diff --git a/packer/config_file_test.go b/packer/config_file_test.go new file mode 100644 index 000000000..ba275793b --- /dev/null +++ b/packer/config_file_test.go @@ -0,0 +1,161 @@ +package packer + +import ( + "fmt" + "os/user" + "path/filepath" + "runtime" + "testing" +) + +// Depending on the platform, find a valid username to use +func platform_user() string { + // XXX: We make an assumption here that there's an Administrator user + // on the windows platform, whereas the correct way is to use + // the api or to scrape `net user`. + if runtime.GOOS == "windows" { + return "Administrator" + } + return "root" +} + +func homedir_current() (string, error) { + u, err := user.Current() + if err != nil { + return "", err + } + + return u.HomeDir, nil +} + +func homedir_user(username string) (string, error) { + u, err := user.Lookup(username) + if err != nil { + return "", err + } + + return u.HomeDir, nil +} + +// Begin the actual tests and stuff +func TestExpandUser_Empty(t *testing.T) { + var path, expected string + + // Try an invalid user + path, err := ExpandUser("~invalid-user-that-should-not-exist") + if err == nil { + t.Fatalf("expected failure") + } + + // Try an empty string + expected = "" + if path, err = ExpandUser(""); err != nil { + t.Fatalf("err: %s", err) + } + + if path != expected { + t.Fatalf("err: %v != %v", path, expected) + } + + // Try an absolute path + expected = "/etc/shadow" + if path, err = ExpandUser("/etc/shadow"); err != nil { + t.Fatalf("err: %s", err) + } + + if path != expected { + t.Fatalf("err: %v != %v", path, expected) + } + + // Try a relative path + expected = "tmp/foo" + if path, err = ExpandUser("tmp/foo"); err != nil { + t.Fatalf("err: %s", err) + } + + if path != expected { + t.Fatalf("err: %v != %v", path, expected) + } +} + +func TestExpandUser_Current(t *testing.T) { + var path, expected string + + // Grab the current user's home directory to verify ExpandUser works + homedir, err := homedir_current() + if err != nil { + t.Fatalf("err: %s", err) + } + + // Try just a tilde + expected = homedir + if path, err = ExpandUser("~"); err != nil { + t.Fatalf("err: %s", err) + } + + if path != expected { + t.Fatalf("err: %v != %v", path, expected) + } + + // Try as a directory + expected = filepath.Join(homedir, "") + if path, err = ExpandUser("~/"); err != nil { + t.Fatalf("err: %s", err) + } + + if path != expected { + t.Fatalf("err: %v != %v", path, expected) + } + + // Try as a file + expected = filepath.Join(homedir, "foo") + if path, err = ExpandUser("~/foo"); err != nil { + t.Fatalf("err: %s", err) + } + + if path != expected { + t.Fatalf("err: %v != %v", path, expected) + } +} + +func TestExpandUser_User(t *testing.T) { + var path, expected string + + username := platform_user() + + // Grab the current user's home directory to verify ExpandUser works + homedir, err := homedir_user(username) + if err != nil { + t.Fatalf("err: %s", err) + } + + // Try just a tilde + expected = homedir + if path, err = ExpandUser(fmt.Sprintf("~%s", username)); err != nil { + t.Fatalf("err: %s", err) + } + + if path != expected { + t.Fatalf("err: %v != %v", path, expected) + } + + // Try as a directory + expected = filepath.Join(homedir, "") + if path, err = ExpandUser(fmt.Sprintf("~%s/", username)); err != nil { + t.Fatalf("err: %s", err) + } + + if path != expected { + t.Fatalf("err: %v != %v", path, expected) + } + + // Try as a file + expected = filepath.Join(homedir, "foo") + if path, err = ExpandUser(fmt.Sprintf("~%s/foo", username)); err != nil { + t.Fatalf("err: %s", err) + } + + if path != expected { + t.Fatalf("err: %v != %v", path, expected) + } +} From d876978337bfcdb0319fefe51d259baef61db901 Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Wed, 5 Dec 2018 17:34:09 -0600 Subject: [PATCH 9/9] Fixed misspelling in a comment residing in packer/config_file.go as suggested by @azr. Co-Authored-By: arizvisa --- packer/config_file.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packer/config_file.go b/packer/config_file.go index 3bb9c8e51..f95914230 100644 --- a/packer/config_file.go +++ b/packer/config_file.go @@ -87,7 +87,10 @@ func configDir() (string, error) { } // Given a path, check to see if it's using ~ to reference a user directory. -// If so, then replace that component with the requrested user's directory. +// If so, then replace that component with the requested user directory. +// In "~/", "~" gets replaced by current user's home dir. +// In "~root/", "~user" gets replaced by root's home dir. +// ~ has to be the first character of path for ExpandUser change it. func ExpandUser(path string) (string, error) { var ( u *user.User