| 332 | } |
| 333 | |
| 334 | func (s *Server) serve(w http.ResponseWriter, r *http.Request) { |
| 335 | if s.mode == ManageServerMode { |
| 336 | // In manage mode, requests must be sent directly to the bare Tailscale IP address. |
| 337 | // If a request comes in on any other hostname, redirect. |
| 338 | if s.requireTailscaleIP(w, r) { |
| 339 | return // user was redirected |
| 340 | } |
| 341 | |
| 342 | // serve HTTP 204 on /ok requests as connectivity check |
| 343 | if r.Method == httpm.GET && r.URL.Path == "/ok" { |
| 344 | w.WriteHeader(http.StatusNoContent) |
| 345 | return |
| 346 | } |
| 347 | |
| 348 | if !s.devMode { |
| 349 | // This hash corresponds to the inline script in index.html that runs when the react app is unavailable. |
| 350 | // It was generated from https://csplite.com/csp/sha/. |
| 351 | // If the contents of the script are changed, this hash must be updated. |
| 352 | const indexScriptHash = "sha384-CW2AYVfS14P7QHZN27thEkMLKiCj3YNURPoLc1elwiEkMVHeuYTWkJOEki1F3nZc" |
| 353 | |
| 354 | w.Header().Set("X-Frame-Options", "DENY") |
| 355 | w.Header().Set("Content-Security-Policy", "default-src 'self'; img-src * data:; script-src 'self' '"+indexScriptHash+"'") |
| 356 | w.Header().Set("Cross-Origin-Resource-Policy", "same-origin") |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | if r.URL.Path == "/metrics" { |
| 361 | r.URL.Path = "/api/local/v0/usermetrics" |
| 362 | s.proxyRequestToLocalAPI(w, r) |
| 363 | return |
| 364 | } |
| 365 | |
| 366 | if strings.HasPrefix(r.URL.Path, "/api/") { |
| 367 | switch { |
| 368 | case r.URL.Path == "/api/auth" && r.Method == httpm.GET: |
| 369 | s.serveAPIAuth(w, r) // serve auth status |
| 370 | return |
| 371 | case r.URL.Path == "/api/auth/session/new" && r.Method == httpm.GET: |
| 372 | s.serveAPIAuthSessionNew(w, r) // create new session |
| 373 | return |
| 374 | case r.URL.Path == "/api/auth/session/wait" && r.Method == httpm.GET: |
| 375 | s.serveAPIAuthSessionWait(w, r) // wait for session to be authorized |
| 376 | return |
| 377 | } |
| 378 | if ok := s.authorizeRequest(w, r); !ok { |
| 379 | http.Error(w, "not authorized", http.StatusUnauthorized) |
| 380 | return |
| 381 | } |
| 382 | // Pass API requests through to the API handler. |
| 383 | s.apiHandler.ServeHTTP(w, r) |
| 384 | return |
| 385 | } |
| 386 | s.assetsHandler.ServeHTTP(w, r) |
| 387 | } |
| 388 | |
| 389 | // requireTailscaleIP redirects an incoming request if the HTTP request was not made to a bare Tailscale IP address. |
| 390 | // The request will be redirected to the Tailscale IP, port 5252, with the original request path. |