store basic auth credentials as hash instead of raw value

Update how the basic auth credentials are stored in the application to use
a hashed value instead of the raw value. This prevents the raw value from
being accidentally leaked.
Add new `auth` package for functions related to authentication.
This commit is contained in:
Hector 2022-01-13 20:34:42 +00:00
parent e176a3ea22
commit 26f0b0d0ce
4 changed files with 59 additions and 19 deletions

14
src/auth/hash.go Normal file
View File

@ -0,0 +1,14 @@
package auth
import (
"crypto/sha256"
)
func Hash(data []byte) []byte {
b := sha256.Sum256(data)
return b[:]
}
func HashString(data string) string {
return string(Hash([]byte(data)))
}

30
src/auth/middleware.go Normal file
View File

@ -0,0 +1,30 @@
package auth
import (
"fail2ban-prometheus-exporter/cfg"
"net/http"
)
func BasicAuthMiddleware(handlerFunc http.HandlerFunc, appSettings *cfg.AppSettings) http.HandlerFunc {
authEnabled := len(appSettings.BasicAuthUsername) > 0 && len(appSettings.BasicAuthPassword) > 0
if authEnabled {
return func(w http.ResponseWriter, r *http.Request) {
if doesBasicAuthMatch(r, appSettings) {
w.WriteHeader(http.StatusUnauthorized)
} else {
handlerFunc.ServeHTTP(w, r)
}
}
}
return handlerFunc
}
func doesBasicAuthMatch(r *http.Request, appSettings *cfg.AppSettings) bool {
rawUsername, rawPassword, ok := r.BasicAuth()
if ok {
username := HashString(rawUsername)
password := HashString(rawPassword)
return username == appSettings.BasicAuthUsername && password == appSettings.BasicAuthPassword
}
return false
}

View File

@ -1,6 +1,7 @@
package cfg
import (
"fail2ban-prometheus-exporter/auth"
"flag"
"fmt"
"os"
@ -23,6 +24,9 @@ type AppSettings struct {
}
func Parse() *AppSettings {
var rawBasicAuthUsername string
var rawBasicAuthPassword string
appSettings := &AppSettings{}
flag.BoolVar(&appSettings.VersionMode, "version", false, "show version info and exit")
flag.StringVar(&appSettings.MetricsAddress, "web.listen-address", "0.0.0.0", "address to use for the metrics server")
@ -30,14 +34,20 @@ func Parse() *AppSettings {
flag.StringVar(&appSettings.Fail2BanSocketPath, "socket", "", "path to the fail2ban server socket")
flag.BoolVar(&appSettings.FileCollectorEnabled, "collector.textfile", false, "enable the textfile collector")
flag.StringVar(&appSettings.FileCollectorPath, "collector.textfile.directory", "", "directory to read text files with metrics from")
flag.StringVar(&appSettings.BasicAuthUsername, "web.basic-auth.username", "", "username to use to protect endpoints with basic auth")
flag.StringVar(&appSettings.BasicAuthPassword, "web.basic-auth.password", "", "password to use to protect endpoints with basic auth")
flag.StringVar(&rawBasicAuthUsername, "web.basic-auth.username", "", "username to use to protect endpoints with basic auth")
flag.StringVar(&rawBasicAuthPassword, "web.basic-auth.password", "", "password to use to protect endpoints with basic auth")
flag.Parse()
appSettings.setBasicAuthValues(rawBasicAuthUsername, rawBasicAuthPassword)
appSettings.validateFlags()
return appSettings
}
func (settings *AppSettings) setBasicAuthValues(username, password string) {
settings.BasicAuthUsername = auth.HashString(username)
settings.BasicAuthPassword = auth.HashString(password)
}
func (settings *AppSettings) validateFlags() {
var flagsValid = true
if !settings.VersionMode {

View File

@ -1,6 +1,7 @@
package main
import (
"fail2ban-prometheus-exporter/auth"
"fail2ban-prometheus-exporter/cfg"
"fail2ban-prometheus-exporter/collector/f2b"
"fail2ban-prometheus-exporter/collector/textfile"
@ -48,21 +49,6 @@ func metricHandler(w http.ResponseWriter, r *http.Request, collector *textfile.C
collector.WriteTextFileMetrics(w, r)
}
func authMiddleware(handlerFunc http.HandlerFunc, appSettings *cfg.AppSettings) http.HandlerFunc {
authEnabled := len(appSettings.BasicAuthUsername) > 0 && len(appSettings.BasicAuthPassword) > 0
if authEnabled {
return func(w http.ResponseWriter, r *http.Request) {
username, password, ok := r.BasicAuth()
if !ok || username != appSettings.BasicAuthUsername || password != appSettings.BasicAuthPassword {
w.WriteHeader(http.StatusUnauthorized)
return
}
handlerFunc.ServeHTTP(w, r)
}
}
return handlerFunc
}
func main() {
appSettings := cfg.Parse()
if appSettings.VersionMode {
@ -78,8 +64,8 @@ func main() {
textFileCollector := textfile.NewCollector(appSettings)
prometheus.MustRegister(textFileCollector)
http.HandleFunc("/", authMiddleware(rootHtmlHandler, appSettings))
http.HandleFunc(metricsPath, authMiddleware(
http.HandleFunc("/", auth.BasicAuthMiddleware(rootHtmlHandler, appSettings))
http.HandleFunc(metricsPath, auth.BasicAuthMiddleware(
func(w http.ResponseWriter, r *http.Request) {
metricHandler(w, r, textFileCollector)
},