2021-09-19 12:54:41 -04:00
|
|
|
package headscale
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"net/http"
|
|
|
|
"text/template"
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/gofrs/uuid"
|
2021-11-13 03:39:04 -05:00
|
|
|
"github.com/rs/zerolog/log"
|
2021-09-19 12:54:41 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// AppleMobileConfig shows a simple message in the browser to point to the CLI
|
2021-11-13 03:39:04 -05:00
|
|
|
// Listens in /register.
|
2021-11-14 14:32:03 -05:00
|
|
|
func (h *Headscale) AppleMobileConfig(ctx *gin.Context) {
|
|
|
|
appleTemplate := template.Must(template.New("apple").Parse(`
|
2021-09-19 12:54:41 -04:00
|
|
|
<html>
|
|
|
|
<body>
|
|
|
|
<h1>Apple configuration profiles</h1>
|
|
|
|
<p>
|
|
|
|
This page provides <a href="https://support.apple.com/guide/mdm/mdm-overview-mdmbf9e668/web">configuration profiles</a> for the official Tailscale clients for <a href="https://apps.apple.com/us/app/tailscale/id1470499037?ls=1">iOS</a> and <a href="https://apps.apple.com/ca/app/tailscale/id1475387142?mt=12">macOS</a>.
|
|
|
|
</p>
|
|
|
|
<p>
|
|
|
|
The profiles will configure Tailscale.app to use {{.Url}} as its control server.
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<h3>Caution</h3>
|
|
|
|
<p>You should always inspect the profile before installing it:</p>
|
2021-09-26 15:41:48 -04:00
|
|
|
<!--
|
2021-09-19 12:54:41 -04:00
|
|
|
<p><code>curl {{.Url}}/apple/ios</code></p>
|
2021-09-26 15:41:48 -04:00
|
|
|
-->
|
2021-09-19 12:54:41 -04:00
|
|
|
<p><code>curl {{.Url}}/apple/macos</code></p>
|
|
|
|
|
2021-09-23 15:22:07 -04:00
|
|
|
<h2>Profiles</h2>
|
2021-09-26 15:41:48 -04:00
|
|
|
|
|
|
|
<!--
|
2021-09-23 15:22:07 -04:00
|
|
|
<h3>iOS</h3>
|
2021-09-19 12:54:41 -04:00
|
|
|
<p>
|
2021-09-23 15:22:07 -04:00
|
|
|
<a href="/apple/ios" download="headscale_ios.mobileconfig">iOS profile</a>
|
2021-09-19 12:54:41 -04:00
|
|
|
</p>
|
2021-09-26 15:41:48 -04:00
|
|
|
-->
|
2021-09-19 12:54:41 -04:00
|
|
|
|
2021-09-23 15:22:07 -04:00
|
|
|
<h3>macOS</h3>
|
|
|
|
<p>Headscale can be set to the default server by installing a Headscale configuration profile:</p>
|
2021-09-19 12:54:41 -04:00
|
|
|
<p>
|
2021-09-23 15:22:07 -04:00
|
|
|
<a href="/apple/macos" download="headscale_macos.mobileconfig">macOS profile</a>
|
2021-09-19 12:54:41 -04:00
|
|
|
</p>
|
2021-09-23 15:22:07 -04:00
|
|
|
|
|
|
|
<ol>
|
|
|
|
<li>Download the profile, then open it. When it has been opened, there should be a notification that a profile can be installed</li>
|
|
|
|
<li>Open System Preferences and go to "Profiles"</li>
|
|
|
|
<li>Find and install the Headscale profile</li>
|
|
|
|
<li>Restart Tailscale.app and log in</li>
|
|
|
|
</ol>
|
|
|
|
|
|
|
|
<p>Or</p>
|
|
|
|
<p>Use your terminal to configure the default setting for Tailscale by issuing:</p>
|
|
|
|
<code>defaults write io.tailscale.ipn.macos ControlURL {{.Url}}</code>
|
|
|
|
|
|
|
|
<p>Restart Tailscale.app and log in.</p>
|
2021-09-19 12:54:41 -04:00
|
|
|
|
|
|
|
</body>
|
|
|
|
</html>`))
|
|
|
|
|
|
|
|
config := map[string]interface{}{
|
|
|
|
"Url": h.cfg.ServerURL,
|
|
|
|
}
|
|
|
|
|
|
|
|
var payload bytes.Buffer
|
2021-11-14 14:32:03 -05:00
|
|
|
if err := appleTemplate.Execute(&payload, config); err != nil {
|
2021-09-20 02:54:18 -04:00
|
|
|
log.Error().
|
|
|
|
Str("handler", "AppleMobileConfig").
|
|
|
|
Err(err).
|
|
|
|
Msg("Could not render Apple index template")
|
2021-11-14 14:32:03 -05:00
|
|
|
ctx.Data(
|
2021-11-13 03:36:45 -05:00
|
|
|
http.StatusInternalServerError,
|
|
|
|
"text/html; charset=utf-8",
|
|
|
|
[]byte("Could not render Apple index template"),
|
|
|
|
)
|
2021-11-14 10:46:09 -05:00
|
|
|
|
2021-09-19 12:54:41 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-11-14 14:32:03 -05:00
|
|
|
ctx.Data(http.StatusOK, "text/html; charset=utf-8", payload.Bytes())
|
2021-09-19 12:54:41 -04:00
|
|
|
}
|
|
|
|
|
2021-11-14 14:32:03 -05:00
|
|
|
func (h *Headscale) ApplePlatformConfig(ctx *gin.Context) {
|
|
|
|
platform := ctx.Param("platform")
|
2021-09-19 12:54:41 -04:00
|
|
|
|
|
|
|
id, err := uuid.NewV4()
|
|
|
|
if err != nil {
|
2021-09-20 02:54:18 -04:00
|
|
|
log.Error().
|
|
|
|
Str("handler", "ApplePlatformConfig").
|
|
|
|
Err(err).
|
|
|
|
Msg("Failed not create UUID")
|
2021-11-14 14:32:03 -05:00
|
|
|
ctx.Data(
|
2021-11-13 03:36:45 -05:00
|
|
|
http.StatusInternalServerError,
|
|
|
|
"text/html; charset=utf-8",
|
|
|
|
[]byte("Failed to create UUID"),
|
|
|
|
)
|
2021-11-14 10:46:09 -05:00
|
|
|
|
2021-09-19 12:54:41 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
contentId, err := uuid.NewV4()
|
|
|
|
if err != nil {
|
2021-09-20 02:54:18 -04:00
|
|
|
log.Error().
|
|
|
|
Str("handler", "ApplePlatformConfig").
|
|
|
|
Err(err).
|
|
|
|
Msg("Failed not create UUID")
|
2021-11-14 14:32:03 -05:00
|
|
|
ctx.Data(
|
2021-11-13 03:36:45 -05:00
|
|
|
http.StatusInternalServerError,
|
|
|
|
"text/html; charset=utf-8",
|
|
|
|
[]byte("Failed to create UUID"),
|
|
|
|
)
|
2021-11-14 10:46:09 -05:00
|
|
|
|
2021-09-19 12:54:41 -04:00
|
|
|
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 {
|
2021-09-20 02:54:18 -04:00
|
|
|
log.Error().
|
|
|
|
Str("handler", "ApplePlatformConfig").
|
|
|
|
Err(err).
|
|
|
|
Msg("Could not render Apple macOS template")
|
2021-11-14 14:32:03 -05:00
|
|
|
ctx.Data(
|
2021-11-13 03:36:45 -05:00
|
|
|
http.StatusInternalServerError,
|
|
|
|
"text/html; charset=utf-8",
|
|
|
|
[]byte("Could not render Apple macOS template"),
|
|
|
|
)
|
2021-11-14 10:46:09 -05:00
|
|
|
|
2021-09-19 12:54:41 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
case "ios":
|
|
|
|
if err := iosTemplate.Execute(&payload, platformConfig); err != nil {
|
2021-09-20 02:54:18 -04:00
|
|
|
log.Error().
|
|
|
|
Str("handler", "ApplePlatformConfig").
|
|
|
|
Err(err).
|
|
|
|
Msg("Could not render Apple iOS template")
|
2021-11-14 14:32:03 -05:00
|
|
|
ctx.Data(
|
2021-11-13 03:36:45 -05:00
|
|
|
http.StatusInternalServerError,
|
|
|
|
"text/html; charset=utf-8",
|
|
|
|
[]byte("Could not render Apple iOS template"),
|
|
|
|
)
|
2021-11-14 10:46:09 -05:00
|
|
|
|
2021-09-19 12:54:41 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
default:
|
2021-11-14 14:32:03 -05:00
|
|
|
ctx.Data(
|
2021-11-13 03:36:45 -05:00
|
|
|
http.StatusOK,
|
|
|
|
"text/html; charset=utf-8",
|
|
|
|
[]byte("Invalid platform, only ios and macos is supported"),
|
|
|
|
)
|
2021-11-14 10:46:09 -05:00
|
|
|
|
2021-09-19 12:54:41 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
config := AppleMobileConfig{
|
|
|
|
UUID: id,
|
|
|
|
Url: h.cfg.ServerURL,
|
|
|
|
Payload: payload.String(),
|
|
|
|
}
|
|
|
|
|
|
|
|
var content bytes.Buffer
|
|
|
|
if err := commonTemplate.Execute(&content, config); err != nil {
|
2021-09-20 02:54:18 -04:00
|
|
|
log.Error().
|
|
|
|
Str("handler", "ApplePlatformConfig").
|
|
|
|
Err(err).
|
|
|
|
Msg("Could not render Apple platform template")
|
2021-11-14 14:32:03 -05:00
|
|
|
ctx.Data(
|
2021-11-13 03:36:45 -05:00
|
|
|
http.StatusInternalServerError,
|
|
|
|
"text/html; charset=utf-8",
|
|
|
|
[]byte("Could not render Apple platform template"),
|
|
|
|
)
|
2021-11-14 10:46:09 -05:00
|
|
|
|
2021-09-19 12:54:41 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-11-14 14:32:03 -05:00
|
|
|
ctx.Data(
|
2021-11-13 03:36:45 -05:00
|
|
|
http.StatusOK,
|
|
|
|
"application/x-apple-aspen-config; charset=utf-8",
|
|
|
|
content.Bytes(),
|
|
|
|
)
|
2021-09-19 12:54:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
type AppleMobileConfig struct {
|
|
|
|
UUID uuid.UUID
|
|
|
|
Url string
|
|
|
|
Payload string
|
|
|
|
}
|
|
|
|
|
|
|
|
type AppleMobilePlatformConfig struct {
|
|
|
|
UUID uuid.UUID
|
|
|
|
Url string
|
|
|
|
}
|
|
|
|
|
2021-11-13 03:36:45 -05:00
|
|
|
var commonTemplate = template.Must(
|
|
|
|
template.New("mobileconfig").Parse(`<?xml version="1.0" encoding="UTF-8"?>
|
2021-09-19 12:54:41 -04:00
|
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
|
|
<plist version="1.0">
|
|
|
|
<dict>
|
|
|
|
<key>PayloadUUID</key>
|
|
|
|
<string>{{.UUID}}</string>
|
|
|
|
<key>PayloadDisplayName</key>
|
|
|
|
<string>Headscale</string>
|
|
|
|
<key>PayloadDescription</key>
|
|
|
|
<string>Configure Tailscale login server to: {{.Url}}</string>
|
|
|
|
<key>PayloadIdentifier</key>
|
|
|
|
<string>com.github.juanfont.headscale</string>
|
|
|
|
<key>PayloadRemovalDisallowed</key>
|
|
|
|
<false/>
|
|
|
|
<key>PayloadType</key>
|
|
|
|
<string>Configuration</string>
|
|
|
|
<key>PayloadVersion</key>
|
|
|
|
<integer>1</integer>
|
|
|
|
<key>PayloadContent</key>
|
|
|
|
<array>
|
|
|
|
{{.Payload}}
|
|
|
|
</array>
|
|
|
|
</dict>
|
2021-11-13 03:36:45 -05:00
|
|
|
</plist>`),
|
|
|
|
)
|
2021-09-19 12:54:41 -04:00
|
|
|
|
|
|
|
var iosTemplate = template.Must(template.New("iosTemplate").Parse(`
|
|
|
|
<dict>
|
|
|
|
<key>PayloadType</key>
|
|
|
|
<string>io.tailscale.ipn.ios</string>
|
|
|
|
<key>PayloadUUID</key>
|
|
|
|
<string>{{.UUID}}</string>
|
|
|
|
<key>PayloadIdentifier</key>
|
|
|
|
<string>com.github.juanfont.headscale</string>
|
|
|
|
<key>PayloadVersion</key>
|
|
|
|
<integer>1</integer>
|
|
|
|
<key>PayloadEnabled</key>
|
|
|
|
<true/>
|
|
|
|
|
|
|
|
<key>ControlURL</key>
|
|
|
|
<string>{{.Url}}</string>
|
|
|
|
</dict>
|
|
|
|
`))
|
|
|
|
|
|
|
|
var macosTemplate = template.Must(template.New("macosTemplate").Parse(`
|
|
|
|
<dict>
|
|
|
|
<key>PayloadType</key>
|
|
|
|
<string>io.tailscale.ipn.macos</string>
|
|
|
|
<key>PayloadUUID</key>
|
|
|
|
<string>{{.UUID}}</string>
|
|
|
|
<key>PayloadIdentifier</key>
|
|
|
|
<string>com.github.juanfont.headscale</string>
|
|
|
|
<key>PayloadVersion</key>
|
|
|
|
<integer>1</integer>
|
|
|
|
<key>PayloadEnabled</key>
|
|
|
|
<true/>
|
|
|
|
|
|
|
|
<key>ControlURL</key>
|
|
|
|
<string>{{.Url}}</string>
|
|
|
|
</dict>
|
|
|
|
`))
|