all: allow dynamically reading *AuthKey flag values from files and urls

Examples:

1) -metricsAuthKey=file:///abs/path/to/file - reads flag value from the given absolute filepath
2) -metricsAuthKey=file://./relative/path/to/file - reads flag value from the given relative filepath
3) -metricsAuthKey=http://some-host/some/path?query_arg=abc - reads flag value from the given url

The flag value is automatically updated when the file contents changes.
This commit is contained in:
Aliaksandr Valialkin
2024-01-21 21:58:26 +02:00
parent 7e68722686
commit 1f105dde98
35 changed files with 577 additions and 228 deletions

View File

@@ -42,10 +42,10 @@ var (
"then all the http requests will be handled on '/foo/bar/*' paths. This may be useful for proxied requests. "+
"See https://www.robustperception.io/using-external-urls-and-proxies-with-prometheus")
httpAuthUsername = flag.String("httpAuth.username", "", "Username for HTTP server's Basic Auth. The authentication is disabled if empty. See also -httpAuth.password")
httpAuthPassword = flag.String("httpAuth.password", "", "Password for HTTP server's Basic Auth. The authentication is disabled if -httpAuth.username is empty")
metricsAuthKey = flag.String("metricsAuthKey", "", "Auth key for /metrics endpoint. It must be passed via authKey query arg. It overrides httpAuth.* settings")
flagsAuthKey = flag.String("flagsAuthKey", "", "Auth key for /flags endpoint. It must be passed via authKey query arg. It overrides httpAuth.* settings")
pprofAuthKey = flag.String("pprofAuthKey", "", "Auth key for /debug/pprof/* endpoints. It must be passed via authKey query arg. It overrides httpAuth.* settings")
httpAuthPassword = flagutil.NewPassword("httpAuth.password", "Password for HTTP server's Basic Auth. The authentication is disabled if -httpAuth.username is empty")
metricsAuthKey = flagutil.NewPassword("metricsAuthKey", "Auth key for /metrics endpoint. It must be passed via authKey query arg. It overrides httpAuth.* settings")
flagsAuthKey = flagutil.NewPassword("flagsAuthKey", "Auth key for /flags endpoint. It must be passed via authKey query arg. It overrides httpAuth.* settings")
pprofAuthKey = flagutil.NewPassword("pprofAuthKey", "Auth key for /debug/pprof/* endpoints. It must be passed via authKey query arg. It overrides httpAuth.* settings")
disableResponseCompression = flag.Bool("http.disableResponseCompression", false, "Disable compression of HTTP responses to save CPU resources. By default, compression is enabled to save network bandwidth")
maxGracefulShutdownDuration = flag.Duration("http.maxGracefulShutdownDuration", 7*time.Second, `The maximum duration for a graceful shutdown of the HTTP server. A highly loaded server may require increased value for a graceful shutdown`)
@@ -317,7 +317,7 @@ func handlerWrapper(s *server, w http.ResponseWriter, r *http.Request, rh Reques
return
case "/metrics":
metricsRequests.Inc()
if !CheckAuthFlag(w, r, *metricsAuthKey, "metricsAuthKey") {
if !CheckAuthFlag(w, r, metricsAuthKey.Get(), "metricsAuthKey") {
return
}
startTime := time.Now()
@@ -326,7 +326,7 @@ func handlerWrapper(s *server, w http.ResponseWriter, r *http.Request, rh Reques
metricsHandlerDuration.UpdateDuration(startTime)
return
case "/flags":
if !CheckAuthFlag(w, r, *flagsAuthKey, "flagsAuthKey") {
if !CheckAuthFlag(w, r, flagsAuthKey.Get(), "flagsAuthKey") {
return
}
h.Set("Content-Type", "text/plain; charset=utf-8")
@@ -350,7 +350,7 @@ func handlerWrapper(s *server, w http.ResponseWriter, r *http.Request, rh Reques
default:
if strings.HasPrefix(r.URL.Path, "/debug/pprof/") {
pprofRequests.Inc()
if !CheckAuthFlag(w, r, *pprofAuthKey, "pprofAuthKey") {
if !CheckAuthFlag(w, r, pprofAuthKey.Get(), "pprofAuthKey") {
return
}
pprofHandler(r.URL.Path[len("/debug/pprof/"):], w, r)
@@ -398,7 +398,7 @@ func CheckBasicAuth(w http.ResponseWriter, r *http.Request) bool {
}
username, password, ok := r.BasicAuth()
if ok {
if username == *httpAuthUsername && password == *httpAuthPassword {
if username == *httpAuthUsername && password == httpAuthPassword.Get() {
return true
}
authBasicRequestErrors.Inc()

View File

@@ -39,9 +39,11 @@ func TestGetQuotedRemoteAddr(t *testing.T) {
func TestBasicAuthMetrics(t *testing.T) {
origUsername := *httpAuthUsername
origPasswd := *httpAuthPassword
origPasswd := httpAuthPassword.Get()
defer func() {
*httpAuthPassword = origPasswd
if err := httpAuthPassword.Set(origPasswd); err != nil {
t.Fatalf("unexpected error: %s", err)
}
*httpAuthUsername = origUsername
}()
@@ -61,14 +63,18 @@ func TestBasicAuthMetrics(t *testing.T) {
}
*httpAuthUsername = "test"
*httpAuthPassword = "pass"
if err := httpAuthPassword.Set("pass"); err != nil {
t.Fatalf("unexpected error: %s", err)
}
f("test", "pass", 200)
f("test", "wrong", 401)
f("wrong", "pass", 401)
f("wrong", "wrong", 401)
*httpAuthUsername = ""
*httpAuthPassword = ""
if err := httpAuthPassword.Set(""); err != nil {
t.Fatalf("unexpected error: %s", err)
}
f("test", "pass", 200)
f("test", "wrong", 200)
f("wrong", "pass", 200)
@@ -77,9 +83,11 @@ func TestBasicAuthMetrics(t *testing.T) {
func TestAuthKeyMetrics(t *testing.T) {
origUsername := *httpAuthUsername
origPasswd := *httpAuthPassword
origPasswd := httpAuthPassword.Get()
defer func() {
*httpAuthPassword = origPasswd
if err := httpAuthPassword.Set(origPasswd); err != nil {
t.Fatalf("unexpected error: %s", err)
}
*httpAuthUsername = origUsername
}()
@@ -117,7 +125,9 @@ func TestAuthKeyMetrics(t *testing.T) {
}
*httpAuthUsername = "test"
*httpAuthPassword = "pass"
if err := httpAuthPassword.Set("pass"); err != nil {
t.Fatalf("unexpected error: %s", err)
}
tstWithOutAuthKey("test", "pass", 200)
tstWithOutAuthKey("test", "wrong", 401)
tstWithOutAuthKey("wrong", "pass", 401)