diff --git a/config/README.md b/config/README.md index 49bc85cc..bb2f27b2 100644 --- a/config/README.md +++ b/config/README.md @@ -16,6 +16,9 @@ log_debug: | default = false [optional] # Whether to ignore security warnings hack_me_please: | default = false [optional] +# Allow ping server +allow_ping: | default = false [optional] + # Named list of cache configurations caches: - ... diff --git a/config/config.go b/config/config.go index 7d32d08d..652cfe9b 100644 --- a/config/config.go +++ b/config/config.go @@ -78,6 +78,9 @@ type Config struct { ConnectionPool ConnectionPool `yaml:"connection_pool,omitempty"` + // Allow to proxy ping requests + AllowPing bool `yaml:"allow_ping,omitempty"` + networkReg map[string]Networks // Catches all undefined fields diff --git a/main.go b/main.go index abc787f5..b25bb503 100644 --- a/main.go +++ b/main.go @@ -20,6 +20,8 @@ import ( "golang.org/x/crypto/acme/autocert" ) +const pingEndpoint string = "/ping" + var ( configFile = flag.String("config", "", "Proxy configuration filename") version = flag.Bool("version", false, "Prints current version and exits") @@ -35,6 +37,7 @@ var ( allowedNetworksHTTPS atomic.Value allowedNetworksMetrics atomic.Value proxyHandler atomic.Value + allowPing atomic.Bool ) func main() { @@ -237,8 +240,15 @@ func serveHTTP(rw http.ResponseWriter, r *http.Request) { } proxy.refreshCacheMetrics() promHandler.ServeHTTP(rw, r) - case "/", "/query": + case "/", "/query", pingEndpoint: var err error + + if r.URL.Path == pingEndpoint && !allowPing.Load() { + err = fmt.Errorf("ping is not allowed") + respondWith(rw, err, http.StatusForbidden) + return + } + // nolint:forcetypeassert // We will cover this by tests as we control what is stored. proxyHandler := proxyHandler.Load().(*ProxyHandler) r.RemoteAddr = proxyHandler.GetRemoteAddr(r) @@ -296,6 +306,7 @@ func applyConfig(cfg *config.Config) error { allowedNetworksHTTPS.Store(&cfg.Server.HTTPS.AllowedNetworks) allowedNetworksMetrics.Store(&cfg.Server.Metrics.AllowedNetworks) proxyHandler.Store(NewProxyHandler(&cfg.Server.Proxy)) + allowPing.Store(cfg.AllowPing) log.SetDebug(cfg.LogDebug) log.Infof("Loaded config:\n%s", cfg) diff --git a/main_test.go b/main_test.go index d3e1d707..7e721a29 100644 --- a/main_test.go +++ b/main_test.go @@ -858,6 +858,44 @@ func TestServe(t *testing.T) { }, startTLS, }, + { + "http ping request", + "testdata/http.ping.yml", + func(t *testing.T) { + httpGet(t, "http://127.0.0.1:9090/ping", http.StatusOK) + }, + startHTTP, + }, + { + "https ping request", + "testdata/https.ping.yml", + func(t *testing.T) { + req, err := http.NewRequest("GET", "https://127.0.0.1:9090/ping", nil) + checkErr(t, err) + req.Close = true + req.SetBasicAuth("default", "qwerty") + + resp, err := tlsClient.Do(req) + checkErr(t, err) + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + t.Fatalf("unexpected status code: %d; expected: %d", resp.StatusCode, http.StatusOK) + } + }, + startTLS, + }, + { + "http ping request is not allowed", + "testdata/http.yml", + func(t *testing.T) { + resp := httpGet(t, "http://127.0.0.1:9090/ping", http.StatusForbidden) + expected := "ping is not allowed\n" + checkResponse(t, resp.Body, expected) + resp.Body.Close() + }, + startHTTP, + }, } // Wait until CHServer starts. diff --git a/scope.go b/scope.go index c3debad1..f04b2655 100644 --- a/scope.go +++ b/scope.go @@ -389,6 +389,13 @@ func (s *scope) decorateRequest(req *http.Request) (*http.Request, url.Values) { // Make new params to purify URL. params := make(url.Values) + // pass ping request + if req.RequestURI == pingEndpoint { + req.URL.Scheme = s.host.Scheme() + req.URL.Host = s.host.Host() + return req, req.URL.Query() + } + // Set user params if s.user.params != nil { for _, param := range s.user.params.params { diff --git a/testdata/http.ping.yml b/testdata/http.ping.yml new file mode 100644 index 00000000..cd7e40bb --- /dev/null +++ b/testdata/http.ping.yml @@ -0,0 +1,15 @@ +allow_ping: true + +server: + http: + listen_addr: ":9090" + allowed_networks: ["127.0.0.1/24"] + +users: + - name: "default" + to_cluster: "default" + to_user: "default" + +clusters: + - name: "default" + nodes: ["127.0.0.1:18124"] diff --git a/testdata/https.ping.yml b/testdata/https.ping.yml new file mode 100644 index 00000000..e165a9f2 --- /dev/null +++ b/testdata/https.ping.yml @@ -0,0 +1,17 @@ +allow_ping: true + +server: + https: + listen_addr: ":9090" + cert_file: "testdata/example.com.cert" + key_file: "testdata/example.com.key" + +users: + - name: "default" + password: "qwerty" + to_cluster: "default" + to_user: "default" + +clusters: + - name: "default" + nodes: ["127.0.0.1:18124"]