Compare commits
19 Commits
bcba6b2086
...
v0.10.1
Author | SHA1 | Date | |
---|---|---|---|
62d47ecfa7 | |||
32444ff82e | |||
a5a7c05640 | |||
bfb81e11b2 | |||
fd4c83f9c0 | |||
fe27c6396a | |||
c89dde186a | |||
18a64de0de | |||
99c622b69f | |||
95e85e8b45 | |||
1a48c7bca5 | |||
5d21764ef1 | |||
c7302f0e17 | |||
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
|
8
.changes/v0.10.1.md
Normal file
8
.changes/v0.10.1.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
## v0.10.1 - 2025-03-11
|
||||||
|
### Added
|
||||||
|
* UserCommands: add ssh public keys when running locally
|
||||||
|
* UserCommands: add field CreateUserHome
|
||||||
|
### Changed
|
||||||
|
* UserCommands: create temp file when modifing password over SSH
|
||||||
|
* UserCommands: change field name
|
||||||
|
* Vault: keys are now referenced by `name`, and the actual data by `data`
|
@ -1,9 +1,7 @@
|
|||||||
name: goreleaser release
|
|
||||||
steps:
|
steps:
|
||||||
golang:
|
golang:
|
||||||
image: golang:1.23
|
image: golang:1.23
|
||||||
commands:
|
commands:
|
||||||
- go mod tidy
|
|
||||||
- go install github.com/goreleaser/goreleaser/v2@v2.7.0
|
- go install github.com/goreleaser/goreleaser/v2@v2.7.0
|
||||||
- goreleaser release -f .goreleaser/gitea.yml --release-notes=".changes/$(go run backy.go version -V).md"
|
- goreleaser release -f .goreleaser/gitea.yml --release-notes=".changes/$(go run backy.go version -V).md"
|
||||||
environment:
|
environment:
|
||||||
|
26
CHANGELOG.md
26
CHANGELOG.md
@ -6,6 +6,32 @@ 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.1 - 2025-03-11
|
||||||
|
### Added
|
||||||
|
* UserCommands: add ssh public keys when running locally
|
||||||
|
* UserCommands: add field CreateUserHome
|
||||||
|
### Changed
|
||||||
|
* UserCommands: create temp file when modifing password over SSH
|
||||||
|
* UserCommands: change field name
|
||||||
|
* Vault: keys are now referenced by `name`, and the actual data by `data`
|
||||||
|
|
||||||
|
## 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.1"
|
||||||
|
|
||||||
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 | Partial |
|
||||||
| `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
|
||||||
|
|
||||||
@ -95,8 +95,9 @@ The following options are available:
|
|||||||
The environment variables support expansion:
|
The environment variables support expansion:
|
||||||
|
|
||||||
- using escaped values `$VAR` or `${VAR}`
|
- using escaped values `$VAR` or `${VAR}`
|
||||||
|
- using any external directive, and if using the env directive, the variable will be read from a `.env` file
|
||||||
|
|
||||||
For now, the variables have to be defined in an `.env` file in the same directory that the program is run from.
|
<!-- For now, the variables expanded have to be defined in an `.env` file in the same directory that the program is run from. -->
|
||||||
|
|
||||||
If using it with host specified, the SSH server has to be configured to accept those env variables.
|
If using it with host specified, the SSH server has to be configured to accept those env variables.
|
||||||
|
|
||||||
|
@ -6,14 +6,18 @@ description: This is dedicated to user commands.
|
|||||||
|
|
||||||
This is dedicated to `user` commands. The command `type` field must be `user`. User is a type that allows one to perform user operations. There are several additional options available when `type` is `user`:
|
This is dedicated to `user` commands. The command `type` field must be `user`. User is a type that allows one to perform user operations. There are several additional options available when `type` is `user`:
|
||||||
|
|
||||||
| name | notes | type | required |
|
| name | notes | type | required | External directive support
|
||||||
| --- | --- | --- | --- |
|
| ----------------| -------------------------------------------------------------| ---------- | ---------| --------------------------|
|
||||||
| `userName` | The name of a user to be configured. | `string` | yes |
|
| `userName` | The name of a user to be configured. | `string` | yes | no |
|
||||||
| `userOperation` | The type of operation to perform. | `string` | yes |
|
| `userOperation` | The type of operation to perform. | `string` | yes | no |
|
||||||
| `userID` | The user ID to use. | `string` | yes |
|
| `userID` | The user ID to use. | `string` | no | no |
|
||||||
| `userGroups` | The groups the user should be added to. | `[]string` | yes |
|
| `userGroups` | The groups the user should be added to. | `[]string` | no | no |
|
||||||
| `userShell` | The shell for the user. | `string` | yes |
|
| `systemUser` | Create a system user. | `bool` | no | no |
|
||||||
| `userHome` | The user's home directory. | `string` | no |
|
| `userCreateHome`| Create the home directory. | `bool` | no | no |
|
||||||
|
| `userSshPubKeys`| The keys to add to the user's authorized keys. | `[]string` | no | yes |
|
||||||
|
| `userShell` | The shell for the user. | `string` | no | no |
|
||||||
|
| `userHome` | The user's home directory. | `string` | no | no |
|
||||||
|
| `userPassword` | The new password value when using the `password` operation. | `string` | no | yes |
|
||||||
|
|
||||||
|
|
||||||
#### 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/)) :
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ description: Set up and configure vault.
|
|||||||
|
|
||||||
[Vault](https://www.vaultproject.io/) is a tool for storing secrets and other data securely.
|
[Vault](https://www.vaultproject.io/) is a tool for storing secrets and other data securely.
|
||||||
|
|
||||||
Vault config can be used by prefixing `vault:` in front of a password or ENV var.
|
A Vault key can be used by prefixing `%{vault:vault.keys.name}%` in a field that supports external directives.
|
||||||
|
|
||||||
This is the object in the config file:
|
This is the object in the config file:
|
||||||
|
|
||||||
@ -18,10 +18,12 @@ vault:
|
|||||||
keys:
|
keys:
|
||||||
- name: mongourl
|
- name: mongourl
|
||||||
mountpath: secret
|
mountpath: secret
|
||||||
|
key: data
|
||||||
path: mongo/url
|
path: mongo/url
|
||||||
type: # KVv1 or KVv2
|
type: KVv2 # KVv1 or KVv2
|
||||||
- name:
|
- name: someKeyName
|
||||||
path:
|
mountpath: secret
|
||||||
type:
|
key: keyData
|
||||||
mountpath:
|
type: KVv2
|
||||||
|
path: some/path
|
||||||
```
|
```
|
||||||
|
@ -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
|
||||||
|
2
go.mod
2
go.mod
@ -10,6 +10,7 @@ require (
|
|||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.76.0
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.76.0
|
||||||
github.com/dmarkham/enumer v1.5.11
|
github.com/dmarkham/enumer v1.5.11
|
||||||
github.com/go-co-op/gocron v1.37.0
|
github.com/go-co-op/gocron v1.37.0
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
github.com/hashicorp/vault/api v1.15.0
|
github.com/hashicorp/vault/api v1.15.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/kevinburke/ssh_config v1.2.0
|
github.com/kevinburke/ssh_config v1.2.0
|
||||||
@ -51,7 +52,6 @@ require (
|
|||||||
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
|
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||||
github.com/goccy/go-json v0.10.5 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"embed"
|
"embed"
|
||||||
@ -95,7 +96,7 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
|
|||||||
command.Shell = "sh"
|
command.Shell = "sh"
|
||||||
}
|
}
|
||||||
localCMD = exec.Command(command.Shell, command.Args...)
|
localCMD = exec.Command(command.Shell, command.Args...)
|
||||||
injectEnvIntoLocalCMD(envVars, localCMD, cmdCtxLogger)
|
injectEnvIntoLocalCMD(envVars, localCMD, cmdCtxLogger, opts)
|
||||||
|
|
||||||
cmdOutWriters = io.MultiWriter(&cmdOutBuf)
|
cmdOutWriters = io.MultiWriter(&cmdOutBuf)
|
||||||
|
|
||||||
@ -166,11 +167,17 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
injectEnvIntoLocalCMD(envVars, localCMD, cmdCtxLogger)
|
injectEnvIntoLocalCMD(envVars, localCMD, cmdCtxLogger, opts)
|
||||||
|
|
||||||
cmdOutWriters = io.MultiWriter(&cmdOutBuf)
|
cmdOutWriters = io.MultiWriter(&cmdOutBuf)
|
||||||
|
|
||||||
@ -188,6 +195,57 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
|
|||||||
cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Name, err)).Send()
|
cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Name, err)).Send()
|
||||||
return outputArr, err
|
return outputArr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if command.Type == UserCT {
|
||||||
|
|
||||||
|
if command.UserOperation == "add" {
|
||||||
|
if command.UserSshPubKeys != nil {
|
||||||
|
var (
|
||||||
|
f *os.File
|
||||||
|
err error
|
||||||
|
userHome []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
cmdCtxLogger.Info().Msg("adding SSH Keys")
|
||||||
|
|
||||||
|
localCMD := exec.Command(fmt.Sprintf("grep \"%s\" /etc/passwd | cut -d: -f6", command.Username))
|
||||||
|
userHome, err = localCMD.CombinedOutput()
|
||||||
|
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))
|
||||||
|
userSshDir := fmt.Sprintf("%s/.ssh", command.UserHome)
|
||||||
|
|
||||||
|
os.MkdirAll(userSshDir, 0700)
|
||||||
|
if _, err := os.Stat(fmt.Sprintf("%s/authorized_keys", userSshDir)); os.IsNotExist(err) {
|
||||||
|
_, err := os.Create(fmt.Sprintf("%s/authorized_keys", userSshDir))
|
||||||
|
if err != nil {
|
||||||
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error creating file %s/authorized_keys: %v", userSshDir, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err = os.OpenFile(fmt.Sprintf("%s/authorized_keys", userSshDir), 0700, os.ModeAppend)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
localCMD = exec.Command(fmt.Sprintf("chown -R %s:%s %s", command.Username, command.Username, userHome))
|
||||||
|
_, err = localCMD.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return outputArr, nil
|
return outputArr, nil
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package backy
|
package backy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -81,15 +80,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 {
|
||||||
@ -127,7 +127,7 @@ func (opts *ConfigOpts) ReadConfig() *ConfigOpts {
|
|||||||
|
|
||||||
unmarshalConfig(backyKoanf, "commands", &opts.Cmds, opts.Logger)
|
unmarshalConfig(backyKoanf, "commands", &opts.Cmds, opts.Logger)
|
||||||
|
|
||||||
validateCommandEnvironments(opts)
|
getCommandEnvironments(opts)
|
||||||
|
|
||||||
unmarshalConfig(backyKoanf, "hosts", &opts.Hosts, opts.Logger)
|
unmarshalConfig(backyKoanf, "hosts", &opts.Hosts, opts.Logger)
|
||||||
|
|
||||||
@ -245,11 +245,14 @@ 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 {
|
for cmdName, cmdConf := range opts.Cmds {
|
||||||
|
if cmdConf.Env == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
opts.Logger.Debug().Str("env file", cmdConf.Env).Str("cmd", cmdName).Send()
|
||||||
if err := testFile(cmdConf.Env); err != nil {
|
if err := testFile(cmdConf.Env); err != nil {
|
||||||
opts.Logger.Info().Str("cmd", cmdName).Err(err).Send()
|
logging.ExitWithMSG("Could not open file"+cmdConf.Env+": "+err.Error(), 1, &opts.Logger)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
expandEnvVars(opts.backyEnv, cmdConf.Environment)
|
expandEnvVars(opts.backyEnv, cmdConf.Environment)
|
||||||
}
|
}
|
||||||
@ -471,71 +474,6 @@ func (opts *ConfigOpts) setupVault() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getVaultSecret(vaultClient *vault.Client, key *VaultKey) (string, error) {
|
|
||||||
var (
|
|
||||||
secret *vault.KVSecret
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
if key.ValueType == "KVv2" {
|
|
||||||
secret, err = vaultClient.KVv2(key.MountPath).Get(context.Background(), key.Path)
|
|
||||||
} else if key.ValueType == "KVv1" {
|
|
||||||
secret, err = vaultClient.KVv1(key.MountPath).Get(context.Background(), key.Path)
|
|
||||||
} else if key.ValueType != "" {
|
|
||||||
return "", fmt.Errorf("type %s for key %s not known. Valid types are KVv1 or KVv2", key.ValueType, key.Name)
|
|
||||||
} else {
|
|
||||||
return "", fmt.Errorf("type for key %s must be specified. Valid types are KVv1 or KVv2", key.Name)
|
|
||||||
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("unable to read secret: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
value, ok := secret.Data[key.Name].(string)
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("value type assertion failed: %T %#v", secret.Data[key.Name], secret.Data[key.Name])
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, k := range keys {
|
|
||||||
if k.Name == keyName {
|
|
||||||
return k, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("key %s not found in vault keys", keyName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetVaultKey(str string, opts *ConfigOpts, log zerolog.Logger) string {
|
|
||||||
key, err := parseVaultKey(str, opts.VaultKeys)
|
|
||||||
if key == nil && err == nil {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
if err != nil && key == nil {
|
|
||||||
log.Err(err).Send()
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
value, secretErr := getVaultSecret(opts.vaultClient, key)
|
|
||||||
if secretErr != nil {
|
|
||||||
log.Err(secretErr).Send()
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
func processCmds(opts *ConfigOpts) error {
|
func processCmds(opts *ConfigOpts) error {
|
||||||
|
|
||||||
// process commands
|
// process commands
|
||||||
@ -634,8 +572,10 @@ 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" {
|
||||||
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 {
|
if cmd.Host != nil {
|
||||||
host, ok := opts.Hosts[*cmd.Host]
|
host, ok := opts.Hosts[*cmd.Host]
|
||||||
@ -645,7 +585,7 @@ func processCmds(opts *ConfigOpts) error {
|
|||||||
}
|
}
|
||||||
for indx, key := range cmd.UserSshPubKeys {
|
for indx, key := range cmd.UserSshPubKeys {
|
||||||
opts.Logger.Debug().Msg("adding SSH Keys")
|
opts.Logger.Debug().Msg("adding SSH Keys")
|
||||||
key = expandExternalConfigDirectives(key, opts)
|
key = getExternalConfigDirectiveValue(key, opts)
|
||||||
cmd.UserSshPubKeys[indx] = key
|
cmd.UserSshPubKeys[indx] = key
|
||||||
}
|
}
|
||||||
if err != nil {
|
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()
|
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))
|
||||||
|
203
pkg/backy/ssh.go
203
pkg/backy/ssh.go
@ -15,6 +15,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/kevinburke/ssh_config"
|
"github.com/kevinburke/ssh_config"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/pkg/sftp"
|
"github.com/pkg/sftp"
|
||||||
@ -23,7 +24,7 @@ import (
|
|||||||
"golang.org/x/crypto/ssh/knownhosts"
|
"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
|
var TS = strings.TrimSpace
|
||||||
|
|
||||||
// ConnectToHost connects to a host by looking up the config values in the file ~/.ssh/config
|
// ConnectToHost connects to a host by looking up the config values in the file ~/.ssh/config
|
||||||
@ -120,7 +121,6 @@ func (remoteConfig *Host) ConnectToHost(opts *ConfigOpts) error {
|
|||||||
return errors.Wrap(err, "could not create hostkeycallback function")
|
return errors.Wrap(err, "could not create hostkeycallback function")
|
||||||
}
|
}
|
||||||
remoteConfig.ClientConfig.HostKeyCallback = hostKeyCallback
|
remoteConfig.ClientConfig.HostKeyCallback = hostKeyCallback
|
||||||
// opts.Logger.Info().Str("user", remoteConfig.ClientConfig.User).Send()
|
|
||||||
|
|
||||||
remoteConfig.SshClient, connectErr = remoteConfig.ConnectThroughBastion(opts.Logger)
|
remoteConfig.SshClient, connectErr = remoteConfig.ConnectThroughBastion(opts.Logger)
|
||||||
if connectErr != nil {
|
if connectErr != nil {
|
||||||
@ -181,11 +181,7 @@ func (remoteHost *Host) GetAuthMethods(opts *ConfigOpts) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteHost.PrivateKeyPassword, err = GetPrivateKeyPassword(remoteHost.PrivateKeyPassword, opts, opts.Logger)
|
remoteHost.PrivateKeyPassword = GetPrivateKeyPassword(remoteHost.PrivateKeyPassword, opts)
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if remoteHost.PrivateKeyPassword == "" {
|
if remoteHost.PrivateKeyPassword == "" {
|
||||||
|
|
||||||
@ -208,14 +204,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))
|
remoteHost.ClientConfig.Auth = append(remoteHost.ClientConfig.Auth, ssh.Password(remoteHost.Password))
|
||||||
}
|
}
|
||||||
@ -250,14 +245,13 @@ func (remoteHost *Host) GetPrivateKeyFileFromConfig() {
|
|||||||
// If it is the port is searched in the SSH config file(s)
|
// If it is the port is searched in the SSH config file(s)
|
||||||
func (remoteHost *Host) GetPort() {
|
func (remoteHost *Host) GetPort() {
|
||||||
port := fmt.Sprintf("%d", remoteHost.Port)
|
port := fmt.Sprintf("%d", remoteHost.Port)
|
||||||
// port specifed?
|
// port specified?
|
||||||
// port will be 0 if missing from backy config
|
// port will be 0 if missing from backy config
|
||||||
if port == "0" {
|
if port == "0" {
|
||||||
port, _ = remoteHost.SSHConfigFile.SshConfigFile.Get(remoteHost.Host, "Port")
|
port, _ = remoteHost.SSHConfigFile.SshConfigFile.Get(remoteHost.Host, "Port")
|
||||||
|
|
||||||
if port == "" {
|
if port == "" {
|
||||||
|
|
||||||
// get port from default SSH config file
|
|
||||||
port = remoteHost.SSHConfigFile.DefaultUserSettings.Get(remoteHost.Host, "Port")
|
port = remoteHost.SSHConfigFile.DefaultUserSettings.Get(remoteHost.Host, "Port")
|
||||||
|
|
||||||
// set port to be default
|
// set port to be default
|
||||||
@ -272,7 +266,6 @@ func (remoteHost *Host) GetPort() {
|
|||||||
|
|
||||||
func (remoteHost *Host) CombineHostNameWithPort() {
|
func (remoteHost *Host) CombineHostNameWithPort() {
|
||||||
|
|
||||||
// if the port is already in the HostName, leave it
|
|
||||||
if strings.HasSuffix(remoteHost.HostName, fmt.Sprintf(":%d", remoteHost.Port)) {
|
if strings.HasSuffix(remoteHost.HostName, fmt.Sprintf(":%d", remoteHost.Port)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -322,74 +315,23 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPrivateKeyPassword(key string, opts *ConfigOpts, log zerolog.Logger) (string, error) {
|
func GetPrivateKeyPassword(key string, opts *ConfigOpts) string {
|
||||||
|
return getExternalConfigDirectiveValue(key, opts)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPassword gets any password
|
// GetPassword gets any password
|
||||||
func GetPassword(pass string, opts *ConfigOpts, log zerolog.Logger) (string, error) {
|
func GetPassword(pass string, opts *ConfigOpts) string {
|
||||||
|
return getExternalConfigDirectiveValue(pass, opts)
|
||||||
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 (remoteConfig *Host) GetProxyJumpFromConfig(hosts map[string]*Host) error {
|
func (remoteConfig *Host) GetProxyJumpFromConfig(hosts map[string]*Host) error {
|
||||||
@ -490,7 +432,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
|
||||||
@ -566,59 +507,85 @@ 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)
|
||||||
|
client, err := sftp.NewClient(command.RemoteHost.SshClient)
|
||||||
|
if err != nil {
|
||||||
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error creating sftp client: %v", err)
|
||||||
|
}
|
||||||
|
uuidFile := uuid.New()
|
||||||
|
passFilePath := fmt.Sprintf("/tmp/%s", uuidFile.String())
|
||||||
|
passFile, passFileErr := client.Create(passFilePath)
|
||||||
|
if passFileErr != nil {
|
||||||
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error creating file /tmp/%s: %v", uuidFile.String(), passFileErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
passFile.Write([]byte(userNamePass))
|
||||||
|
|
||||||
|
ArgsStr = fmt.Sprintf("cat %s | chpasswd", passFilePath)
|
||||||
|
defer passFile.Close()
|
||||||
|
|
||||||
|
defer client.Remove(passFilePath)
|
||||||
|
// 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
|
||||||
@ -115,7 +115,9 @@ type (
|
|||||||
|
|
||||||
UserShell string `yaml:"userShell,omitempty"`
|
UserShell string `yaml:"userShell,omitempty"`
|
||||||
|
|
||||||
SystemUser bool `yaml:"systemUser,omitempty"`
|
UserCreateHome bool `yaml:"userCreateHome,omitempty"`
|
||||||
|
|
||||||
|
UserIsSystem bool `yaml:"userIsSystem,omitempty"`
|
||||||
|
|
||||||
UserPassword string `yaml:"userPassword,omitempty"`
|
UserPassword string `yaml:"userPassword,omitempty"`
|
||||||
|
|
||||||
@ -221,6 +223,7 @@ type (
|
|||||||
|
|
||||||
VaultKey struct {
|
VaultKey struct {
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
|
Key string `yaml:"key"`
|
||||||
Path string `yaml:"path"`
|
Path string `yaml:"path"`
|
||||||
ValueType string `yaml:"type"`
|
ValueType string `yaml:"type"`
|
||||||
MountPath string `yaml:"mountpath"`
|
MountPath string `yaml:"mountpath"`
|
||||||
|
@ -6,6 +6,7 @@ package backy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@ -16,6 +17,7 @@ import (
|
|||||||
|
|
||||||
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
|
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
|
||||||
"git.andrewnw.xyz/CyberShell/backy/pkg/remotefetcher"
|
"git.andrewnw.xyz/CyberShell/backy/pkg/remotefetcher"
|
||||||
|
vault "github.com/hashicorp/vault/api"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
"github.com/knadh/koanf/v2"
|
"github.com/knadh/koanf/v2"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
@ -119,12 +121,12 @@ errEnvFile:
|
|||||||
if strings.Contains(envVal, "=") {
|
if strings.Contains(envVal, "=") {
|
||||||
envVarArr := strings.Split(envVal, "=")
|
envVarArr := strings.Split(envVal, "=")
|
||||||
|
|
||||||
process.Setenv(envVarArr[0], GetVaultKey(envVarArr[1], opts, log))
|
process.Setenv(envVarArr[0], getExternalConfigDirectiveValue(envVarArr[1], opts))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func injectEnvIntoLocalCMD(envVarsToInject environmentVars, process *exec.Cmd, log zerolog.Logger) {
|
func injectEnvIntoLocalCMD(envVarsToInject environmentVars, process *exec.Cmd, log zerolog.Logger, opts *ConfigOpts) {
|
||||||
if envVarsToInject.file != "" {
|
if envVarsToInject.file != "" {
|
||||||
envPath, _ := getFullPathWithHomeDir(envVarsToInject.file)
|
envPath, _ := getFullPathWithHomeDir(envVarsToInject.file)
|
||||||
|
|
||||||
@ -148,7 +150,8 @@ errEnvFile:
|
|||||||
|
|
||||||
for _, envVal := range envVarsToInject.env {
|
for _, envVal := range envVarsToInject.env {
|
||||||
if strings.Contains(envVal, "=") {
|
if strings.Contains(envVal, "=") {
|
||||||
process.Env = append(process.Env, envVal)
|
envVarArr := strings.Split(envVal, "=")
|
||||||
|
process.Env = append(process.Env, fmt.Sprintf("%s=%s", envVarArr[0], getExternalConfigDirectiveValue(envVarArr[1], opts)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
process.Env = append(process.Env, os.Environ()...)
|
process.Env = append(process.Env, os.Environ()...)
|
||||||
@ -181,7 +184,6 @@ func testFile(c string) error {
|
|||||||
return fileOpenErr
|
return fileOpenErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +252,6 @@ func (opts *ConfigOpts) loadEnv() {
|
|||||||
func expandEnvVars(backyEnv map[string]string, envVars []string) {
|
func expandEnvVars(backyEnv map[string]string, envVars []string) {
|
||||||
|
|
||||||
env := func(name string) string {
|
env := func(name string) string {
|
||||||
name = strings.ToUpper(name)
|
|
||||||
envVar, found := backyEnv[name]
|
envVar, found := backyEnv[name]
|
||||||
if found {
|
if found {
|
||||||
return envVar
|
return envVar
|
||||||
@ -258,16 +259,15 @@ func expandEnvVars(backyEnv map[string]string, envVars []string) {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse env variables using new macros
|
|
||||||
for indx, v := range envVars {
|
for indx, v := range envVars {
|
||||||
if strings.HasPrefix(v, externDirectiveStart) && strings.HasSuffix(v, externDirectiveEnd) {
|
|
||||||
if strings.HasPrefix(v, envExternDirectiveStart) {
|
if strings.HasPrefix(v, envExternDirectiveStart) && strings.HasSuffix(v, externDirectiveEnd) {
|
||||||
v = strings.TrimPrefix(v, envExternDirectiveStart)
|
v = strings.TrimPrefix(v, envExternDirectiveStart)
|
||||||
v = strings.TrimRight(v, externDirectiveEnd)
|
v = strings.TrimRight(v, externDirectiveEnd)
|
||||||
out, _ := shell.Expand(v, env)
|
out, _ := shell.Expand(v, env)
|
||||||
envVars[indx] = out
|
envVars[indx] = out
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,7 +295,8 @@ func getCommandTypeAndSetCommandInfo(command *Command) *Command {
|
|||||||
command.Username,
|
command.Username,
|
||||||
command.UserHome,
|
command.UserHome,
|
||||||
command.UserShell,
|
command.UserShell,
|
||||||
command.SystemUser,
|
command.UserIsSystem,
|
||||||
|
command.UserCreateHome,
|
||||||
command.UserGroups,
|
command.UserGroups,
|
||||||
command.Args)
|
command.Args)
|
||||||
case "modify":
|
case "modify":
|
||||||
@ -349,15 +350,15 @@ func parsePackageVersion(output string, cmdCtxLogger zerolog.Logger, command *Co
|
|||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, false), err
|
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)) {
|
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)
|
||||||
return os.Getenv(key)
|
key = os.Getenv(key)
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(key, externFileDirectiveStart) {
|
if strings.HasPrefix(key, externFileDirectiveStart) {
|
||||||
var err error
|
var err error
|
||||||
@ -369,6 +370,9 @@ func expandExternalConfigDirectives(key string, opts *ConfigOpts) string {
|
|||||||
opts.Logger.Err(err).Send()
|
opts.Logger.Err(err).Send()
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
if !path.IsAbs(key) {
|
||||||
|
key = path.Join(opts.ConfigDir, key)
|
||||||
|
}
|
||||||
keyValue, err = os.ReadFile(key)
|
keyValue, err = os.ReadFile(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
opts.Logger.Err(err).Send()
|
opts.Logger.Err(err).Send()
|
||||||
@ -381,6 +385,62 @@ func expandExternalConfigDirectives(key string, opts *ConfigOpts) string {
|
|||||||
key = strings.TrimSuffix(key, externDirectiveEnd)
|
key = strings.TrimSuffix(key, externDirectiveEnd)
|
||||||
key = GetVaultKey(key, opts, opts.Logger)
|
key = GetVaultKey(key, opts, opts.Logger)
|
||||||
}
|
}
|
||||||
|
println(key)
|
||||||
|
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getVaultSecret(vaultClient *vault.Client, key *VaultKey) (string, error) {
|
||||||
|
var (
|
||||||
|
secret *vault.KVSecret
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if key.ValueType == "KVv2" {
|
||||||
|
secret, err = vaultClient.KVv2(key.MountPath).Get(context.Background(), key.Path)
|
||||||
|
} else if key.ValueType == "KVv1" {
|
||||||
|
secret, err = vaultClient.KVv1(key.MountPath).Get(context.Background(), key.Path)
|
||||||
|
} else if key.ValueType != "" {
|
||||||
|
return "", fmt.Errorf("type %s for key %s not known. Valid types are KVv1 or KVv2", key.ValueType, key.Name)
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("type for key %s must be specified. Valid types are KVv1 or KVv2", key.Name)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to read secret: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
value, ok := secret.Data[key.Key].(string)
|
||||||
|
println(value)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("value type assertion failed for vault key %s: %T %#v", key.Name, secret.Data[key.Name], secret.Data[key.Name])
|
||||||
|
}
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getVaultKeyData(keyName string, keys []*VaultKey) (*VaultKey, error) {
|
||||||
|
for _, k := range keys {
|
||||||
|
if k.Name == keyName {
|
||||||
|
return k, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("key %s not found in vault keys", keyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetVaultKey(str string, opts *ConfigOpts, log zerolog.Logger) string {
|
||||||
|
key, err := getVaultKeyData(str, opts.VaultKeys)
|
||||||
|
if key == nil && err == nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
if err != nil && key == nil {
|
||||||
|
log.Err(err).Send()
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
value, secretErr := getVaultSecret(opts.vaultClient, key)
|
||||||
|
if secretErr != nil {
|
||||||
|
log.Err(secretErr).Send()
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
@ -15,7 +15,7 @@ func (l LinuxUserManager) NewLinuxManager() *LinuxUserManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddUser adds a new user to the system.
|
// AddUser adds a new user to the system.
|
||||||
func (l LinuxUserManager) AddUser(username, homeDir, shell string, isSystem bool, groups, args []string) (string, []string) {
|
func (l LinuxUserManager) AddUser(username, homeDir, shell string, isSystem, createHome bool, groups, args []string) (string, []string) {
|
||||||
baseArgs := []string{}
|
baseArgs := []string{}
|
||||||
|
|
||||||
if isSystem {
|
if isSystem {
|
||||||
@ -38,6 +38,10 @@ func (l LinuxUserManager) AddUser(username, homeDir, shell string, isSystem bool
|
|||||||
baseArgs = append(baseArgs, args...)
|
baseArgs = append(baseArgs, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if createHome {
|
||||||
|
baseArgs = append(baseArgs, "-m")
|
||||||
|
}
|
||||||
|
|
||||||
args = append(baseArgs, username)
|
args = append(baseArgs, username)
|
||||||
|
|
||||||
cmd := "useradd"
|
cmd := "useradd"
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
// UserManager defines the interface for user management operations.
|
// UserManager defines the interface for user management operations.
|
||||||
// All functions but one return a string for the command and any args.
|
// All functions but one return a string for the command and any args.
|
||||||
type UserManager interface {
|
type UserManager interface {
|
||||||
AddUser(username, homeDir, shell string, isSystem bool, groups, args []string) (string, []string)
|
AddUser(username, homeDir, shell string, createHome, isSystem bool, groups, args []string) (string, []string)
|
||||||
RemoveUser(username string) (string, []string)
|
RemoveUser(username string) (string, []string)
|
||||||
ModifyUser(username, homeDir, shell string, groups []string) (string, []string)
|
ModifyUser(username, homeDir, shell string, groups []string) (string, []string)
|
||||||
// Modify password uses chpasswd for Linux systems to build the command to change the password
|
// Modify password uses chpasswd for Linux systems to build the command to change the password
|
||||||
|
1
release
1
release
@ -1,5 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -eou pipefail
|
set -eou pipefail
|
||||||
|
go mod tidy
|
||||||
go generate ./...
|
go generate ./...
|
||||||
CURRENT_TAG="$(go run backy.go version -V)"
|
CURRENT_TAG="$(go run backy.go version -V)"
|
||||||
goreleaser -f .goreleaser/github.yml check
|
goreleaser -f .goreleaser/github.yml check
|
||||||
|
27
tests/VaultTest.yml
Normal file
27
tests/VaultTest.yml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
commands:
|
||||||
|
vaultEnvVar:
|
||||||
|
cmd: echo
|
||||||
|
shell: /bin/zsh
|
||||||
|
Args:
|
||||||
|
- ${VAULT_VAR}
|
||||||
|
environment:
|
||||||
|
"VAULT_VAR=%{vault:vaultTestSecret}%"
|
||||||
|
|
||||||
|
logging:
|
||||||
|
verbose: true
|
||||||
|
|
||||||
|
vault:
|
||||||
|
token: root
|
||||||
|
address: http://127.0.0.1:8200
|
||||||
|
enabled: true
|
||||||
|
keys:
|
||||||
|
- name: vaultTestSecret
|
||||||
|
key: data
|
||||||
|
mountpath: secret
|
||||||
|
path: test/var
|
||||||
|
type: KVv2 # KVv1 or KVv2
|
||||||
|
|
||||||
|
cmdLists:
|
||||||
|
addUsers:
|
||||||
|
order:
|
||||||
|
- vaultEnvVar
|
Reference in New Issue
Block a user