Skip to content

Commit

Permalink
feat: add basic auth
Browse files Browse the repository at this point in the history
  • Loading branch information
dundee committed Feb 28, 2024
1 parent 78e54d9 commit eadff94
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 14 deletions.
41 changes: 31 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,17 @@ Usage:
disk_usage_exporter [flags]
Flags:
-p, --analyzed-path string Path where to analyze disk usage (default "/")
-b, --bind-address string Address to bind to (default "0.0.0.0:9995")
-c, --config string config file (default is $HOME/.disk_usage_exporter.yaml)
-l, --dir-level int Directory nesting level to show (0 = only selected dir) (default 2)
-L, --follow-symlinks Follow symlinks for files, i.e. show the size of the file to which symlink points to (symlinks to directories are not followed)
-h, --help help for disk_usage_exporter
-i, --ignore-dirs strings Absolute paths to ignore (separated by comma) (default [/proc,/dev,/sys,/run,/var/cache/rsnapshot])
-m, --mode string Expose method - either 'file' or 'http' (default "http")
--multi-paths stringToString Multiple paths where to analyze disk usage, in format /path1=level1,/path2=level2,... (default [])
-f, --output-file string Target file to store metrics in (default "./disk-usage-exporter.prom")
-p, --analyzed-path string Path where to analyze disk usage (default "/")
--basic-auth-users stringToString Basic Auth users and their passwords as bcypt hashes (default [])
-b, --bind-address string Address to bind to (default "0.0.0.0:9995")
-c, --config string config file (default is $HOME/.disk_usage_exporter.yaml)
-l, --dir-level int Directory nesting level to show (0 = only selected dir) (default 2)
-L, --follow-symlinks Follow symlinks for files, i.e. show the size of the file to which symlink points to (symlinks to directories are not followed)
-h, --help help for disk_usage_exporter
-i, --ignore-dirs strings Absolute paths to ignore (separated by comma) (default [/proc,/dev,/sys,/run,/var/cache/rsnapshot])
-m, --mode string Expose method - either 'file' or 'http' (default "http")
--multi-paths stringToString Multiple paths where to analyze disk usage, in format /path1=level1,/path2=level2,... (default [])
-f, --output-file string Target file to store metrics in (default "./disk-usage-exporter.prom")
```

Either one path can be specified using `--analyzed-path` and `--dir-level` flags or multiple can be set
Expand Down Expand Up @@ -129,6 +130,8 @@ ignore-dirs:
- /dev
- /sys
- /run
basic-auth-users:
prom: $2b$12$MzUQjmLxPRM9WW6OI4ZzwuZHB7ubHiiSnngJxIufgZms27nw.5ZAq
```

## Prometheus scrape config
Expand All @@ -154,6 +157,24 @@ of this exporter to any name ending in `.prom` within said folder (and of course
A common use case for this is when the calculation of metrics takes particularly long and therefore can only be done
once in a while. To automate the periodic update of the output file, simply set up a cronjob.

## Basic Auth

You can enable HTTP Basic authorization by setting the `--basic-auth-users` flag (password is "test"):

```
disk_usage_exporter --basic-auth-users='admin=$2b$12$hNf2lSsxfm0.i4a.1kVpSOVyBCfIB51VRjgBUyv6kdnyTlgWj81Ay'
```

or by setting the key in config:

```yaml
basic-auth-users:
admin: $2b$12$hNf2lSsxfm0.i4a.1kVpSOVyBCfIB51VRjgBUyv6kdnyTlgWj81Ay
```

The password needs to be hashed by [bcrypt](https://bcrypt-generator.com/) in both cases.


## Example systemd unit file

```
Expand Down
5 changes: 5 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ and reporting which directories consume what space.`,
)
e.SetIgnoreDirPaths(viper.GetStringSlice("ignore-dirs"))

if viper.IsSet("basic-auth-users") {
e.SetBasicAuth(viper.GetStringMapString("basic-auth-users"))
}

if viper.GetString("mode") == "file" {
e.WriteToTextfile(viper.GetString("output-file"))
log.Info("Done - exiting.")
Expand Down Expand Up @@ -71,6 +75,7 @@ func init() {
"Follow symlinks for files, i.e. show the size of the file to which symlink points to (symlinks to directories are not followed)",
)
flags.StringToString("multi-paths", map[string]string{}, "Multiple paths where to analyze disk usage, in format /path1=level1,/path2=level2,...")
flags.StringToString("basic-auth-users", map[string]string{}, "Basic Auth users and their passwords as bcypt hashes")

viper.BindPFlags(flags)
}
Expand Down
32 changes: 31 additions & 1 deletion exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/bcrypt"
)

var (
Expand Down Expand Up @@ -39,6 +40,7 @@ type Exporter struct {
paths map[string]int
ignoreDirPaths map[string]struct{}
followSymlinks bool
basicAuth map[string]string
}

// NewExporter creates new Exporter
Expand Down Expand Up @@ -71,7 +73,12 @@ func (e *Exporter) SetIgnoreDirPaths(paths []string) {
}
}

func (e *Exporter) shouldDirBeIgnored(name, path string) bool {
// SetBasicAuth sets Basic Auth credentials
func (e *Exporter) SetBasicAuth(users map[string]string) {
e.basicAuth = users
}

func (e *Exporter) shouldDirBeIgnored(_, path string) bool {
_, ok := e.ignoreDirPaths[path]
return ok
}
Expand All @@ -90,6 +97,7 @@ func (e *Exporter) reportItem(item fs.Item, level, maxLevel int) {
}
}

// WriteToTextfile writes the prometheus report to file
func (e *Exporter) WriteToTextfile(name string) {
e.runAnalysis()

Expand All @@ -105,10 +113,32 @@ func (e *Exporter) WriteToTextfile(name string) {
}

func (e *Exporter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if len(e.basicAuth) > 0 {
if ok := e.authorizeReq(w, req); !ok {
return
}
}

e.runAnalysis()
promhttp.Handler().ServeHTTP(w, req)
}

func (e *Exporter) authorizeReq(w http.ResponseWriter, req *http.Request) bool {
user, pass, ok := req.BasicAuth()
if ok {
if hashed, found := e.basicAuth[user]; found {
err := bcrypt.CompareHashAndPassword([]byte(hashed), []byte(pass))
if err == nil {
return true
}
}
}

w.Header().Add("WWW-Authenticate", "Basic realm=\"Access to disk usage exporter\"")
w.WriteHeader(401)
return false
}

// RunServer starts HTTP server loop
func (e *Exporter) RunServer(addr string) {
http.Handle("/", http.HandlerFunc(ServeIndex))
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ require (
github.com/spf13/cobra v1.7.0
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.4
golang.org/x/crypto v0.20.0
google.golang.org/protobuf v1.30.0 // indirect
)
14 changes: 11 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,9 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg=
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand Down Expand Up @@ -909,6 +912,8 @@ golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfS
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down Expand Up @@ -1039,14 +1044,16 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand All @@ -1060,8 +1067,9 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down

0 comments on commit eadff94

Please sign in to comment.