Compare commits
5 Commits
25ddd65f25
...
v0.10.0
Author | SHA1 | Date | |
---|---|---|---|
fb1c8ec4fb | |||
fe9462dac0 | |||
d8453d1fb0 | |||
65c46a1e26 | |||
f859b5961f |
@ -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: Changed
|
|
||||||
body: 'FileDirective: use the config directory if path is not absolute'
|
|
||||||
time: 2025-03-05T00:34:15.689980075-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
|
|
@ -1,3 +0,0 @@
|
|||||||
kind: Fixed
|
|
||||||
body: 'SSH: password authentication bugs'
|
|
||||||
time: 2025-03-04T23:57:06.326604774-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).
|
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
|
## v0.9.1 - 2025-03-01
|
||||||
### Changed
|
### Changed
|
||||||
* Use EnvVar AWS_PROFILE to get S3 profile
|
* Use EnvVar AWS_PROFILE to get S3 profile
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
const versionStr = "0.9.2"
|
const versionStr = "0.10.0"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
versionCmd = &cobra.Command{
|
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.
|
The commands have to be defined in the config file. The hosts need to at least be in the ssh_config(5) file.
|
||||||
|
|
||||||
```sh
|
```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)**:
|
Values available for this section **(case-sensitive)**:
|
||||||
|
|
||||||
| name | notes | type | required
|
| name | notes | type | required | External directive support |
|
||||||
| --- | --- | --- | --- |
|
| ----------------| ------------------------------------------------------------------------------------------------------- | --------------------- | -------- |----------------------------|
|
||||||
| `cmd` | Defines the command to execute | `string` | yes |
|
| `cmd` | Defines the command to execute | `string` | yes | No |
|
||||||
| `Args` | Defines the arguments to the command | `[]string` | no |
|
| `Args` | Defines the arguments to the command | `[]string` | no | No |
|
||||||
| `environment` | Defines environment variables for the command | `[]string` | 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 |
|
| `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 |
|
| `getOutput` | Command(s) output is in the notification(s) | `bool` | no | No |
|
||||||
| `host` | If not specified, the command will execute locally. | `string` | 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 |
|
| `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 |
|
| `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 |
|
| `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
|
#### 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 |
|
| `userName` | The name of a user to be configured. | `string` | yes |
|
||||||
| `userOperation` | The type of operation to perform. | `string` | yes |
|
| `userOperation` | The type of operation to perform. | `string` | yes |
|
||||||
| `userID` | The user ID to use. | `string` | yes |
|
| `userID` | The user ID to use. | `string` | no |
|
||||||
| `userGroups` | The groups the user should be added to. | `[]string` | yes |
|
| `userGroups` | The groups the user should be added to. | `[]string` | no |
|
||||||
| `userShell` | The shell for the user. | `string` | yes |
|
| `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 |
|
| `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
|
#### 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.
|
This page tells you how to use hosts.
|
||||||
---
|
---
|
||||||
|
|
||||||
| Key | Description | Type | Required |
|
| Key | Description | Type | Required | External directive support |
|
||||||
|----------------------|---------------------------------------------------------------|----------|----------|
|
|----------------------|---------------------------------------------------------------|----------|----------|----------------------------|
|
||||||
| `OS` | Operating system of the host (used for package commands) | `string` | no |
|
| `OS` | Operating system of the host (used for package commands) | `string` | no | No |
|
||||||
| `config` | Path to the SSH config file | `string` | no |
|
| `config` | Path to the SSH config file | `string` | no | No |
|
||||||
| `host` | Specifies the `Host` ssh_config(5) directive | `string` | yes |
|
| `host` | Specifies the `Host` ssh_config(5) directive | `string` | yes | No |
|
||||||
| `hostname` | Hostname of the host | `string` | no |
|
| `hostname` | Hostname of the host | `string` | no | No |
|
||||||
| `knownhostsfile` | Path to the known hosts file | `string` | no |
|
| `knownHostsFile` | Path to the known hosts file | `string` | no | No |
|
||||||
| `port` | Port number to connect to | `uint16` | no |
|
| `port` | Port number to connect to | `uint16` | no | No |
|
||||||
| `proxyjump` | Proxy jump hosts, comma-separated | `string` | no |
|
| `proxyjump` | Proxy jump hosts, comma-separated | `string` | no | No |
|
||||||
| `password` | Password for SSH authentication | `string` | no |
|
| `password` | Password for SSH authentication | `string` | no | No |
|
||||||
| `privatekeypath` | Path to the private key file | `string` | no |
|
| `privateKeyPath` | Path to the private key file | `string` | no | No |
|
||||||
| `privatekeypassword` | Password for the private key file | `string` | no |
|
| `privateKeyPassword` | Password for the private key file | `string` | no | Yes |
|
||||||
| `user` | Username for SSH authentication | `string` | no |
|
| `user` | Username for SSH authentication | `string` | no | No |
|
||||||
|
|
||||||
## exec host subcommand
|
## exec host subcommand
|
||||||
|
|
||||||
|
@ -39,23 +39,23 @@ There must be a section with an id (eg. `mail.test-svr`) following one of these
|
|||||||
|
|
||||||
### mail
|
### mail
|
||||||
|
|
||||||
| key | description | type
|
| key | description | type | External directive support |
|
||||||
| --- | --- | ---
|
| --- | --- | --- | --- |
|
||||||
| `host` | Specifies the SMTP host to connect to | `string`
|
| `host` | Specifies the SMTP host to connect to | `string` | no
|
||||||
| `port` | Specifies the SMTP port | `uint16`
|
| `port` | Specifies the SMTP port | `uint16` | no
|
||||||
| `senderaddress` | Address from which to send mail | `string`
|
| `senderaddress` | Address from which to send mail | `string` | no
|
||||||
| `to` | Recipients to send emails to | `[]string`
|
| `to` | Recipients to send emails to | `[]string` | no
|
||||||
| `username` | SMTP username | `string`
|
| `username` | SMTP username | `string` | no
|
||||||
| `password` | SMTP password | `string`
|
| `password` | SMTP password | `string` | yes
|
||||||
|
|
||||||
### matrix
|
### matrix
|
||||||
|
|
||||||
| key | description | type
|
| key | description | type | External directive support |
|
||||||
| --- | --- | ---
|
| --- | --- | ---| ---- |
|
||||||
| `home-server` | Specifies the Matrix server connect to | `string`
|
| `home-server` | Specifies the Matrix server connect to | `string` | no
|
||||||
| `room-id` | Specifies the room ID of the room to send messages to | `string`
|
| `room-id` | Specifies the room ID of the room to send messages to | `string` | no
|
||||||
| `access-token` | Matrix access token | `string`
|
| `access-token` | Matrix access token | `string` | yes
|
||||||
| `user-id` | Matrix user ID | `string`
|
| `user-id` | Matrix user ID | `string` | no
|
||||||
|
|
||||||
To get your access token (assumes you are using [Element](https://element.io/)) :
|
To get your access token (assumes you are using [Element](https://element.io/)) :
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ hosts:
|
|||||||
hostname: some-hostname
|
hostname: some-hostname
|
||||||
config: ~/.ssh/config
|
config: ~/.ssh/config
|
||||||
user: user
|
user: user
|
||||||
privatekeypath: /path/to/private/key
|
privateKeyPath: /path/to/private/key
|
||||||
port: 22
|
port: 22
|
||||||
# can also be env:VAR
|
# can also be env:VAR
|
||||||
password: file:/path/to/file
|
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 {
|
if command.Dir != nil {
|
||||||
localCMD.Dir = *command.Dir
|
localCMD.Dir = *command.Dir
|
||||||
}
|
}
|
||||||
|
@ -81,15 +81,16 @@ func (opts *ConfigOpts) InitConfig() {
|
|||||||
logging.ExitWithMSG(fmt.Sprintf("error initializing cache: %v", err), 1, nil)
|
logging.ExitWithMSG(fmt.Sprintf("error initializing cache: %v", err), 1, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
fetcher, err := remotefetcher.NewRemoteFetcher(opts.ConfigFilePath, opts.Cache)
|
|
||||||
|
|
||||||
if isRemoteURL(opts.ConfigFilePath) {
|
if isRemoteURL(opts.ConfigFilePath) {
|
||||||
p, _ := getRemoteDir(opts.ConfigFilePath)
|
p, _ := getRemoteDir(opts.ConfigFilePath)
|
||||||
opts.ConfigDir = p
|
opts.ConfigDir = p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetcher, err := remotefetcher.NewRemoteFetcher(opts.ConfigFilePath, opts.Cache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.ExitWithMSG(fmt.Sprintf("error initializing config fetcher: %v", err), 1, nil)
|
logging.ExitWithMSG(fmt.Sprintf("error initializing config fetcher: %v", err), 1, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.ConfigFilePath != "" {
|
if opts.ConfigFilePath != "" {
|
||||||
loadConfigFile(fetcher, opts.ConfigFilePath, backyKoanf, opts)
|
loadConfigFile(fetcher, opts.ConfigFilePath, backyKoanf, opts)
|
||||||
} else {
|
} else {
|
||||||
@ -625,7 +626,9 @@ func processCmds(opts *ConfigOpts) error {
|
|||||||
switch cmd.UserOperation {
|
switch cmd.UserOperation {
|
||||||
case "add", "remove", "modify", "checkIfExists", "delete", "password":
|
case "add", "remove", "modify", "checkIfExists", "delete", "password":
|
||||||
cmd.userMan, err = usermanager.NewUserManager(cmd.OS)
|
cmd.userMan, err = usermanager.NewUserManager(cmd.OS)
|
||||||
|
|
||||||
if cmd.UserOperation == "password" {
|
if cmd.UserOperation == "password" {
|
||||||
|
opts.Logger.Debug().Msg("changing password for user: " + cmd.Username)
|
||||||
cmd.UserPassword = getExternalConfigDirectiveValue(cmd.UserPassword, opts)
|
cmd.UserPassword = getExternalConfigDirectiveValue(cmd.UserPassword, opts)
|
||||||
}
|
}
|
||||||
if cmd.Host != nil {
|
if cmd.Host != 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()
|
opts.Logger.Info().Err(fmt.Errorf("error: ID %s not found in mail object", confId)).Str("list", confName).Send()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
conf.Password = getExternalConfigDirectiveValue(conf.Password, opts)
|
||||||
mailConf := setupMail(conf)
|
mailConf := setupMail(conf)
|
||||||
services = append(services, mailConf)
|
services = append(services, mailConf)
|
||||||
case "matrix":
|
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()
|
opts.Logger.Info().Err(fmt.Errorf("error: ID %s not found in matrix object", confId)).Str("list", confName).Send()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
conf.AccessToken = getExternalConfigDirectiveValue(conf.AccessToken, opts)
|
||||||
mtrxConf, mtrxErr := setupMatrix(conf)
|
mtrxConf, mtrxErr := setupMatrix(conf)
|
||||||
if mtrxErr != nil {
|
if mtrxErr != nil {
|
||||||
opts.Logger.Info().Str("list", confName).Err(fmt.Errorf("error: configuring matrix id %s failed during setup: %w", id, mtrxErr))
|
opts.Logger.Info().Str("list", confName).Err(fmt.Errorf("error: configuring matrix id %s failed during setup: %w", id, mtrxErr))
|
||||||
|
109
pkg/backy/ssh.go
109
pkg/backy/ssh.go
@ -205,8 +205,12 @@ func (remoteHost *Host) GetAuthMethods(opts *ConfigOpts) error {
|
|||||||
|
|
||||||
if remoteHost.Password != "" {
|
if remoteHost.Password != "" {
|
||||||
|
|
||||||
|
opts.Logger.Debug().Str("password", remoteHost.Password).Str("Host", remoteHost.Host).Send()
|
||||||
|
|
||||||
remoteHost.Password = GetPassword(remoteHost.Password, opts)
|
remoteHost.Password = GetPassword(remoteHost.Password, opts)
|
||||||
|
|
||||||
|
// opts.Logger.Debug().Str("actual password", remoteHost.Password).Str("Host", remoteHost.Host).Send()
|
||||||
|
|
||||||
remoteHost.ClientConfig.Auth = append(remoteHost.ClientConfig.Auth, ssh.Password(remoteHost.Password))
|
remoteHost.ClientConfig.Auth = append(remoteHost.ClientConfig.Auth, ssh.Password(remoteHost.Password))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,13 +314,13 @@ func (remoteHost *Host) ConnectThroughBastion(log zerolog.Logger) (*ssh.Client,
|
|||||||
|
|
||||||
// GetKnownHosts resolves the host's KnownHosts file if it is defined
|
// GetKnownHosts resolves the host's KnownHosts file if it is defined
|
||||||
// if not defined, the default location for this file is used
|
// if not defined, the default location for this file is used
|
||||||
func (remotehHost *Host) GetKnownHosts() error {
|
func (remoteHost *Host) GetKnownHosts() error {
|
||||||
var knownHostsFileErr error
|
var knownHostsFileErr error
|
||||||
if TS(remotehHost.KnownHostsFile) != "" {
|
if TS(remoteHost.KnownHostsFile) != "" {
|
||||||
remotehHost.KnownHostsFile, knownHostsFileErr = getFullPathWithHomeDir(remotehHost.KnownHostsFile)
|
remoteHost.KnownHostsFile, knownHostsFileErr = getFullPathWithHomeDir(remoteHost.KnownHostsFile)
|
||||||
return knownHostsFileErr
|
return knownHostsFileErr
|
||||||
}
|
}
|
||||||
remotehHost.KnownHostsFile, knownHostsFileErr = getFullPathWithHomeDir("~/.ssh/known_hosts")
|
remoteHost.KnownHostsFile, knownHostsFileErr = getFullPathWithHomeDir("~/.ssh/known_hosts")
|
||||||
return knownHostsFileErr
|
return knownHostsFileErr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -427,7 +431,6 @@ func (command *Command) RunCmdSSH(cmdCtxLogger zerolog.Logger, opts *ConfigOpts)
|
|||||||
env: command.Environment,
|
env: command.Environment,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// Getting the command type must be done before concatenating the arguments
|
|
||||||
command = getCommandTypeAndSetCommandInfo(command)
|
command = getCommandTypeAndSetCommandInfo(command)
|
||||||
|
|
||||||
// Prepare command arguments
|
// Prepare command arguments
|
||||||
@ -503,59 +506,69 @@ func (command *Command) RunCmdSSH(cmdCtxLogger zerolog.Logger, opts *ConfigOpts)
|
|||||||
ArgsStr = fmt.Sprintf("%s %s", command.Cmd, ArgsStr)
|
ArgsStr = fmt.Sprintf("%s %s", command.Cmd, ArgsStr)
|
||||||
}
|
}
|
||||||
cmdCtxLogger.Debug().Str("cmd + args", ArgsStr).Send()
|
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 {
|
if err := commandSession.Run(ArgsStr); err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error running command: %w", err)
|
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.UserSshPubKeys != nil {
|
|
||||||
var (
|
|
||||||
f *sftp.File
|
|
||||||
err error
|
|
||||||
userHome []byte
|
|
||||||
client *sftp.Client
|
|
||||||
)
|
|
||||||
|
|
||||||
cmdCtxLogger.Info().Msg("adding SSH Keys")
|
if command.UserOperation == "add" {
|
||||||
|
if command.UserSshPubKeys != nil {
|
||||||
|
var (
|
||||||
|
f *sftp.File
|
||||||
|
err error
|
||||||
|
userHome []byte
|
||||||
|
client *sftp.Client
|
||||||
|
)
|
||||||
|
|
||||||
commandSession, _ = command.RemoteHost.createSSHSession(opts)
|
cmdCtxLogger.Info().Msg("adding SSH Keys")
|
||||||
userHome, err = commandSession.CombinedOutput(fmt.Sprintf("grep \"%s\" /etc/passwd | cut -d: -f6", command.Username))
|
|
||||||
if err != nil {
|
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error finding user home from /etc/passwd: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
command.UserHome = strings.TrimSpace(string(userHome))
|
commandSession, _ = command.RemoteHost.createSSHSession(opts)
|
||||||
userSshDir := fmt.Sprintf("%s/.ssh", command.UserHome)
|
userHome, err = commandSession.CombinedOutput(fmt.Sprintf("grep \"%s\" /etc/passwd | cut -d: -f6", command.Username))
|
||||||
client, err = sftp.NewClient(command.RemoteHost.SshClient)
|
if err != nil {
|
||||||
if err != nil {
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error finding user home from /etc/passwd: %v", err)
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error creating sftp client: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
client.MkdirAll(userSshDir)
|
|
||||||
_, err = client.Create(fmt.Sprintf("%s/authorized_keys", userSshDir))
|
|
||||||
if err != nil {
|
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error opening file %s/authorized_keys: %v", userSshDir, err)
|
|
||||||
}
|
|
||||||
f, err = client.OpenFile(fmt.Sprintf("%s/authorized_keys", userSshDir), os.O_APPEND|os.O_CREATE|os.O_WRONLY)
|
|
||||||
if err != nil {
|
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error opening file %s/authorized_keys: %v", userSshDir, err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
for _, k := range command.UserSshPubKeys {
|
|
||||||
buf := bytes.NewBufferString(k)
|
|
||||||
cmdCtxLogger.Info().Str("key", k).Msg("adding SSH key")
|
|
||||||
if _, err := f.ReadFrom(buf); err != nil {
|
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error adding to authorized keys: %v", err)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
commandSession, _ = command.RemoteHost.createSSHSession(opts)
|
command.UserHome = strings.TrimSpace(string(userHome))
|
||||||
_, err = commandSession.CombinedOutput(fmt.Sprintf("chown -R %s:%s %s", command.Username, command.Username, userHome))
|
userSshDir := fmt.Sprintf("%s/.ssh", command.UserHome)
|
||||||
if err != nil {
|
client, err = sftp.NewClient(command.RemoteHost.SshClient)
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), err
|
if err != nil {
|
||||||
}
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error creating sftp client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
client.MkdirAll(userSshDir)
|
||||||
|
_, err = client.Create(fmt.Sprintf("%s/authorized_keys", userSshDir))
|
||||||
|
if err != nil {
|
||||||
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error opening file %s/authorized_keys: %v", userSshDir, err)
|
||||||
|
}
|
||||||
|
f, err = client.OpenFile(fmt.Sprintf("%s/authorized_keys", userSshDir), os.O_APPEND|os.O_CREATE|os.O_WRONLY)
|
||||||
|
if err != nil {
|
||||||
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error opening file %s/authorized_keys: %v", userSshDir, err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
for _, k := range command.UserSshPubKeys {
|
||||||
|
buf := bytes.NewBufferString(k)
|
||||||
|
cmdCtxLogger.Info().Str("key", k).Msg("adding SSH key")
|
||||||
|
if _, err := f.ReadFrom(buf); err != nil {
|
||||||
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error adding to authorized keys: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commandSession, _ = command.RemoteHost.createSSHSession(opts)
|
||||||
|
_, err = commandSession.CombinedOutput(fmt.Sprintf("chown -R %s:%s %s", command.Username, command.Username, userHome))
|
||||||
|
if err != nil {
|
||||||
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,15 +26,15 @@ type (
|
|||||||
ConfigFilePath string `yaml:"config,omitempty"`
|
ConfigFilePath string `yaml:"config,omitempty"`
|
||||||
Host string `yaml:"host,omitempty"`
|
Host string `yaml:"host,omitempty"`
|
||||||
HostName string `yaml:"hostname,omitempty"`
|
HostName string `yaml:"hostname,omitempty"`
|
||||||
KnownHostsFile string `yaml:"knownhostsfile,omitempty"`
|
KnownHostsFile string `yaml:"knownHostsFile,omitempty"`
|
||||||
ClientConfig *ssh.ClientConfig
|
ClientConfig *ssh.ClientConfig
|
||||||
SSHConfigFile *sshConfigFile
|
SSHConfigFile *sshConfigFile
|
||||||
SshClient *ssh.Client
|
SshClient *ssh.Client
|
||||||
Port uint16 `yaml:"port,omitempty"`
|
Port uint16 `yaml:"port,omitempty"`
|
||||||
ProxyJump string `yaml:"proxyjump,omitempty"`
|
ProxyJump string `yaml:"proxyjump,omitempty"`
|
||||||
Password string `yaml:"password,omitempty"`
|
Password string `yaml:"password,omitempty"`
|
||||||
PrivateKeyPath string `yaml:"privatekeypath,omitempty"`
|
PrivateKeyPath string `yaml:"privateKeyPath,omitempty"`
|
||||||
PrivateKeyPassword string `yaml:"privatekeypassword,omitempty"`
|
PrivateKeyPassword string `yaml:"privateKeyPassword,omitempty"`
|
||||||
useDefaultConfig bool
|
useDefaultConfig bool
|
||||||
User string `yaml:"user,omitempty"`
|
User string `yaml:"user,omitempty"`
|
||||||
isProxyHost bool
|
isProxyHost bool
|
||||||
|
@ -181,7 +181,6 @@ func testFile(c string) error {
|
|||||||
return fileOpenErr
|
return fileOpenErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,7 +351,7 @@ func getExternalConfigDirectiveValue(key string, opts *ConfigOpts) string {
|
|||||||
if !(strings.HasPrefix(key, externDirectiveStart) && strings.HasSuffix(key, externDirectiveEnd)) {
|
if !(strings.HasPrefix(key, externDirectiveStart) && strings.HasSuffix(key, externDirectiveEnd)) {
|
||||||
return key
|
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) {
|
if strings.HasPrefix(key, envExternDirectiveStart) {
|
||||||
key = strings.TrimPrefix(key, envExternDirectiveStart)
|
key = strings.TrimPrefix(key, envExternDirectiveStart)
|
||||||
key = strings.TrimSuffix(key, externDirectiveEnd)
|
key = strings.TrimSuffix(key, externDirectiveEnd)
|
||||||
|
Reference in New Issue
Block a user