requireTailscaleIP redirects an incoming request if the HTTP request was not made to a bare Tailscale IP address. The request will be redirected to the Tailscale IP, port 5252, with the original request path. This allows any custom hostname to be used to access the device, but protects against DNS r
(w http.ResponseWriter, r *http.Request)
| 391 | // This allows any custom hostname to be used to access the device, but protects against DNS rebinding attacks. |
| 392 | // Returns true if the request has been fully handled, either be returning a redirect or an HTTP error. |
| 393 | func (s *Server) requireTailscaleIP(w http.ResponseWriter, r *http.Request) (handled bool) { |
| 394 | const ( |
| 395 | ipv4ServiceHost = tsaddr.TailscaleServiceIPString |
| 396 | ipv6ServiceHost = "[" + tsaddr.TailscaleServiceIPv6String + "]" |
| 397 | ) |
| 398 | // allow requests on quad-100 (or ipv6 equivalent) |
| 399 | host := strings.TrimSuffix(r.Host, ":80") |
| 400 | if host == ipv4ServiceHost || host == ipv6ServiceHost { |
| 401 | return false |
| 402 | } |
| 403 | |
| 404 | st, err := s.lc.StatusWithoutPeers(r.Context()) |
| 405 | if err != nil { |
| 406 | s.logf("error getting status: %v", err) |
| 407 | http.Error(w, "internal error", http.StatusInternalServerError) |
| 408 | return true |
| 409 | } |
| 410 | |
| 411 | ipv4, ipv6 := s.selfNodeAddresses(r, st) |
| 412 | if r.Host == fmt.Sprintf("%s:%d", ipv4.String(), ListenPort) { |
| 413 | return false // already accessing over Tailscale IP |
| 414 | } |
| 415 | if r.Host == fmt.Sprintf("[%s]:%d", ipv6.String(), ListenPort) { |
| 416 | return false // already accessing over Tailscale IP |
| 417 | } |
| 418 | |
| 419 | // Not currently accessing via Tailscale IP, |
| 420 | // redirect them. |
| 421 | |
| 422 | var preferV6 bool |
| 423 | if ap, err := netip.ParseAddrPort(r.Host); err == nil { |
| 424 | // If Host was already ipv6, keep them on same protocol. |
| 425 | preferV6 = ap.Addr().Is6() |
| 426 | } |
| 427 | |
| 428 | newURL := *r.URL |
| 429 | if (preferV6 && ipv6.IsValid()) || !ipv4.IsValid() { |
| 430 | newURL.Host = fmt.Sprintf("[%s]:%d", ipv6.String(), ListenPort) |
| 431 | } else { |
| 432 | newURL.Host = fmt.Sprintf("%s:%d", ipv4.String(), ListenPort) |
| 433 | } |
| 434 | http.Redirect(w, r, newURL.String(), http.StatusMovedPermanently) |
| 435 | return true |
| 436 | } |
| 437 | |
| 438 | // selfNodeAddresses return the Tailscale IPv4 and IPv6 addresses for the self node. |
| 439 | // st is expected to be a status with peers included. |