Skip to content

Commit

Permalink
✨ Add push secret (#27)
Browse files Browse the repository at this point in the history
* ✨ Add push secret

* 👷 Increase job timeout

---------

Co-authored-by: DUBOIS Charles <[email protected]>
  • Loading branch information
LiskiCharlesDubois and DUBOIS Charles authored Oct 23, 2023
1 parent 9f23a91 commit 65b65b2
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 84 deletions.
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
}

0 comments on commit 65b65b2

Please sign in to comment.