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:
parent
e176a3ea22
commit
26f0b0d0ce
14
src/auth/hash.go
Normal file
14
src/auth/hash.go
Normal 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
30
src/auth/middleware.go
Normal 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
|
||||
}
|
@ -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 {
|
||||
|
@ -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)
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user