6 Commits

Author SHA1 Message Date
fb1c8ec4fb v0.10.0
Some checks failed
ci/woodpecker/push/publish-docs Pipeline was successful
ci/woodpecker/tag/gitea Pipeline was successful
ci/woodpecker/tag/publish-docs Pipeline was successful
ci/woodpecker/release/publish-docs Pipeline was successful
ci/woodpecker/push/go-lint Pipeline failed
2025-03-08 00:25:44 -06:00
fe9462dac0 version bump
Some checks failed
ci/woodpecker/push/go-lint Pipeline failed
ci/woodpecker/push/publish-docs Pipeline was successful
2025-03-08 00:24:23 -06:00
d8453d1fb0 added external directives to Notifications, change case of keys in host, and update docs 2025-03-08 00:23:08 -06:00
65c46a1e26 add password change
Some checks failed
ci/woodpecker/push/go-lint Pipeline failed
2025-03-06 23:35:45 -06:00
f859b5961f add password change 2025-03-06 23:35:29 -06:00
25ddd65f25 v0.10.0
Some checks failed
ci/woodpecker/push/go-lint Pipeline failed
2025-03-05 00:35:14 -06:00
24 changed files with 220 additions and 209 deletions

View File

@ -1,3 +0,0 @@
kind: Added
body: 'Hooks: improved logging when executing'
time: 2025-03-01T13:29:32.195438013-06:00

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,3 +0,0 @@
kind: Fixed
body: 'LocalFetcher: return fetch error'
time: 2025-03-01T13:26:00.330176712-06:00

View File

@ -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

View File

@ -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
View 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

View File

@ -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

View File

@ -7,7 +7,7 @@ import (
"github.com/spf13/cobra"
)
const versionStr = "0.9.2"
const versionStr = "0.10.0"
var (
versionCmd = &cobra.Command{

View File

@ -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
View 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
```

View File

@ -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

View File

@ -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

View 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.

View File

@ -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

View File

@ -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/)) :

View File

@ -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

View 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
}

View File

@ -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 {

View File

@ -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))

View File

@ -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
}

View File

@ -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

View File

@ -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()