mirror of
https://github.com/passbolt/go-passbolt-cli.git
synced 2025-05-11 02:28:22 +00:00
Feature: adding support for exposing secrets to subprocesses (#68)
Some checks failed
Go / build (push) Has been cancelled
Some checks failed
Go / build (push) Has been cancelled
* adding support for exposing secrets to subprocesses * updating readme * wip 3 * wip 4 * wip 56
This commit is contained in:
parent
6033d6bbb3
commit
1183813dcb
2 changed files with 116 additions and 0 deletions
13
README.md
13
README.md
|
@ -105,6 +105,19 @@ For Scripting we have a -j or --json flag to convert the Output for the create,
|
||||||
|
|
||||||
Note: The JSON Output does not cover Error Messages, you can detect Errors by checking if the Exitcode is not 0
|
Note: The JSON Output does not cover Error Messages, you can detect Errors by checking if the Exitcode is not 0
|
||||||
|
|
||||||
|
# Exposing Secrets to Subprocesses
|
||||||
|
The `exec` command allows you to execute another command with environment variables that reference secrets stored in Passbolt.
|
||||||
|
Any environment variables containing `passbolt://` references are automatically resolved to their corresponding secret values
|
||||||
|
before the specified command is executed. This ensures that secrets are securely injected into the child process's environment
|
||||||
|
without exposing them to the parent shell.
|
||||||
|
For example:
|
||||||
|
```bash
|
||||||
|
export GITHUB_TOKEN=passbolt://<PASSBOLT_RESOURCE_ID_HERE>
|
||||||
|
passbolt exec -- gh auth login
|
||||||
|
```
|
||||||
|
|
||||||
|
This would resolve the passbolt:// reference in GITHUB_TOKEN to its actual secret value and pass it to the gh process.
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
Usage for all Subcommands is [here](https://github.com/passbolt/go-passbolt-cli/wiki/passbolt).
|
Usage for all Subcommands is [here](https://github.com/passbolt/go-passbolt-cli/wiki/passbolt).
|
||||||
And is also available via `man passbolt`
|
And is also available via `man passbolt`
|
||||||
|
|
103
cmd/exec.go
Normal file
103
cmd/exec.go
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/passbolt/go-passbolt-cli/util"
|
||||||
|
"github.com/passbolt/go-passbolt/api"
|
||||||
|
"github.com/passbolt/go-passbolt/helper"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
const PassboltPrefix = "passbolt://"
|
||||||
|
|
||||||
|
// execCmd represents the exec command
|
||||||
|
var execCmd = &cobra.Command{
|
||||||
|
Use: "exec -- command [args...]",
|
||||||
|
Short: "Run a command with secrets injected into the environment.",
|
||||||
|
Long: `The command allows you to execute another command with environment variables that reference secrets stored in Passbolt.
|
||||||
|
Any environment variables containing passbolt:// references are automatically resolved to their corresponding secret values
|
||||||
|
before the specified command is executed. This ensures that secrets are securely injected into the child process's environment
|
||||||
|
without exposing them to the parent shell.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
export GITHUB_TOKEN=passbolt://<PASSBOLT_RESOURCE_ID_HERE>
|
||||||
|
passbolt exec -- gh auth login
|
||||||
|
|
||||||
|
This would resolve the passbolt:// reference in GITHUB_TOKEN to its actual secret value and pass it to the gh process.
|
||||||
|
`,
|
||||||
|
Args: cobra.MinimumNArgs(1),
|
||||||
|
RunE: execAction,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(execCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func execAction(_ *cobra.Command, args []string) error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), viper.GetDuration("timeout"))
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
client, err := util.GetClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Creating client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
envVars, err := resolveEnvironmentSecrets(ctx, client)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Resolving secrets: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = client.Logout(ctx); err != nil {
|
||||||
|
return fmt.Errorf("Logging out client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
subCmd := exec.Command(args[0], args[1:]...)
|
||||||
|
subCmd.Stdin = os.Stdin
|
||||||
|
subCmd.Stdout = os.Stdout
|
||||||
|
subCmd.Stderr = os.Stderr
|
||||||
|
subCmd.Env = envVars
|
||||||
|
|
||||||
|
if err = subCmd.Run(); err != nil {
|
||||||
|
return fmt.Errorf("Running command: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveEnvironmentSecrets(ctx context.Context, client *api.Client) ([]string, error) {
|
||||||
|
envVars := os.Environ()
|
||||||
|
|
||||||
|
for i, envVar := range envVars {
|
||||||
|
splitIndex := strings.Index(envVar, "=")
|
||||||
|
if splitIndex == -1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
key := envVar[:splitIndex]
|
||||||
|
value := envVar[splitIndex+1:]
|
||||||
|
|
||||||
|
if !strings.HasPrefix(value, PassboltPrefix) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceId := strings.TrimPrefix(value, PassboltPrefix)
|
||||||
|
_, _, _, _, secret, _, err := helper.GetResource(ctx, client, resourceId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Getting resource: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
envVars[i] = key + "=" + secret
|
||||||
|
|
||||||
|
if viper.GetBool("debug") {
|
||||||
|
fmt.Fprintf(os.Stdout, "%v env var populated with resource id %v\n", key, resourceId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return envVars, nil
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue