Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions vault/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,30 @@ func Provider() terraform.ResourceProvider {
DefaultFunc: schema.EnvDefaultFunc("VAULT_CAPATH", ""),
Description: "Path to directory containing CA certificate files to validate the server's certificate.",
},
"auth_login": {
Comment thread
tyrannosaurus-becks marked this conversation as resolved.
Type: schema.TypeList,
Optional: true,
Description: "Login to vault with an existing auth method using auth/<mount>/login",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"path": {
Type: schema.TypeString,
Required: true,
},
"namespace": {
Comment thread
tyrannosaurus-becks marked this conversation as resolved.
Type: schema.TypeString,
Optional: true,
},
"parameters": {
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
},
"client_auth": {
Type: schema.TypeList,
Optional: true,
Expand Down Expand Up @@ -560,6 +584,29 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
if err != nil {
return nil, err
}

// Attempt to use auth/<mount>login if 'auth_login' is provided in provider config
authLoginI := d.Get("auth_login").([]interface{})
if len(authLoginI) > 1 {
return "", fmt.Errorf("auth_login block may appear only once")
}

if len(authLoginI) == 1 {
authLogin := authLoginI[0].(map[string]interface{})
authLoginPath := authLogin["path"].(string)
authLoginNamespace := ""
if authLoginNamespaceI, ok := authLogin["namespace"]; ok {
authLoginNamespace = authLoginNamespaceI.(string)
client.SetNamespace(authLoginNamespace)
}
authLoginParameters := authLogin["parameters"].(map[string]interface{})

secret, err := client.Logical().Write(authLoginPath, authLoginParameters)
if err != nil {
return nil, err
}
token = secret.Auth.ClientToken
}
if token != "" {
client.SetToken(token)
}
Expand Down
102 changes: 101 additions & 1 deletion vault/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
"github.com/hashicorp/vault/command/config"
homedir "github.com/mitchellh/go-homedir"
"github.com/mitchellh/go-homedir"
)

// How to run the acceptance tests for this provider:
Expand Down Expand Up @@ -121,6 +121,30 @@ const tokenHelperScript = `
echo "helper-token"
`

func TestAccAuthLoginProviderConfigure(t *testing.T) {
rootProvider := Provider().(*schema.Provider)
rootProviderResource := &schema.Resource{
Schema: rootProvider.Schema,
}
rootProviderData := rootProviderResource.TestResourceData()
if _, err := providerConfigure(rootProviderData); err != nil {
t.Fatal(err)
}

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: map[string]terraform.ResourceProvider{
"vault": rootProvider,
},
Steps: []resource.TestStep{
{
Config: testResourceApproleConfig_basic(),
Check: testResourceApproleLoginCheckAttrs(t),
},
},
})
}

func TestAccNamespaceProviderConfigure(t *testing.T) {
isEnterprise := os.Getenv("TF_ACC_ENTERPRISE")
if isEnterprise == "" {
Expand Down Expand Up @@ -158,6 +182,7 @@ func TestAccNamespaceProviderConfigure(t *testing.T) {
}
nsProviderData := nsProviderResource.TestResourceData()
nsProviderData.Set("namespace", namespacePath)
nsProviderData.Set("token", os.Getenv("VAULT_TOKEN"))
if _, err := providerConfigure(nsProviderData); err != nil {
t.Fatal(err)
}
Expand All @@ -178,6 +203,81 @@ func TestAccNamespaceProviderConfigure(t *testing.T) {

}

func testResourceApproleConfig_basic() string {
return `
resource "vault_auth_backend" "approle" {
type = "approle"
path = "approle"
}

resource "vault_policy" "admin" {
name = "admin"
policy = <<EOT
path "*" { capabilities = ["create", "read", "update", "delete", "list", "sudo"] }
EOT
}

resource "vault_approle_auth_backend_role" "admin" {
backend = vault_auth_backend.approle.path
role_name = "admin"
policies = [vault_policy.admin.name]
}

resource "vault_approle_auth_backend_role_secret_id" "admin" {
backend = vault_auth_backend.approle.path
role_name = vault_approle_auth_backend_role.admin.role_name
}
`
}

func testResourceApproleLoginCheckAttrs(t *testing.T) resource.TestCheckFunc {
return func(s *terraform.State) error {
resourceState := s.Modules[0].Resources["vault_approle_auth_backend_role_secret_id.admin"]
if resourceState == nil {
return fmt.Errorf("approle secret id resource not found in state")
}

roleResourceState := s.Modules[0].Resources["vault_approle_auth_backend_role.admin"]
if roleResourceState == nil {
return fmt.Errorf("approle role resource not found in state")
}

backendResourceState := s.Modules[0].Resources["vault_auth_backend.approle"]
if backendResourceState == nil {
return fmt.Errorf("approle mount resource not found in state")
}

instanceState := resourceState.Primary
if instanceState == nil {
return fmt.Errorf("approle secret id resource has no primary instance")
}

roleId := roleResourceState.Primary.Attributes["role_id"]
secretId := instanceState.Attributes["secret_id"]

authLoginData := []map[string]interface{}{
{
"path": "auth/approle/login",
"parameters": map[string]interface{}{
"role_id": roleId,
"secret_id": secretId,
},
},
}
approleProvider := Provider().(*schema.Provider)
approleProviderResource := &schema.Resource{
Schema: approleProvider.Schema,
}
approleProviderData := approleProviderResource.TestResourceData()
approleProviderData.Set("auth_login", authLoginData)
_, err := providerConfigure(approleProviderData)
if err != nil {
t.Fatal(err)
}
return nil
}
}

func testResourceAdminPeriodicOrphanTokenConfig_basic() string {
return `
resource "vault_policy" "test" {
Expand Down
53 changes: 53 additions & 0 deletions website/docs/index.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ variables in order to keep credential information out of the configuration.
contains one or more certificate files that will be used to validate
the certificate presented by the Vault server. May be set via the
`VAULT_CAPATH` environment variable.

* `auth_login` - (Optional) A configuration block, described below, that
attempts to authenticate using the `auth/<method>/login` path to
aquire a token which Terraform will use. Terraform still issues itself
a limited child token using auth/token/create in order to enforce a short
TTL and limit exposure.

* `client_auth` - (Optional) A configuration block, described below, that
provides credentials used by Terraform to authenticate with the Vault
Expand All @@ -132,6 +138,20 @@ variables in order to keep credential information out of the configuration.

* `namespace` - (Optional) Set the namespace to use. May be set via the
`VAULT_NAMESPACE` environment variable. *Available only for Vault Enterprise*.

The `auth_login` configuration block accepts the following arguments:

* `path` - (Required) The login path of the auth backend. For example, login with
approle by setting this path to `auth/approle/login`. Additionally, some mounts use parameters
in the URL, like with `userpass`: `auth/userpass/login/:username`.

* `namespace` - (Optional) The path to the namespace that has the mounted auth method.
This defaults to the root namespace. Cannot contain any leading or trailing slashes.
*Available only for Vault Enterprise*

* `parameters` - (Optional) A map of key-value parameters to send when authenticating
against the auth backend. Refer to [Vault API documentation](https://www.vaultproject.io/api/auth/index.html) for a particular auth method
to see what can go here.

The `client_auth` configuration block accepts the following arguments:

Expand Down Expand Up @@ -165,3 +185,36 @@ resource "vault_generic_secret" "example" {
EOT
}
```

### Example `auth_login` Usage
With the `userpass` backend:
```hcl-terraform
variable login_username {}
variable login_password {}

provider "vault" {
auth_login {
path = "auth/userpass/login/${var.login_username}"

parameters = {
password = var.login_password
}
}
}
```
Or, using approle:
```hcl-terraform
variable login_approle_role_id {}
variable login_approle_secret_id {}

provider "vault" {
auth_login {
path = "auth/approle/login"

parameters = {
role_id = var.login_approle_role_id
secret_id = var.login_approle_secret_id
}
}
}
```