mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	Add initial code for generating Apple profiles
This code adds new http handlers that will generate iOS and macOS configuration profiles allowing us to override the Control server of the official Tailscale.app. Currently, macOS is working, as I have not found the correct "key" to inject for iOS. This means that a profile will allow users to no longer log in via the command line, but they can use the app.
This commit is contained in:
		
							parent
							
								
									6c903d4a2f
								
							
						
					
					
						commit
						40c5263927
					
				
							
								
								
									
										179
									
								
								apple_mobileconfig.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								apple_mobileconfig.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,179 @@ | ||||
| package headscale | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"net/http" | ||||
| 	"text/template" | ||||
| 
 | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/gofrs/uuid" | ||||
| ) | ||||
| 
 | ||||
| // AppleMobileConfig shows a simple message in the browser to point to the CLI
 | ||||
| // Listens in /register
 | ||||
| func (h *Headscale) AppleMobileConfig(c *gin.Context) { | ||||
| 	t := template.Must(template.New("apple").Parse(` | ||||
| <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> | ||||
| 		<p><code>curl {{.Url}}/apple/ios</code></p> | ||||
| 		<p><code>curl {{.Url}}/apple/macos</code></p> | ||||
| 		 | ||||
| 		<h3>Profiles</h3> | ||||
| 		<p> | ||||
| 		    <a href="/apple/ios" download="headscale_ios.mobileconfig">iOS</a> | ||||
| 		</p> | ||||
| 		 | ||||
| 		<p> | ||||
| 		    <a href="/apple/macos" download="headscale_macos.mobileconfig">macOS</a> | ||||
| 		</p> | ||||
| 	 | ||||
| 	</body> | ||||
| </html>`)) | ||||
| 
 | ||||
| 	config := map[string]interface{}{ | ||||
| 		"Url": h.cfg.ServerURL, | ||||
| 	} | ||||
| 
 | ||||
| 	var payload bytes.Buffer | ||||
| 	if err := t.Execute(&payload, config); err != nil { | ||||
| 		c.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	c.Data(http.StatusOK, "text/html; charset=utf-8", payload.Bytes()) | ||||
| } | ||||
| 
 | ||||
| func (h *Headscale) ApplePlatformConfig(c *gin.Context) { | ||||
| 	platform := c.Param("platform") | ||||
| 
 | ||||
| 	id, err := uuid.NewV4() | ||||
| 	if err != nil { | ||||
| 		c.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	contentId, err := uuid.NewV4() | ||||
| 	if err != nil { | ||||
| 		c.Error(err) | ||||
| 		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 { | ||||
| 			c.Error(err) | ||||
| 			return | ||||
| 		} | ||||
| 	case "ios": | ||||
| 		if err := iosTemplate.Execute(&payload, platformConfig); err != nil { | ||||
| 			c.Error(err) | ||||
| 			return | ||||
| 		} | ||||
| 	default: | ||||
| 		c.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 { | ||||
| 		c.Error(err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	c.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 = template.Must(template.New("mobileconfig").Parse(`<?xml version="1.0" encoding="UTF-8"?> | ||||
| <!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> | ||||
| </plist>`)) | ||||
| 
 | ||||
| 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> | ||||
| `)) | ||||
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							| @ -5,6 +5,7 @@ go 1.16 | ||||
| require ( | ||||
| 	github.com/AlecAivazis/survey/v2 v2.0.5 | ||||
| 	github.com/gin-gonic/gin v1.7.2 | ||||
| 	github.com/gofrs/uuid v4.0.0+incompatible // indirect | ||||
| 	github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b | ||||
| 	github.com/klauspost/compress v1.13.1 | ||||
| 	github.com/lib/pq v1.10.2 // indirect | ||||
|  | ||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							| @ -219,6 +219,8 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x | ||||
| github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= | ||||
| github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= | ||||
| github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= | ||||
| github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= | ||||
| github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= | ||||
| github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= | ||||
| github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= | ||||
| github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user