Skip to content

Commit

Permalink
This changes the way policies are reported in audit logs.
Browse files Browse the repository at this point in the history
Previously, only policies tied to tokens would be reported. This could
make it difficult to perform after-the-fact analysis based on both the
initial response entry and further requests. Now, the full set of
applicable policies from both the token and any derived policies from
Identity are reported.

To keep things consistent, token authentications now also return the
full set of policies in api.Secret.Auth responses, so this both makes it
easier for users to understand their actual full set, and it matches
what the audit logs now report.
  • Loading branch information
jefferai committed Jun 13, 2018
1 parent 53e6dc5 commit e418a55
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 30 deletions.
4 changes: 2 additions & 2 deletions vault/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -1004,7 +1004,7 @@ func (c *Core) sealInitCommon(ctx context.Context, req *logical.Request) (retErr
}

// Validate the token is a root token
acl, te, entity, err := c.fetchACLTokenEntryAndEntity(req)
acl, te, entity, allPolicies, err := c.fetchACLTokenEntryAndEntity(req)
if err != nil {
retErr = multierror.Append(retErr, err)
c.stateLock.RUnlock()
Expand All @@ -1014,9 +1014,9 @@ func (c *Core) sealInitCommon(ctx context.Context, req *logical.Request) (retErr
// Audit-log the request before going any further
auth := &logical.Auth{
ClientToken: req.ClientToken,
Policies: allPolicies,
}
if te != nil {
auth.Policies = te.Policies
auth.Metadata = te.Meta
auth.DisplayName = te.DisplayName
auth.EntityID = te.EntityID
Expand Down
4 changes: 2 additions & 2 deletions vault/ha.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func (c *Core) StepDown(req *logical.Request) (retErr error) {

ctx := c.activeContext

acl, te, entity, err := c.fetchACLTokenEntryAndEntity(req)
acl, te, entity, allPolicies, err := c.fetchACLTokenEntryAndEntity(req)
if err != nil {
retErr = multierror.Append(retErr, err)
return retErr
Expand All @@ -163,9 +163,9 @@ func (c *Core) StepDown(req *logical.Request) (retErr error) {
// Audit-log the request before going any further
auth := &logical.Auth{
ClientToken: req.ClientToken,
Policies: allPolicies,
}
if te != nil {
auth.Policies = te.Policies
auth.Metadata = te.Meta
auth.DisplayName = te.DisplayName
auth.EntityID = te.EntityID
Expand Down
191 changes: 191 additions & 0 deletions vault/identity_store_entities_ext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/builtin/credential/approle"
"github.com/hashicorp/vault/helper/strutil"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/vault"
Expand Down Expand Up @@ -174,3 +175,193 @@ func TestIdentityStore_EntityDisabled(t *testing.T) {
t.Fatal("expected a client token")
}
}

func TestIdentityStore_EntityPoliciesInInitialAuth(t *testing.T) {
// Use a TestCluster and the approle backend to get a token and entity for testing
coreConfig := &vault.CoreConfig{
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

// Mount the auth backend
err := client.Sys().EnableAuthWithOptions("approle", &api.EnableAuthOptions{
Type: "approle",
})
if err != nil {
t.Fatal(err)
}

// Tune the mount
err = client.Sys().TuneMount("auth/approle", api.MountConfigInput{
DefaultLeaseTTL: "5m",
MaxLeaseTTL: "5m",
})
if err != nil {
t.Fatal(err)
}

// Create role
resp, err := client.Logical().Write("auth/approle/role/role-period", map[string]interface{}{
"period": "5m",
})
if err != nil {
t.Fatal(err)
}

// Get role_id
resp, err = client.Logical().Read("auth/approle/role/role-period/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/role-period/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
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 !strutil.EquivalentSlices(resp.Auth.Policies, []string{"default"}) {
t.Fatalf("policy mismatch, got policies: %v", resp.Auth.Policies)
}

// Check policies
client.SetToken(resp.Auth.ClientToken)
resp, err = client.Auth().Token().LookupSelf()
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("expected a response for token lookup")
}
entityIDRaw, ok := resp.Data["entity_id"]
if !ok {
t.Fatal("expected an entity ID")
}
entityID, ok := entityIDRaw.(string)
if !ok {
t.Fatal("entity_id not a string")
}
policiesRaw := resp.Data["policies"]
if policiesRaw == nil {
t.Fatal("expected policies, got nil")
}
var policies []string
for _, v := range policiesRaw.([]interface{}) {
policies = append(policies, v.(string))
}
policiesRaw = resp.Data["identity_policies"]
if policiesRaw != nil {
t.Fatalf("expected nil policies, got %#v", policiesRaw)
}
if !strutil.EquivalentSlices(policies, []string{"default"}) {
t.Fatalf("policy mismatch, got policies: %v", resp.Auth.Policies)
}

// Write more policies into the entity
client.SetToken(cluster.RootToken)
resp, err = client.Logical().Write("identity/entity/id/"+entityID, map[string]interface{}{
"policies": []string{"foo", "bar"},
})
if err != nil {
t.Fatal(err)
}

// Reauthenticate to get a token with updated policies
client.SetToken("")
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 !strutil.EquivalentSlices(resp.Auth.Policies, []string{"default", "foo", "bar"}) {
t.Fatalf("policy mismatch, got policies: %v", resp.Auth.Policies)
}

// Validate the policies on lookup again -- this ensures that the right
// policies were encoded on the token but all were looked up successfully
client.SetToken(resp.Auth.ClientToken)
resp, err = client.Auth().Token().LookupSelf()
if err != nil {
t.Fatal(err)
}
if resp == nil {
t.Fatal("expected a response for token lookup")
}
entityIDRaw, ok = resp.Data["entity_id"]
if !ok {
t.Fatal("expected an entity ID")
}
entityID, ok = entityIDRaw.(string)
if !ok {
t.Fatal("entity_id not a string")
}
policies = nil
policiesRaw = resp.Data["policies"]
if policiesRaw == nil {
t.Fatal("expected policies, got nil")
}
for _, v := range policiesRaw.([]interface{}) {
policies = append(policies, v.(string))
}
if !strutil.EquivalentSlices(policies, []string{"default"}) {
t.Fatalf("policy mismatch, got policies: %v", policies)
}
policies = nil
policiesRaw = resp.Data["identity_policies"]
if policiesRaw == nil {
t.Fatal("expected policies, got nil")
}
for _, v := range policiesRaw.([]interface{}) {
policies = append(policies, v.(string))
}
if !strutil.EquivalentSlices(policies, []string{"foo", "bar"}) {
t.Fatalf("policy mismatch, got policies: %v", policies)
}

}
6 changes: 3 additions & 3 deletions vault/logical_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -3472,7 +3472,7 @@ func (b *SystemBackend) pathInternalUIMountsRead(ctx context.Context, req *logic

var entity *identity.Entity
// Load the ACL policies so we can walk the prefix for this mount
acl, _, entity, err = b.Core.fetchACLTokenEntryAndEntity(req)
acl, _, entity, _, err = b.Core.fetchACLTokenEntryAndEntity(req)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -3553,7 +3553,7 @@ func (b *SystemBackend) pathInternalUIMountRead(ctx context.Context, req *logica
resp.Data["path"] = me.Path

// Load the ACL policies so we can walk the prefix for this mount
acl, _, entity, err := b.Core.fetchACLTokenEntryAndEntity(req)
acl, _, entity, _, err := b.Core.fetchACLTokenEntryAndEntity(req)
if err != nil {
return nil, err
}
Expand All @@ -3574,7 +3574,7 @@ func (b *SystemBackend) pathInternalUIResultantACL(ctx context.Context, req *log
return nil, nil
}

acl, _, entity, err := b.Core.fetchACLTokenEntryAndEntity(req)
acl, _, entity, _, err := b.Core.fetchACLTokenEntryAndEntity(req)
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit e418a55

Please sign in to comment.