From 78c280b1b7245af27acf1a0b5d918398b2a12b25 Mon Sep 17 00:00:00 2001 From: jsmcnair Date: Wed, 12 Aug 2020 22:34:13 +0100 Subject: [PATCH] Use common funcs / fix panic / clarify docs --- common/template/funcs.go | 44 +++++++++++++++++ hcl2template/function/vault.go | 47 ++----------------- template/interpolate/funcs.go | 36 +------------- .../docs/from-1.5/functions/vault/vault.mdx | 17 +++++-- 4 files changed, 61 insertions(+), 83 deletions(-) diff --git a/common/template/funcs.go b/common/template/funcs.go index bf1de23c6..f086cdf1c 100644 --- a/common/template/funcs.go +++ b/common/template/funcs.go @@ -1,8 +1,14 @@ package template import ( + "errors" + "fmt" "log" + "os" + "strings" "sync" + + vaultapi "github.com/hashicorp/vault/api" ) // DeprecatedTemplateFunc wraps a template func to warn users that it's @@ -17,3 +23,41 @@ func DeprecatedTemplateFunc(funcName, useInstead string, deprecated func(string) return deprecated(in) } } + +// Vault retrieves a secret from an HC vault KV store +func Vault(path string, key string) (string, error) { + + if token := os.Getenv("VAULT_TOKEN"); token == "" { + return "", errors.New("Must set VAULT_TOKEN env var in order to use vault template function") + } + + vaultConfig := vaultapi.DefaultConfig() + cli, err := vaultapi.NewClient(vaultConfig) + if err != nil { + return "", fmt.Errorf("Error getting Vault client: %s", err) + } + secret, err := cli.Logical().Read(path) + if err != nil { + return "", fmt.Errorf("Error reading vault secret: %s", err) + } + if secret == nil { + return "", errors.New("Vault Secret does not exist at the given path") + } + + data, ok := secret.Data["data"] + if !ok { + // maybe ths is v1, not v2 kv store + value, ok := secret.Data[key] + if ok { + return value.(string), nil + } + + // neither v1 nor v2 proudced a valid value + return "", fmt.Errorf("Vault data was empty at the given path. Warnings: %s", strings.Join(secret.Warnings, "; ")) + } + + if val, ok := data.(map[string]interface{})[key]; ok { + return val.(string), nil + } + return "", errors.New("Vault path does not contain the requested key") +} diff --git a/hcl2template/function/vault.go b/hcl2template/function/vault.go index 09a7a75bd..6bec45935 100644 --- a/hcl2template/function/vault.go +++ b/hcl2template/function/vault.go @@ -1,15 +1,10 @@ package function import ( - "errors" - "fmt" - "os" - "strings" - "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/function" - vaultapi "github.com/hashicorp/vault/api" + commontpl "github.com/hashicorp/packer/common/template" ) // VaultFunc constructs a function that retrieves KV secrets from HC vault @@ -26,47 +21,11 @@ var VaultFunc = function.New(&function.Spec{ }, Type: function.StaticReturnType(cty.String), Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - path := args[0].AsString() key := args[1].AsString() - if token := os.Getenv("VAULT_TOKEN"); token == "" { - return cty.StringVal(""), errors.New("Must set VAULT_TOKEN env var in order to " + - "use vault template function") - } + val, err := commontpl.Vault(path, key) - vaultConfig := vaultapi.DefaultConfig() - cli, err := vaultapi.NewClient(vaultConfig) - if err != nil { - return cty.StringVal(""), fmt.Errorf("Error getting Vault client: %s", err) - } - secret, err := cli.Logical().Read(path) - if err != nil { - return cty.StringVal(""), fmt.Errorf("Error reading vault secret: %s", err) - } - if secret == nil { - return cty.StringVal(""), errors.New("Vault Secret does not exist at the given path") - } - - data, ok := secret.Data["data"] - if !ok { - // maybe ths is v1, not v2 kv store - value, ok := secret.Data[key] - if ok { - return cty.StringVal(value.(string)), nil - } - - // neither v1 nor v2 proudced a valid value - return cty.StringVal(""), fmt.Errorf("Vault data was empty at the "+ - "given path. Warnings: %s", strings.Join(secret.Warnings, "; ")) - } - - value := data.(map[string]interface{})[key].(string) - return cty.StringVal(value), nil + return cty.StringVal(val), err }, }) - -// Vault returns a secret from a KV store in HC vault -func Vault() (cty.Value, error) { - return VaultFunc.Call([]cty.Value{}) -} diff --git a/template/interpolate/funcs.go b/template/interpolate/funcs.go index 7885ae0c4..6f2c8fce1 100644 --- a/template/interpolate/funcs.go +++ b/template/interpolate/funcs.go @@ -11,11 +11,11 @@ import ( "time" consulapi "github.com/hashicorp/consul/api" + commontpl "github.com/hashicorp/packer/common/template" "github.com/hashicorp/packer/common/uuid" "github.com/hashicorp/packer/helper/common" awssmapi "github.com/hashicorp/packer/template/interpolate/aws/secretsmanager" "github.com/hashicorp/packer/version" - vaultapi "github.com/hashicorp/vault/api" strftime "github.com/jehiah/go-strftime" ) @@ -289,40 +289,8 @@ func funcGenVault(ctx *Context) interface{} { // semantic checks should catch this. return "", errors.New("Vault vars are only allowed in the variables section") } - if token := os.Getenv("VAULT_TOKEN"); token == "" { - return "", errors.New("Must set VAULT_TOKEN env var in order to " + - "use vault template function") - } - // const EnvVaultAddress = "VAULT_ADDR" - // const EnvVaultToken = "VAULT_TOKEN" - vaultConfig := vaultapi.DefaultConfig() - cli, err := vaultapi.NewClient(vaultConfig) - if err != nil { - return "", fmt.Errorf("Error getting Vault client: %s", err) - } - secret, err := cli.Logical().Read(path) - if err != nil { - return "", fmt.Errorf("Error reading vault secret: %s", err) - } - if secret == nil { - return "", errors.New("Vault Secret does not exist at the given path") - } - data, ok := secret.Data["data"] - if !ok { - // maybe ths is v1, not v2 kv store - value, ok := secret.Data[key] - if ok { - return value.(string), nil - } - - // neither v1 nor v2 proudced a valid value - return "", fmt.Errorf("Vault data was empty at the "+ - "given path. Warnings: %s", strings.Join(secret.Warnings, "; ")) - } - - value := data.(map[string]interface{})[key].(string) - return value, nil + return commontpl.Vault(path, key) } } diff --git a/website/pages/docs/from-1.5/functions/vault/vault.mdx b/website/pages/docs/from-1.5/functions/vault/vault.mdx index f0d7b0908..1bf1ff1be 100644 --- a/website/pages/docs/from-1.5/functions/vault/vault.mdx +++ b/website/pages/docs/from-1.5/functions/vault/vault.mdx @@ -18,10 +18,12 @@ If you store a value in vault using `vault kv put secret/hello foo=world`, you can access it using the following: ```hcl -vault("/secret/data/hello" "foo") +locals { + foo = vault("/secret/data/hello" "foo") +} ``` -which will assign "my_secret": "world" +which will assign `local.foo` with the value "world" An example of using a v1 kv engine: @@ -33,15 +35,20 @@ If you store a value in vault using: You can access it using the following: ```hcl -vault("secrets/hello", "foo") +locals { + foo = vault("secrets/hello", "foo") +} ``` -This example accesses the Vault path `secret/data/foo` and returns the value -stored at the key `bar`, storing it as "my_secret". +This example accesses the Vault path `secret/foo` and returns the value +stored at the key `foo`, storing it as the local variable `local.foo`. In order for this to work, you must set the environment variables `VAULT_TOKEN` and `VAULT_ADDR` to valid values. +-> **NOTE:** HCL functions can be used in local variable definitions or inline +with a provisioner/post-processor. They cannot be used in global variable definitions. + The api tool we use allows for more custom configuration of the Vault client via environment variables.