mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-11-04 10:01:05 +01:00 
			
		
		
		
	In TS2021 the MachineKey can be obtained from noiseConn.Peer() - contrary to what I thought before, where I assumed MachineKey was dropped in TS2021. By having a ts2021App and hanging from there the TS2021 handlers, we can fetch again the MachineKey.
		
			
				
	
	
		
			79 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			79 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package headscale
 | 
						|
 | 
						|
import (
 | 
						|
	"net/http"
 | 
						|
 | 
						|
	"github.com/gorilla/mux"
 | 
						|
	"github.com/rs/zerolog/log"
 | 
						|
	"golang.org/x/net/http2"
 | 
						|
	"golang.org/x/net/http2/h2c"
 | 
						|
	"tailscale.com/control/controlbase"
 | 
						|
	"tailscale.com/control/controlhttp"
 | 
						|
	"tailscale.com/net/netutil"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	// ts2021UpgradePath is the path that the server listens on for the WebSockets upgrade.
 | 
						|
	ts2021UpgradePath = "/ts2021"
 | 
						|
)
 | 
						|
 | 
						|
type ts2021App struct {
 | 
						|
	headscale *Headscale
 | 
						|
 | 
						|
	conn *controlbase.Conn
 | 
						|
}
 | 
						|
 | 
						|
// NoiseUpgradeHandler is to upgrade the connection and hijack the net.Conn
 | 
						|
// in order to use the Noise-based TS2021 protocol. Listens in /ts2021.
 | 
						|
func (h *Headscale) NoiseUpgradeHandler(
 | 
						|
	writer http.ResponseWriter,
 | 
						|
	req *http.Request,
 | 
						|
) {
 | 
						|
	log.Trace().Caller().Msgf("Noise upgrade handler for client %s", req.RemoteAddr)
 | 
						|
 | 
						|
	upgrade := req.Header.Get("Upgrade")
 | 
						|
	if upgrade == "" {
 | 
						|
		// This probably means that the user is running Headscale behind an
 | 
						|
		// improperly configured reverse proxy. TS2021 requires WebSockets to
 | 
						|
		// be passed to Headscale. Let's give them a hint.
 | 
						|
		log.Warn().
 | 
						|
			Caller().
 | 
						|
			Msg("No Upgrade header in TS2021 request. If headscale is behind a reverse proxy, make sure it is configured to pass WebSockets through.")
 | 
						|
		http.Error(writer, "Internal error", http.StatusInternalServerError)
 | 
						|
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	noiseConn, err := controlhttp.AcceptHTTP(req.Context(), writer, req, *h.noisePrivateKey, nil)
 | 
						|
	if err != nil {
 | 
						|
		log.Error().Err(err).Msg("noise upgrade failed")
 | 
						|
		http.Error(writer, err.Error(), http.StatusInternalServerError)
 | 
						|
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	ts2021App := ts2021App{
 | 
						|
		headscale: h,
 | 
						|
		conn:      noiseConn,
 | 
						|
	}
 | 
						|
 | 
						|
	// This router is served only over the Noise connection, and exposes only the new API.
 | 
						|
	//
 | 
						|
	// The HTTP2 server that exposes this router is created for
 | 
						|
	// a single hijacked connection from /ts2021, using netutil.NewOneConnListener
 | 
						|
	router := mux.NewRouter()
 | 
						|
 | 
						|
	router.HandleFunc("/machine/register", ts2021App.NoiseRegistrationHandler).
 | 
						|
		Methods(http.MethodPost)
 | 
						|
	router.HandleFunc("/machine/map", ts2021App.NoisePollNetMapHandler)
 | 
						|
 | 
						|
	server := http.Server{
 | 
						|
		ReadTimeout: HTTPReadTimeout,
 | 
						|
	}
 | 
						|
	server.Handler = h2c.NewHandler(router, &http2.Server{})
 | 
						|
	err = server.Serve(netutil.NewOneConnListener(noiseConn, nil))
 | 
						|
	if err != nil {
 | 
						|
		log.Info().Err(err).Msg("The HTTP2 server was closed")
 | 
						|
	}
 | 
						|
}
 |