From d77b5add6c7bf2e31437a79b3052a6302124fce1 Mon Sep 17 00:00:00 2001 From: Drew Bailey <2614075+drewbailey@users.noreply.github.com> Date: Fri, 13 Dec 2019 13:41:55 -0500 Subject: [PATCH] RPC server EnableDebug option Passes in agent enable_debug config to nomad server and client configs. This allows for rpc endpoints to have more granular control if they should be enabled or not in combination with ACLs. enable debug on client test --- client/agent_endpoint.go | 12 ++++++++++-- client/agent_endpoint_test.go | 28 ++++++++++++++++++++++++++++ client/config/config.go | 4 ++++ command/agent/agent.go | 4 ++++ nomad/client_agent_endpoint.go | 12 ++++++++++-- nomad/client_agent_endpoint_test.go | 13 +++++++++++-- nomad/config.go | 4 ++++ 7 files changed, 71 insertions(+), 6 deletions(-) diff --git a/client/agent_endpoint.go b/client/agent_endpoint.go index 238d414d9..a9f2e6dda 100644 --- a/client/agent_endpoint.go +++ b/client/agent_endpoint.go @@ -31,14 +31,22 @@ func NewAgentEndpoint(c *Client) *Agent { func (a *Agent) Profile(args *structs.AgentPprofRequest, reply *structs.AgentPprofResponse) error { // Check ACL for agent write - if aclObj, err := a.c.ResolveToken(args.AuthToken); err != nil { + aclObj, err := a.c.ResolveToken(args.AuthToken) + if err != nil { return err } else if aclObj != nil && !aclObj.AllowAgentWrite() { return structs.ErrPermissionDenied } + // If ACLs are disabled, EnableDebug must be enabled + if aclObj == nil { + enableDebug := a.c.config.EnableDebug + if enableDebug == false { + return structs.ErrPermissionDenied + } + } + var resp []byte - var err error var headers map[string]string // Determine which profile to run and generate profile. diff --git a/client/agent_endpoint_test.go b/client/agent_endpoint_test.go index 5c5269da3..52e1aa100 100644 --- a/client/agent_endpoint_test.go +++ b/client/agent_endpoint_test.go @@ -215,6 +215,33 @@ func TestMonitor_Monitor_ACL(t *testing.T) { } } +// Test that by default with no acl, endpoint is disabled +func TestAgentProfile_DefaultDisabled(t *testing.T) { + t.Parallel() + require := require.New(t) + + // start server and client + s1, cleanup := nomad.TestServer(t, nil) + defer cleanup() + + testutil.WaitForLeader(t, s1.RPC) + + c, cleanupC := TestClient(t, func(c *config.Config) { + c.Servers = []string{s1.GetConfig().RPCAddr.String()} + }) + defer cleanupC() + + req := structs.AgentPprofRequest{ + ReqType: profile.CPUReq, + NodeID: c.NodeID(), + } + + reply := structs.AgentPprofResponse{} + + err := c.ClientRPC("Agent.Profile", &req, &reply) + require.EqualError(err, structs.ErrPermissionDenied.Error()) +} + func TestAgentProfile(t *testing.T) { t.Parallel() require := require.New(t) @@ -227,6 +254,7 @@ func TestAgentProfile(t *testing.T) { c, cleanupC := TestClient(t, func(c *config.Config) { c.Servers = []string{s1.GetConfig().RPCAddr.String()} + c.EnableDebug = true }) defer cleanupC() diff --git a/client/config/config.go b/client/config/config.go index d9cabf8d8..b6a9b0835 100644 --- a/client/config/config.go +++ b/client/config/config.go @@ -71,6 +71,10 @@ type Config struct { // avoids persistent storage. DevMode bool + // EnableDebug is used to enable debugging RPC endpoints + // in the absence of ACLs + EnableDebug bool + // StateDir is where we store our state StateDir string diff --git a/command/agent/agent.go b/command/agent/agent.go index 7695d7dea..f8d85f562 100644 --- a/command/agent/agent.go +++ b/command/agent/agent.go @@ -132,6 +132,8 @@ func convertServerConfig(agentConfig *Config) (*nomad.Config, error) { conf = nomad.DefaultConfig() } conf.DevMode = agentConfig.DevMode + conf.EnableDebug = agentConfig.EnableDebug + conf.Build = agentConfig.Version.VersionNumber() if agentConfig.Region != "" { conf.Region = agentConfig.Region @@ -433,6 +435,8 @@ func convertClientConfig(agentConfig *Config) (*clientconfig.Config, error) { conf.Servers = agentConfig.Client.Servers conf.LogLevel = agentConfig.LogLevel conf.DevMode = agentConfig.DevMode + conf.EnableDebug = agentConfig.EnableDebug + if agentConfig.Region != "" { conf.Region = agentConfig.Region } diff --git a/nomad/client_agent_endpoint.go b/nomad/client_agent_endpoint.go index b2946c34b..ea21551ce 100644 --- a/nomad/client_agent_endpoint.go +++ b/nomad/client_agent_endpoint.go @@ -61,15 +61,23 @@ func (a *Agent) Profile(args *structs.AgentPprofRequest, reply *structs.AgentPpr } // Check ACL for agent write - if aclObj, err := a.srv.ResolveToken(args.AuthToken); err != nil { + aclObj, err := a.srv.ResolveToken(args.AuthToken) + if err != nil { return err } else if aclObj != nil && !aclObj.AllowAgentWrite() { return structs.ErrPermissionDenied } + // If ACLs are disabled, EnableDebug must be enabled + if aclObj == nil { + enableDebug := a.srv.config.EnableDebug + if enableDebug == false { + return structs.ErrPermissionDenied + } + } + // Process the request on this server var resp []byte - var err error var headers map[string]string // Determine which profile to run and generate profile. diff --git a/nomad/client_agent_endpoint_test.go b/nomad/client_agent_endpoint_test.go index d1eb80d06..bc9e92c20 100644 --- a/nomad/client_agent_endpoint_test.go +++ b/nomad/client_agent_endpoint_test.go @@ -464,7 +464,9 @@ func TestAgentProfile_RemoteClient(t *testing.T) { require := require.New(t) // start server and client - s1, cleanup := TestServer(t, nil) + s1, cleanup := TestServer(t, func(c *Config) { + c.DevDisableBootstrap = true + }) defer cleanup() s2, cleanup := TestServer(t, func(c *Config) { @@ -478,6 +480,7 @@ func TestAgentProfile_RemoteClient(t *testing.T) { c, cleanupC := client.TestClient(t, func(c *config.Config) { c.Servers = []string{s2.GetConfig().RPCAddr.String()} + c.EnableDebug = true }) defer cleanupC() @@ -513,12 +516,14 @@ func TestAgentProfile_RemoteRegionMisMatch(t *testing.T) { s1, cleanupS1 := TestServer(t, func(c *Config) { c.NumSchedulers = 0 c.Region = "foo" + c.EnableDebug = true }) defer cleanupS1() s2, cleanup := TestServer(t, func(c *Config) { c.NumSchedulers = 0 c.Region = "bar" + c.EnableDebug = true }) defer cleanup() @@ -555,6 +560,7 @@ func TestAgentProfile_RemoteRegion(t *testing.T) { s2, cleanup := TestServer(t, func(c *Config) { c.NumSchedulers = 0 c.Region = "bar" + c.EnableDebug = true }) defer cleanup() @@ -582,11 +588,14 @@ func TestAgentProfile_Server(t *testing.T) { t.Parallel() // start servers - s1, cleanup := TestServer(t, nil) + s1, cleanup := TestServer(t, func(c *Config) { + c.EnableDebug = true + }) defer cleanup() s2, cleanup := TestServer(t, func(c *Config) { c.DevDisableBootstrap = true + c.EnableDebug = true }) defer cleanup() diff --git a/nomad/config.go b/nomad/config.go index 053408eca..4b441f135 100644 --- a/nomad/config.go +++ b/nomad/config.go @@ -67,6 +67,10 @@ type Config struct { // use of persistence or state. DevMode bool + // EnableDebug is used to enable debugging RPC endpoints + // in the absence of ACLs + EnableDebug bool + // DevDisableBootstrap is used to disable bootstrap mode while // in DevMode. This is largely used for testing. DevDisableBootstrap bool