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