diff --git a/.changes/unreleased/Changed-20250305-003415.yaml b/.changes/unreleased/Changed-20250305-003415.yaml new file mode 100644 index 0000000..d88e8c8 --- /dev/null +++ b/.changes/unreleased/Changed-20250305-003415.yaml @@ -0,0 +1,3 @@ +kind: Changed +body: 'FileDirective: use the config directory if path is not absolute' +time: 2025-03-05T00:34:15.689980075-06:00 diff --git a/.changes/unreleased/Fixed-20250304-235706.yaml b/.changes/unreleased/Fixed-20250304-235706.yaml new file mode 100644 index 0000000..d3dd9b2 --- /dev/null +++ b/.changes/unreleased/Fixed-20250304-235706.yaml @@ -0,0 +1,3 @@ +kind: Fixed +body: 'SSH: password authentication bugs' +time: 2025-03-04T23:57:06.326604774-06:00 diff --git a/pkg/backy/config.go b/pkg/backy/config.go index 26ad032..4bb2d88 100644 --- a/pkg/backy/config.go +++ b/pkg/backy/config.go @@ -127,7 +127,7 @@ func (opts *ConfigOpts) ReadConfig() *ConfigOpts { unmarshalConfig(backyKoanf, "commands", &opts.Cmds, opts.Logger) - validateCommandEnvironments(opts) + getCommandEnvironments(opts) unmarshalConfig(backyKoanf, "hosts", &opts.Hosts, opts.Logger) @@ -245,11 +245,11 @@ func unmarshalConfig(k *koanf.Koanf, key string, target interface{}, log zerolog } } -func validateCommandEnvironments(opts *ConfigOpts) { +func getCommandEnvironments(opts *ConfigOpts) { for cmdName, cmdConf := range opts.Cmds { + opts.Logger.Debug().Str("env file", cmdConf.Env).Str("cmd", cmdName).Send() if err := testFile(cmdConf.Env); err != nil { - opts.Logger.Info().Str("cmd", cmdName).Err(err).Send() - os.Exit(1) + logging.ExitWithMSG("Could not open file"+cmdConf.Env+": "+err.Error(), 1, &opts.Logger) } expandEnvVars(opts.backyEnv, cmdConf.Environment) } @@ -499,16 +499,7 @@ func getVaultSecret(vaultClient *vault.Client, key *VaultKey) (string, error) { return value, nil } -func isVaultKey(str string) (string, bool) { - str = strings.TrimSpace(str) - return strings.TrimPrefix(str, "vault:"), strings.HasPrefix(str, "vault:") -} - -func parseVaultKey(str string, keys []*VaultKey) (*VaultKey, error) { - keyName, isKey := isVaultKey(str) - if !isKey { - return nil, nil - } +func parseVaultKey(keyName string, keys []*VaultKey) (*VaultKey, error) { for _, k := range keys { if k.Name == keyName { @@ -635,7 +626,7 @@ func processCmds(opts *ConfigOpts) error { case "add", "remove", "modify", "checkIfExists", "delete", "password": cmd.userMan, err = usermanager.NewUserManager(cmd.OS) if cmd.UserOperation == "password" { - cmd.UserPassword = expandExternalConfigDirectives(cmd.UserPassword, opts) + cmd.UserPassword = getExternalConfigDirectiveValue(cmd.UserPassword, opts) } if cmd.Host != nil { host, ok := opts.Hosts[*cmd.Host] @@ -645,7 +636,7 @@ func processCmds(opts *ConfigOpts) error { } for indx, key := range cmd.UserSshPubKeys { opts.Logger.Debug().Msg("adding SSH Keys") - key = expandExternalConfigDirectives(key, opts) + key = getExternalConfigDirectiveValue(key, opts) cmd.UserSshPubKeys[indx] = key } if err != nil { diff --git a/pkg/backy/ssh.go b/pkg/backy/ssh.go index 8a64fbe..217ca32 100644 --- a/pkg/backy/ssh.go +++ b/pkg/backy/ssh.go @@ -23,7 +23,7 @@ import ( "golang.org/x/crypto/ssh/knownhosts" ) -var PrivateKeyExtraInfoErr = errors.New("Private key may be encrypted. \nIf encrypted, make sure the password is specified correctly in the correct section. This may be done in one of three ways: \n privatekeypassword: env:PR_KEY_PASS \n privatekeypassword: file:/path/to/password-file \n privatekeypassword: password (not recommended). \n ") +var PrivateKeyExtraInfoErr = errors.New("Private key may be encrypted. \nIf encrypted, make sure the password is specified correctly in the correct section. This may be done in one of two ways: \n Using external directives - see docs \n privatekeypassword: password (not recommended). \n ") var TS = strings.TrimSpace // ConnectToHost connects to a host by looking up the config values in the file ~/.ssh/config @@ -120,7 +120,6 @@ func (remoteConfig *Host) ConnectToHost(opts *ConfigOpts) error { return errors.Wrap(err, "could not create hostkeycallback function") } remoteConfig.ClientConfig.HostKeyCallback = hostKeyCallback - // opts.Logger.Info().Str("user", remoteConfig.ClientConfig.User).Send() remoteConfig.SshClient, connectErr = remoteConfig.ConnectThroughBastion(opts.Logger) if connectErr != nil { @@ -181,11 +180,7 @@ func (remoteHost *Host) GetAuthMethods(opts *ConfigOpts) error { return err } - remoteHost.PrivateKeyPassword, err = GetPrivateKeyPassword(remoteHost.PrivateKeyPassword, opts, opts.Logger) - - if err != nil { - return err - } + remoteHost.PrivateKeyPassword = GetPrivateKeyPassword(remoteHost.PrivateKeyPassword, opts) if remoteHost.PrivateKeyPassword == "" { @@ -208,14 +203,9 @@ func (remoteHost *Host) GetAuthMethods(opts *ConfigOpts) error { } } - if remoteHost.Password == "" { + if remoteHost.Password != "" { - remoteHost.Password, err = GetPassword(remoteHost.Password, opts, opts.Logger) - - if err != nil { - - return err - } + remoteHost.Password = GetPassword(remoteHost.Password, opts) remoteHost.ClientConfig.Auth = append(remoteHost.ClientConfig.Auth, ssh.Password(remoteHost.Password)) } @@ -250,14 +240,13 @@ func (remoteHost *Host) GetPrivateKeyFileFromConfig() { // If it is the port is searched in the SSH config file(s) func (remoteHost *Host) GetPort() { port := fmt.Sprintf("%d", remoteHost.Port) - // port specifed? + // port specified? // port will be 0 if missing from backy config if port == "0" { port, _ = remoteHost.SSHConfigFile.SshConfigFile.Get(remoteHost.Host, "Port") if port == "" { - // get port from default SSH config file port = remoteHost.SSHConfigFile.DefaultUserSettings.Get(remoteHost.Host, "Port") // set port to be default @@ -272,7 +261,6 @@ func (remoteHost *Host) GetPort() { func (remoteHost *Host) CombineHostNameWithPort() { - // if the port is already in the HostName, leave it if strings.HasSuffix(remoteHost.HostName, fmt.Sprintf(":%d", remoteHost.Port)) { return } @@ -332,64 +320,13 @@ func (remotehHost *Host) GetKnownHosts() error { return knownHostsFileErr } -func GetPrivateKeyPassword(key string, opts *ConfigOpts, log zerolog.Logger) (string, error) { - - var prKeyPassword string - if strings.HasPrefix(key, "file:") { - privKeyPassFilePath := strings.TrimPrefix(key, "file:") - privKeyPassFilePath, _ = getFullPathWithHomeDir(privKeyPassFilePath) - keyFile, keyFileErr := os.Open(privKeyPassFilePath) - if keyFileErr != nil { - return "", errors.Errorf("Private key password file %s failed to open. \n Make sure it is accessible and correct.", privKeyPassFilePath) - } - passwordScanner := bufio.NewScanner(keyFile) - for passwordScanner.Scan() { - prKeyPassword = passwordScanner.Text() - } - } else if strings.HasPrefix(key, "env:") { - privKey := strings.TrimPrefix(key, "env:") - privKey = strings.TrimPrefix(privKey, "${") - privKey = strings.TrimSuffix(privKey, "}") - privKey = strings.TrimPrefix(privKey, "$") - prKeyPassword = os.Getenv(privKey) - } else { - prKeyPassword = key - } - prKeyPassword = GetVaultKey(prKeyPassword, opts, opts.Logger) - return prKeyPassword, nil +func GetPrivateKeyPassword(key string, opts *ConfigOpts) string { + return getExternalConfigDirectiveValue(key, opts) } // GetPassword gets any password -func GetPassword(pass string, opts *ConfigOpts, log zerolog.Logger) (string, error) { - - pass = strings.TrimSpace(pass) - if pass == "" { - return "", nil - } - var password string - if strings.HasPrefix(pass, "file:") { - passFilePath := strings.TrimPrefix(pass, "file:") - passFilePath, _ = getFullPathWithHomeDir(passFilePath) - keyFile, keyFileErr := os.Open(passFilePath) - if keyFileErr != nil { - return "", errors.New("Password file failed to open") - } - passwordScanner := bufio.NewScanner(keyFile) - for passwordScanner.Scan() { - password = passwordScanner.Text() - } - } else if strings.HasPrefix(pass, "env:") { - passEnv := strings.TrimPrefix(pass, "env:") - passEnv = strings.TrimPrefix(passEnv, "${") - passEnv = strings.TrimSuffix(passEnv, "}") - passEnv = strings.TrimPrefix(passEnv, "$") - password = os.Getenv(passEnv) - } else { - password = pass - } - password = GetVaultKey(password, opts, opts.Logger) - - return password, nil +func GetPassword(pass string, opts *ConfigOpts) string { + return getExternalConfigDirectiveValue(pass, opts) } func (remoteConfig *Host) GetProxyJumpFromConfig(hosts map[string]*Host) error { diff --git a/pkg/backy/utils.go b/pkg/backy/utils.go index 30ecbd9..8f53d54 100644 --- a/pkg/backy/utils.go +++ b/pkg/backy/utils.go @@ -258,7 +258,6 @@ func expandEnvVars(backyEnv map[string]string, envVars []string) { return "" } - // parse env variables using new macros for indx, v := range envVars { if strings.HasPrefix(v, externDirectiveStart) && strings.HasSuffix(v, externDirectiveEnd) { if strings.HasPrefix(v, envExternDirectiveStart) { @@ -349,7 +348,7 @@ func parsePackageVersion(output string, cmdCtxLogger zerolog.Logger, command *Co return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, false), err } -func expandExternalConfigDirectives(key string, opts *ConfigOpts) string { +func getExternalConfigDirectiveValue(key string, opts *ConfigOpts) string { if !(strings.HasPrefix(key, externDirectiveStart) && strings.HasSuffix(key, externDirectiveEnd)) { return key } @@ -357,7 +356,7 @@ func expandExternalConfigDirectives(key string, opts *ConfigOpts) string { if strings.HasPrefix(key, envExternDirectiveStart) { key = strings.TrimPrefix(key, envExternDirectiveStart) key = strings.TrimSuffix(key, externDirectiveEnd) - return os.Getenv(key) + key = os.Getenv(key) } if strings.HasPrefix(key, externFileDirectiveStart) { var err error @@ -369,6 +368,9 @@ func expandExternalConfigDirectives(key string, opts *ConfigOpts) string { opts.Logger.Err(err).Send() return "" } + if !path.IsAbs(key) { + key = path.Join(opts.ConfigDir, key) + } keyValue, err = os.ReadFile(key) if err != nil { opts.Logger.Err(err).Send()