2016-09-30 20:44:12 +02:00
|
|
|
package vault
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2017-01-30 22:42:57 +01:00
|
|
|
"io/ioutil"
|
2016-09-30 20:44:12 +02:00
|
|
|
"log"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
|
|
"github.com/hashicorp/terraform/terraform"
|
|
|
|
"github.com/hashicorp/vault/api"
|
2017-01-31 03:39:58 +01:00
|
|
|
"github.com/mitchellh/go-homedir"
|
2016-09-30 20:44:12 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
func Provider() terraform.ResourceProvider {
|
|
|
|
return &schema.Provider{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"address": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
DefaultFunc: schema.EnvDefaultFunc("VAULT_ADDR", nil),
|
|
|
|
Description: "URL of the root of the target Vault server.",
|
|
|
|
},
|
|
|
|
"token": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
2017-01-30 22:42:57 +01:00
|
|
|
DefaultFunc: schema.EnvDefaultFunc("VAULT_TOKEN", ""),
|
2016-09-30 20:44:12 +02:00
|
|
|
Description: "Token to use to authenticate to Vault.",
|
|
|
|
},
|
|
|
|
"ca_cert_file": &schema.Schema{
|
2017-01-17 13:06:55 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
DefaultFunc: schema.EnvDefaultFunc("VAULT_CACERT", ""),
|
|
|
|
Description: "Path to a CA certificate file to validate the server's certificate.",
|
2016-09-30 20:44:12 +02:00
|
|
|
},
|
|
|
|
"ca_cert_dir": &schema.Schema{
|
2017-01-17 13:06:55 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
DefaultFunc: schema.EnvDefaultFunc("VAULT_CAPATH", ""),
|
|
|
|
Description: "Path to directory containing CA certificate files to validate the server's certificate.",
|
2016-09-30 20:44:12 +02:00
|
|
|
},
|
|
|
|
"client_auth": &schema.Schema{
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Optional: true,
|
|
|
|
Description: "Client authentication credentials.",
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"cert_file": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
2017-01-17 13:06:55 +01:00
|
|
|
DefaultFunc: schema.EnvDefaultFunc("VAULT_CLIENT_CERT", ""),
|
2016-09-30 20:44:12 +02:00
|
|
|
Description: "Path to a file containing the client certificate.",
|
|
|
|
},
|
|
|
|
"key_file": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
2017-01-17 13:06:55 +01:00
|
|
|
DefaultFunc: schema.EnvDefaultFunc("VAULT_CLIENT_KEY", ""),
|
2016-09-30 20:44:12 +02:00
|
|
|
Description: "Path to a file containing the private key that the certificate was issued for.",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"skip_tls_verify": &schema.Schema{
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
2017-01-17 13:06:55 +01:00
|
|
|
DefaultFunc: schema.EnvDefaultFunc("VAULT_SKIP_VERIFY", ""),
|
2016-09-30 20:44:12 +02:00
|
|
|
Description: "Set this to true only if the target Vault server is an insecure development instance.",
|
|
|
|
},
|
|
|
|
"max_lease_ttl_seconds": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
|
|
|
|
// Default is 20min, which is intended to be enough time for
|
|
|
|
// a reasonable Terraform run can complete but not
|
|
|
|
// significantly longer, so that any leases are revoked shortly
|
|
|
|
// after Terraform has finished running.
|
|
|
|
DefaultFunc: schema.EnvDefaultFunc("TERRAFORM_VAULT_MAX_TTL", 1200),
|
|
|
|
|
|
|
|
Description: "Maximum TTL for secret leases requested by this provider",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
ConfigureFunc: providerConfigure,
|
|
|
|
|
2016-10-01 01:20:08 +02:00
|
|
|
DataSourcesMap: map[string]*schema.Resource{
|
|
|
|
"vault_generic_secret": genericSecretDataSource(),
|
|
|
|
},
|
|
|
|
|
2016-09-30 20:44:12 +02:00
|
|
|
ResourcesMap: map[string]*schema.Resource{
|
2017-05-03 21:43:10 +02:00
|
|
|
"vault_auth_backend": authBackendResource(),
|
2016-10-01 00:12:54 +02:00
|
|
|
"vault_generic_secret": genericSecretResource(),
|
2017-02-13 19:53:45 +01:00
|
|
|
"vault_policy": policyResource(),
|
2017-05-13 00:55:58 +02:00
|
|
|
"vault_mount": mountResource(),
|
2016-09-30 20:44:12 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
2017-01-30 21:24:19 +01:00
|
|
|
config := api.DefaultConfig()
|
|
|
|
config.Address = d.Get("address").(string)
|
2016-09-30 20:44:12 +02:00
|
|
|
|
|
|
|
clientAuthI := d.Get("client_auth").([]interface{})
|
|
|
|
if len(clientAuthI) > 1 {
|
|
|
|
return nil, fmt.Errorf("client_auth block may appear only once")
|
|
|
|
}
|
|
|
|
|
|
|
|
clientAuthCert := ""
|
|
|
|
clientAuthKey := ""
|
|
|
|
if len(clientAuthI) == 1 {
|
|
|
|
clientAuth := clientAuthI[0].(map[string]interface{})
|
|
|
|
clientAuthCert = clientAuth["cert_file"].(string)
|
|
|
|
clientAuthKey = clientAuth["key_file"].(string)
|
|
|
|
}
|
|
|
|
|
2017-02-03 17:07:03 +01:00
|
|
|
err := config.ConfigureTLS(&api.TLSConfig{
|
2016-09-30 20:44:12 +02:00
|
|
|
CACert: d.Get("ca_cert_file").(string),
|
|
|
|
CAPath: d.Get("ca_cert_dir").(string),
|
|
|
|
Insecure: d.Get("skip_tls_verify").(bool),
|
|
|
|
|
|
|
|
ClientCert: clientAuthCert,
|
|
|
|
ClientKey: clientAuthKey,
|
|
|
|
})
|
2017-02-03 17:07:03 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to configure TLS for Vault API: %s", err)
|
|
|
|
}
|
2016-09-30 20:44:12 +02:00
|
|
|
|
|
|
|
client, err := api.NewClient(config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to configure Vault API: %s", err)
|
|
|
|
}
|
|
|
|
|
2017-01-30 22:42:57 +01:00
|
|
|
token := d.Get("token").(string)
|
|
|
|
if token == "" {
|
|
|
|
// Use the vault CLI's token, if present.
|
2017-01-31 03:39:58 +01:00
|
|
|
homePath, err := homedir.Dir()
|
|
|
|
if err != nil {
|
2017-02-03 17:41:52 +01:00
|
|
|
return nil, fmt.Errorf("Can't find home directory when looking for ~/.vault-token: %s", err)
|
2017-01-31 03:39:58 +01:00
|
|
|
}
|
|
|
|
tokenBytes, err := ioutil.ReadFile(homePath + "/.vault-token")
|
2017-01-30 22:42:57 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("No vault token found: %s", err)
|
|
|
|
}
|
|
|
|
|
2017-01-31 03:39:58 +01:00
|
|
|
token = strings.TrimSpace(string(tokenBytes))
|
2017-01-30 22:42:57 +01:00
|
|
|
}
|
|
|
|
|
2016-09-30 20:44:12 +02:00
|
|
|
// In order to enforce our relatively-short lease TTL, we derive a
|
|
|
|
// temporary child token that inherits all of the policies of the
|
|
|
|
// token we were given but expires after max_lease_ttl_seconds.
|
|
|
|
//
|
|
|
|
// The intent here is that Terraform will need to re-fetch any
|
|
|
|
// secrets on each run and so we limit the exposure risk of secrets
|
|
|
|
// that end up stored in the Terraform state, assuming that they are
|
|
|
|
// credentials that Vault is able to revoke.
|
|
|
|
//
|
|
|
|
// Caution is still required with state files since not all secrets
|
|
|
|
// can explicitly be revoked, and this limited scope won't apply to
|
|
|
|
// any secrets that are *written* by Terraform to Vault.
|
|
|
|
|
2017-01-30 22:42:57 +01:00
|
|
|
client.SetToken(token)
|
2016-09-30 20:44:12 +02:00
|
|
|
renewable := false
|
|
|
|
childTokenLease, err := client.Auth().Token().Create(&api.TokenCreateRequest{
|
|
|
|
DisplayName: "terraform",
|
|
|
|
TTL: fmt.Sprintf("%ds", d.Get("max_lease_ttl_seconds").(int)),
|
|
|
|
ExplicitMaxTTL: fmt.Sprintf("%ds", d.Get("max_lease_ttl_seconds").(int)),
|
|
|
|
Renewable: &renewable,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to create limited child token: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
childToken := childTokenLease.Auth.ClientToken
|
|
|
|
policies := childTokenLease.Auth.Policies
|
|
|
|
|
|
|
|
log.Printf("[INFO] Using Vault token with the following policies: %s", strings.Join(policies, ", "))
|
|
|
|
|
|
|
|
client.SetToken(childToken)
|
|
|
|
|
|
|
|
return client, nil
|
|
|
|
}
|