27bb03bbc0
* adding copyright header * fix fmt and a test
529 lines
13 KiB
Go
529 lines
13 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package token
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/vault/api"
|
|
"github.com/hashicorp/vault/builtin/credential/approle"
|
|
vaulthttp "github.com/hashicorp/vault/http"
|
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
"github.com/hashicorp/vault/vault"
|
|
)
|
|
|
|
func TestBatchTokens(t *testing.T) {
|
|
coreConfig := &vault.CoreConfig{
|
|
LogicalBackends: map[string]logical.Factory{
|
|
"kv": vault.LeasedPassthroughBackendFactory,
|
|
},
|
|
CredentialBackends: map[string]logical.Factory{
|
|
"approle": approle.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
|
|
rootToken := client.Token()
|
|
var err error
|
|
|
|
// Set up a KV path
|
|
err = client.Sys().Mount("kv", &api.MountInput{
|
|
Type: "kv",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err = client.Logical().Write("kv/foo", map[string]interface{}{
|
|
"foo": "bar",
|
|
"ttl": "5m",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Write the test policy
|
|
err = client.Sys().PutPolicy("test", `
|
|
path "kv/*" {
|
|
capabilities = ["read"]
|
|
}`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Mount the auth backend
|
|
err = client.Sys().EnableAuthWithOptions("approle", &api.EnableAuthOptions{
|
|
Type: "approle",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Tune the mount
|
|
if err = client.Sys().TuneMount("auth/approle", api.MountConfigInput{
|
|
DefaultLeaseTTL: "5s",
|
|
MaxLeaseTTL: "5s",
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create role
|
|
resp, err := client.Logical().Write("auth/approle/role/test", map[string]interface{}{
|
|
"policies": "test",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Get role_id
|
|
resp, err = client.Logical().Read("auth/approle/role/test/role-id")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("expected a response for fetching the role-id")
|
|
}
|
|
roleID := resp.Data["role_id"]
|
|
|
|
// Get secret_id
|
|
resp, err = client.Logical().Write("auth/approle/role/test/secret-id", map[string]interface{}{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("expected a response for fetching the secret-id")
|
|
}
|
|
secretID := resp.Data["secret_id"]
|
|
|
|
// Login
|
|
testLogin := func(mountTuneType, roleType string, batch bool) string {
|
|
t.Helper()
|
|
if err = client.Sys().TuneMount("auth/approle", api.MountConfigInput{
|
|
TokenType: mountTuneType,
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = client.Logical().Write("auth/approle/role/test", map[string]interface{}{
|
|
"token_type": roleType,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
resp, err = client.Logical().Write("auth/approle/login", map[string]interface{}{
|
|
"role_id": roleID,
|
|
"secret_id": secretID,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("expected a response for login")
|
|
}
|
|
if resp.Auth == nil {
|
|
t.Fatal("expected auth object from response")
|
|
}
|
|
if resp.Auth.ClientToken == "" {
|
|
t.Fatal("expected a client token")
|
|
}
|
|
if batch && !strings.HasPrefix(resp.Auth.ClientToken, consts.BatchTokenPrefix) {
|
|
t.Fatal("expected a batch token")
|
|
}
|
|
if !batch && strings.HasPrefix(resp.Auth.ClientToken, consts.BatchTokenPrefix) {
|
|
t.Fatal("expected a non-batch token")
|
|
}
|
|
return resp.Auth.ClientToken
|
|
}
|
|
testLogin("service", "default", false)
|
|
testLogin("service", "batch", false)
|
|
testLogin("service", "service", false)
|
|
testLogin("batch", "default", true)
|
|
testLogin("batch", "batch", true)
|
|
testLogin("batch", "service", true)
|
|
testLogin("default-service", "default", false)
|
|
testLogin("default-service", "batch", true)
|
|
testLogin("default-service", "service", false)
|
|
testLogin("default-batch", "default", true)
|
|
testLogin("default-batch", "batch", true)
|
|
testLogin("default-batch", "service", false)
|
|
|
|
finalToken := testLogin("batch", "batch", true)
|
|
|
|
client.SetToken(finalToken)
|
|
resp, err = client.Logical().Read("kv/foo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp.Data["foo"].(string) != "bar" {
|
|
t.Fatal("bad")
|
|
}
|
|
if resp.LeaseID == "" {
|
|
t.Fatal("expected lease")
|
|
}
|
|
if !resp.Renewable {
|
|
t.Fatal("expected renewable")
|
|
}
|
|
if resp.LeaseDuration > 5 {
|
|
t.Fatalf("lease duration too big: %d", resp.LeaseDuration)
|
|
}
|
|
leaseID := resp.LeaseID
|
|
|
|
lastDuration := resp.LeaseDuration
|
|
for i := 0; i < 3; i++ {
|
|
time.Sleep(time.Second)
|
|
resp, err = client.Sys().Renew(leaseID, 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp.LeaseDuration >= lastDuration {
|
|
t.Fatal("expected duration to go down")
|
|
}
|
|
lastDuration = resp.LeaseDuration
|
|
}
|
|
|
|
client.SetToken(rootToken)
|
|
time.Sleep(2 * time.Second)
|
|
resp, err = client.Logical().Write("sys/leases/lookup", map[string]interface{}{
|
|
"lease_id": leaseID,
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
}
|
|
|
|
func TestBatchToken_ParentLeaseRevoke(t *testing.T) {
|
|
coreConfig := &vault.CoreConfig{
|
|
LogicalBackends: map[string]logical.Factory{
|
|
"kv": vault.LeasedPassthroughBackendFactory,
|
|
},
|
|
CredentialBackends: map[string]logical.Factory{
|
|
"approle": approle.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
|
|
rootToken := client.Token()
|
|
var err error
|
|
|
|
// Set up a KV path
|
|
err = client.Sys().Mount("kv", &api.MountInput{
|
|
Type: "kv",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
_, err = client.Logical().Write("kv/foo", map[string]interface{}{
|
|
"foo": "bar",
|
|
"ttl": "5m",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Write the test policy
|
|
err = client.Sys().PutPolicy("test", `
|
|
path "kv/*" {
|
|
capabilities = ["read"]
|
|
}`)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Create a second root token
|
|
secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{
|
|
Policies: []string{"root"},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rootToken2 := secret.Auth.ClientToken
|
|
|
|
// Use this new token to create a batch token
|
|
client.SetToken(rootToken2)
|
|
secret, err = client.Auth().Token().Create(&api.TokenCreateRequest{
|
|
Policies: []string{"test"},
|
|
Type: "batch",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
batchToken := secret.Auth.ClientToken
|
|
client.SetToken(batchToken)
|
|
_, err = client.Auth().Token().LookupSelf()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if secret.Auth.ClientToken[0:vault.TokenPrefixLength] != consts.BatchTokenPrefix {
|
|
t.Fatal(secret.Auth.ClientToken)
|
|
}
|
|
|
|
// Get a lease with the batch token
|
|
resp, err := client.Logical().Read("kv/foo")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp.Data["foo"].(string) != "bar" {
|
|
t.Fatal("bad")
|
|
}
|
|
if resp.LeaseID == "" {
|
|
t.Fatal("expected lease")
|
|
}
|
|
leaseID := resp.LeaseID
|
|
|
|
// Check the lease
|
|
resp, err = client.Logical().Write("sys/leases/lookup", map[string]interface{}{
|
|
"lease_id": leaseID,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Revoke the parent
|
|
client.SetToken(rootToken2)
|
|
err = client.Auth().Token().RevokeSelf("")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
// Verify the batch token is not usable anymore
|
|
client.SetToken(rootToken)
|
|
_, err = client.Auth().Token().Lookup(batchToken)
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
|
|
// Verify the lease has been revoked
|
|
resp, err = client.Logical().Write("sys/leases/lookup", map[string]interface{}{
|
|
"lease_id": leaseID,
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
}
|
|
|
|
func TestTokenStore_Roles_Batch(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 service
|
|
{
|
|
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
|
|
"bound_cidrs": []string{},
|
|
"token_type": "service",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{
|
|
Policies: []string{"default"},
|
|
Type: "batch",
|
|
}, "testrole")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
client.SetToken(secret.Auth.ClientToken)
|
|
_, err = client.Auth().Token().LookupSelf()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if secret.Auth.ClientToken[0:vault.TokenPrefixLength] != consts.ServiceTokenPrefix {
|
|
t.Fatal(secret.Auth.ClientToken)
|
|
}
|
|
}
|
|
|
|
// Test batch
|
|
{
|
|
client.SetToken(rootToken)
|
|
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
|
|
"token_type": "batch",
|
|
})
|
|
// Orphan not set so we should error
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
|
|
"token_type": "batch",
|
|
"orphan": true,
|
|
})
|
|
// Renewable set so we should error
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
|
|
"token_type": "batch",
|
|
"orphan": true,
|
|
"renewable": false,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{
|
|
Policies: []string{"default"},
|
|
Type: "service",
|
|
}, "testrole")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
client.SetToken(secret.Auth.ClientToken)
|
|
_, err = client.Auth().Token().LookupSelf()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if secret.Auth.ClientToken[0:vault.TokenPrefixLength] != consts.BatchTokenPrefix {
|
|
t.Fatal(secret.Auth.ClientToken)
|
|
}
|
|
}
|
|
|
|
// Test default-service
|
|
{
|
|
client.SetToken(rootToken)
|
|
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
|
|
"token_type": "default-service",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Client specifies batch
|
|
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{
|
|
Policies: []string{"default"},
|
|
Type: "batch",
|
|
}, "testrole")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
client.SetToken(secret.Auth.ClientToken)
|
|
_, err = client.Auth().Token().LookupSelf()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if secret.Auth.ClientToken[0:vault.TokenPrefixLength] != consts.BatchTokenPrefix {
|
|
t.Fatal(secret.Auth.ClientToken)
|
|
}
|
|
// Client specifies service
|
|
client.SetToken(rootToken)
|
|
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{
|
|
Policies: []string{"default"},
|
|
Type: "service",
|
|
}, "testrole")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
client.SetToken(secret.Auth.ClientToken)
|
|
_, err = client.Auth().Token().LookupSelf()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if secret.Auth.ClientToken[0:vault.TokenPrefixLength] != consts.ServiceTokenPrefix {
|
|
t.Fatal(secret.Auth.ClientToken)
|
|
}
|
|
// Client doesn't specify
|
|
client.SetToken(rootToken)
|
|
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)
|
|
}
|
|
if secret.Auth.ClientToken[0:vault.TokenPrefixLength] != consts.ServiceTokenPrefix {
|
|
t.Fatal(secret.Auth.ClientToken)
|
|
}
|
|
}
|
|
|
|
// Test default-batch
|
|
{
|
|
client.SetToken(rootToken)
|
|
_, err = client.Logical().Write("auth/token/roles/testrole", map[string]interface{}{
|
|
"token_type": "default-batch",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Client specifies batch
|
|
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{
|
|
Policies: []string{"default"},
|
|
Type: "batch",
|
|
}, "testrole")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
client.SetToken(secret.Auth.ClientToken)
|
|
_, err = client.Auth().Token().LookupSelf()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if secret.Auth.ClientToken[0:vault.TokenPrefixLength] != consts.BatchTokenPrefix {
|
|
t.Fatal(secret.Auth.ClientToken)
|
|
}
|
|
// Client specifies service
|
|
client.SetToken(rootToken)
|
|
secret, err = client.Auth().Token().CreateWithRole(&api.TokenCreateRequest{
|
|
Policies: []string{"default"},
|
|
Type: "service",
|
|
}, "testrole")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
client.SetToken(secret.Auth.ClientToken)
|
|
_, err = client.Auth().Token().LookupSelf()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if secret.Auth.ClientToken[0:vault.TokenPrefixLength] != consts.ServiceTokenPrefix {
|
|
t.Fatal(secret.Auth.ClientToken)
|
|
}
|
|
// Client doesn't specify
|
|
client.SetToken(rootToken)
|
|
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)
|
|
}
|
|
if secret.Auth.ClientToken[0:vault.TokenPrefixLength] != consts.BatchTokenPrefix {
|
|
t.Fatal(secret.Auth.ClientToken)
|
|
}
|
|
}
|
|
}
|