diff --git a/agent/agent.go b/agent/agent.go index f71e811d1..7751a2f1c 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -364,6 +364,7 @@ func (a *Agent) Start() error { // done here after the local state above is loaded in so we can have // a more accurate initial state view. a.proxyManager = proxy.NewManager() + a.proxyManager.AllowRoot = a.config.ConnectProxyAllowManagedRoot a.proxyManager.State = a.State a.proxyManager.Logger = a.logger if a.config.DataDir != "" { diff --git a/agent/proxy/manager.go b/agent/proxy/manager.go index 410a6b5d1..e3c6dd779 100644 --- a/agent/proxy/manager.go +++ b/agent/proxy/manager.go @@ -85,6 +85,12 @@ type Manager struct { CoalescePeriod time.Duration QuiescentPeriod time.Duration + // AllowRoot configures whether proxies can be executed as root (EUID == 0). + // If this is false then the manager will run and proxies can be added + // and removed but none will be started an errors will be logged + // to the logger. + AllowRoot bool + // lock is held while reading/writing any internal state of the manager. // cond is a condition variable on lock that is broadcasted for runState // changes. @@ -323,6 +329,12 @@ func (m *Manager) sync() { m.lock.Lock() defer m.lock.Unlock() + // If we don't allow root and we're root, then log a high sev message. + if !m.AllowRoot && isRoot() { + m.Logger.Println("[WARN] agent/proxy: running as root, will not start managed proxies") + return + } + // Get the current set of proxies state := m.State.Proxies() diff --git a/agent/proxy/manager_test.go b/agent/proxy/manager_test.go index 22c3b7330..42286493c 100644 --- a/agent/proxy/manager_test.go +++ b/agent/proxy/manager_test.go @@ -347,6 +347,41 @@ func TestManagerRun_snapshotRestore(t *testing.T) { }) } +// Manager should not run any proxies if we're running as root. Tests +// stub the value. +func TestManagerRun_rootDisallow(t *testing.T) { + // Pretend we are root + defer testSetRootValue(true)() + + state := local.TestState(t) + m, closer := testManager(t) + defer closer() + m.State = state + defer m.Kill() + + // Add the proxy before we start the manager to verify initial sync + td, closer := testTempDir(t) + defer closer() + path := filepath.Join(td, "file") + testStateProxy(t, state, "web", helperProcess("restart", path)) + + // Start the manager + go m.Run() + + // Sleep a bit just to verify + time.Sleep(100 * time.Millisecond) + + // We should see the path appear shortly + retry.Run(t, func(r *retry.R) { + _, err := os.Stat(path) + if err != nil { + return + } + + r.Fatalf("path exists") + }) +} + func testManager(t *testing.T) (*Manager, func()) { m := NewManager() diff --git a/agent/proxy/proxy_test.go b/agent/proxy/proxy_test.go index 0a9237319..bb4020630 100644 --- a/agent/proxy/proxy_test.go +++ b/agent/proxy/proxy_test.go @@ -48,6 +48,7 @@ func helperProcess(s ...string) *exec.Cmd { cmd := exec.Command(os.Args[0], cs...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr + println(fmt.Sprintf("YO WHAT %#v", cmd.Args)) return cmd } diff --git a/agent/proxy/root.go b/agent/proxy/root.go new file mode 100644 index 000000000..3be5904ae --- /dev/null +++ b/agent/proxy/root.go @@ -0,0 +1,24 @@ +package proxy + +import ( + "os" +) + +// isRoot returns true if the process is executing as root. +func isRoot() bool { + if testRootValue != nil { + return *testRootValue + } + + return os.Geteuid() == 0 +} + +// testSetRootValue is a test helper for setting the root value. +func testSetRootValue(v bool) func() { + testRootValue = &v + return func() { testRootValue = nil } +} + +// testRootValue should be set to a non-nil value to return it as a stub +// from isRoot. This should only be used in tests. +var testRootValue *bool