github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/vault/provider.go (about) 1 package vault 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "log" 7 "strings" 8 9 "github.com/hashicorp/terraform/helper/schema" 10 "github.com/hashicorp/terraform/terraform" 11 "github.com/hashicorp/vault/api" 12 "github.com/mitchellh/go-homedir" 13 ) 14 15 func Provider() terraform.ResourceProvider { 16 return &schema.Provider{ 17 Schema: map[string]*schema.Schema{ 18 "address": &schema.Schema{ 19 Type: schema.TypeString, 20 Required: true, 21 DefaultFunc: schema.EnvDefaultFunc("VAULT_ADDR", nil), 22 Description: "URL of the root of the target Vault server.", 23 }, 24 "token": &schema.Schema{ 25 Type: schema.TypeString, 26 Required: true, 27 DefaultFunc: schema.EnvDefaultFunc("VAULT_TOKEN", ""), 28 Description: "Token to use to authenticate to Vault.", 29 }, 30 "ca_cert_file": &schema.Schema{ 31 Type: schema.TypeString, 32 Optional: true, 33 DefaultFunc: schema.EnvDefaultFunc("VAULT_CACERT", ""), 34 Description: "Path to a CA certificate file to validate the server's certificate.", 35 }, 36 "ca_cert_dir": &schema.Schema{ 37 Type: schema.TypeString, 38 Optional: true, 39 DefaultFunc: schema.EnvDefaultFunc("VAULT_CAPATH", ""), 40 Description: "Path to directory containing CA certificate files to validate the server's certificate.", 41 }, 42 "client_auth": &schema.Schema{ 43 Type: schema.TypeList, 44 Optional: true, 45 Description: "Client authentication credentials.", 46 Elem: &schema.Resource{ 47 Schema: map[string]*schema.Schema{ 48 "cert_file": &schema.Schema{ 49 Type: schema.TypeString, 50 Required: true, 51 DefaultFunc: schema.EnvDefaultFunc("VAULT_CLIENT_CERT", ""), 52 Description: "Path to a file containing the client certificate.", 53 }, 54 "key_file": &schema.Schema{ 55 Type: schema.TypeString, 56 Required: true, 57 DefaultFunc: schema.EnvDefaultFunc("VAULT_CLIENT_KEY", ""), 58 Description: "Path to a file containing the private key that the certificate was issued for.", 59 }, 60 }, 61 }, 62 }, 63 "skip_tls_verify": &schema.Schema{ 64 Type: schema.TypeBool, 65 Optional: true, 66 DefaultFunc: schema.EnvDefaultFunc("VAULT_SKIP_VERIFY", ""), 67 Description: "Set this to true only if the target Vault server is an insecure development instance.", 68 }, 69 "max_lease_ttl_seconds": &schema.Schema{ 70 Type: schema.TypeInt, 71 Optional: true, 72 73 // Default is 20min, which is intended to be enough time for 74 // a reasonable Terraform run can complete but not 75 // significantly longer, so that any leases are revoked shortly 76 // after Terraform has finished running. 77 DefaultFunc: schema.EnvDefaultFunc("TERRAFORM_VAULT_MAX_TTL", 1200), 78 79 Description: "Maximum TTL for secret leases requested by this provider", 80 }, 81 }, 82 83 ConfigureFunc: providerConfigure, 84 85 DataSourcesMap: map[string]*schema.Resource{ 86 "vault_generic_secret": genericSecretDataSource(), 87 }, 88 89 ResourcesMap: map[string]*schema.Resource{ 90 "vault_generic_secret": genericSecretResource(), 91 "vault_policy": policyResource(), 92 }, 93 } 94 } 95 96 func providerConfigure(d *schema.ResourceData) (interface{}, error) { 97 config := api.DefaultConfig() 98 config.Address = d.Get("address").(string) 99 100 clientAuthI := d.Get("client_auth").([]interface{}) 101 if len(clientAuthI) > 1 { 102 return nil, fmt.Errorf("client_auth block may appear only once") 103 } 104 105 clientAuthCert := "" 106 clientAuthKey := "" 107 if len(clientAuthI) == 1 { 108 clientAuth := clientAuthI[0].(map[string]interface{}) 109 clientAuthCert = clientAuth["cert_file"].(string) 110 clientAuthKey = clientAuth["key_file"].(string) 111 } 112 113 err := config.ConfigureTLS(&api.TLSConfig{ 114 CACert: d.Get("ca_cert_file").(string), 115 CAPath: d.Get("ca_cert_dir").(string), 116 Insecure: d.Get("skip_tls_verify").(bool), 117 118 ClientCert: clientAuthCert, 119 ClientKey: clientAuthKey, 120 }) 121 if err != nil { 122 return nil, fmt.Errorf("failed to configure TLS for Vault API: %s", err) 123 } 124 125 client, err := api.NewClient(config) 126 if err != nil { 127 return nil, fmt.Errorf("failed to configure Vault API: %s", err) 128 } 129 130 token := d.Get("token").(string) 131 if token == "" { 132 // Use the vault CLI's token, if present. 133 homePath, err := homedir.Dir() 134 if err != nil { 135 return nil, fmt.Errorf("Can't find home directory when looking for ~/.vault-token: %s", err) 136 } 137 tokenBytes, err := ioutil.ReadFile(homePath + "/.vault-token") 138 if err != nil { 139 return nil, fmt.Errorf("No vault token found: %s", err) 140 } 141 142 token = strings.TrimSpace(string(tokenBytes)) 143 } 144 145 // In order to enforce our relatively-short lease TTL, we derive a 146 // temporary child token that inherits all of the policies of the 147 // token we were given but expires after max_lease_ttl_seconds. 148 // 149 // The intent here is that Terraform will need to re-fetch any 150 // secrets on each run and so we limit the exposure risk of secrets 151 // that end up stored in the Terraform state, assuming that they are 152 // credentials that Vault is able to revoke. 153 // 154 // Caution is still required with state files since not all secrets 155 // can explicitly be revoked, and this limited scope won't apply to 156 // any secrets that are *written* by Terraform to Vault. 157 158 client.SetToken(token) 159 renewable := false 160 childTokenLease, err := client.Auth().Token().Create(&api.TokenCreateRequest{ 161 DisplayName: "terraform", 162 TTL: fmt.Sprintf("%ds", d.Get("max_lease_ttl_seconds").(int)), 163 ExplicitMaxTTL: fmt.Sprintf("%ds", d.Get("max_lease_ttl_seconds").(int)), 164 Renewable: &renewable, 165 }) 166 if err != nil { 167 return nil, fmt.Errorf("failed to create limited child token: %s", err) 168 } 169 170 childToken := childTokenLease.Auth.ClientToken 171 policies := childTokenLease.Auth.Policies 172 173 log.Printf("[INFO] Using Vault token with the following policies: %s", strings.Join(policies, ", ")) 174 175 client.SetToken(childToken) 176 177 return client, nil 178 }