[Security] Add finer control over script checks (#4715)

* Add -enable-local-script-checks options

These options allow for a finer control over when script checks are enabled by
giving the option to only allow them when they are declared from the local
file system.

* Add documentation for the new option

* Nitpick doc wording
This commit is contained in:
Aestek 2018-10-11 14:22:11 +02:00 committed by Paul Banks
parent 298af6dca7
commit 25f04fbd21
16 changed files with 415 additions and 149 deletions

View File

@ -69,6 +69,13 @@ const (
"service, but no reason was provided. This is a default message." "service, but no reason was provided. This is a default message."
) )
type configSource int
const (
ConfigSourceLocal configSource = iota
ConfigSourceRemote
)
// delegate defines the interface shared by both // delegate defines the interface shared by both
// consul.Client and consul.Server. // consul.Client and consul.Server.
type delegate interface { type delegate interface {
@ -1858,7 +1865,7 @@ func (a *Agent) purgeCheck(checkID types.CheckID) error {
// AddService is used to add a service entry. // AddService is used to add a service entry.
// This entry is persistent and the agent will make a best effort to // This entry is persistent and the agent will make a best effort to
// ensure it is registered // ensure it is registered
func (a *Agent) AddService(service *structs.NodeService, chkTypes []*structs.CheckType, persist bool, token string) error { func (a *Agent) AddService(service *structs.NodeService, chkTypes []*structs.CheckType, persist bool, token string, source configSource) error {
if service.Service == "" { if service.Service == "" {
return fmt.Errorf("Service name missing") return fmt.Errorf("Service name missing")
} }
@ -1940,7 +1947,7 @@ func (a *Agent) AddService(service *structs.NodeService, chkTypes []*structs.Che
if chkType.Status != "" { if chkType.Status != "" {
check.Status = chkType.Status check.Status = chkType.Status
} }
if err := a.AddCheck(check, chkType, persist, token); err != nil { if err := a.AddCheck(check, chkType, persist, token, source); err != nil {
return err return err
} }
} }
@ -2010,7 +2017,7 @@ func (a *Agent) RemoveService(serviceID string, persist bool) error {
// This entry is persistent and the agent will make a best effort to // This entry is persistent and the agent will make a best effort to
// ensure it is registered. The Check may include a CheckType which // ensure it is registered. The Check may include a CheckType which
// is used to automatically update the check status // is used to automatically update the check status
func (a *Agent) AddCheck(check *structs.HealthCheck, chkType *structs.CheckType, persist bool, token string) error { func (a *Agent) AddCheck(check *structs.HealthCheck, chkType *structs.CheckType, persist bool, token string, source configSource) error {
if check.CheckID == "" { if check.CheckID == "" {
return fmt.Errorf("CheckID missing") return fmt.Errorf("CheckID missing")
} }
@ -2020,8 +2027,14 @@ func (a *Agent) AddCheck(check *structs.HealthCheck, chkType *structs.CheckType,
return fmt.Errorf("Check is not valid: %v", err) return fmt.Errorf("Check is not valid: %v", err)
} }
if chkType.IsScript() && !a.config.EnableScriptChecks { if chkType.IsScript() {
return fmt.Errorf("Scripts are disabled on this agent; to enable, configure 'enable_script_checks' to true") if source == ConfigSourceLocal && !a.config.EnableLocalScriptChecks {
return fmt.Errorf("Scripts are disabled on this agent; to enable, configure 'enable_script_checks' or 'enable_local_script_checks' to true")
}
if source == ConfigSourceRemote && !a.config.EnableRemoteScriptChecks {
return fmt.Errorf("Scripts are disabled on this agent from remote calls; to enable, configure 'enable_script_checks' to true")
}
} }
} }
@ -2330,7 +2343,7 @@ func (a *Agent) RemoveCheck(checkID types.CheckID, persist bool) error {
// assigned. We need to restore from disk to enable to continue authenticating // assigned. We need to restore from disk to enable to continue authenticating
// running proxies that already had that credential injected. // running proxies that already had that credential injected.
func (a *Agent) addProxyLocked(proxy *structs.ConnectManagedProxy, persist, FromFile bool, func (a *Agent) addProxyLocked(proxy *structs.ConnectManagedProxy, persist, FromFile bool,
restoredProxyToken string) error { restoredProxyToken string, source configSource) error {
// Lookup the target service token in state if there is one. // Lookup the target service token in state if there is one.
token := a.State.ServiceToken(proxy.TargetServiceID) token := a.State.ServiceToken(proxy.TargetServiceID)
@ -2372,7 +2385,7 @@ func (a *Agent) addProxyLocked(proxy *structs.ConnectManagedProxy, persist, From
} }
} }
err = a.AddService(proxyService, chkTypes, persist, token) err = a.AddService(proxyService, chkTypes, persist, token, source)
if err != nil { if err != nil {
// Remove the state too // Remove the state too
a.State.RemoveProxy(proxyService.ID) a.State.RemoveProxy(proxyService.ID)
@ -2402,10 +2415,10 @@ func (a *Agent) addProxyLocked(proxy *structs.ConnectManagedProxy, persist, From
// assigned. We need to restore from disk to enable to continue authenticating // assigned. We need to restore from disk to enable to continue authenticating
// running proxies that already had that credential injected. // running proxies that already had that credential injected.
func (a *Agent) AddProxy(proxy *structs.ConnectManagedProxy, persist, FromFile bool, func (a *Agent) AddProxy(proxy *structs.ConnectManagedProxy, persist, FromFile bool,
restoredProxyToken string) error { restoredProxyToken string, source configSource) error {
a.proxyLock.Lock() a.proxyLock.Lock()
defer a.proxyLock.Unlock() defer a.proxyLock.Unlock()
return a.addProxyLocked(proxy, persist, FromFile, restoredProxyToken) return a.addProxyLocked(proxy, persist, FromFile, restoredProxyToken, source)
} }
// resolveProxyCheckAddress returns the best address to use for a TCP check of // resolveProxyCheckAddress returns the best address to use for a TCP check of
@ -2893,13 +2906,13 @@ func (a *Agent) loadServices(conf *config.RuntimeConfig) error {
// syntax sugar and shouldn't be persisted in local or server state. // syntax sugar and shouldn't be persisted in local or server state.
ns.Connect.SidecarService = nil ns.Connect.SidecarService = nil
if err := a.AddService(ns, chkTypes, false, service.Token); err != nil { if err := a.AddService(ns, chkTypes, false, service.Token, ConfigSourceLocal); err != nil {
return fmt.Errorf("Failed to register service %q: %v", service.Name, err) return fmt.Errorf("Failed to register service %q: %v", service.Name, err)
} }
// If there is a sidecar service, register that too. // If there is a sidecar service, register that too.
if sidecar != nil { if sidecar != nil {
if err := a.AddService(sidecar, sidecarChecks, false, sidecarToken); err != nil { if err := a.AddService(sidecar, sidecarChecks, false, sidecarToken, ConfigSourceLocal); err != nil {
return fmt.Errorf("Failed to register sidecar for service %q: %v", service.Name, err) return fmt.Errorf("Failed to register sidecar for service %q: %v", service.Name, err)
} }
} }
@ -2962,7 +2975,7 @@ func (a *Agent) loadServices(conf *config.RuntimeConfig) error {
} else { } else {
a.logger.Printf("[DEBUG] agent: restored service definition %q from %q", a.logger.Printf("[DEBUG] agent: restored service definition %q from %q",
serviceID, file) serviceID, file)
if err := a.AddService(p.Service, nil, false, p.Token); err != nil { if err := a.AddService(p.Service, nil, false, p.Token, ConfigSourceLocal); err != nil {
return fmt.Errorf("failed adding service %q: %s", serviceID, err) return fmt.Errorf("failed adding service %q: %s", serviceID, err)
} }
} }
@ -2988,7 +3001,7 @@ func (a *Agent) loadChecks(conf *config.RuntimeConfig) error {
for _, check := range conf.Checks { for _, check := range conf.Checks {
health := check.HealthCheck(conf.NodeName) health := check.HealthCheck(conf.NodeName)
chkType := check.CheckType() chkType := check.CheckType()
if err := a.AddCheck(health, chkType, false, check.Token); err != nil { if err := a.AddCheck(health, chkType, false, check.Token, ConfigSourceLocal); err != nil {
return fmt.Errorf("Failed to register check '%s': %v %v", check.Name, err, check) return fmt.Errorf("Failed to register check '%s': %v %v", check.Name, err, check)
} }
} }
@ -3043,7 +3056,7 @@ func (a *Agent) loadChecks(conf *config.RuntimeConfig) error {
// services into the active pool // services into the active pool
p.Check.Status = api.HealthCritical p.Check.Status = api.HealthCritical
if err := a.AddCheck(p.Check, p.ChkType, false, p.Token); err != nil { if err := a.AddCheck(p.Check, p.ChkType, false, p.Token, ConfigSourceLocal); err != nil {
// Purge the check if it is unable to be restored. // Purge the check if it is unable to be restored.
a.logger.Printf("[WARN] agent: Failed to restore check %q: %s", a.logger.Printf("[WARN] agent: Failed to restore check %q: %s",
checkID, err) checkID, err)
@ -3144,7 +3157,7 @@ func (a *Agent) loadProxies(conf *config.RuntimeConfig) error {
restoredToken = persisted.ProxyToken restoredToken = persisted.ProxyToken
} }
if err := a.addProxyLocked(proxy, true, true, restoredToken); err != nil { if err := a.addProxyLocked(proxy, true, true, restoredToken, ConfigSourceLocal); err != nil {
return fmt.Errorf("failed adding proxy: %s", err) return fmt.Errorf("failed adding proxy: %s", err)
} }
} }
@ -3161,7 +3174,7 @@ func (a *Agent) loadProxies(conf *config.RuntimeConfig) error {
} else if !persisted.FromFile { } else if !persisted.FromFile {
if a.State.Proxy(proxyID) == nil { if a.State.Proxy(proxyID) == nil {
a.logger.Printf("[DEBUG] agent: restored proxy definition %q", proxyID) a.logger.Printf("[DEBUG] agent: restored proxy definition %q", proxyID)
if err := a.addProxyLocked(persisted.Proxy, false, false, persisted.ProxyToken); err != nil { if err := a.addProxyLocked(persisted.Proxy, false, false, persisted.ProxyToken, ConfigSourceLocal); err != nil {
return fmt.Errorf("failed adding proxy %q: %v", proxyID, err) return fmt.Errorf("failed adding proxy %q: %v", proxyID, err)
} }
} else { } else {
@ -3251,7 +3264,7 @@ func (a *Agent) EnableServiceMaintenance(serviceID, reason, token string) error
ServiceName: service.Service, ServiceName: service.Service,
Status: api.HealthCritical, Status: api.HealthCritical,
} }
a.AddCheck(check, nil, true, token) a.AddCheck(check, nil, true, token, ConfigSourceLocal)
a.logger.Printf("[INFO] agent: Service %q entered maintenance mode", serviceID) a.logger.Printf("[INFO] agent: Service %q entered maintenance mode", serviceID)
return nil return nil
@ -3297,7 +3310,7 @@ func (a *Agent) EnableNodeMaintenance(reason, token string) {
Notes: reason, Notes: reason,
Status: api.HealthCritical, Status: api.HealthCritical,
} }
a.AddCheck(check, nil, true, token) a.AddCheck(check, nil, true, token, ConfigSourceLocal)
a.logger.Printf("[INFO] agent: Node entered maintenance mode") a.logger.Printf("[INFO] agent: Node entered maintenance mode")
} }

View File

@ -570,7 +570,7 @@ func (s *HTTPServer) AgentRegisterCheck(resp http.ResponseWriter, req *http.Requ
} }
// Add the check. // Add the check.
if err := s.agent.AddCheck(health, chkType, true, token); err != nil { if err := s.agent.AddCheck(health, chkType, true, token, ConfigSourceRemote); err != nil {
return nil, err return nil, err
} }
s.syncChanges() s.syncChanges()
@ -889,18 +889,18 @@ func (s *HTTPServer) AgentRegisterService(resp http.ResponseWriter, req *http.Re
} }
// Add the service. // Add the service.
if err := s.agent.AddService(ns, chkTypes, true, token); err != nil { if err := s.agent.AddService(ns, chkTypes, true, token, ConfigSourceRemote); err != nil {
return nil, err return nil, err
} }
// Add proxy (which will add proxy service so do it before we trigger sync) // Add proxy (which will add proxy service so do it before we trigger sync)
if proxy != nil { if proxy != nil {
if err := s.agent.AddProxy(proxy, true, false, ""); err != nil { if err := s.agent.AddProxy(proxy, true, false, "", ConfigSourceRemote); err != nil {
return nil, err return nil, err
} }
} }
// Add sidecar. // Add sidecar.
if sidecar != nil { if sidecar != nil {
if err := s.agent.AddService(sidecar, sidecarChecks, true, sidecarToken); err != nil { if err := s.agent.AddService(sidecar, sidecarChecks, true, sidecarToken, ConfigSourceRemote); err != nil {
return nil, err return nil, err
} }
} }

View File

@ -1329,6 +1329,60 @@ func TestAgent_RegisterCheck_Scripts(t *testing.T) {
} }
} }
func TestAgent_RegisterCheckScriptsExecDisable(t *testing.T) {
t.Parallel()
a := NewTestAgent(t.Name(), "")
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
args := &structs.CheckDefinition{
Name: "test",
ScriptArgs: []string{"true"},
Interval: time.Second,
}
req, _ := http.NewRequest("PUT", "/v1/agent/check/register?token=abc123", jsonReader(args))
res := httptest.NewRecorder()
_, err := a.srv.AgentRegisterCheck(res, req)
if err == nil {
t.Fatalf("expected error but got nil")
}
if !strings.Contains(err.Error(), "Scripts are disabled on this agent") {
t.Fatalf("expected script disabled error, got: %s", err)
}
checkID := types.CheckID("test")
if _, ok := a.State.Checks()[checkID]; ok {
t.Fatalf("check registered with exec disable")
}
}
func TestAgent_RegisterCheckScriptsExecRemoteDisable(t *testing.T) {
t.Parallel()
a := NewTestAgent(t.Name(), `
enable_local_script_checks = true
`)
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
args := &structs.CheckDefinition{
Name: "test",
ScriptArgs: []string{"true"},
Interval: time.Second,
}
req, _ := http.NewRequest("PUT", "/v1/agent/check/register?token=abc123", jsonReader(args))
res := httptest.NewRecorder()
_, err := a.srv.AgentRegisterCheck(res, req)
if err == nil {
t.Fatalf("expected error but got nil")
}
if !strings.Contains(err.Error(), "Scripts are disabled on this agent") {
t.Fatalf("expected script disabled error, got: %s", err)
}
checkID := types.CheckID("test")
if _, ok := a.State.Checks()[checkID]; ok {
t.Fatalf("check registered with exec disable")
}
}
func TestAgent_RegisterCheck_Passing(t *testing.T) { func TestAgent_RegisterCheck_Passing(t *testing.T) {
t.Parallel() t.Parallel()
a := NewTestAgent(t.Name(), "") a := NewTestAgent(t.Name(), "")
@ -1419,7 +1473,7 @@ func TestAgent_DeregisterCheck(t *testing.T) {
testrpc.WaitForTestAgent(t, a.RPC, "dc1") testrpc.WaitForTestAgent(t, a.RPC, "dc1")
chk := &structs.HealthCheck{Name: "test", CheckID: "test"} chk := &structs.HealthCheck{Name: "test", CheckID: "test"}
if err := a.AddCheck(chk, nil, false, ""); err != nil { if err := a.AddCheck(chk, nil, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1445,7 +1499,7 @@ func TestAgent_DeregisterCheckACLDeny(t *testing.T) {
testrpc.WaitForLeader(t, a.RPC, "dc1") testrpc.WaitForLeader(t, a.RPC, "dc1")
chk := &structs.HealthCheck{Name: "test", CheckID: "test"} chk := &structs.HealthCheck{Name: "test", CheckID: "test"}
if err := a.AddCheck(chk, nil, false, ""); err != nil { if err := a.AddCheck(chk, nil, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1472,7 +1526,7 @@ func TestAgent_PassCheck(t *testing.T) {
chk := &structs.HealthCheck{Name: "test", CheckID: "test"} chk := &structs.HealthCheck{Name: "test", CheckID: "test"}
chkType := &structs.CheckType{TTL: 15 * time.Second} chkType := &structs.CheckType{TTL: 15 * time.Second}
if err := a.AddCheck(chk, chkType, false, ""); err != nil { if err := a.AddCheck(chk, chkType, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1500,7 +1554,7 @@ func TestAgent_PassCheck_ACLDeny(t *testing.T) {
chk := &structs.HealthCheck{Name: "test", CheckID: "test"} chk := &structs.HealthCheck{Name: "test", CheckID: "test"}
chkType := &structs.CheckType{TTL: 15 * time.Second} chkType := &structs.CheckType{TTL: 15 * time.Second}
if err := a.AddCheck(chk, chkType, false, ""); err != nil { if err := a.AddCheck(chk, chkType, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1527,7 +1581,7 @@ func TestAgent_WarnCheck(t *testing.T) {
chk := &structs.HealthCheck{Name: "test", CheckID: "test"} chk := &structs.HealthCheck{Name: "test", CheckID: "test"}
chkType := &structs.CheckType{TTL: 15 * time.Second} chkType := &structs.CheckType{TTL: 15 * time.Second}
if err := a.AddCheck(chk, chkType, false, ""); err != nil { if err := a.AddCheck(chk, chkType, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1555,7 +1609,7 @@ func TestAgent_WarnCheck_ACLDeny(t *testing.T) {
chk := &structs.HealthCheck{Name: "test", CheckID: "test"} chk := &structs.HealthCheck{Name: "test", CheckID: "test"}
chkType := &structs.CheckType{TTL: 15 * time.Second} chkType := &structs.CheckType{TTL: 15 * time.Second}
if err := a.AddCheck(chk, chkType, false, ""); err != nil { if err := a.AddCheck(chk, chkType, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1582,7 +1636,7 @@ func TestAgent_FailCheck(t *testing.T) {
chk := &structs.HealthCheck{Name: "test", CheckID: "test"} chk := &structs.HealthCheck{Name: "test", CheckID: "test"}
chkType := &structs.CheckType{TTL: 15 * time.Second} chkType := &structs.CheckType{TTL: 15 * time.Second}
if err := a.AddCheck(chk, chkType, false, ""); err != nil { if err := a.AddCheck(chk, chkType, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1610,7 +1664,7 @@ func TestAgent_FailCheck_ACLDeny(t *testing.T) {
chk := &structs.HealthCheck{Name: "test", CheckID: "test"} chk := &structs.HealthCheck{Name: "test", CheckID: "test"}
chkType := &structs.CheckType{TTL: 15 * time.Second} chkType := &structs.CheckType{TTL: 15 * time.Second}
if err := a.AddCheck(chk, chkType, false, ""); err != nil { if err := a.AddCheck(chk, chkType, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1637,7 +1691,7 @@ func TestAgent_UpdateCheck(t *testing.T) {
chk := &structs.HealthCheck{Name: "test", CheckID: "test"} chk := &structs.HealthCheck{Name: "test", CheckID: "test"}
chkType := &structs.CheckType{TTL: 15 * time.Second} chkType := &structs.CheckType{TTL: 15 * time.Second}
if err := a.AddCheck(chk, chkType, false, ""); err != nil { if err := a.AddCheck(chk, chkType, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1721,7 +1775,7 @@ func TestAgent_UpdateCheck_ACLDeny(t *testing.T) {
chk := &structs.HealthCheck{Name: "test", CheckID: "test"} chk := &structs.HealthCheck{Name: "test", CheckID: "test"}
chkType := &structs.CheckType{TTL: 15 * time.Second} chkType := &structs.CheckType{TTL: 15 * time.Second}
if err := a.AddCheck(chk, chkType, false, ""); err != nil { if err := a.AddCheck(chk, chkType, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -2751,10 +2805,10 @@ func TestAgent_RegisterServiceDeregisterService_Sidecar(t *testing.T) {
testrpc.WaitForLeader(t, a.RPC, "dc1") testrpc.WaitForLeader(t, a.RPC, "dc1")
if tt.preRegister != nil { if tt.preRegister != nil {
require.NoError(a.AddService(tt.preRegister, nil, false, "")) require.NoError(a.AddService(tt.preRegister, nil, false, "", ConfigSourceLocal))
} }
if tt.preRegister2 != nil { if tt.preRegister2 != nil {
require.NoError(a.AddService(tt.preRegister2, nil, false, "")) require.NoError(a.AddService(tt.preRegister2, nil, false, "", ConfigSourceLocal))
} }
// Create an ACL token with require policy // Create an ACL token with require policy
@ -2910,6 +2964,80 @@ func TestAgent_RegisterService_ConnectNative(t *testing.T) {
assert.True(svc.Connect.Native) assert.True(svc.Connect.Native)
} }
func TestAgent_RegisterService_ScriptCheck_ExecDisable(t *testing.T) {
t.Parallel()
a := NewTestAgent(t.Name(), "")
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
args := &structs.ServiceDefinition{
Name: "test",
Meta: map[string]string{"hello": "world"},
Tags: []string{"master"},
Port: 8000,
Check: structs.CheckType{
Name: "test-check",
Interval: time.Second,
ScriptArgs: []string{"true"},
},
Weights: &structs.Weights{
Passing: 100,
Warning: 3,
},
}
req, _ := http.NewRequest("PUT", "/v1/agent/service/register?token=abc123", jsonReader(args))
_, err := a.srv.AgentRegisterService(nil, req)
if err == nil {
t.Fatalf("expected error but got nil")
}
if !strings.Contains(err.Error(), "Scripts are disabled on this agent") {
t.Fatalf("expected script disabled error, got: %s", err)
}
checkID := types.CheckID("test-check")
if _, ok := a.State.Checks()[checkID]; ok {
t.Fatalf("check registered with exec disable")
}
}
func TestAgent_RegisterService_ScriptCheck_ExecRemoteDisable(t *testing.T) {
t.Parallel()
a := NewTestAgent(t.Name(), `
enable_local_script_checks = true
`)
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
args := &structs.ServiceDefinition{
Name: "test",
Meta: map[string]string{"hello": "world"},
Tags: []string{"master"},
Port: 8000,
Check: structs.CheckType{
Name: "test-check",
Interval: time.Second,
ScriptArgs: []string{"true"},
},
Weights: &structs.Weights{
Passing: 100,
Warning: 3,
},
}
req, _ := http.NewRequest("PUT", "/v1/agent/service/register?token=abc123", jsonReader(args))
_, err := a.srv.AgentRegisterService(nil, req)
if err == nil {
t.Fatalf("expected error but got nil")
}
if !strings.Contains(err.Error(), "Scripts are disabled on this agent") {
t.Fatalf("expected script disabled error, got: %s", err)
}
checkID := types.CheckID("test-check")
if _, ok := a.State.Checks()[checkID]; ok {
t.Fatalf("check registered with exec disable")
}
}
func TestAgent_DeregisterService(t *testing.T) { func TestAgent_DeregisterService(t *testing.T) {
t.Parallel() t.Parallel()
a := NewTestAgent(t.Name(), "") a := NewTestAgent(t.Name(), "")
@ -2920,7 +3048,7 @@ func TestAgent_DeregisterService(t *testing.T) {
ID: "test", ID: "test",
Service: "test", Service: "test",
} }
if err := a.AddService(service, nil, false, ""); err != nil { if err := a.AddService(service, nil, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -2953,7 +3081,7 @@ func TestAgent_DeregisterService_ACLDeny(t *testing.T) {
ID: "test", ID: "test",
Service: "test", Service: "test",
} }
if err := a.AddService(service, nil, false, ""); err != nil { if err := a.AddService(service, nil, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -3127,7 +3255,7 @@ func TestAgent_ServiceMaintenance_Enable(t *testing.T) {
ID: "test", ID: "test",
Service: "test", Service: "test",
} }
if err := a.AddService(service, nil, false, ""); err != nil { if err := a.AddService(service, nil, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -3170,7 +3298,7 @@ func TestAgent_ServiceMaintenance_Disable(t *testing.T) {
ID: "test", ID: "test",
Service: "test", Service: "test",
} }
if err := a.AddService(service, nil, false, ""); err != nil { if err := a.AddService(service, nil, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -3207,7 +3335,7 @@ func TestAgent_ServiceMaintenance_ACLDeny(t *testing.T) {
ID: "test", ID: "test",
Service: "test", Service: "test",
} }
if err := a.AddService(service, nil, false, ""); err != nil { if err := a.AddService(service, nil, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }

View File

@ -438,7 +438,7 @@ func TestAgent_AddService(t *testing.T) {
t.Run(tt.desc, func(t *testing.T) { t.Run(tt.desc, func(t *testing.T) {
// check the service registration // check the service registration
t.Run(tt.srv.ID, func(t *testing.T) { t.Run(tt.srv.ID, func(t *testing.T) {
err := a.AddService(tt.srv, tt.chkTypes, false, "") err := a.AddService(tt.srv, tt.chkTypes, false, "", ConfigSourceLocal)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -474,6 +474,62 @@ func TestAgent_AddService(t *testing.T) {
} }
} }
func TestAgent_AddServiceNoExec(t *testing.T) {
t.Parallel()
a := NewTestAgent(t.Name(), `
node_name = "node1"
`)
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
srv := &structs.NodeService{
ID: "svcid1",
Service: "svcname1",
Tags: []string{"tag1"},
Port: 8100,
}
chk := &structs.CheckType{
ScriptArgs: []string{"exit", "0"},
Interval: 15 * time.Second,
}
err := a.AddService(srv, []*structs.CheckType{chk}, false, "", ConfigSourceLocal)
if err == nil || !strings.Contains(err.Error(), "Scripts are disabled on this agent") {
t.Fatalf("err: %v", err)
}
err = a.AddService(srv, []*structs.CheckType{chk}, false, "", ConfigSourceRemote)
if err == nil || !strings.Contains(err.Error(), "Scripts are disabled on this agent") {
t.Fatalf("err: %v", err)
}
}
func TestAgent_AddServiceNoRemoteExec(t *testing.T) {
t.Parallel()
a := NewTestAgent(t.Name(), `
node_name = "node1"
enable_local_script_checks = true
`)
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
srv := &structs.NodeService{
ID: "svcid1",
Service: "svcname1",
Tags: []string{"tag1"},
Port: 8100,
}
chk := &structs.CheckType{
ScriptArgs: []string{"exit", "0"},
Interval: 15 * time.Second,
}
err := a.AddService(srv, []*structs.CheckType{chk}, false, "", ConfigSourceRemote)
if err == nil || !strings.Contains(err.Error(), "Scripts are disabled on this agent") {
t.Fatalf("err: %v", err)
}
}
func TestAgent_RemoveService(t *testing.T) { func TestAgent_RemoveService(t *testing.T) {
t.Parallel() t.Parallel()
a := NewTestAgent(t.Name(), "") a := NewTestAgent(t.Name(), "")
@ -498,7 +554,7 @@ func TestAgent_RemoveService(t *testing.T) {
} }
chkTypes := []*structs.CheckType{&structs.CheckType{TTL: time.Minute}} chkTypes := []*structs.CheckType{&structs.CheckType{TTL: time.Minute}}
if err := a.AddService(srv, chkTypes, false, ""); err != nil { if err := a.AddService(srv, chkTypes, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -510,7 +566,7 @@ func TestAgent_RemoveService(t *testing.T) {
TTL: time.Minute, TTL: time.Minute,
} }
hc := check.HealthCheck("node1") hc := check.HealthCheck("node1")
if err := a.AddCheck(hc, check.CheckType(), false, ""); err != nil { if err := a.AddCheck(hc, check.CheckType(), false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -536,7 +592,7 @@ func TestAgent_RemoveService(t *testing.T) {
&structs.CheckType{TTL: time.Minute}, &structs.CheckType{TTL: time.Minute},
&structs.CheckType{TTL: 30 * time.Second}, &structs.CheckType{TTL: 30 * time.Second},
} }
if err := a.AddService(srv, chkTypes, false, ""); err != nil { if err := a.AddService(srv, chkTypes, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -582,7 +638,7 @@ func TestAgent_RemoveServiceRemovesAllChecks(t *testing.T) {
hchk2 := &structs.HealthCheck{Node: "node1", CheckID: "chk2", Name: "chk2", Status: "critical", ServiceID: "redis", ServiceName: "redis"} hchk2 := &structs.HealthCheck{Node: "node1", CheckID: "chk2", Name: "chk2", Status: "critical", ServiceID: "redis", ServiceName: "redis"}
// register service with chk1 // register service with chk1
if err := a.AddService(svc, []*structs.CheckType{chk1}, false, ""); err != nil { if err := a.AddService(svc, []*structs.CheckType{chk1}, false, "", ConfigSourceLocal); err != nil {
t.Fatal("Failed to register service", err) t.Fatal("Failed to register service", err)
} }
@ -592,7 +648,7 @@ func TestAgent_RemoveServiceRemovesAllChecks(t *testing.T) {
} }
// update the service with chk2 // update the service with chk2
if err := a.AddService(svc, []*structs.CheckType{chk2}, false, ""); err != nil { if err := a.AddService(svc, []*structs.CheckType{chk2}, false, "", ConfigSourceLocal); err != nil {
t.Fatal("Failed to update service", err) t.Fatal("Failed to update service", err)
} }
@ -655,7 +711,7 @@ func verifyIndexChurn(t *testing.T, tags []string) {
Tags: tags, Tags: tags,
Weights: weights, Weights: weights,
} }
if err := a.AddService(svc, nil, true, ""); err != nil { if err := a.AddService(svc, nil, true, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -668,7 +724,7 @@ func verifyIndexChurn(t *testing.T, tags []string) {
chkt := &structs.CheckType{ chkt := &structs.CheckType{
TTL: time.Hour, TTL: time.Hour,
} }
if err := a.AddCheck(chk, chkt, true, ""); err != nil { if err := a.AddCheck(chk, chkt, true, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -680,7 +736,7 @@ func verifyIndexChurn(t *testing.T, tags []string) {
chkt = &structs.CheckType{ chkt = &structs.CheckType{
TTL: time.Hour, TTL: time.Hour,
} }
if err := a.AddCheck(chk, chkt, true, ""); err != nil { if err := a.AddCheck(chk, chkt, true, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -745,7 +801,7 @@ func TestAgent_AddCheck(t *testing.T) {
ScriptArgs: []string{"exit", "0"}, ScriptArgs: []string{"exit", "0"},
Interval: 15 * time.Second, Interval: 15 * time.Second,
} }
err := a.AddCheck(health, chk, false, "") err := a.AddCheck(health, chk, false, "", ConfigSourceLocal)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -784,7 +840,7 @@ func TestAgent_AddCheck_StartPassing(t *testing.T) {
ScriptArgs: []string{"exit", "0"}, ScriptArgs: []string{"exit", "0"},
Interval: 15 * time.Second, Interval: 15 * time.Second,
} }
err := a.AddCheck(health, chk, false, "") err := a.AddCheck(health, chk, false, "", ConfigSourceLocal)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -823,7 +879,7 @@ func TestAgent_AddCheck_MinInterval(t *testing.T) {
ScriptArgs: []string{"exit", "0"}, ScriptArgs: []string{"exit", "0"},
Interval: time.Microsecond, Interval: time.Microsecond,
} }
err := a.AddCheck(health, chk, false, "") err := a.AddCheck(health, chk, false, "", ConfigSourceLocal)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -858,7 +914,7 @@ func TestAgent_AddCheck_MissingService(t *testing.T) {
ScriptArgs: []string{"exit", "0"}, ScriptArgs: []string{"exit", "0"},
Interval: time.Microsecond, Interval: time.Microsecond,
} }
err := a.AddCheck(health, chk, false, "") err := a.AddCheck(health, chk, false, "", ConfigSourceLocal)
if err == nil || err.Error() != `ServiceID "baz" does not exist` { if err == nil || err.Error() != `ServiceID "baz" does not exist` {
t.Fatalf("expected service id error, got: %v", err) t.Fatalf("expected service id error, got: %v", err)
} }
@ -888,7 +944,7 @@ func TestAgent_AddCheck_RestoreState(t *testing.T) {
chk := &structs.CheckType{ chk := &structs.CheckType{
TTL: time.Minute, TTL: time.Minute,
} }
err = a.AddCheck(health, chk, false, "") err = a.AddCheck(health, chk, false, "", ConfigSourceLocal)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -923,7 +979,7 @@ func TestAgent_AddCheck_ExecDisable(t *testing.T) {
ScriptArgs: []string{"exit", "0"}, ScriptArgs: []string{"exit", "0"},
Interval: 15 * time.Second, Interval: 15 * time.Second,
} }
err := a.AddCheck(health, chk, false, "") err := a.AddCheck(health, chk, false, "", ConfigSourceLocal)
if err == nil || !strings.Contains(err.Error(), "Scripts are disabled on this agent") { if err == nil || !strings.Contains(err.Error(), "Scripts are disabled on this agent") {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -932,6 +988,46 @@ func TestAgent_AddCheck_ExecDisable(t *testing.T) {
if memChk := a.State.Checks()["mem"]; memChk != nil { if memChk := a.State.Checks()["mem"]; memChk != nil {
t.Fatalf("should be missing mem check") t.Fatalf("should be missing mem check")
} }
err = a.AddCheck(health, chk, false, "", ConfigSourceRemote)
if err == nil || !strings.Contains(err.Error(), "Scripts are disabled on this agent") {
t.Fatalf("err: %v", err)
}
// Ensure we don't have a check mapping
if memChk := a.State.Checks()["mem"]; memChk != nil {
t.Fatalf("should be missing mem check")
}
}
func TestAgent_AddCheck_ExecRemoteDisable(t *testing.T) {
t.Parallel()
a := NewTestAgent(t.Name(), `
enable_local_script_checks = true
`)
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
health := &structs.HealthCheck{
Node: "foo",
CheckID: "mem",
Name: "memory util",
Status: api.HealthCritical,
}
chk := &structs.CheckType{
ScriptArgs: []string{"exit", "0"},
Interval: 15 * time.Second,
}
err := a.AddCheck(health, chk, false, "", ConfigSourceRemote)
if err == nil || !strings.Contains(err.Error(), "Scripts are disabled on this agent from remote calls") {
t.Fatalf("err: %v", err)
}
// Ensure we don't have a check mapping
if memChk := a.State.Checks()["mem"]; memChk != nil {
t.Fatalf("should be missing mem check")
}
} }
func TestAgent_AddCheck_GRPC(t *testing.T) { func TestAgent_AddCheck_GRPC(t *testing.T) {
@ -949,7 +1045,7 @@ func TestAgent_AddCheck_GRPC(t *testing.T) {
GRPC: "localhost:12345/package.Service", GRPC: "localhost:12345/package.Service",
Interval: 15 * time.Second, Interval: 15 * time.Second,
} }
err := a.AddCheck(health, chk, false, "") err := a.AddCheck(health, chk, false, "", ConfigSourceLocal)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -987,7 +1083,7 @@ func TestAgent_AddCheck_Alias(t *testing.T) {
chk := &structs.CheckType{ chk := &structs.CheckType{
AliasService: "foo", AliasService: "foo",
} }
err := a.AddCheck(health, chk, false, "") err := a.AddCheck(health, chk, false, "", ConfigSourceLocal)
require.NoError(err) require.NoError(err)
// Ensure we have a check mapping // Ensure we have a check mapping
@ -1021,7 +1117,7 @@ func TestAgent_AddCheck_Alias_setToken(t *testing.T) {
chk := &structs.CheckType{ chk := &structs.CheckType{
AliasService: "foo", AliasService: "foo",
} }
err := a.AddCheck(health, chk, false, "foo") err := a.AddCheck(health, chk, false, "foo", ConfigSourceLocal)
require.NoError(err) require.NoError(err)
cs := a.State.CheckState("aliashealth") cs := a.State.CheckState("aliashealth")
@ -1051,7 +1147,7 @@ acl_token = "hello"
chk := &structs.CheckType{ chk := &structs.CheckType{
AliasService: "foo", AliasService: "foo",
} }
err := a.AddCheck(health, chk, false, "") err := a.AddCheck(health, chk, false, "", ConfigSourceLocal)
require.NoError(err) require.NoError(err)
cs := a.State.CheckState("aliashealth") cs := a.State.CheckState("aliashealth")
@ -1081,7 +1177,7 @@ acl_token = "hello"
chk := &structs.CheckType{ chk := &structs.CheckType{
AliasService: "foo", AliasService: "foo",
} }
err := a.AddCheck(health, chk, false, "goodbye") err := a.AddCheck(health, chk, false, "goodbye", ConfigSourceLocal)
require.NoError(err) require.NoError(err)
cs := a.State.CheckState("aliashealth") cs := a.State.CheckState("aliashealth")
@ -1120,7 +1216,7 @@ func TestAgent_RemoveCheck(t *testing.T) {
ScriptArgs: []string{"exit", "0"}, ScriptArgs: []string{"exit", "0"},
Interval: 15 * time.Second, Interval: 15 * time.Second,
} }
err := a.AddCheck(health, chk, false, "") err := a.AddCheck(health, chk, false, "", ConfigSourceLocal)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1165,7 +1261,7 @@ func TestAgent_HTTPCheck_TLSSkipVerify(t *testing.T) {
TLSSkipVerify: true, TLSSkipVerify: true,
} }
err := a.AddCheck(health, chk, false, "") err := a.AddCheck(health, chk, false, "", ConfigSourceLocal)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1214,7 +1310,7 @@ func TestAgent_HTTPCheck_EnableAgentTLSForChecks(t *testing.T) {
Interval: 20 * time.Millisecond, Interval: 20 * time.Millisecond,
} }
err := a.AddCheck(health, chk, false, "") err := a.AddCheck(health, chk, false, "", ConfigSourceLocal)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1263,7 +1359,7 @@ func TestAgent_updateTTLCheck(t *testing.T) {
} }
// Add check and update it. // Add check and update it.
err := a.AddCheck(health, chk, false, "") err := a.AddCheck(health, chk, false, "", ConfigSourceLocal)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1304,7 +1400,7 @@ func TestAgent_PersistService(t *testing.T) {
file := filepath.Join(a.Config.DataDir, servicesDir, stringHash(svc.ID)) file := filepath.Join(a.Config.DataDir, servicesDir, stringHash(svc.ID))
// Check is not persisted unless requested // Check is not persisted unless requested
if err := a.AddService(svc, nil, false, ""); err != nil { if err := a.AddService(svc, nil, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
if _, err := os.Stat(file); err == nil { if _, err := os.Stat(file); err == nil {
@ -1312,7 +1408,7 @@ func TestAgent_PersistService(t *testing.T) {
} }
// Persists to file if requested // Persists to file if requested
if err := a.AddService(svc, nil, true, "mytoken"); err != nil { if err := a.AddService(svc, nil, true, "mytoken", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
if _, err := os.Stat(file); err != nil { if _, err := os.Stat(file); err != nil {
@ -1335,7 +1431,7 @@ func TestAgent_PersistService(t *testing.T) {
// Updates service definition on disk // Updates service definition on disk
svc.Port = 8001 svc.Port = 8001
if err := a.AddService(svc, nil, true, "mytoken"); err != nil { if err := a.AddService(svc, nil, true, "mytoken", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
expected, err = json.Marshal(persistedService{ expected, err = json.Marshal(persistedService{
@ -1429,7 +1525,7 @@ func TestAgent_PurgeService(t *testing.T) {
} }
file := filepath.Join(a.Config.DataDir, servicesDir, stringHash(svc.ID)) file := filepath.Join(a.Config.DataDir, servicesDir, stringHash(svc.ID))
if err := a.AddService(svc, nil, true, ""); err != nil { if err := a.AddService(svc, nil, true, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1442,7 +1538,7 @@ func TestAgent_PurgeService(t *testing.T) {
} }
// Re-add the service // Re-add the service
if err := a.AddService(svc, nil, true, ""); err != nil { if err := a.AddService(svc, nil, true, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1476,7 +1572,7 @@ func TestAgent_PurgeServiceOnDuplicate(t *testing.T) {
} }
// First persist the service // First persist the service
if err := a.AddService(svc1, nil, true, ""); err != nil { if err := a.AddService(svc1, nil, true, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
a.Shutdown() a.Shutdown()
@ -1530,7 +1626,7 @@ func TestAgent_PersistProxy(t *testing.T) {
Tags: []string{"foo"}, Tags: []string{"foo"},
Port: 8000, Port: 8000,
} }
require.NoError(a.AddService(svc1, nil, true, "")) require.NoError(a.AddService(svc1, nil, true, "", ConfigSourceLocal))
// Add a proxy for it // Add a proxy for it
proxy := &structs.ConnectManagedProxy{ proxy := &structs.ConnectManagedProxy{
@ -1541,12 +1637,12 @@ func TestAgent_PersistProxy(t *testing.T) {
file := filepath.Join(a.Config.DataDir, proxyDir, stringHash("redis-proxy")) file := filepath.Join(a.Config.DataDir, proxyDir, stringHash("redis-proxy"))
// Proxy is not persisted unless requested // Proxy is not persisted unless requested
require.NoError(a.AddProxy(proxy, false, false, "")) require.NoError(a.AddProxy(proxy, false, false, "", ConfigSourceLocal))
_, err := os.Stat(file) _, err := os.Stat(file)
require.Error(err, "proxy should not be persisted") require.Error(err, "proxy should not be persisted")
// Proxy is persisted if requested // Proxy is persisted if requested
require.NoError(a.AddProxy(proxy, true, false, "")) require.NoError(a.AddProxy(proxy, true, false, "", ConfigSourceLocal))
_, err = os.Stat(file) _, err = os.Stat(file)
require.NoError(err, "proxy should be persisted") require.NoError(err, "proxy should be persisted")
@ -1562,7 +1658,7 @@ func TestAgent_PersistProxy(t *testing.T) {
proxy.Config = map[string]interface{}{ proxy.Config = map[string]interface{}{
"foo": "bar", "foo": "bar",
} }
require.NoError(a.AddProxy(proxy, true, false, "")) require.NoError(a.AddProxy(proxy, true, false, "", ConfigSourceLocal))
content, err = ioutil.ReadFile(file) content, err = ioutil.ReadFile(file)
require.NoError(err) require.NoError(err)
@ -1601,7 +1697,7 @@ func TestAgent_PurgeProxy(t *testing.T) {
Tags: []string{"foo"}, Tags: []string{"foo"},
Port: 8000, Port: 8000,
} }
require.NoError(a.AddService(svc1, nil, true, "")) require.NoError(a.AddService(svc1, nil, true, "", ConfigSourceLocal))
// Add a proxy for it // Add a proxy for it
proxy := &structs.ConnectManagedProxy{ proxy := &structs.ConnectManagedProxy{
@ -1609,7 +1705,7 @@ func TestAgent_PurgeProxy(t *testing.T) {
Command: []string{"/bin/sleep", "3600"}, Command: []string{"/bin/sleep", "3600"},
} }
proxyID := "redis-proxy" proxyID := "redis-proxy"
require.NoError(a.AddProxy(proxy, true, false, "")) require.NoError(a.AddProxy(proxy, true, false, "", ConfigSourceLocal))
file := filepath.Join(a.Config.DataDir, proxyDir, stringHash("redis-proxy")) file := filepath.Join(a.Config.DataDir, proxyDir, stringHash("redis-proxy"))
@ -1619,7 +1715,7 @@ func TestAgent_PurgeProxy(t *testing.T) {
require.NoError(err, "should not be removed") require.NoError(err, "should not be removed")
// Re-add the proxy // Re-add the proxy
require.NoError(a.AddProxy(proxy, true, false, "")) require.NoError(a.AddProxy(proxy, true, false, "", ConfigSourceLocal))
// Removed // Removed
require.NoError(a.RemoveProxy(proxyID, true)) require.NoError(a.RemoveProxy(proxyID, true))
@ -1649,7 +1745,7 @@ func TestAgent_PurgeProxyOnDuplicate(t *testing.T) {
Tags: []string{"foo"}, Tags: []string{"foo"},
Port: 8000, Port: 8000,
} }
require.NoError(a.AddService(svc1, nil, true, "")) require.NoError(a.AddService(svc1, nil, true, "", ConfigSourceLocal))
// Add a proxy for it // Add a proxy for it
proxy := &structs.ConnectManagedProxy{ proxy := &structs.ConnectManagedProxy{
@ -1657,7 +1753,7 @@ func TestAgent_PurgeProxyOnDuplicate(t *testing.T) {
Command: []string{"/bin/sleep", "3600"}, Command: []string{"/bin/sleep", "3600"},
} }
proxyID := "redis-proxy" proxyID := "redis-proxy"
require.NoError(a.AddProxy(proxy, true, false, "")) require.NoError(a.AddProxy(proxy, true, false, "", ConfigSourceLocal))
a.Shutdown() a.Shutdown()
@ -1716,7 +1812,7 @@ func TestAgent_PersistCheck(t *testing.T) {
file := filepath.Join(a.Config.DataDir, checksDir, checkIDHash(check.CheckID)) file := filepath.Join(a.Config.DataDir, checksDir, checkIDHash(check.CheckID))
// Not persisted if not requested // Not persisted if not requested
if err := a.AddCheck(check, chkType, false, ""); err != nil { if err := a.AddCheck(check, chkType, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
if _, err := os.Stat(file); err == nil { if _, err := os.Stat(file); err == nil {
@ -1724,7 +1820,7 @@ func TestAgent_PersistCheck(t *testing.T) {
} }
// Should persist if requested // Should persist if requested
if err := a.AddCheck(check, chkType, true, "mytoken"); err != nil { if err := a.AddCheck(check, chkType, true, "mytoken", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
if _, err := os.Stat(file); err != nil { if _, err := os.Stat(file); err != nil {
@ -1748,7 +1844,7 @@ func TestAgent_PersistCheck(t *testing.T) {
// Updates the check definition on disk // Updates the check definition on disk
check.Name = "mem1" check.Name = "mem1"
if err := a.AddCheck(check, chkType, true, "mytoken"); err != nil { if err := a.AddCheck(check, chkType, true, "mytoken", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
expected, err = json.Marshal(persistedCheck{ expected, err = json.Marshal(persistedCheck{
@ -1806,7 +1902,7 @@ func TestAgent_PurgeCheck(t *testing.T) {
} }
file := filepath.Join(a.Config.DataDir, checksDir, checkIDHash(check.CheckID)) file := filepath.Join(a.Config.DataDir, checksDir, checkIDHash(check.CheckID))
if err := a.AddCheck(check, nil, true, ""); err != nil { if err := a.AddCheck(check, nil, true, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1850,7 +1946,7 @@ func TestAgent_PurgeCheckOnDuplicate(t *testing.T) {
} }
// First persist the check // First persist the check
if err := a.AddCheck(check1, nil, true, ""); err != nil { if err := a.AddCheck(check1, nil, true, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
a.Shutdown() a.Shutdown()
@ -1926,7 +2022,7 @@ func TestAgent_unloadChecks(t *testing.T) {
Tags: []string{"foo"}, Tags: []string{"foo"},
Port: 8000, Port: 8000,
} }
if err := a.AddService(svc, nil, false, ""); err != nil { if err := a.AddService(svc, nil, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -1939,7 +2035,7 @@ func TestAgent_unloadChecks(t *testing.T) {
ServiceID: "redis", ServiceID: "redis",
ServiceName: "redis", ServiceName: "redis",
} }
if err := a.AddCheck(check1, nil, false, ""); err != nil { if err := a.AddCheck(check1, nil, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
found := false found := false
@ -2066,7 +2162,7 @@ func TestAgent_unloadServices(t *testing.T) {
} }
// Register the service // Register the service
if err := a.AddService(svc, nil, false, ""); err != nil { if err := a.AddService(svc, nil, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
found := false found := false
@ -2192,7 +2288,7 @@ func TestAgent_Service_MaintenanceMode(t *testing.T) {
} }
// Register the service // Register the service
if err := a.AddService(svc, nil, false, ""); err != nil { if err := a.AddService(svc, nil, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -2267,7 +2363,7 @@ func TestAgent_Service_Reap(t *testing.T) {
} }
// Register the service. // Register the service.
if err := a.AddService(svc, chkTypes, false, ""); err != nil { if err := a.AddService(svc, chkTypes, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -2340,7 +2436,7 @@ func TestAgent_Service_NoReap(t *testing.T) {
} }
// Register the service. // Register the service.
if err := a.AddService(svc, chkTypes, false, ""); err != nil { if err := a.AddService(svc, chkTypes, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -2383,7 +2479,7 @@ func TestAgent_addCheck_restoresSnapshot(t *testing.T) {
Tags: []string{"foo"}, Tags: []string{"foo"},
Port: 8000, Port: 8000,
} }
if err := a.AddService(svc, nil, false, ""); err != nil { if err := a.AddService(svc, nil, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -2396,13 +2492,13 @@ func TestAgent_addCheck_restoresSnapshot(t *testing.T) {
ServiceID: "redis", ServiceID: "redis",
ServiceName: "redis", ServiceName: "redis",
} }
if err := a.AddCheck(check1, nil, false, ""); err != nil { if err := a.AddCheck(check1, nil, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
// Re-registering the service preserves the state of the check // Re-registering the service preserves the state of the check
chkTypes := []*structs.CheckType{&structs.CheckType{TTL: 30 * time.Second}} chkTypes := []*structs.CheckType{&structs.CheckType{TTL: 30 * time.Second}}
if err := a.AddService(svc, chkTypes, false, ""); err != nil { if err := a.AddService(svc, chkTypes, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
check, ok := a.State.Checks()["service:redis"] check, ok := a.State.Checks()["service:redis"]
@ -2471,7 +2567,7 @@ func TestAgent_checkStateSnapshot(t *testing.T) {
Tags: []string{"foo"}, Tags: []string{"foo"},
Port: 8000, Port: 8000,
} }
if err := a.AddService(svc, nil, false, ""); err != nil { if err := a.AddService(svc, nil, false, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -2484,7 +2580,7 @@ func TestAgent_checkStateSnapshot(t *testing.T) {
ServiceID: "redis", ServiceID: "redis",
ServiceName: "redis", ServiceName: "redis",
} }
if err := a.AddCheck(check1, nil, true, ""); err != nil { if err := a.AddCheck(check1, nil, true, "", ConfigSourceLocal); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -2976,9 +3072,9 @@ func TestAgent_AddProxy(t *testing.T) {
Service: "web", Service: "web",
Port: 8080, Port: 8080,
} }
require.NoError(a.AddService(reg, nil, false, "")) require.NoError(a.AddService(reg, nil, false, "", ConfigSourceLocal))
err := a.AddProxy(tt.proxy, false, false, "") err := a.AddProxy(tt.proxy, false, false, "", ConfigSourceLocal)
if tt.wantErr { if tt.wantErr {
require.Error(err) require.Error(err)
return return
@ -3030,7 +3126,7 @@ func TestAgent_RemoveProxy(t *testing.T) {
Service: "web", Service: "web",
Port: 8080, Port: 8080,
} }
require.NoError(a.AddService(reg, nil, false, "")) require.NoError(a.AddService(reg, nil, false, "", ConfigSourceLocal))
// Add a proxy for web // Add a proxy for web
pReg := &structs.ConnectManagedProxy{ pReg := &structs.ConnectManagedProxy{
@ -3038,7 +3134,7 @@ func TestAgent_RemoveProxy(t *testing.T) {
ExecMode: structs.ProxyExecModeDaemon, ExecMode: structs.ProxyExecModeDaemon,
Command: []string{"foo"}, Command: []string{"foo"},
} }
require.NoError(a.AddProxy(pReg, false, false, "")) require.NoError(a.AddProxy(pReg, false, false, "", ConfigSourceLocal))
// Test the ID was created as we expect. // Test the ID was created as we expect.
gotProxy := a.State.Proxy("web-proxy") gotProxy := a.State.Proxy("web-proxy")
@ -3069,7 +3165,7 @@ func TestAgent_ReLoadProxiesFromConfig(t *testing.T) {
Service: "web", Service: "web",
Port: 8080, Port: 8080,
} }
require.NoError(a.AddService(reg, nil, false, "")) require.NoError(a.AddService(reg, nil, false, "", ConfigSourceLocal))
proxies := a.State.Proxies() proxies := a.State.Proxies()
require.Len(proxies, 0) require.Len(proxies, 0)

View File

@ -588,6 +588,9 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
proxyDefaultScriptCommand := c.Connect.ProxyDefaults.ScriptCommand proxyDefaultScriptCommand := c.Connect.ProxyDefaults.ScriptCommand
proxyDefaultConfig := c.Connect.ProxyDefaults.Config proxyDefaultConfig := c.Connect.ProxyDefaults.Config
enableRemoteScriptChecks := b.boolVal(c.EnableScriptChecks)
enableLocalScriptChecks := b.boolValWithDefault(c.EnableLocalScriptChecks, enableRemoteScriptChecks)
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// build runtime config // build runtime config
// //
@ -743,7 +746,8 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
DiscoveryMaxStale: b.durationVal("discovery_max_stale", c.DiscoveryMaxStale), DiscoveryMaxStale: b.durationVal("discovery_max_stale", c.DiscoveryMaxStale),
EnableAgentTLSForChecks: b.boolVal(c.EnableAgentTLSForChecks), EnableAgentTLSForChecks: b.boolVal(c.EnableAgentTLSForChecks),
EnableDebug: b.boolVal(c.EnableDebug), EnableDebug: b.boolVal(c.EnableDebug),
EnableScriptChecks: b.boolVal(c.EnableScriptChecks), EnableRemoteScriptChecks: enableRemoteScriptChecks,
EnableLocalScriptChecks: enableLocalScriptChecks,
EnableSyslog: b.boolVal(c.EnableSyslog), EnableSyslog: b.boolVal(c.EnableSyslog),
EnableUI: b.boolVal(c.UI), EnableUI: b.boolVal(c.UI),
EncryptKey: b.stringVal(c.EncryptKey), EncryptKey: b.stringVal(c.EncryptKey),

View File

@ -191,6 +191,7 @@ type Config struct {
EnableAgentTLSForChecks *bool `json:"enable_agent_tls_for_checks,omitempty" hcl:"enable_agent_tls_for_checks" mapstructure:"enable_agent_tls_for_checks"` EnableAgentTLSForChecks *bool `json:"enable_agent_tls_for_checks,omitempty" hcl:"enable_agent_tls_for_checks" mapstructure:"enable_agent_tls_for_checks"`
EnableDebug *bool `json:"enable_debug,omitempty" hcl:"enable_debug" mapstructure:"enable_debug"` EnableDebug *bool `json:"enable_debug,omitempty" hcl:"enable_debug" mapstructure:"enable_debug"`
EnableScriptChecks *bool `json:"enable_script_checks,omitempty" hcl:"enable_script_checks" mapstructure:"enable_script_checks"` EnableScriptChecks *bool `json:"enable_script_checks,omitempty" hcl:"enable_script_checks" mapstructure:"enable_script_checks"`
EnableLocalScriptChecks *bool `json:"enable_local_script_checks,omitempty" hcl:"enable_local_script_checks" mapstructure:"enable_local_script_checks"`
EnableSyslog *bool `json:"enable_syslog,omitempty" hcl:"enable_syslog" mapstructure:"enable_syslog"` EnableSyslog *bool `json:"enable_syslog,omitempty" hcl:"enable_syslog" mapstructure:"enable_syslog"`
EncryptKey *string `json:"encrypt,omitempty" hcl:"encrypt" mapstructure:"encrypt"` EncryptKey *string `json:"encrypt,omitempty" hcl:"encrypt" mapstructure:"encrypt"`
EncryptVerifyIncoming *bool `json:"encrypt_verify_incoming,omitempty" hcl:"encrypt_verify_incoming" mapstructure:"encrypt_verify_incoming"` EncryptVerifyIncoming *bool `json:"encrypt_verify_incoming,omitempty" hcl:"encrypt_verify_incoming" mapstructure:"encrypt_verify_incoming"`

View File

@ -71,6 +71,7 @@ func AddFlags(fs *flag.FlagSet, f *Flags) {
add(&f.Config.Ports.DNS, "dns-port", "DNS port to use.") add(&f.Config.Ports.DNS, "dns-port", "DNS port to use.")
add(&f.Config.DNSDomain, "domain", "Domain to use for DNS interface.") add(&f.Config.DNSDomain, "domain", "Domain to use for DNS interface.")
add(&f.Config.EnableScriptChecks, "enable-script-checks", "Enables health check scripts.") add(&f.Config.EnableScriptChecks, "enable-script-checks", "Enables health check scripts.")
add(&f.Config.EnableLocalScriptChecks, "enable-local-script-checks", "Enables health check scripts from configuration file.")
add(&f.Config.EncryptKey, "encrypt", "Provides the gossip encryption key.") add(&f.Config.EncryptKey, "encrypt", "Provides the gossip encryption key.")
add(&f.Config.Ports.GRPC, "grpc-port", "Sets the gRPC API port to listen on (currently needed for Envoy xDS only).") add(&f.Config.Ports.GRPC, "grpc-port", "Sets the gRPC API port to listen on (currently needed for Envoy xDS only).")
add(&f.Config.Ports.HTTP, "http-port", "Sets the HTTP API port to listen on.") add(&f.Config.Ports.HTTP, "http-port", "Sets the HTTP API port to listen on.")

View File

@ -638,13 +638,21 @@ type RuntimeConfig struct {
// hcl: enable_debug = (true|false) // hcl: enable_debug = (true|false)
EnableDebug bool EnableDebug bool
// EnableScriptChecks controls whether health checks which execute // EnableLocalScriptChecks controls whether health checks declared from the local
// scripts are enabled. This includes regular script checks and Docker // config file which execute scripts are enabled. This includes regular script
// checks and Docker checks.
//
// hcl: (enable_script_checks|enable_local_script_checks) = (true|false)
// flag: -enable-script-checks, -enable-local-script-checks
EnableLocalScriptChecks bool
// EnableRemoeScriptChecks controls whether health checks declared from the http API
// which execute scripts are enabled. This includes regular script checks and Docker
// checks. // checks.
// //
// hcl: enable_script_checks = (true|false) // hcl: enable_script_checks = (true|false)
// flag: -enable-script-checks // flag: -enable-script-checks
EnableScriptChecks bool EnableRemoteScriptChecks bool
// EnableSyslog is used to also tee all the logs over to syslog. Only supported // EnableSyslog is used to also tee all the logs over to syslog. Only supported
// on linux and OSX. Other platforms will generate an error. // on linux and OSX. Other platforms will generate an error.

View File

@ -351,7 +351,8 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
`-data-dir=` + dataDir, `-data-dir=` + dataDir,
}, },
patch: func(rt *RuntimeConfig) { patch: func(rt *RuntimeConfig) {
rt.EnableScriptChecks = true rt.EnableLocalScriptChecks = true
rt.EnableRemoteScriptChecks = true
rt.DataDir = dataDir rt.DataDir = dataDir
}, },
}, },
@ -2973,6 +2974,7 @@ func TestFullConfig(t *testing.T) {
"enable_agent_tls_for_checks": true, "enable_agent_tls_for_checks": true,
"enable_debug": true, "enable_debug": true,
"enable_script_checks": true, "enable_script_checks": true,
"enable_local_script_checks": true,
"enable_syslog": true, "enable_syslog": true,
"encrypt": "A4wELWqH", "encrypt": "A4wELWqH",
"encrypt_verify_incoming": true, "encrypt_verify_incoming": true,
@ -3504,6 +3506,7 @@ func TestFullConfig(t *testing.T) {
enable_agent_tls_for_checks = true enable_agent_tls_for_checks = true
enable_debug = true enable_debug = true
enable_script_checks = true enable_script_checks = true
enable_local_script_checks = true
enable_syslog = true enable_syslog = true
encrypt = "A4wELWqH" encrypt = "A4wELWqH"
encrypt_verify_incoming = true encrypt_verify_incoming = true
@ -4129,7 +4132,8 @@ func TestFullConfig(t *testing.T) {
EnableACLReplication: true, EnableACLReplication: true,
EnableAgentTLSForChecks: true, EnableAgentTLSForChecks: true,
EnableDebug: true, EnableDebug: true,
EnableScriptChecks: true, EnableRemoteScriptChecks: true,
EnableLocalScriptChecks: true,
EnableSyslog: true, EnableSyslog: true,
EnableUI: true, EnableUI: true,
EncryptKey: "A4wELWqH", EncryptKey: "A4wELWqH",
@ -4918,7 +4922,8 @@ func TestSanitize(t *testing.T) {
"EnableACLReplication": false, "EnableACLReplication": false,
"EnableAgentTLSForChecks": false, "EnableAgentTLSForChecks": false,
"EnableDebug": false, "EnableDebug": false,
"EnableScriptChecks": false, "EnableLocalScriptChecks": false,
"EnableRemoteScriptChecks": false,
"EnableSyslog": false, "EnableSyslog": false,
"EnableUI": false, "EnableUI": false,
"EncryptKey": "hidden", "EncryptKey": "hidden",

View File

@ -283,7 +283,7 @@ func TestAgent_sidecarServiceFromNodeService(t *testing.T) {
a := NewTestAgent("jones", hcl) a := NewTestAgent("jones", hcl)
if tt.preRegister != nil { if tt.preRegister != nil {
err := a.AddService(tt.preRegister.NodeService(), nil, false, "") err := a.AddService(tt.preRegister.NodeService(), nil, false, "", ConfigSourceLocal)
require.NoError(err) require.NoError(err)
} }

View File

@ -49,7 +49,7 @@ func TestMaintCommand_NoArgs(t *testing.T) {
ID: "test", ID: "test",
Service: "test", Service: "test",
} }
if err := a.AddService(service, nil, false, ""); err != nil { if err := a.AddService(service, nil, false, "", agent.ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
if err := a.EnableServiceMaintenance("test", "broken 1", ""); err != nil { if err := a.EnableServiceMaintenance("test", "broken 1", ""); err != nil {
@ -145,7 +145,7 @@ func TestMaintCommand_EnableServiceMaintenance(t *testing.T) {
ID: "test", ID: "test",
Service: "test", Service: "test",
} }
if err := a.AddService(service, nil, false, ""); err != nil { if err := a.AddService(service, nil, false, "", agent.ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -179,7 +179,7 @@ func TestMaintCommand_DisableServiceMaintenance(t *testing.T) {
ID: "test", ID: "test",
Service: "test", Service: "test",
} }
if err := a.AddService(service, nil, false, ""); err != nil { if err := a.AddService(service, nil, false, "", agent.ConfigSourceLocal); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }

View File

@ -28,8 +28,13 @@ There are several different kinds of checks:
Consul will wait for any child processes spawned by the script to finish. For any Consul will wait for any child processes spawned by the script to finish. For any
other system, Consul will attempt to force-kill the script and any child processes other system, Consul will attempt to force-kill the script and any child processes
it has spawned once the timeout has passed. it has spawned once the timeout has passed.
In Consul 0.9.0 and later, the agent must be configured with [`enable_script_checks`] In Consul 0.9.0 and later, script checks are not enabled by default. To use them you
(/docs/agent/options.html#_enable_script_checks) set to `true` in order to enable script checks. can either use :
* [`enable_local_script_checks`](/docs/agent/options.html#_enable_local_script_checks):
enable script checks defile in local config files. Script checks defined via the HTTP
API will not be allowed.
* [`enable_script_checks`](/docs/agent/options.html#_enable_script_checks): enable
script checks regardless of how they are defined.
* HTTP + Interval - These checks make an HTTP `GET` request every Interval (e.g. * HTTP + Interval - These checks make an HTTP `GET` request every Interval (e.g.
every 30 seconds) to the specified URL. The status of the service depends on every 30 seconds) to the specified URL. The status of the service depends on

View File

@ -200,9 +200,13 @@ will exit with an error at startup.
* <a name="_enable_script_checks"></a><a href="#_enable_script_checks">`-enable-script-checks`</a> This * <a name="_enable_script_checks"></a><a href="#_enable_script_checks">`-enable-script-checks`</a> This
controls whether [health checks that execute scripts](/docs/agent/checks.html) are enabled on controls whether [health checks that execute scripts](/docs/agent/checks.html) are enabled on
this agent, and defaults to `false` so operators must opt-in to allowing these. If enabled, this agent, and defaults to `false` so operators must opt-in to allowing these. If enabled, it is recommended
it is recommended to [enable ACLs](/docs/guides/acl.html) as well to control which users are to [enable ACLs](/docs/guides/acl.html) as well to control which users are allowed to register new checks to
allowed to register new checks to execute scripts. This was added in Consul 0.9.0. execute scripts. This was added in Consul 0.9.0.
* <a name="_enable_local_script_checks"></a><a href="#_enable_local_script_checks">`-enable-local-script-checks`</a>
Like [`enable_script_checks`](#_enable_script_checks), but only enable them when they are defined in the local
config files. Script checks defined in HTTP API registratrions will still not be allowed.
* <a name="_encrypt"></a><a href="#_encrypt">`-encrypt`</a> - Specifies the secret key to * <a name="_encrypt"></a><a href="#_encrypt">`-encrypt`</a> - Specifies the secret key to
use for encryption of Consul use for encryption of Consul

View File

@ -996,8 +996,9 @@ to use for registration events:
access. access.
In addition to ACLs, in Consul 0.9.0 and later, the agent must be configured with In addition to ACLs, in Consul 0.9.0 and later, the agent must be configured with
[`enable_script_checks`](/docs/agent/options.html#_enable_script_checks) set to `true` in order to enable [`enable_script_checks`](/docs/agent/options.html#_enable_script_checks) or
script checks. [`enable_local_script_checks`](/docs/agent/options.html#_enable_local_script_checks)
set to `true` in order to enable script checks.
#### Session Rules #### Session Rules