Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add push secret #27

Merged
merged 2 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:

- name: Run Golang CI Lint
uses: golangci/golangci-lint-action@v2
timeout-minutes: 5

- name: Build
run: CGO_ENABLED=0 go build -ldflags="-w -s" -o gogci
Expand Down
25 changes: 4 additions & 21 deletions cmd/vault_aws_sts.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ package cmd

import (
"fmt"
"io/ioutil"
"os"

vault "github.com/hashicorp/vault/api"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
Expand Down Expand Up @@ -43,23 +39,10 @@ var vaultAwsStsCmd = &cobra.Command{
}

// Read vault token from env
token := os.Getenv("VAULT_TOKEN")

if token == "" {
// Read vault token on disk
tokenPath, err := homedir.Expand("~/.vault-token")
if err != nil {
ErrorToEval(fmt.Errorf("failed to construct vault token path: %w", err))
return
}

tokenFile, err := ioutil.ReadFile(tokenPath)
if err != nil {
ErrorToEval(fmt.Errorf("failed to read token: %w", err))
return
}

token = string(tokenFile)
token, err := getVaultToken()
if err != nil {
ErrorToEval(fmt.Errorf("failed to get token: %s", err))
return
}

// Set token to Vault client
Expand Down
72 changes: 9 additions & 63 deletions cmd/vault_env.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
package cmd

import (
"bytes"
"fmt"
"io/ioutil"
"os"
"text/template"

"github.com/Masterminds/sprig/v3"
vault "github.com/hashicorp/vault/api"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
Expand Down Expand Up @@ -49,76 +42,29 @@ var vaultEnvCmd = &cobra.Command{
}

// Read vault token from env
token := os.Getenv("VAULT_TOKEN")

if token == "" {
// Read vault token on disk
tokenPath, err := homedir.Expand("~/.vault-token")
if err != nil {
ErrorToEval(fmt.Errorf("failed to construct vault token path: %s", err))
return
}

tokenFile, err := ioutil.ReadFile(tokenPath)
if err != nil {
ErrorToEval(fmt.Errorf("failed to read token: %s", err))
return
}

token = string(tokenFile)
}

// Set token to Vault client
vc.SetToken(string(token))

// Template prefix
prefixTmpl, err := template.New("prefix").Funcs(sprig.TxtFuncMap()).Parse(viper.GetString("vault-secret-prefix"))
token, err := getVaultToken()
if err != nil {
ErrorToEval(fmt.Errorf("failed to create prefix template: %s", err))
return
}

var prefix bytes.Buffer
if err := prefixTmpl.Execute(&prefix, nil); err != nil {
ErrorToEval(fmt.Errorf("failed to execute prefix template: %s", err))
ErrorToEval(fmt.Errorf("failed to get token: %s", err))
return
}
// Set token to Vault client
vc.SetToken(string(token))

// Template secret
secretPathTmpl, err := template.New("secret").Funcs(sprig.TxtFuncMap()).Parse(viper.GetString("vault-secret"))
secretPath, err := getSecretPath()
if err != nil {
ErrorToEval(fmt.Errorf("failed to create secretPath template: %s", err))
return
}

var secretPath bytes.Buffer
if err := secretPathTmpl.Execute(&secretPath, nil); err != nil {
ErrorToEval(fmt.Errorf("failed to execute secretPath template: %s", err))
ErrorToEval(fmt.Errorf("failed to get secret path: %s", err))
return
}

// Get Vault secret
secret, err := vc.Logical().Read(fmt.Sprintf("%s/%s", prefix.String(), secretPath.String()))
// Get Vault secret data
data, err := getSecretData(vc, secretPath)
if err != nil {
ErrorToEval(fmt.Errorf("failed to get secret from Vault: %s", err))
return
}

// Check if secret exists
if secret == nil {
ErrorToEval(fmt.Errorf("no secret found at path %s/%s", prefix.String(), secretPath.String()))
return
}

// Check if data entry exists
data, ok := secret.Data["data"]
if !ok {
ErrorToEval(fmt.Errorf("no data found at path %s/%s", prefix.String(), secretPath.String()))
return
}

// Export keys as env vars
for key, value := range data.(map[string]interface{}) {
for key, value := range data {
envName, err := convertToEnvName(key)
if err != nil {
ErrorToEval(fmt.Errorf("failed to calculate env var name from vault secret: %s", err))
Expand Down
98 changes: 98 additions & 0 deletions cmd/vault_push_var.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package cmd

import (
"fmt"
vault "github.com/hashicorp/vault/api"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var vaultPushEnv = &cobra.Command{
Use: "push-secret",
Short: "Push secret to vault",
PreRun: func(cmd *cobra.Command, args []string) {

for _, flag := range []string{"vault-addr", "vault-secret", "vault-secret-prefix", "vault-push-secret-key", "vault-push-secret-value"} {

// Bind viper to flag
err := viper.BindPFlag(flag, cmd.Flags().Lookup(flag))
if err != nil {
ErrorToEval(fmt.Errorf("failed to bind flag %s to viper: %s", flag, err))
return
}
}

for _, flag := range []string{"vault-addr", "vault-secret", "vault-push-secret-key", "vault-push-secret-value"} {

// Check flag has a value
if viper.GetString(flag) == "" {
ErrorToEval(fmt.Errorf("flag %s must be defined", flag))
return
}
}
},
Run: func(cmd *cobra.Command, args []string) {

// Create vault client
vc, err := vault.NewClient(&vault.Config{Address: viper.GetString("vault-addr")})
if err != nil {
ErrorToEval(fmt.Errorf("failed to create vault client: %s", err))
return
}

// Read vault token from env

// Read vault token from env
token, err := getVaultToken()
if err != nil {
ErrorToEval(fmt.Errorf("failed to get token: %s", err))
return
}
// Set token to Vault client
vc.SetToken(string(token))

secretPath, err := getSecretPath()
if err != nil {
ErrorToEval(fmt.Errorf("failed to get secret path: %s", err))
return
}

// Get Vault secret data
data, err := getSecretData(vc, secretPath)
if err != nil {
ErrorToEval(fmt.Errorf("failed to get secret from Vault: %s", err))
return
}

key := viper.GetString("vault-push-secret-key")
if key == "" {
ErrorToEval(fmt.Errorf("failed to get key to push: %s", err))
return
}
value := viper.GetString("vault-push-secret-value")
if value == "" {
ErrorToEval(fmt.Errorf("failed to get value to push: %s", err))
return
}

//merge entries
data[key] = value

// Get Vault secret
_, err = vc.Logical().Write(secretPath, map[string]interface{}{"data": data})
if err != nil {
ErrorToEval(fmt.Errorf("failed to push secret to Vault: %s", err))
return
}
},
}

func init() {
vaultPushEnv.Flags().String("vault-addr", "", "Vault server address [GOGCI_VAULT_ADDR]")
vaultPushEnv.Flags().String("vault-secret", "", "Vault secret path [GOGCI_VAULT_SECRET]")
vaultPushEnv.Flags().String("vault-secret-prefix", "", "Vault secret path prefix [GOGCI_VAULT_SECRET_PREFIX]")
vaultPushEnv.Flags().String("vault-push-secret-key", "", "Key of the secret to push [GOGCI_VAULT_PUSH_SECRET_KEY]")
vaultPushEnv.Flags().String("vault-push-secret-value", "", "Value of the secret to push [GOGCI_VAULT_PUSH_SECRET_VALUE]")

vaultCmd.AddCommand(vaultPushEnv)
}
82 changes: 82 additions & 0 deletions cmd/vault_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package cmd

import (
"bytes"
"fmt"
"github.com/Masterminds/sprig/v3"
"github.com/hashicorp/vault/api"
"github.com/mitchellh/go-homedir"
"github.com/spf13/viper"
"io/ioutil"
"os"
"text/template"
)

func getVaultToken() (string, error) {

token := os.Getenv("VAULT_TOKEN")

if token == "" {
// Read vault token on disk
tokenPath, err := homedir.Expand("~/.vault-token")
if err != nil {
return "", fmt.Errorf("failed to construct vault token path: %s", err)
}

tokenFile, err := ioutil.ReadFile(tokenPath)
if err != nil {
return "", fmt.Errorf("failed to read token: %s", err)
}

token = string(tokenFile)
}

return token, nil
}

func getSecretPath() (string, error) {
// Template prefix
prefixTmpl, err := template.New("prefix").Funcs(sprig.TxtFuncMap()).Parse(viper.GetString("vault-secret-prefix"))
if err != nil {
return "", fmt.Errorf("failed to create prefix template: %s", err)
}

var prefix bytes.Buffer
if err := prefixTmpl.Execute(&prefix, nil); err != nil {
return "", fmt.Errorf("failed to execute prefix template: %s", err)
}

// Template secret
secretPathTmpl, err := template.New("secret").Funcs(sprig.TxtFuncMap()).Parse(viper.GetString("vault-secret"))
if err != nil {
return "", fmt.Errorf("failed to create secretPath template: %s", err)
}

var secretPath bytes.Buffer
if err := secretPathTmpl.Execute(&secretPath, nil); err != nil {
return "", fmt.Errorf("failed to execute secretPath template: %s", err)
}

return fmt.Sprintf("%s/%s", prefix.String(), secretPath.String()), nil
}

func getSecretData(vc *api.Client, secretPath string) (map[string]interface{}, error) {
// Get Vault secret
secret, err := vc.Logical().Read(secretPath)
if err != nil {
return nil, fmt.Errorf("failed to get secret from Vault: %s", err)
}

// Check if secret exists
if secret == nil {
return nil, fmt.Errorf("no secret found at path %s", secretPath)
}

// Check if data entry exists
data, ok := secret.Data["data"].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("no data found at path %s", secretPath)
}

return data, nil
}
Loading