From 97ee0b261b53997c674fe8b68e054caeb377f813 Mon Sep 17 00:00:00 2001 From: Matt Keeler Date: Wed, 3 Jun 2020 11:22:22 -0400 Subject: [PATCH] Fix legacy management tokens in unupgraded secondary dcs (#7908) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ACL.GetPolicy RPC endpoint was supposed to return the “parent” policy and not always the default policy. In the case of legacy management tokens the parent policy was supposed to be “manage”. The result of us not sending this properly was that operations that required specifically a management token such as saving a snapshot would not work in secondary DCs until they were upgraded. --- agent/consul/acl.go | 11 +++--- agent/consul/acl_endpoint.go | 6 ++- agent/consul/acl_endpoint_test.go | 55 +++++++++++++++++----------- agent/consul/config_endpoint_test.go | 2 +- 4 files changed, 45 insertions(+), 29 deletions(-) diff --git a/agent/consul/acl.go b/agent/consul/acl.go index 0a7ca3c16caf..b36120a8f750 100644 --- a/agent/consul/acl.go +++ b/agent/consul/acl.go @@ -1026,16 +1026,17 @@ func (r *ACLResolver) ACLsEnabled() bool { return true } -func (r *ACLResolver) GetMergedPolicyForToken(token string) (*acl.Policy, error) { - policies, err := r.resolveTokenToPolicies(token) +func (r *ACLResolver) GetMergedPolicyForToken(token string) (structs.ACLIdentity, *acl.Policy, error) { + ident, policies, err := r.resolveTokenToIdentityAndPolicies(token) if err != nil { - return nil, err + return nil, nil, err } if len(policies) == 0 { - return nil, acl.ErrNotFound + return nil, nil, acl.ErrNotFound } - return policies.Merge(r.cache, r.sentinel) + policy, err := policies.Merge(r.cache, r.sentinel) + return ident, policy, err } // aclFilter is used to filter results from our state store based on ACL rules diff --git a/agent/consul/acl_endpoint.go b/agent/consul/acl_endpoint.go index fda4b302a996..daf916ad65ef 100644 --- a/agent/consul/acl_endpoint.go +++ b/agent/consul/acl_endpoint.go @@ -1154,11 +1154,15 @@ func (a *ACL) GetPolicy(args *structs.ACLPolicyResolveLegacyRequest, reply *stru // Get the policy via the cache parent := a.srv.config.ACLDefaultPolicy - policy, err := a.srv.acls.GetMergedPolicyForToken(args.ACL) + ident, policy, err := a.srv.acls.GetMergedPolicyForToken(args.ACL) if err != nil { return err } + if token, ok := ident.(*structs.ACLToken); ok && token.Type == structs.ACLTokenTypeManagement { + parent = "manage" + } + // translates the structures internals to most closely match what could be expressed in the original rule language policy = policy.ConvertToLegacy() diff --git a/agent/consul/acl_endpoint_test.go b/agent/consul/acl_endpoint_test.go index cc8b7a2f7edc..688758010d05 100644 --- a/agent/consul/acl_endpoint_test.go +++ b/agent/consul/acl_endpoint_test.go @@ -449,6 +449,7 @@ func TestACLEndpoint_GetPolicy(t *testing.T) { c.ACLDatacenter = "dc1" c.ACLsEnabled = true c.ACLMasterToken = "root" + c.ACLDefaultPolicy = "deny" }) defer os.RemoveAll(dir1) defer s1.Shutdown() @@ -467,9 +468,7 @@ func TestACLEndpoint_GetPolicy(t *testing.T) { WriteRequest: structs.WriteRequest{Token: "root"}, } var out string - if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &arg, &out); err != nil { - t.Fatalf("err: %v", err) - } + require.NoError(t, msgpackrpc.CallWithCodec(codec, "ACL.Apply", &arg, &out)) getR := structs.ACLPolicyResolveLegacyRequest{ Datacenter: "dc1", @@ -478,32 +477,44 @@ func TestACLEndpoint_GetPolicy(t *testing.T) { var acls structs.ACLPolicyResolveLegacyResponse retry.Run(t, func(r *retry.R) { - - if err := msgpackrpc.CallWithCodec(codec, "ACL.GetPolicy", &getR, &acls); err != nil { - r.Fatalf("err: %v", err) - } - - if acls.Policy == nil { - r.Fatalf("Bad: %v", acls) - } - if acls.TTL != 30*time.Second { - r.Fatalf("bad: %v", acls) - } + require.NoError(r, msgpackrpc.CallWithCodec(codec, "ACL.GetPolicy", &getR, &acls)) + require.NotNil(t, acls.Policy) + require.Equal(t, "deny", acls.Parent) + require.Equal(t, 30*time.Second, acls.TTL) }) // Do a conditional lookup with etag getR.ETag = acls.ETag var out2 structs.ACLPolicyResolveLegacyResponse - if err := msgpackrpc.CallWithCodec(codec, "ACL.GetPolicy", &getR, &out2); err != nil { - t.Fatalf("err: %v", err) - } + require.NoError(t, msgpackrpc.CallWithCodec(codec, "ACL.GetPolicy", &getR, &out2)) - if out2.Policy != nil { - t.Fatalf("Bad: %v", out2) - } - if out2.TTL != 30*time.Second { - t.Fatalf("bad: %v", out2) + require.Nil(t, out2.Policy) + require.Equal(t, 30*time.Second, out2.TTL) +} + +func TestACLEndpoint_GetPolicy_Management(t *testing.T) { + t.Parallel() + dir1, s1 := testServerWithConfig(t, func(c *Config) { + c.ACLDatacenter = "dc1" + c.ACLsEnabled = true + c.ACLMasterToken = "root" + c.ACLDefaultPolicy = "deny" + }) + defer os.RemoveAll(dir1) + defer s1.Shutdown() + codec := rpcClient(t, s1) + defer codec.Close() + + testrpc.WaitForLeader(t, s1.RPC, "dc1") + + req := structs.ACLPolicyResolveLegacyRequest{ + Datacenter: s1.config.Datacenter, + ACL: "root", } + + var resp structs.ACLPolicyResolveLegacyResponse + require.NoError(t, msgpackrpc.CallWithCodec(codec, "ACL.GetPolicy", &req, &resp)) + require.Equal(t, "manage", resp.Parent) } func TestACLEndpoint_List(t *testing.T) { diff --git a/agent/consul/config_endpoint_test.go b/agent/consul/config_endpoint_test.go index b3ec1d52cdd0..6a1d873a75bc 100644 --- a/agent/consul/config_endpoint_test.go +++ b/agent/consul/config_endpoint_test.go @@ -1049,7 +1049,7 @@ func TestConfigEntry_ResolveServiceConfig_ProxyDefaultsProtocol_UsedForAllUpstre Config: map[string]interface{}{ "protocol": "http", }, - }, nil)) + })) args := structs.ServiceConfigRequest{ Name: "foo",