From 8a2373e8c89bb493ebb9217856a4c038c36d5105 Mon Sep 17 00:00:00 2001
From: Benjamin Stromski <benjamin.stromski@gmail.com>
Date: Wed, 9 Aug 2017 13:38:54 -0500
Subject: [PATCH] Add option to run varnish under sudo (#3097)

---
 plugins/inputs/varnish/README.md       | 60 ++++++++++++++++++++++++++
 plugins/inputs/varnish/varnish.go      | 29 +++++++++----
 plugins/inputs/varnish/varnish_test.go | 12 +++---
 3 files changed, 86 insertions(+), 15 deletions(-)

diff --git a/plugins/inputs/varnish/README.md b/plugins/inputs/varnish/README.md
index cba3506f..1866ca18 100644
--- a/plugins/inputs/varnish/README.md
+++ b/plugins/inputs/varnish/README.md
@@ -7,6 +7,9 @@ This plugin gathers stats from [Varnish HTTP Cache](https://varnish-cache.org/)
 ```toml
  # A plugin to collect stats from Varnish HTTP Cache
  [[inputs.varnish]]
+   ## If running as a restricted user you can prepend sudo for additional access:
+   #use_sudo = false
+
    ## The default location of the varnishstat binary can be overridden with:
    binary = "/usr/bin/varnishstat"
 
@@ -330,6 +333,63 @@ the following values:
   - LCK
   
   
+
+### Permissions:
+
+It's important to note that this plugin references varnishstat, which may require additional permissions to execute successfully.
+Depending on the user/group permissions of the telegraf user executing this plugin, you may need to alter the group membership, set facls, or use sudo.
+
+**Group membership (Recommended)**:
+```bash
+$ groups telegraf
+telegraf : telegraf
+
+$ usermod -a -G varnish telegraf
+
+$ groups telegraf
+telegraf : telegraf varnish
+```
+
+**Extended filesystem ACL's**:
+```bash
+$ getfacl /var/lib/varnish/<hostname>/_.vsm
+# file: var/lib/varnish/<hostname>/_.vsm
+# owner: root
+# group: root
+user::rw-
+group::r--
+other::---
+
+$ setfacl -m u:telegraf:r /var/lib/varnish/<hostname>/_.vsm
+
+$ getfacl /var/lib/varnish/<hostname>/_.vsm
+# file: var/lib/varnish/<hostname>/_.vsm
+# owner: root
+# group: root
+user::rw-
+user:telegraf:r--
+group::r--
+mask::r--
+other::---
+```
+
+**Sudo privileges**:
+```bash
+# If you use this method, you will need the following in your telegraf config:
+[[inputs.varnish]]
+  use_sudo = true
+
+$ visudo
+
+# Add the following line:
+telegraf ALL=(ALL) NOPASSWD: /usr/bin/varnishstat
+
+$ grep varnish /etc/sudoers
+telegraf ALL = NOPASSWD: /usr/bin/varnishstat
+```
+
+Please use the solution you see as most appropriate.
+
 ### Example Output:
 
 ```
diff --git a/plugins/inputs/varnish/varnish.go b/plugins/inputs/varnish/varnish.go
index 896215aa..08c885e6 100644
--- a/plugins/inputs/varnish/varnish.go
+++ b/plugins/inputs/varnish/varnish.go
@@ -17,12 +17,13 @@ import (
 	"github.com/influxdata/telegraf/plugins/inputs"
 )
 
-type runner func(cmdName string) (*bytes.Buffer, error)
+type runner func(cmdName string, UseSudo bool) (*bytes.Buffer, error)
 
 // Varnish is used to store configuration values
 type Varnish struct {
-	Stats  []string
-	Binary string
+	Stats   []string
+	Binary  string
+	UseSudo bool
 
 	filter filter.Filter
 	run    runner
@@ -32,6 +33,9 @@ var defaultStats = []string{"MAIN.cache_hit", "MAIN.cache_miss", "MAIN.uptime"}
 var defaultBinary = "/usr/bin/varnishstat"
 
 var sampleConfig = `
+  ## If running as a restricted user you can prepend sudo for additional access:
+  #use_sudo = false
+
   ## The default location of the varnishstat binary can be overridden with:
   binary = "/usr/bin/varnishstat"
 
@@ -52,10 +56,16 @@ func (s *Varnish) SampleConfig() string {
 }
 
 // Shell out to varnish_stat and return the output
-func varnishRunner(cmdName string) (*bytes.Buffer, error) {
+func varnishRunner(cmdName string, UseSudo bool) (*bytes.Buffer, error) {
 	cmdArgs := []string{"-1"}
-
 	cmd := exec.Command(cmdName, cmdArgs...)
+
+	if UseSudo {
+		cmdArgs = append([]string{cmdName}, cmdArgs...)
+		cmdArgs = append([]string{"-n"}, cmdArgs...)
+		cmd = exec.Command("sudo", cmdArgs...)
+	}
+
 	var out bytes.Buffer
 	cmd.Stdout = &out
 	err := internal.RunTimeout(cmd, time.Millisecond*200)
@@ -89,7 +99,7 @@ func (s *Varnish) Gather(acc telegraf.Accumulator) error {
 		}
 	}
 
-	out, err := s.run(s.Binary)
+	out, err := s.run(s.Binary, s.UseSudo)
 	if err != nil {
 		return fmt.Errorf("error gathering metrics: %s", err)
 	}
@@ -145,9 +155,10 @@ func (s *Varnish) Gather(acc telegraf.Accumulator) error {
 func init() {
 	inputs.Add("varnish", func() telegraf.Input {
 		return &Varnish{
-			run:    varnishRunner,
-			Stats:  defaultStats,
-			Binary: defaultBinary,
+			run:     varnishRunner,
+			Stats:   defaultStats,
+			Binary:  defaultBinary,
+			UseSudo: false,
 		}
 	})
 }
diff --git a/plugins/inputs/varnish/varnish_test.go b/plugins/inputs/varnish/varnish_test.go
index e3f84809..a2b388a0 100644
--- a/plugins/inputs/varnish/varnish_test.go
+++ b/plugins/inputs/varnish/varnish_test.go
@@ -11,8 +11,8 @@ import (
 	"testing"
 )
 
-func fakeVarnishStat(output string) func(string) (*bytes.Buffer, error) {
-	return func(string) (*bytes.Buffer, error) {
+func fakeVarnishStat(output string, useSudo bool) func(string, bool) (*bytes.Buffer, error) {
+	return func(string, bool) (*bytes.Buffer, error) {
 		return bytes.NewBuffer([]byte(output)), nil
 	}
 }
@@ -20,7 +20,7 @@ func fakeVarnishStat(output string) func(string) (*bytes.Buffer, error) {
 func TestGather(t *testing.T) {
 	acc := &testutil.Accumulator{}
 	v := &Varnish{
-		run:   fakeVarnishStat(smOutput),
+		run:   fakeVarnishStat(smOutput, false),
 		Stats: []string{"*"},
 	}
 	v.Gather(acc)
@@ -36,7 +36,7 @@ func TestGather(t *testing.T) {
 func TestParseFullOutput(t *testing.T) {
 	acc := &testutil.Accumulator{}
 	v := &Varnish{
-		run:   fakeVarnishStat(fullOutput),
+		run:   fakeVarnishStat(fullOutput, true),
 		Stats: []string{"*"},
 	}
 	err := v.Gather(acc)
@@ -51,7 +51,7 @@ func TestParseFullOutput(t *testing.T) {
 func TestFilterSomeStats(t *testing.T) {
 	acc := &testutil.Accumulator{}
 	v := &Varnish{
-		run:   fakeVarnishStat(fullOutput),
+		run:   fakeVarnishStat(fullOutput, false),
 		Stats: []string{"MGT.*", "VBE.*"},
 	}
 	err := v.Gather(acc)
@@ -74,7 +74,7 @@ func TestFieldConfig(t *testing.T) {
 	for fieldCfg, expected := range expect {
 		acc := &testutil.Accumulator{}
 		v := &Varnish{
-			run:   fakeVarnishStat(fullOutput),
+			run:   fakeVarnishStat(fullOutput, true),
 			Stats: strings.Split(fieldCfg, ","),
 		}
 		err := v.Gather(acc)
-- 
GitLab