github.com/nathanielks/terraform@v0.6.1-0.20170509030759-13e1a62319dc/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_auth_backend": authBackendResource(), 91 "vault_generic_secret": genericSecretResource(), 92 "vault_policy": policyResource(), 93 }, 94 } 95 } 96 97 func providerConfigure(d *schema.ResourceData) (interface{}, error) { 98 config := api.DefaultConfig() 99 config.Address = d.Get("address").(string) 100 101 clientAuthI := d.Get("client_auth").([]interface{}) 102 if len(clientAuthI) > 1 { 103 return nil, fmt.Errorf("client_auth block may appear only once") 104 } 105 106 clientAuthCert := "" 107 clientAuthKey := "" 108 if len(clientAuthI) == 1 { 109 clientAuth := clientAuthI[0].(map[string]interface{}) 110 clientAuthCert = clientAuth["cert_file"].(string) 111 clientAuthKey = clientAuth["key_file"].(string) 112 } 113 114 err := config.ConfigureTLS(&api.TLSConfig{ 115 CACert: d.Get("ca_cert_file").(string), 116 CAPath: d.Get("ca_cert_dir").(string), 117 Insecure: d.Get("skip_tls_verify").(bool), 118 119 ClientCert: clientAuthCert, 120 ClientKey: clientAuthKey, 121 }) 122 if err != nil { 123 return nil, fmt.Errorf("failed to configure TLS for Vault API: %s", err) 124 } 125 126 client, err := api.NewClient(config) 127 if err != nil { 128 return nil, fmt.Errorf("failed to configure Vault API: %s", err) 129 } 130 131 token := d.Get("token").(string) 132 if token == "" { 133 // Use the vault CLI's token, if present. 134 homePath, err := homedir.Dir() 135 if err != nil { 136 return nil, fmt.Errorf("Can't find home directory when looking for ~/.vault-token: %s", err) 137 } 138 tokenBytes, err := ioutil.ReadFile(homePath + "/.vault-token") 139 if err != nil { 140 return nil, fmt.Errorf("No vault token found: %s", err) 141 } 142 143 token = strings.TrimSpace(string(tokenBytes)) 144 } 145 146 // In order to enforce our relatively-short lease TTL, we derive a 147 // temporary child token that inherits all of the policies of the 148 // token we were given but expires after max_lease_ttl_seconds. 149 // 150 // The intent here is that Terraform will need to re-fetch any 151 // secrets on each run and so we limit the exposure risk of secrets 152 // that end up stored in the Terraform state, assuming that they are 153 // credentials that Vault is able to revoke. 154 // 155 // Caution is still required with state files since not all secrets 156 // can explicitly be revoked, and this limited scope won't apply to 157 // any secrets that are *written* by Terraform to Vault. 158 159 client.SetToken(token) 160 renewable := false 161 childTokenLease, err := client.Auth().Token().Create(&api.TokenCreateRequest{ 162 DisplayName: "terraform", 163 TTL: fmt.Sprintf("%ds", d.Get("max_lease_ttl_seconds").(int)), 164 ExplicitMaxTTL: fmt.Sprintf("%ds", d.Get("max_lease_ttl_seconds").(int)), 165 Renewable: &renewable, 166 }) 167 if err != nil { 168 return nil, fmt.Errorf("failed to create limited child token: %s", err) 169 } 170 171 childToken := childTokenLease.Auth.ClientToken 172 policies := childTokenLease.Auth.Policies 173 174 log.Printf("[INFO] Using Vault token with the following policies: %s", strings.Join(policies, ", ")) 175 176 client.SetToken(childToken) 177 178 return client, nil 179 }