package headscale import ( "bytes" "html/template" "net/http" textTemplate "text/template" "github.com/gin-gonic/gin" "github.com/gofrs/uuid" "github.com/rs/zerolog/log" ) // AppleMobileConfig shows a simple message in the browser to point to the CLI // Listens in /register. func (h *Headscale) AppleMobileConfig(ctx *gin.Context) { appleTemplate := template.Must(template.New("apple").Parse(`
This page provides configuration profiles for the official Tailscale clients for iOS and macOS.
The profiles will configure Tailscale.app to use {{.Url}} as its control server.
You should always inspect the profile before installing it:
curl {{.Url}}/apple/macos
Headscale can be set to the default server by installing a Headscale configuration profile:
Or
Use your terminal to configure the default setting for Tailscale by issuing:
defaults write io.tailscale.ipn.macos ControlURL {{.URL}}
Restart Tailscale.app and log in.
`)) config := map[string]interface{}{ "URL": h.cfg.ServerURL, } var payload bytes.Buffer if err := appleTemplate.Execute(&payload, config); err != nil { log.Error(). Str("handler", "AppleMobileConfig"). Err(err). Msg("Could not render Apple index template") ctx.Data( http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Could not render Apple index template"), ) return } ctx.Data(http.StatusOK, "text/html; charset=utf-8", payload.Bytes()) } func (h *Headscale) ApplePlatformConfig(ctx *gin.Context) { platform := ctx.Param("platform") id, err := uuid.NewV4() if err != nil { log.Error(). Str("handler", "ApplePlatformConfig"). Err(err). Msg("Failed not create UUID") ctx.Data( http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Failed to create UUID"), ) return } contentID, err := uuid.NewV4() if err != nil { log.Error(). Str("handler", "ApplePlatformConfig"). Err(err). Msg("Failed not create UUID") ctx.Data( http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Failed to create UUID"), ) return } platformConfig := AppleMobilePlatformConfig{ UUID: contentID, URL: h.cfg.ServerURL, } var payload bytes.Buffer switch platform { case "macos": if err := macosTemplate.Execute(&payload, platformConfig); err != nil { log.Error(). Str("handler", "ApplePlatformConfig"). Err(err). Msg("Could not render Apple macOS template") ctx.Data( http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Could not render Apple macOS template"), ) return } case "ios": if err := iosTemplate.Execute(&payload, platformConfig); err != nil { log.Error(). Str("handler", "ApplePlatformConfig"). Err(err). Msg("Could not render Apple iOS template") ctx.Data( http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Could not render Apple iOS template"), ) return } default: ctx.Data( http.StatusOK, "text/html; charset=utf-8", []byte("Invalid platform, only ios and macos is supported"), ) return } config := AppleMobileConfig{ UUID: id, URL: h.cfg.ServerURL, Payload: payload.String(), } var content bytes.Buffer if err := commonTemplate.Execute(&content, config); err != nil { log.Error(). Str("handler", "ApplePlatformConfig"). Err(err). Msg("Could not render Apple platform template") ctx.Data( http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Could not render Apple platform template"), ) return } ctx.Data( http.StatusOK, "application/x-apple-aspen-config; charset=utf-8", content.Bytes(), ) } type AppleMobileConfig struct { UUID uuid.UUID URL string Payload string } type AppleMobilePlatformConfig struct { UUID uuid.UUID URL string } var commonTemplate = textTemplate.Must( textTemplate.New("mobileconfig").Parse(`