open-vault/vault/token_store_ext_test.go
Jeff Mitchell d144f2935e Two-pronged fix for renew policy checking (#4960)
1) In backends, ensure they are now using TokenPolicies
2) Don't reassign auth.Policies until after expmgr registration as we
don't need them at that point

Fixes #4829
2018-07-24 12:03:11 -07:00

576 lines
15 KiB
Go

package vault_test
import (
"encoding/base64"
"reflect"
"sort"
"strings"
"testing"
"time"
"github.com/hashicorp/vault/api"
credLdap "github.com/hashicorp/vault/builtin/credential/ldap"
credUserpass "github.com/hashicorp/vault/builtin/credential/userpass"
"github.com/hashicorp/vault/helper/jsonutil"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/vault"
)
func TestTokenStore_TokenInvalidEntityID(t *testing.T) {
coreConfig := &vault.CoreConfig{
CredentialBackends: map[string]logical.Factory{
"userpass": credUserpass.Factory,
},
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()
core := cluster.Cores[0].Core
vault.TestWaitActive(t, core)
client := cluster.Cores[0].Client
// Enable userpass auth
err := client.Sys().EnableAuthWithOptions("userpass", &api.EnableAuthOptions{
Type: "userpass",
})
if err != nil {
t.Fatal(err)
}
// Add a user to userpass backend
_, err = client.Logical().Write("auth/userpass/users/testuser", map[string]interface{}{
"password": "testpassword",
})
if err != nil {
t.Fatal(err)
}
secret, err := client.Logical().Write("auth/userpass/login/testuser", map[string]interface{}{
"password": "testpassword",
})
if err != nil {
t.Fatal(err)
}
clientToken := secret.Auth.ClientToken
secret, err = client.Logical().Write("auth/token/lookup", map[string]interface{}{
"token": clientToken,
})
if err != nil {
t.Fatal(err)
}
entityID := secret.Data["entity_id"].(string)
_, err = client.Logical().Delete("identity/entity/id/" + entityID)
if err != nil {
t.Fatal(err)
}
client.SetToken(clientToken)
secret, err = client.Logical().Write("auth/token/lookup-self", nil)
if err == nil {
t.Fatalf("expected error due to token being invalid when its entity is invalid")
}
}
func TestTokenStore_IdentityPolicies(t *testing.T) {
coreConfig := &vault.CoreConfig{
CredentialBackends: map[string]logical.Factory{
"ldap": credLdap.Factory,
},
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()
core := cluster.Cores[0].Core
vault.TestWaitActive(t, core)
client := cluster.Cores[0].Client
// Enable LDAP auth
err := client.Sys().EnableAuthWithOptions("ldap", &api.EnableAuthOptions{
Type: "ldap",
})
if err != nil {
t.Fatal(err)
}
// Configure LDAP auth
_, err = client.Logical().Write("auth/ldap/config", map[string]interface{}{
"url": "ldap://ldap.forumsys.com",
"userattr": "uid",
"userdn": "dc=example,dc=com",
"groupdn": "dc=example,dc=com",
"binddn": "cn=read-only-admin,dc=example,dc=com",
})
if err != nil {
t.Fatal(err)
}
// Create group in LDAP auth
_, err = client.Logical().Write("auth/ldap/groups/testgroup1", map[string]interface{}{
"policies": "testgroup1-policy",
})
if err != nil {
t.Fatal(err)
}
// Create user in LDAP auth
_, err = client.Logical().Write("auth/ldap/users/tesla", map[string]interface{}{
"policies": "default",
"groups": "testgroup1",
})
if err != nil {
t.Fatal(err)
}
// Login using LDAP
secret, err := client.Logical().Write("auth/ldap/login/tesla", map[string]interface{}{
"password": "password",
})
if err != nil {
t.Fatal(err)
}
ldapClientToken := secret.Auth.ClientToken
expectedPolicies := []string{
"default",
"testgroup1-policy",
}
if !reflect.DeepEqual(expectedPolicies, secret.Auth.Policies) {
t.Fatalf("bad: identity policies; expected: %#v\nactual: %#v", expectedPolicies, secret.Auth.Policies)
}
// At this point there shouldn't be any identity policy on the token
secret, err = client.Logical().Read("auth/token/lookup/" + ldapClientToken)
if err != nil {
t.Fatal(err)
}
_, ok := secret.Data["identity_policies"]
if ok {
t.Fatalf("identity_policies should not have been set")
}
// Extract the entity ID of the token and set some policies on the entity
entityID := secret.Data["entity_id"].(string)
_, err = client.Logical().Write("identity/entity/id/"+entityID, map[string]interface{}{
"policies": []string{
"entity_policy_1",
"entity_policy_2",
},
})
if err != nil {
t.Fatal(err)
}
// Lookup the token and expect entity policies on the token
secret, err = client.Logical().Read("auth/token/lookup/" + ldapClientToken)
if err != nil {
t.Fatal(err)
}
identityPolicies := secret.Data["identity_policies"].([]interface{})
var actualPolicies []string
for _, item := range identityPolicies {
actualPolicies = append(actualPolicies, item.(string))
}
sort.Strings(actualPolicies)
expectedPolicies = []string{
"entity_policy_1",
"entity_policy_2",
}
sort.Strings(expectedPolicies)
if !reflect.DeepEqual(expectedPolicies, actualPolicies) {
t.Fatalf("bad: identity policies; expected: %#v\nactual: %#v", expectedPolicies, actualPolicies)
}
// Create identity group and add entity as its member
secret, err = client.Logical().Write("identity/group", map[string]interface{}{
"policies": []string{
"group_policy_1",
"group_policy_2",
},
"member_entity_ids": []string{
entityID,
},
})
if err != nil {
t.Fatal(err)
}
// Lookup token and expect both entity and group policies on the token
secret, err = client.Logical().Read("auth/token/lookup/" + ldapClientToken)
if err != nil {
t.Fatal(err)
}
identityPolicies = secret.Data["identity_policies"].([]interface{})
actualPolicies = nil
for _, item := range identityPolicies {
actualPolicies = append(actualPolicies, item.(string))
}
sort.Strings(actualPolicies)
expectedPolicies = []string{
"entity_policy_1",
"entity_policy_2",
"group_policy_1",
"group_policy_2",
}
sort.Strings(expectedPolicies)
if !reflect.DeepEqual(expectedPolicies, actualPolicies) {
t.Fatalf("bad: identity policies; expected: %#v\nactual: %#v", expectedPolicies, actualPolicies)
}
// Create an external group and renew the token. This should add external
// group policies to the token.
auths, err := client.Sys().ListAuth()
if err != nil {
t.Fatal(err)
}
ldapMountAccessor1 := auths["ldap/"].Accessor
// Create an external group
secret, err = client.Logical().Write("identity/group", map[string]interface{}{
"type": "external",
"policies": []string{
"external_group_policy_1",
"external_group_policy_2",
},
})
if err != nil {
t.Fatal(err)
}
ldapExtGroupID1 := secret.Data["id"].(string)
// Associate a group from LDAP auth as a group-alias in the external group
_, err = client.Logical().Write("identity/group-alias", map[string]interface{}{
"name": "testgroup1",
"mount_accessor": ldapMountAccessor1,
"canonical_id": ldapExtGroupID1,
})
if err != nil {
t.Fatal(err)
}
// Renew token to refresh external group memberships
secret, err = client.Auth().Token().Renew(ldapClientToken, 10)
if err != nil {
t.Fatal(err)
}
// Lookup token and expect entity, group and external group policies on the
// token
secret, err = client.Logical().Read("auth/token/lookup/" + ldapClientToken)
if err != nil {
t.Fatal(err)
}
identityPolicies = secret.Data["identity_policies"].([]interface{})
actualPolicies = nil
for _, item := range identityPolicies {
actualPolicies = append(actualPolicies, item.(string))
}
sort.Strings(actualPolicies)
expectedPolicies = []string{
"entity_policy_1",
"entity_policy_2",
"group_policy_1",
"group_policy_2",
"external_group_policy_1",
"external_group_policy_2",
}
sort.Strings(expectedPolicies)
if !reflect.DeepEqual(expectedPolicies, actualPolicies) {
t.Fatalf("bad: identity policies; expected: %#v\nactual: %#v", expectedPolicies, actualPolicies)
}
// Log in and get a new token, then renew it. See issue #4829
secret, err = client.Logical().Write("auth/ldap/login/tesla", map[string]interface{}{
"password": "password",
})
if err != nil {
t.Fatal(err)
}
secret, err = client.Auth().Token().Renew(secret.Auth.ClientToken, 10)
if err != nil {
t.Fatal(err)
}
}
func TestTokenStore_CIDRBlocks(t *testing.T) {
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()
core := cluster.Cores[0].Core
vault.TestWaitActive(t, core)
client := cluster.Cores[0].Client
rootToken := client.Token()
var err error
var secret *api.Secret
// Test normally
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
"bound_cidrs": []string{},
})
if err != nil {
t.Fatal(err)
}
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{
Policies: []string{"default"},
}, "testrole")
if err != nil {
t.Fatal(err)
}
client.SetToken(secret.Auth.ClientToken)
_, err = client.Auth().Token().LookupSelf()
if err != nil {
t.Fatal(err)
}
// CIDR blocks, containing localhost
client.SetToken(rootToken)
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
"bound_cidrs": []string{"127.0.0.1/32", "1.2.3.4/8", "5.6.7.8/24"},
})
if err != nil {
t.Fatal(err)
}
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{
Policies: []string{"default"},
}, "testrole")
if err != nil {
t.Fatal(err)
}
client.SetToken(secret.Auth.ClientToken)
_, err = client.Auth().Token().LookupSelf()
if err != nil {
t.Fatal(err)
}
// CIDR blocks, not containing localhost (should fail)
client.SetToken(rootToken)
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
"bound_cidrs": []string{"1.2.3.4/8", "5.6.7.8/24"},
})
if err != nil {
t.Fatal(err)
}
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{
Policies: []string{"default"},
}, "testrole")
if err != nil {
t.Fatal(err)
}
client.SetToken(secret.Auth.ClientToken)
_, err = client.Auth().Token().LookupSelf()
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "permission denied") {
t.Fatalf("unexpected error: %v", err)
}
// Root token, no ttl, should work
client.SetToken(rootToken)
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
"bound_cidrs": []string{"1.2.3.4/8", "5.6.7.8/24"},
})
if err != nil {
t.Fatal(err)
}
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{}, "testrole")
if err != nil {
t.Fatal(err)
}
client.SetToken(secret.Auth.ClientToken)
_, err = client.Auth().Token().LookupSelf()
if err != nil {
t.Fatal(err)
}
// Root token, ttl, should not work
client.SetToken(rootToken)
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
"bound_cidrs": []string{"1.2.3.4/8", "5.6.7.8/24"},
"period": 3600,
})
if err != nil {
t.Fatal(err)
}
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{}, "testrole")
if err != nil {
t.Fatal(err)
}
client.SetToken(secret.Auth.ClientToken)
_, err = client.Auth().Token().LookupSelf()
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "permission denied") {
t.Fatalf("unexpected error: %v", err)
}
}
func TestTokenStore_RevocationOnStartup(t *testing.T) {
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
NumCores: 1,
})
cluster.Start()
defer cluster.Cleanup()
core := cluster.Cores[0].Core
vault.TestWaitActive(t, core)
client := cluster.Cores[0].Client
rootToken := client.Token()
type leaseEntry struct {
LeaseID string `json:"lease_id"`
ClientToken string `json:"client_token"`
Path string `json:"path"`
Data map[string]interface{} `json:"data"`
Secret *logical.Secret `json:"secret"`
Auth *logical.Auth `json:"auth"`
IssueTime time.Time `json:"issue_time"`
ExpireTime time.Time `json:"expire_time"`
LastRenewalTime time.Time `json:"last_renewal_time"`
}
var secret *api.Secret
var err error
var tokens []string
// Create tokens
for i := 0; i < 500; i++ {
secret, err = client.Auth().Token().Create(&api.TokenCreateRequest{
Policies: []string{"default"},
})
if err != nil {
t.Fatal(err)
}
tokens = append(tokens, secret.Auth.ClientToken)
}
const tokenPath string = "sys/raw/sys/token/id/"
secret, err = client.Logical().List(tokenPath)
if err != nil {
t.Fatal(err)
}
totalTokens := len(secret.Data["keys"].([]interface{}))
// Get the list of leases
const leasePath string = "sys/raw/sys/expire/id/auth/token/create/"
secret, err = client.Logical().List(leasePath)
if err != nil {
t.Fatal(err)
}
leases := secret.Data["keys"].([]interface{})
if len(leases) != 500 {
t.Fatalf("unexpected number of leases: %d", len(leases))
}
// Holds non-root leases
var validLeases []string
// Fake times in the past
for _, lease := range leases {
secret, err = client.Logical().Read(leasePath + lease.(string))
var entry leaseEntry
if err := jsonutil.DecodeJSON([]byte(secret.Data["value"].(string)), &entry); err != nil {
t.Fatal(err)
}
if entry.ExpireTime.IsZero() {
continue
}
validLeases = append(validLeases, lease.(string))
entry.IssueTime = entry.IssueTime.Add(-1 * time.Hour * 24 * 365)
entry.ExpireTime = entry.ExpireTime.Add(-1 * time.Hour * 24 * 365)
jsonEntry, err := jsonutil.EncodeJSON(&entry)
if err != nil {
t.Fatal(err)
}
if _, err := client.Logical().Write(leasePath+lease.(string), map[string]interface{}{
"value": string(jsonEntry),
}); err != nil {
t.Fatal(err)
}
}
if err := client.Sys().Seal(); err != nil {
t.Fatal(err)
}
var status *api.SealStatusResponse
for i := 0; i < len(cluster.BarrierKeys); i++ {
status, err = client.Sys().Unseal(string(base64.StdEncoding.EncodeToString(cluster.BarrierKeys[i])))
if err != nil {
t.Fatal(err)
}
if !status.Sealed {
break
}
}
if status.Sealed {
t.Fatal("did not unseal properly")
}
// Give lease loading some time to process
time.Sleep(5 * time.Second)
for i, token := range tokens {
client.SetToken(token)
_, err := client.Logical().Write("cubbyhole/foo", map[string]interface{}{
"value": "bar",
})
if err == nil {
t.Errorf("expected error but did not get one, token num %d", i)
}
}
expectedLeases := len(leases) - len(validLeases)
client.SetToken(rootToken)
secret, err = client.Logical().List(leasePath)
if err != nil {
t.Fatal(err)
}
switch {
case secret == nil:
if expectedLeases != 0 {
t.Fatalf("nil secret back but expected %d leases", expectedLeases)
}
case secret.Data == nil:
if expectedLeases != 0 {
t.Fatalf("nil secret data back but expected %d leases, secret is %#v", expectedLeases, *secret)
}
default:
leasesLeft := len(secret.Data["keys"].([]interface{}))
if leasesLeft != expectedLeases {
t.Fatalf("found %d leases left, expected %d", leasesLeft, expectedLeases)
}
}
expectedTokens := totalTokens - len(validLeases)
secret, err = client.Logical().List(tokenPath)
if err != nil {
t.Fatal(err)
}
tokensLeft := len(secret.Data["keys"].([]interface{}))
if tokensLeft != expectedTokens {
t.Fatalf("found %d tokens left, expected %d", tokensLeft, expectedTokens)
}
}