mirror of
https://github.com/passbolt/go-passbolt-cli.git
synced 2025-05-12 10:58:21 +00:00
Merge 4634641adb
into 6033d6bbb3
This commit is contained in:
commit
056d0dfdcd
1 changed files with 98 additions and 0 deletions
98
cmd/exec.go
Normal file
98
cmd/exec.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"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"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
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("error creating client: %w", err)
|
||||
}
|
||||
|
||||
envVars, err := resolveEnvironmentSecrets(ctx, client)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error resolving secrets: %w", err)
|
||||
}
|
||||
|
||||
if err = client.Logout(ctx); err != nil {
|
||||
return fmt.Errorf("error 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("error 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("error getting resource: %w", err)
|
||||
}
|
||||
|
||||
envVars[i] = key + "=" + secret
|
||||
}
|
||||
|
||||
return envVars, nil
|
||||
}
|
Loading…
Add table
Reference in a new issue