Repository: syncthing/syncthing (language: go)

Issue:
Add HTTP health check without using API Token
/kind feature

**Description**
Each Router or API gateway (for example [Traefik](https://doc.traefik.io/traefik/routing/services/#health-check)) can verify if the destination service is working correctly, usually connecting on a specific path `/status` and only receiving an empty HTML body with HTTP code `200 OK` or `500 ERROR`. Such a check gives two crucial information to the gateway:
 - service is working correctly and it's ready for incoming traffic
 - service is operated from a network point of view (network and firewall are configured correctly)
 
I would like to have the possibility to check the status of  [syncthing](https://github.com/syncthing/syncthing) by HTTP request.
I know I could try to query [/rest/system/ping](https://docs.syncthing.net/rest/system-ping-get.html) but the problem is that the REST requires to use of API Token for authorization.  I don't want to put full RW API Token just to check PING and some API gateways are not able to send extra headers just for heatlcheck. 

I am open to any suggestion on how I could achieve an HTTP health check without API Token


Discussion / hints:
What does correctly mean here? If its responding, even with a 401, its working, if its not responding, it's not working.
Or it could mean there's a broken proxy or something in between... I can see that it would be nice with something answering with a 200 OK and a small JSON response indicating things are fine.

`{"status": "OK"}`

Fix patch:
diff --git a/lib/api/api.go b/lib/api/api.go
index f24a974112d..e332432d8db 100644
--- a/lib/api/api.go
+++ b/lib/api/api.go
@@ -258,6 +258,7 @@ func (s *service) Serve(ctx context.Context) error {
 	restMux.HandlerFunc(http.MethodGet, "/rest/folder/pullerrors", s.getFolderErrors)         // folder (deprecated)
 	restMux.HandlerFunc(http.MethodGet, "/rest/events", s.getIndexEvents)                     // [since] [limit] [timeout] [events]
 	restMux.HandlerFunc(http.MethodGet, "/rest/events/disk", s.getDiskEvents)                 // [since] [limit] [timeout]
+	restMux.HandlerFunc(http.MethodGet, "/rest/noauth/health", s.getHealth)                   // -
 	restMux.HandlerFunc(http.MethodGet, "/rest/stats/device", s.getDeviceStats)               // -
 	restMux.HandlerFunc(http.MethodGet, "/rest/stats/folder", s.getFolderStats)               // -
 	restMux.HandlerFunc(http.MethodGet, "/rest/svc/deviceid", s.getDeviceID)                  // id
@@ -1566,6 +1567,10 @@ func (s *service) postDBPrio(w http.ResponseWriter, r *http.Request) {
 	s.getDBNeed(w, r)
 }
 
+func (*service) getHealth(w http.ResponseWriter, _ *http.Request) {
+	sendJSON(w, map[string]string{"status": "OK"})
+}
+
 func (*service) getQR(w http.ResponseWriter, r *http.Request) {
 	var qs = r.URL.Query()
 	var text = qs.Get("text")
diff --git a/lib/api/api_auth.go b/lib/api/api_auth.go
index 70add9a9541..e84ae645e49 100644
--- a/lib/api/api_auth.go
+++ b/lib/api/api_auth.go
@@ -44,6 +44,12 @@ func basicAuthAndSessionMiddleware(cookieName string, guiCfg config.GUIConfigura
 			return
 		}
 
+		// Exception for REST calls that don't require authentication.
+		if strings.HasPrefix(r.URL.Path, "/rest/noauth") {
+			next.ServeHTTP(w, r)
+			return
+		}
+
 		cookie, err := r.Cookie(cookieName)
 		if err == nil && cookie != nil {
 			sessionsMut.Lock()
diff --git a/lib/api/api_csrf.go b/lib/api/api_csrf.go
index 97e0f3357f9..b597c2e4932 100644
--- a/lib/api/api_csrf.go
+++ b/lib/api/api_csrf.go
@@ -74,6 +74,13 @@ func (m *csrfManager) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	if strings.HasPrefix(r.URL.Path, "/rest/noauth") {
+		// REST calls that don't require authentication also do not
+		// need a CSRF token.
+		m.next.ServeHTTP(w, r)
+		return
+	}
+
 	// Allow requests for anything not under the protected path prefix,
 	// and set a CSRF cookie if there isn't already a valid one.
 	if !strings.HasPrefix(r.URL.Path, m.prefix) {


Test patch: