agent: augment /v1/connect/authorize to cache intentions

This commit is contained in:
Mitchell Hashimoto 2018-04-17 18:26:58 -05:00
parent 56774f24d0
commit a1f8cb9570
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
3 changed files with 85 additions and 9 deletions

View File

@ -2649,4 +2649,13 @@ func (a *Agent) registerCache() {
RefreshTimer: 0, RefreshTimer: 0,
RefreshTimeout: 10 * time.Minute, RefreshTimeout: 10 * time.Minute,
}) })
a.cache.RegisterType(cachetype.IntentionMatchName, &cachetype.IntentionMatch{
RPC: a.delegate,
}, &cache.RegisterOptions{
// Maintain a blocking query, retry dropped connections quickly
Refresh: true,
RefreshTimer: 0,
RefreshTimeout: 10 * time.Minute,
})
} }

View File

@ -1124,10 +1124,16 @@ func (s *HTTPServer) AgentConnectAuthorize(resp http.ResponseWriter, req *http.R
}, },
} }
args.Token = token args.Token = token
var reply structs.IndexedIntentionMatches
if err := s.agent.RPC("Intention.Match", args, &reply); err != nil { raw, err := s.agent.cache.Get(cachetype.IntentionMatchName, args)
if err != nil {
return nil, err return nil, err
} }
reply, ok := raw.(*structs.IndexedIntentionMatches)
if !ok {
return nil, fmt.Errorf("internal error: response type not correct")
}
if len(reply.Matches) != 1 { if len(reply.Matches) != 1 {
return nil, fmt.Errorf("Internal error loading matches") return nil, fmt.Errorf("Internal error loading matches")
} }

View File

@ -2495,13 +2495,14 @@ func TestAgentConnectAuthorize_idNotService(t *testing.T) {
func TestAgentConnectAuthorize_allow(t *testing.T) { func TestAgentConnectAuthorize_allow(t *testing.T) {
t.Parallel() t.Parallel()
assert := assert.New(t) require := require.New(t)
a := NewTestAgent(t.Name(), "") a := NewTestAgent(t.Name(), "")
defer a.Shutdown() defer a.Shutdown()
target := "db" target := "db"
// Create some intentions // Create some intentions
var ixnId string
{ {
req := structs.IntentionRequest{ req := structs.IntentionRequest{
Datacenter: "dc1", Datacenter: "dc1",
@ -2514,10 +2515,12 @@ func TestAgentConnectAuthorize_allow(t *testing.T) {
req.Intention.DestinationName = target req.Intention.DestinationName = target
req.Intention.Action = structs.IntentionActionAllow req.Intention.Action = structs.IntentionActionAllow
var reply string require.Nil(a.RPC("Intention.Apply", &req, &ixnId))
assert.Nil(a.RPC("Intention.Apply", &req, &reply))
} }
// Grab the initial cache hit count
cacheHits := a.cache.Hits()
args := &structs.ConnectAuthorizeRequest{ args := &structs.ConnectAuthorizeRequest{
Target: target, Target: target,
ClientCertURI: connect.TestSpiffeIDService(t, "web").URI().String(), ClientCertURI: connect.TestSpiffeIDService(t, "web").URI().String(),
@ -2525,12 +2528,70 @@ func TestAgentConnectAuthorize_allow(t *testing.T) {
req, _ := http.NewRequest("POST", "/v1/agent/connect/authorize", jsonReader(args)) req, _ := http.NewRequest("POST", "/v1/agent/connect/authorize", jsonReader(args))
resp := httptest.NewRecorder() resp := httptest.NewRecorder()
respRaw, err := a.srv.AgentConnectAuthorize(resp, req) respRaw, err := a.srv.AgentConnectAuthorize(resp, req)
assert.Nil(err) require.Nil(err)
assert.Equal(200, resp.Code) require.Equal(200, resp.Code)
obj := respRaw.(*connectAuthorizeResp) obj := respRaw.(*connectAuthorizeResp)
assert.True(obj.Authorized) require.True(obj.Authorized)
assert.Contains(obj.Reason, "Matched") require.Contains(obj.Reason, "Matched")
// That should've been a cache miss, so not hit change
require.Equal(cacheHits, a.cache.Hits())
// Make the request again
{
req, _ := http.NewRequest("POST", "/v1/agent/connect/authorize", jsonReader(args))
resp := httptest.NewRecorder()
respRaw, err := a.srv.AgentConnectAuthorize(resp, req)
require.Nil(err)
require.Equal(200, resp.Code)
obj := respRaw.(*connectAuthorizeResp)
require.True(obj.Authorized)
require.Contains(obj.Reason, "Matched")
}
// That should've been a cache hit
require.Equal(cacheHits+1, a.cache.Hits())
cacheHits++
// Change the intention
{
req := structs.IntentionRequest{
Datacenter: "dc1",
Op: structs.IntentionOpUpdate,
Intention: structs.TestIntention(t),
}
req.Intention.ID = ixnId
req.Intention.SourceNS = structs.IntentionDefaultNamespace
req.Intention.SourceName = "web"
req.Intention.DestinationNS = structs.IntentionDefaultNamespace
req.Intention.DestinationName = target
req.Intention.Action = structs.IntentionActionDeny
require.Nil(a.RPC("Intention.Apply", &req, &ixnId))
}
// Short sleep lets the cache background refresh happen
time.Sleep(100 * time.Millisecond)
// Make the request again
{
req, _ := http.NewRequest("POST", "/v1/agent/connect/authorize", jsonReader(args))
resp := httptest.NewRecorder()
respRaw, err := a.srv.AgentConnectAuthorize(resp, req)
require.Nil(err)
require.Equal(200, resp.Code)
obj := respRaw.(*connectAuthorizeResp)
require.False(obj.Authorized)
require.Contains(obj.Reason, "Matched")
}
// That should've been a cache hit, too, since it updated in the
// background.
require.Equal(cacheHits+1, a.cache.Hits())
cacheHits++
} }
// Test when there is an intention denying the connection // Test when there is an intention denying the connection