Skip to content

Commit

Permalink
New Mutable Authorized Entry Cache (#4451)
Browse files Browse the repository at this point in the history
Signed-off-by: Andrew Harding <[email protected]>
  • Loading branch information
azdagron authored Oct 4, 2023
1 parent 1cc57e3 commit 420db05
Show file tree
Hide file tree
Showing 14 changed files with 1,179 additions and 39 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ require (
github.com/gofrs/uuid v4.4.0+incompatible
github.com/gofrs/uuid/v5 v5.0.0
github.com/golang/protobuf v1.5.3
github.com/google/btree v1.1.2
github.com/google/go-cmp v0.5.9
github.com/google/go-containerregistry v0.16.1
github.com/google/go-tpm v0.9.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,8 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/certificate-transparency-go v1.1.6 h1:SW5K3sr7ptST/pIvNkSVWMiJqemRmkjJPPT0jzXdOOY=
github.com/google/certificate-transparency-go v1.1.6/go.mod h1:0OJjOsOk+wj6aYQgP7FU0ioQ0AJUmnWPFMqTjQeazPQ=
github.com/google/flatbuffers v23.5.26+incompatible h1:M9dgRyhJemaM4Sw8+66GHBu8ioaQmyPLg1b8VwK5WJg=
Expand Down
26 changes: 26 additions & 0 deletions pkg/server/authorizedentries/agent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package authorizedentries

type agentRecord struct {
ID string

// ExpiresAt is seconds since unix epoch. Using intead of time.Time for
// reduced memory usage and better cache locality.
ExpiresAt int64

Selectors selectorSet
}

func agentRecordByID(a, b agentRecord) bool {
return a.ID < b.ID
}

func agentRecordByExpiresAt(a, b agentRecord) bool {
switch {
case a.ExpiresAt < b.ExpiresAt:
return true
case a.ExpiresAt > b.ExpiresAt:
return false
default:
return a.ID < b.ID
}
}
64 changes: 64 additions & 0 deletions pkg/server/authorizedentries/agent_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package authorizedentries

import (
"testing"
"unsafe"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestAgentRecordSize(t *testing.T) {
// The motivation for this test is to bring awareness and visibility into
// how much size the record occupies. We want to minimize the size to
// increase cache locality in the btree.
require.Equal(t, uintptr(32), unsafe.Sizeof(agentRecord{}))
}

func TestAgentRecordByID(t *testing.T) {
assertLess := func(lesser, greater agentRecord) {
t.Helper()
assert.Truef(t, agentRecordByID(lesser, greater), "expected A%sE%s<A%sE%s", lesser.ID, lesser.ExpiresAt, greater.ID, greater.ExpiresAt)
assert.Falsef(t, agentRecordByID(greater, lesser), "expected A%sE%s>A%sE%s", greater.ID, greater.ExpiresAt, lesser.ID, lesser.ExpiresAt)
}

// ExpiresAt is irrelevant.
records := []agentRecord{
agentRecord{ID: "1", ExpiresAt: 9999},
agentRecord{ID: "2", ExpiresAt: 8888},
}

lesser := agentRecord{}
for _, greater := range records {
assertLess(lesser, greater)
lesser = greater
}

// Since there should only be one agent record by ID, the ExpiresAt field
// is ignored for purposes of placement in the btree.
assert.False(t, agentRecordByID(agentRecord{ID: "FOO", ExpiresAt: 1}, agentRecord{ID: "FOO", ExpiresAt: 2}))
assert.False(t, agentRecordByID(agentRecord{ID: "FOO", ExpiresAt: 2}, agentRecord{ID: "FOO", ExpiresAt: 1}))
}

func TestAgentRecordByExpiresAt(t *testing.T) {
assertLess := func(lesser, greater agentRecord) {
t.Helper()
assert.Truef(t, agentRecordByExpiresAt(lesser, greater), "expected A%sE%d<A%sE%d", lesser.ID, lesser.ExpiresAt, greater.ID, greater.ExpiresAt)
assert.Falsef(t, agentRecordByExpiresAt(greater, lesser), "expected A%sE%d>A%sE%d", greater.ID, greater.ExpiresAt, lesser.ID, lesser.ExpiresAt)
}

records := []agentRecord{
agentRecord{ID: "1"},
agentRecord{ID: "2"},
agentRecord{ID: "1", ExpiresAt: 1},
agentRecord{ID: "2", ExpiresAt: 1},
agentRecord{ID: "1", ExpiresAt: 2},
agentRecord{ID: "2", ExpiresAt: 2},
}

lesser := agentRecord{}
for _, greater := range records {
assertLess(lesser, greater)
lesser = greater
}
}
49 changes: 49 additions & 0 deletions pkg/server/authorizedentries/aliases.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package authorizedentries

type aliasRecord struct {
// EntryID is the ID of the registration entry that defines this node
// alias.
EntryID string

// AliasID is the SPIFFE ID of nodes that match this alias.
AliasID string

// Selector is the specific selector we use to fan out to this record
// during the crawl.
Selector Selector

// AllSelectors is here out of convenience to verify that the agent
// possesses a superset of the alias's selectors and is therefore
// authorized for the alias.
AllSelectors selectorSet
}

func aliasRecordByEntryID(a, b aliasRecord) bool {
switch {
case a.EntryID < b.EntryID:
return true
case a.EntryID > b.EntryID:
return false
case a.Selector.Type < b.Selector.Type:
return true
case a.Selector.Type > b.Selector.Type:
return false
default:
return a.Selector.Value < b.Selector.Value
}
}

func aliasRecordBySelector(a, b aliasRecord) bool {
switch {
case a.Selector.Type < b.Selector.Type:
return true
case a.Selector.Type > b.Selector.Type:
return false
case a.Selector.Value < b.Selector.Value:
return true
case a.Selector.Value > b.Selector.Value:
return false
default:
return a.EntryID < b.EntryID
}
}
70 changes: 70 additions & 0 deletions pkg/server/authorizedentries/aliases_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package authorizedentries

import (
"testing"
"unsafe"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestAliasRecordSize(t *testing.T) {
// The motivation for this test is to bring awareness and visibility into
// how much size the record occupies. We want to minimize the size to
// increase cache locality in the btree.
require.Equal(t, uintptr(72), unsafe.Sizeof(aliasRecord{}))
}

func TestAliasRecordByEntryID(t *testing.T) {
assertLess := func(lesser, greater aliasRecord) {
t.Helper()
assert.Truef(t, aliasRecordByEntryID(lesser, greater), "expected E%sP%s<E%sP%s", lesser.EntryID, lesser.Selector, greater.EntryID, greater.Selector)
assert.Falsef(t, aliasRecordByEntryID(greater, lesser), "expected E%sP%s>E%sP%s", greater.EntryID, greater.Selector, lesser.EntryID, lesser.Selector)
}

records := []aliasRecord{
aliasRecord{EntryID: "1"},
aliasRecord{EntryID: "1", Selector: Selector{Type: "1", Value: "1"}},
aliasRecord{EntryID: "1", Selector: Selector{Type: "1", Value: "2"}},
aliasRecord{EntryID: "1", Selector: Selector{Type: "2", Value: "1"}},
aliasRecord{EntryID: "1", Selector: Selector{Type: "2", Value: "2"}},
aliasRecord{EntryID: "2"},
aliasRecord{EntryID: "2", Selector: Selector{Type: "1", Value: "1"}},
aliasRecord{EntryID: "2", Selector: Selector{Type: "1", Value: "2"}},
aliasRecord{EntryID: "2", Selector: Selector{Type: "2", Value: "1"}},
aliasRecord{EntryID: "2", Selector: Selector{Type: "2", Value: "2"}},
}

lesser := aliasRecord{}
for _, greater := range records {
assertLess(lesser, greater)
lesser = greater
}
}

func TestAliasRecordBySelector(t *testing.T) {
assertLess := func(lesser, greater aliasRecord) {
t.Helper()
assert.True(t, aliasRecordBySelector(lesser, greater), "expected P%sE%s<P%sE%s", lesser.Selector, lesser.EntryID, greater.Selector, greater.EntryID)
assert.False(t, aliasRecordBySelector(greater, lesser), "expected P%sE%s>P%sE%s", greater.Selector, greater.EntryID, lesser.Selector, lesser.EntryID)
}

records := []aliasRecord{
aliasRecord{Selector: Selector{Type: "1", Value: "1"}},
aliasRecord{Selector: Selector{Type: "1", Value: "1"}, EntryID: "1"},
aliasRecord{Selector: Selector{Type: "1", Value: "1"}, EntryID: "2"},
aliasRecord{Selector: Selector{Type: "1", Value: "2"}, EntryID: "1"},
aliasRecord{Selector: Selector{Type: "1", Value: "2"}, EntryID: "2"},
aliasRecord{Selector: Selector{Type: "2", Value: "1"}},
aliasRecord{Selector: Selector{Type: "2", Value: "1"}, EntryID: "1"},
aliasRecord{Selector: Selector{Type: "2", Value: "1"}, EntryID: "2"},
aliasRecord{Selector: Selector{Type: "2", Value: "2"}},
aliasRecord{Selector: Selector{Type: "2", Value: "2"}, EntryID: "1"},
aliasRecord{Selector: Selector{Type: "2", Value: "2"}, EntryID: "2"},
}
lesser := aliasRecord{}
for _, greater := range records {
assertLess(lesser, greater)
lesser = greater
}
}
Loading

0 comments on commit 420db05

Please sign in to comment.