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(`

Apple configuration profiles

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.

Caution

You should always inspect the profile before installing it:

curl {{.Url}}/apple/macos

Profiles

macOS

Headscale can be set to the default server by installing a Headscale configuration profile:

macOS profile

  1. Download the profile, then open it. When it has been opened, there should be a notification that a profile can be installed
  2. Open System Preferences and go to "Profiles"
  3. Find and install the Headscale profile
  4. Restart Tailscale.app and log in

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(` PayloadUUID {{.UUID}} PayloadDisplayName Headscale PayloadDescription Configure Tailscale login server to: {{.URL}} PayloadIdentifier com.github.juanfont.headscale PayloadRemovalDisallowed PayloadType Configuration PayloadVersion 1 PayloadContent {{.Payload}} `), ) var iosTemplate = textTemplate.Must(textTemplate.New("iosTemplate").Parse(` PayloadType io.tailscale.ipn.ios PayloadUUID {{.UUID}} PayloadIdentifier com.github.juanfont.headscale PayloadVersion 1 PayloadEnabled ControlURL {{.URL}} `)) var macosTemplate = template.Must(template.New("macosTemplate").Parse(` PayloadType io.tailscale.ipn.macos PayloadUUID {{.UUID}} PayloadIdentifier com.github.juanfont.headscale PayloadVersion 1 PayloadEnabled ControlURL {{.URL}} `))