Use common funcs / fix panic / clarify docs

This commit is contained in:
jsmcnair 2020-08-12 22:34:13 +01:00
parent 8666ebceca
commit 78c280b1b7
4 changed files with 61 additions and 83 deletions

View File

@ -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")
}

View File

@ -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{})
}

View File

@ -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)
}
}

View File

@ -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.