Compare commits
6 Commits
bcba6b2086
...
v0.10.0
Author | SHA1 | Date | |
---|---|---|---|
fb1c8ec4fb | |||
fe9462dac0 | |||
d8453d1fb0 | |||
65c46a1e26 | |||
f859b5961f | |||
25ddd65f25 |
@ -1,3 +0,0 @@
|
||||
kind: Added
|
||||
body: 'Hooks: improved logging when executing'
|
||||
time: 2025-03-01T13:29:32.195438013-06:00
|
@ -1,3 +0,0 @@
|
||||
kind: Added
|
||||
body: 'User commands: adding SSH keys using config key `userSshPubKeys`'
|
||||
time: 2025-03-03T23:42:48.009294808-06:00
|
@ -1,3 +0,0 @@
|
||||
kind: Added
|
||||
body: 'directives: added support for fetching values using directive `%{externalSource:key}%`'
|
||||
time: 2025-03-03T23:45:05.666939653-06:00
|
@ -1,3 +0,0 @@
|
||||
kind: Changed
|
||||
body: 'Commands: if dir is not specified, run in config dir'
|
||||
time: 2025-03-01T19:43:21.323077376-06:00
|
@ -1,3 +0,0 @@
|
||||
kind: Fixed
|
||||
body: 'LocalFetcher: return fetch error'
|
||||
time: 2025-03-01T13:26:00.330176712-06:00
|
@ -1,3 +0,0 @@
|
||||
kind: Fixed
|
||||
body: 'Lists: load file key before attempting to load from current file'
|
||||
time: 2025-03-01T13:28:01.739467944-06:00
|
@ -1,3 +0,0 @@
|
||||
kind: Fixed
|
||||
body: 'fix: host not in config file, but in ssh config, properly added to hosts struct'
|
||||
time: 2025-03-01T18:24:34.81395054-06:00
|
16
.changes/v0.10.0.md
Normal file
16
.changes/v0.10.0.md
Normal file
@ -0,0 +1,16 @@
|
||||
## v0.10.0 - 2025-03-08
|
||||
### Added
|
||||
* Hooks: improved logging when executing
|
||||
* User commands: adding SSH keys using config key `userSshPubKeys`
|
||||
* directives: added support for fetching values using directive `%{externalSource:key}%`
|
||||
### Changed
|
||||
* Commands: if dir is not specified, run in config dir
|
||||
* FileDirective: use the config directory if path is not absolute
|
||||
* Host: changes to case of some keys
|
||||
* Notifications: added external directive to sensitive keys
|
||||
### Fixed
|
||||
* LocalFetcher: return fetch error
|
||||
* Lists: load file key before attempting to load from current file
|
||||
* fix: host not in config file, but in ssh config, properly added to hosts struct
|
||||
* SSH: password authentication bugs
|
||||
* User commands: change user password works
|
17
CHANGELOG.md
17
CHANGELOG.md
@ -6,6 +6,23 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
|
||||
and is generated by [Changie](https://github.com/miniscruff/changie).
|
||||
|
||||
|
||||
## v0.10.0 - 2025-03-08
|
||||
### Added
|
||||
* Hooks: improved logging when executing
|
||||
* User commands: adding SSH keys using config key `userSshPubKeys`
|
||||
* directives: added support for fetching values using directive `%{externalSource:key}%`
|
||||
### Changed
|
||||
* Commands: if dir is not specified, run in config dir
|
||||
* FileDirective: use the config directory if path is not absolute
|
||||
* Host: changes to case of some keys
|
||||
* Notifications: added external directive to sensitive keys
|
||||
### Fixed
|
||||
* LocalFetcher: return fetch error
|
||||
* Lists: load file key before attempting to load from current file
|
||||
* fix: host not in config file, but in ssh config, properly added to hosts struct
|
||||
* SSH: password authentication bugs
|
||||
* User commands: change user password works
|
||||
|
||||
## v0.9.1 - 2025-03-01
|
||||
### Changed
|
||||
* Use EnvVar AWS_PROFILE to get S3 profile
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const versionStr = "0.9.2"
|
||||
const versionStr = "0.10.0"
|
||||
|
||||
var (
|
||||
versionCmd = &cobra.Command{
|
||||
|
@ -15,5 +15,5 @@ The `exec` subcommand can do some things that the configuration file can't do ye
|
||||
The commands have to be defined in the config file. The hosts need to at least be in the ssh_config(5) file.
|
||||
|
||||
```sh
|
||||
backy exec host [--commands=command1 -commands=command2 ... | -c command1 -c command2 ...] [--hosts=host1 --hosts=hosts2 ... | -m host1 -c host2 ...] [flags]
|
||||
backy exec host [--commands=command1 -commands=command2 ... | -c command1 -c command2 ...] [--hosts=host1 --hosts=hosts2 ... | -m host1 -m host2 ...] [flags]
|
||||
```
|
||||
|
29
docs/content/cli/list.md
Normal file
29
docs/content/cli/list.md
Normal file
@ -0,0 +1,29 @@
|
||||
---
|
||||
title: List
|
||||
---
|
||||
|
||||
|
||||
List commands, lists, or hosts defined in config file
|
||||
|
||||
Usage:
|
||||
```
|
||||
backy list [command]
|
||||
```
|
||||
|
||||
Available Commands:
|
||||
cmds List commands defined in config file.
|
||||
lists List lists defined in config file.
|
||||
|
||||
Flags:
|
||||
```
|
||||
-h, --help help for list
|
||||
```
|
||||
|
||||
Global Flags:
|
||||
```
|
||||
--cmdStdOut Pass to print command output to stdout
|
||||
-f, --config string config file to read from
|
||||
--log-file string log file to write to
|
||||
--s3-endpoint string Sets the S3 endpoint used for config file fetching. Overrides S3_ENDPOINT env variable.
|
||||
-v, --verbose Sets verbose level
|
||||
```
|
@ -12,17 +12,17 @@ weight: 1
|
||||
|
||||
Values available for this section **(case-sensitive)**:
|
||||
|
||||
| name | notes | type | required
|
||||
| --- | --- | --- | --- |
|
||||
| `cmd` | Defines the command to execute | `string` | yes |
|
||||
| `Args` | Defines the arguments to the command | `[]string` | no |
|
||||
| `environment` | Defines environment variables for the command | `[]string` | no |
|
||||
| `type` | See documentation further down the page. Additional fields may be required. | `string` | no |
|
||||
| `getOutput` | Command(s) output is in the notification(s) | `bool` | no |
|
||||
| `host` | If not specified, the command will execute locally. | `string` | no |
|
||||
| `scriptEnvFile` | When type is `scriptFile` or `script`, this file is prepended to the input. | `string` | no |
|
||||
| `shell` | Run the command in the shell | `string` | no |
|
||||
| `hooks` | Hooks are used at the end of the individual command. Must have at least `error`, `success`, or `final`. | `map[string][]string` | no |
|
||||
| name | notes | type | required | External directive support |
|
||||
| ----------------| ------------------------------------------------------------------------------------------------------- | --------------------- | -------- |----------------------------|
|
||||
| `cmd` | Defines the command to execute | `string` | yes | No |
|
||||
| `Args` | Defines the arguments to the command | `[]string` | no | No |
|
||||
| `environment` | Defines environment variables for the command | `[]string` | no | No |
|
||||
| `type` | See documentation further down the page. Additional fields may be required. | `string` | no | No |
|
||||
| `getOutput` | Command(s) output is in the notification(s) | `bool` | no | No |
|
||||
| `host` | If not specified, the command will execute locally. | `string` | no | No |
|
||||
| `scriptEnvFile` | When type is `scriptFile` or `script`, this file is prepended to the input. | `string` | no | No |
|
||||
| `shell` | Run the command in the shell | `string` | no | No |
|
||||
| `hooks` | Hooks are used at the end of the individual command. Must have at least `error`, `success`, or `final`. | `map[string][]string` | no | No |
|
||||
|
||||
#### cmd
|
||||
|
||||
|
@ -10,10 +10,12 @@ This is dedicated to `user` commands. The command `type` field must be `user`. U
|
||||
| --- | --- | --- | --- |
|
||||
| `userName` | The name of a user to be configured. | `string` | yes |
|
||||
| `userOperation` | The type of operation to perform. | `string` | yes |
|
||||
| `userID` | The user ID to use. | `string` | yes |
|
||||
| `userGroups` | The groups the user should be added to. | `[]string` | yes |
|
||||
| `userShell` | The shell for the user. | `string` | yes |
|
||||
| `userID` | The user ID to use. | `string` | no |
|
||||
| `userGroups` | The groups the user should be added to. | `[]string` | no |
|
||||
| `userSshPubKeys` | The keys to add to the user's authorized keys. | `[]string` | no |
|
||||
| `userShell` | The shell for the user. | `string` | no |
|
||||
| `userHome` | The user's home directory. | `string` | no |
|
||||
| `userPassword` | The new password value when using the `password` operation. Can be specified by using external directive. | `string` | no |
|
||||
|
||||
|
||||
#### example
|
||||
|
15
docs/content/config/directives.md
Normal file
15
docs/content/config/directives.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "External Directives"
|
||||
weight: 2
|
||||
description: How to set up external directives.
|
||||
---
|
||||
|
||||
External directives are for including data that should not be in the config file. The following directives are supported:
|
||||
|
||||
- `%{file:path/to/file}%`
|
||||
- `%{env:ENV_VAR}%`
|
||||
- `%{vault:vault-key}%`
|
||||
|
||||
See the docs of each command if the field is supported.
|
||||
|
||||
If the file path does not begin with a `/`, the config file's directory will be used as the starting point.
|
@ -5,19 +5,19 @@ description: >
|
||||
This page tells you how to use hosts.
|
||||
---
|
||||
|
||||
| Key | Description | Type | Required |
|
||||
|----------------------|---------------------------------------------------------------|----------|----------|
|
||||
| `OS` | Operating system of the host (used for package commands) | `string` | no |
|
||||
| `config` | Path to the SSH config file | `string` | no |
|
||||
| `host` | Specifies the `Host` ssh_config(5) directive | `string` | yes |
|
||||
| `hostname` | Hostname of the host | `string` | no |
|
||||
| `knownhostsfile` | Path to the known hosts file | `string` | no |
|
||||
| `port` | Port number to connect to | `uint16` | no |
|
||||
| `proxyjump` | Proxy jump hosts, comma-separated | `string` | no |
|
||||
| `password` | Password for SSH authentication | `string` | no |
|
||||
| `privatekeypath` | Path to the private key file | `string` | no |
|
||||
| `privatekeypassword` | Password for the private key file | `string` | no |
|
||||
| `user` | Username for SSH authentication | `string` | no |
|
||||
| Key | Description | Type | Required | External directive support |
|
||||
|----------------------|---------------------------------------------------------------|----------|----------|----------------------------|
|
||||
| `OS` | Operating system of the host (used for package commands) | `string` | no | No |
|
||||
| `config` | Path to the SSH config file | `string` | no | No |
|
||||
| `host` | Specifies the `Host` ssh_config(5) directive | `string` | yes | No |
|
||||
| `hostname` | Hostname of the host | `string` | no | No |
|
||||
| `knownHostsFile` | Path to the known hosts file | `string` | no | No |
|
||||
| `port` | Port number to connect to | `uint16` | no | No |
|
||||
| `proxyjump` | Proxy jump hosts, comma-separated | `string` | no | No |
|
||||
| `password` | Password for SSH authentication | `string` | no | No |
|
||||
| `privateKeyPath` | Path to the private key file | `string` | no | No |
|
||||
| `privateKeyPassword` | Password for the private key file | `string` | no | Yes |
|
||||
| `user` | Username for SSH authentication | `string` | no | No |
|
||||
|
||||
## exec host subcommand
|
||||
|
||||
|
@ -39,23 +39,23 @@ There must be a section with an id (eg. `mail.test-svr`) following one of these
|
||||
|
||||
### mail
|
||||
|
||||
| key | description | type
|
||||
| --- | --- | ---
|
||||
| `host` | Specifies the SMTP host to connect to | `string`
|
||||
| `port` | Specifies the SMTP port | `uint16`
|
||||
| `senderaddress` | Address from which to send mail | `string`
|
||||
| `to` | Recipients to send emails to | `[]string`
|
||||
| `username` | SMTP username | `string`
|
||||
| `password` | SMTP password | `string`
|
||||
| key | description | type | External directive support |
|
||||
| --- | --- | --- | --- |
|
||||
| `host` | Specifies the SMTP host to connect to | `string` | no
|
||||
| `port` | Specifies the SMTP port | `uint16` | no
|
||||
| `senderaddress` | Address from which to send mail | `string` | no
|
||||
| `to` | Recipients to send emails to | `[]string` | no
|
||||
| `username` | SMTP username | `string` | no
|
||||
| `password` | SMTP password | `string` | yes
|
||||
|
||||
### matrix
|
||||
|
||||
| key | description | type
|
||||
| --- | --- | ---
|
||||
| `home-server` | Specifies the Matrix server connect to | `string`
|
||||
| `room-id` | Specifies the room ID of the room to send messages to | `string`
|
||||
| `access-token` | Matrix access token | `string`
|
||||
| `user-id` | Matrix user ID | `string`
|
||||
| key | description | type | External directive support |
|
||||
| --- | --- | ---| ---- |
|
||||
| `home-server` | Specifies the Matrix server connect to | `string` | no
|
||||
| `room-id` | Specifies the room ID of the room to send messages to | `string` | no
|
||||
| `access-token` | Matrix access token | `string` | yes
|
||||
| `user-id` | Matrix user ID | `string` | no
|
||||
|
||||
To get your access token (assumes you are using [Element](https://element.io/)) :
|
||||
|
||||
|
@ -71,7 +71,7 @@ hosts:
|
||||
hostname: some-hostname
|
||||
config: ~/.ssh/config
|
||||
user: user
|
||||
privatekeypath: /path/to/private/key
|
||||
privateKeyPath: /path/to/private/key
|
||||
port: 22
|
||||
# can also be env:VAR
|
||||
password: file:/path/to/file
|
||||
|
@ -166,6 +166,12 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
|
||||
}
|
||||
}
|
||||
|
||||
if command.Type == UserCT {
|
||||
if command.UserOperation == "password" {
|
||||
localCMD.Stdin = command.stdin
|
||||
cmdCtxLogger.Info().Str("password", command.UserPassword).Msg("user password to be updated")
|
||||
}
|
||||
}
|
||||
if command.Dir != nil {
|
||||
localCMD.Dir = *command.Dir
|
||||
}
|
||||
|
@ -81,15 +81,16 @@ func (opts *ConfigOpts) InitConfig() {
|
||||
logging.ExitWithMSG(fmt.Sprintf("error initializing cache: %v", err), 1, nil)
|
||||
}
|
||||
|
||||
fetcher, err := remotefetcher.NewRemoteFetcher(opts.ConfigFilePath, opts.Cache)
|
||||
|
||||
if isRemoteURL(opts.ConfigFilePath) {
|
||||
p, _ := getRemoteDir(opts.ConfigFilePath)
|
||||
opts.ConfigDir = p
|
||||
}
|
||||
|
||||
fetcher, err := remotefetcher.NewRemoteFetcher(opts.ConfigFilePath, opts.Cache)
|
||||
if err != nil {
|
||||
logging.ExitWithMSG(fmt.Sprintf("error initializing config fetcher: %v", err), 1, nil)
|
||||
}
|
||||
|
||||
if opts.ConfigFilePath != "" {
|
||||
loadConfigFile(fetcher, opts.ConfigFilePath, backyKoanf, opts)
|
||||
} else {
|
||||
@ -127,7 +128,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 +246,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 +500,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 {
|
||||
@ -634,8 +626,10 @@ func processCmds(opts *ConfigOpts) error {
|
||||
switch cmd.UserOperation {
|
||||
case "add", "remove", "modify", "checkIfExists", "delete", "password":
|
||||
cmd.userMan, err = usermanager.NewUserManager(cmd.OS)
|
||||
|
||||
if cmd.UserOperation == "password" {
|
||||
cmd.UserPassword = expandExternalConfigDirectives(cmd.UserPassword, opts)
|
||||
opts.Logger.Debug().Msg("changing password for user: " + cmd.Username)
|
||||
cmd.UserPassword = getExternalConfigDirectiveValue(cmd.UserPassword, opts)
|
||||
}
|
||||
if cmd.Host != nil {
|
||||
host, ok := opts.Hosts[*cmd.Host]
|
||||
@ -645,7 +639,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 {
|
||||
|
@ -58,6 +58,7 @@ func (opts *ConfigOpts) SetupNotify() {
|
||||
opts.Logger.Info().Err(fmt.Errorf("error: ID %s not found in mail object", confId)).Str("list", confName).Send()
|
||||
continue
|
||||
}
|
||||
conf.Password = getExternalConfigDirectiveValue(conf.Password, opts)
|
||||
mailConf := setupMail(conf)
|
||||
services = append(services, mailConf)
|
||||
case "matrix":
|
||||
@ -66,6 +67,7 @@ func (opts *ConfigOpts) SetupNotify() {
|
||||
opts.Logger.Info().Err(fmt.Errorf("error: ID %s not found in matrix object", confId)).Str("list", confName).Send()
|
||||
continue
|
||||
}
|
||||
conf.AccessToken = getExternalConfigDirectiveValue(conf.AccessToken, opts)
|
||||
mtrxConf, mtrxErr := setupMatrix(conf)
|
||||
if mtrxErr != nil {
|
||||
opts.Logger.Info().Str("list", confName).Err(fmt.Errorf("error: configuring matrix id %s failed during setup: %w", id, mtrxErr))
|
||||
|
104
pkg/backy/ssh.go
104
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,13 @@ func (remoteHost *Host) GetAuthMethods(opts *ConfigOpts) error {
|
||||
}
|
||||
}
|
||||
|
||||
if remoteHost.Password == "" {
|
||||
if remoteHost.Password != "" {
|
||||
|
||||
remoteHost.Password, err = GetPassword(remoteHost.Password, opts, opts.Logger)
|
||||
opts.Logger.Debug().Str("password", remoteHost.Password).Str("Host", remoteHost.Host).Send()
|
||||
|
||||
if err != nil {
|
||||
remoteHost.Password = GetPassword(remoteHost.Password, opts)
|
||||
|
||||
return err
|
||||
}
|
||||
// opts.Logger.Debug().Str("actual password", remoteHost.Password).Str("Host", remoteHost.Host).Send()
|
||||
|
||||
remoteHost.ClientConfig.Auth = append(remoteHost.ClientConfig.Auth, ssh.Password(remoteHost.Password))
|
||||
}
|
||||
@ -250,14 +244,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 +265,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
|
||||
}
|
||||
@ -322,74 +314,23 @@ func (remoteHost *Host) ConnectThroughBastion(log zerolog.Logger) (*ssh.Client,
|
||||
|
||||
// GetKnownHosts resolves the host's KnownHosts file if it is defined
|
||||
// if not defined, the default location for this file is used
|
||||
func (remotehHost *Host) GetKnownHosts() error {
|
||||
func (remoteHost *Host) GetKnownHosts() error {
|
||||
var knownHostsFileErr error
|
||||
if TS(remotehHost.KnownHostsFile) != "" {
|
||||
remotehHost.KnownHostsFile, knownHostsFileErr = getFullPathWithHomeDir(remotehHost.KnownHostsFile)
|
||||
if TS(remoteHost.KnownHostsFile) != "" {
|
||||
remoteHost.KnownHostsFile, knownHostsFileErr = getFullPathWithHomeDir(remoteHost.KnownHostsFile)
|
||||
return knownHostsFileErr
|
||||
}
|
||||
remotehHost.KnownHostsFile, knownHostsFileErr = getFullPathWithHomeDir("~/.ssh/known_hosts")
|
||||
remoteHost.KnownHostsFile, knownHostsFileErr = getFullPathWithHomeDir("~/.ssh/known_hosts")
|
||||
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 {
|
||||
@ -490,7 +431,6 @@ func (command *Command) RunCmdSSH(cmdCtxLogger zerolog.Logger, opts *ConfigOpts)
|
||||
env: command.Environment,
|
||||
}
|
||||
)
|
||||
// Getting the command type must be done before concatenating the arguments
|
||||
command = getCommandTypeAndSetCommandInfo(command)
|
||||
|
||||
// Prepare command arguments
|
||||
@ -566,12 +506,21 @@ func (command *Command) RunCmdSSH(cmdCtxLogger zerolog.Logger, opts *ConfigOpts)
|
||||
ArgsStr = fmt.Sprintf("%s %s", command.Cmd, ArgsStr)
|
||||
}
|
||||
cmdCtxLogger.Debug().Str("cmd + args", ArgsStr).Send()
|
||||
// Run simple command
|
||||
|
||||
if command.Type == UserCT && command.UserOperation == "password" {
|
||||
// cmdCtxLogger.Debug().Msgf("adding stdin")
|
||||
userNamePass := fmt.Sprintf("%s:%s", command.Username, command.UserPassword)
|
||||
ArgsStr = fmt.Sprintf("echo %s | chpasswd", userNamePass)
|
||||
|
||||
// commandSession.Stdin = command.stdin
|
||||
}
|
||||
if err := commandSession.Run(ArgsStr); err != nil {
|
||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error running command: %w", err)
|
||||
}
|
||||
|
||||
if command.Type == UserCT && command.UserOperation == "add" {
|
||||
if command.Type == UserCT {
|
||||
|
||||
if command.UserOperation == "add" {
|
||||
if command.UserSshPubKeys != nil {
|
||||
var (
|
||||
f *sftp.File
|
||||
@ -622,6 +571,7 @@ func (command *Command) RunCmdSSH(cmdCtxLogger zerolog.Logger, opts *ConfigOpts)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), nil
|
||||
}
|
||||
|
@ -26,15 +26,15 @@ type (
|
||||
ConfigFilePath string `yaml:"config,omitempty"`
|
||||
Host string `yaml:"host,omitempty"`
|
||||
HostName string `yaml:"hostname,omitempty"`
|
||||
KnownHostsFile string `yaml:"knownhostsfile,omitempty"`
|
||||
KnownHostsFile string `yaml:"knownHostsFile,omitempty"`
|
||||
ClientConfig *ssh.ClientConfig
|
||||
SSHConfigFile *sshConfigFile
|
||||
SshClient *ssh.Client
|
||||
Port uint16 `yaml:"port,omitempty"`
|
||||
ProxyJump string `yaml:"proxyjump,omitempty"`
|
||||
Password string `yaml:"password,omitempty"`
|
||||
PrivateKeyPath string `yaml:"privatekeypath,omitempty"`
|
||||
PrivateKeyPassword string `yaml:"privatekeypassword,omitempty"`
|
||||
PrivateKeyPath string `yaml:"privateKeyPath,omitempty"`
|
||||
PrivateKeyPassword string `yaml:"privateKeyPassword,omitempty"`
|
||||
useDefaultConfig bool
|
||||
User string `yaml:"user,omitempty"`
|
||||
isProxyHost bool
|
||||
|
@ -181,7 +181,6 @@ func testFile(c string) error {
|
||||
return fileOpenErr
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -258,7 +257,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,15 +347,15 @@ 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
|
||||
}
|
||||
opts.Logger.Info().Str("expanding external key", key).Send()
|
||||
opts.Logger.Debug().Str("expanding external key", key).Send()
|
||||
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 +367,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()
|
||||
|
Reference in New Issue
Block a user