terraform/builtin/providers/vault/provider.go

180 lines
5.8 KiB
Go

package vault
import (
"fmt"
"io/ioutil"
"log"
"strings"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
"github.com/hashicorp/vault/api"
"github.com/mitchellh/go-homedir"
)
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,
DefaultFunc: schema.EnvDefaultFunc("VAULT_TOKEN", ""),
Description: "Token to use to authenticate to Vault.",
},
"ca_cert_file": &schema.Schema{
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("VAULT_CACERT", ""),
Description: "Path to a CA certificate file to validate the server's certificate.",
},
"ca_cert_dir": &schema.Schema{
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("VAULT_CAPATH", ""),
Description: "Path to directory containing CA certificate files to validate the server's certificate.",
},
"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,
DefaultFunc: schema.EnvDefaultFunc("VAULT_CLIENT_CERT", ""),
Description: "Path to a file containing the client certificate.",
},
"key_file": &schema.Schema{
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("VAULT_CLIENT_KEY", ""),
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,
DefaultFunc: schema.EnvDefaultFunc("VAULT_SKIP_VERIFY", ""),
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,
DataSourcesMap: map[string]*schema.Resource{
"vault_generic_secret": genericSecretDataSource(),
},
ResourcesMap: map[string]*schema.Resource{
"vault_auth_backend": authBackendResource(),
"vault_generic_secret": genericSecretResource(),
"vault_policy": policyResource(),
},
}
}
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
config := api.DefaultConfig()
config.Address = d.Get("address").(string)
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)
}
err := config.ConfigureTLS(&api.TLSConfig{
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,
})
if err != nil {
return nil, fmt.Errorf("failed to configure TLS for Vault API: %s", err)
}
client, err := api.NewClient(config)
if err != nil {
return nil, fmt.Errorf("failed to configure Vault API: %s", err)
}
token := d.Get("token").(string)
if token == "" {
// Use the vault CLI's token, if present.
homePath, err := homedir.Dir()
if err != nil {
return nil, fmt.Errorf("Can't find home directory when looking for ~/.vault-token: %s", err)
}
tokenBytes, err := ioutil.ReadFile(homePath + "/.vault-token")
if err != nil {
return nil, fmt.Errorf("No vault token found: %s", err)
}
token = strings.TrimSpace(string(tokenBytes))
}
// 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.
client.SetToken(token)
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
}