mirror of
https://github.com/juanfont/headscale.git
synced 2024-11-29 18:33:05 +00:00
Changed all the html into go using go-elem (#2161)
* Changed all the HTML into go using go-elem Created templates package in ./hscontrol/templates. Moved the registerWebAPITemplate into the templates package as a function to be called. Replaced the apple and windows html files with go-elem. * update flake Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> --------- Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> Co-authored-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
parent
9515040161
commit
24e7851a40
11 changed files with 323 additions and 294 deletions
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
# When updating go.mod or go.sum, a new sha will need to be calculated,
|
# When updating go.mod or go.sum, a new sha will need to be calculated,
|
||||||
# update this if you have a mismatch after doing a change to thos files.
|
# update this if you have a mismatch after doing a change to thos files.
|
||||||
vendorHash = "sha256-SDJSFji6498WI9bJLmY62VGt21TtD2GxrxRAWyYyr0c=";
|
vendorHash = "sha256-CMkYTRjmhvTTrB7JbLj0cj9VEyzpG0iUWXkaOagwYTk=";
|
||||||
|
|
||||||
subPackages = ["cmd/headscale"];
|
subPackages = ["cmd/headscale"];
|
||||||
|
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -4,6 +4,7 @@ go 1.23.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||||
|
github.com/chasefleming/elem-go v0.29.0
|
||||||
github.com/coder/websocket v1.8.12
|
github.com/coder/websocket v1.8.12
|
||||||
github.com/coreos/go-oidc/v3 v3.11.0
|
github.com/coreos/go-oidc/v3 v3.11.0
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -90,6 +90,8 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/chasefleming/elem-go v0.29.0 h1:WwrjQcVn6xldhexluvl2Z3sgKi9HTMuzWeEXO4PHsmg=
|
||||||
|
github.com/chasefleming/elem-go v0.29.0/go.mod h1:hz73qILBIKnTgOujnSMtEj20/epI+f6vg71RUilJAA4=
|
||||||
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||||
github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
|
github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
|
||||||
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
package hscontrol
|
package hscontrol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/chasefleming/elem-go"
|
||||||
|
"github.com/chasefleming/elem-go/attrs"
|
||||||
|
"github.com/chasefleming/elem-go/styles"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/juanfont/headscale/hscontrol/templates"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
|
@ -135,38 +137,37 @@ func (h *Headscale) HealthHandler(
|
||||||
respond(nil)
|
respond(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
type registerWebAPITemplateConfig struct {
|
var codeStyleRegisterWebAPI = styles.Props{
|
||||||
Key string
|
styles.Display: "block",
|
||||||
|
styles.Padding: "20px",
|
||||||
|
styles.Border: "1px solid #bbb",
|
||||||
|
styles.BackgroundColor: "#eee",
|
||||||
}
|
}
|
||||||
|
|
||||||
var registerWebAPITemplate = template.Must(
|
func registerWebHTML(key string) *elem.Element {
|
||||||
template.New("registerweb").Parse(`
|
return elem.Html(nil,
|
||||||
<html>
|
elem.Head(
|
||||||
<head>
|
nil,
|
||||||
<title>Registration - Headscale</title>
|
elem.Title(nil, elem.Text("Registration - Headscale")),
|
||||||
<meta name=viewport content="width=device-width, initial-scale=1">
|
elem.Meta(attrs.Props{
|
||||||
<style>
|
attrs.Name: "viewport",
|
||||||
body {
|
attrs.Content: "width=device-width, initial-scale=1",
|
||||||
font-family: sans;
|
}),
|
||||||
}
|
),
|
||||||
code {
|
elem.Body(attrs.Props{
|
||||||
display: block;
|
attrs.Style: styles.Props{
|
||||||
padding: 20px;
|
styles.FontFamily: "sans",
|
||||||
border: 1px solid #bbb;
|
}.ToInline(),
|
||||||
background-color: #eee;
|
},
|
||||||
}
|
elem.H1(nil, elem.Text("headscale")),
|
||||||
</style>
|
elem.H2(nil, elem.Text("Machine registration")),
|
||||||
</head>
|
elem.P(nil, elem.Text("Run the command below in the headscale server to add this machine to your network:")),
|
||||||
<body>
|
elem.Code(attrs.Props{attrs.Style: codeStyleRegisterWebAPI.ToInline()},
|
||||||
<h1>headscale</h1>
|
elem.Text(fmt.Sprintf("headscale nodes register --user USERNAME --key %s", key)),
|
||||||
<h2>Machine registration</h2>
|
),
|
||||||
<p>
|
),
|
||||||
Run the command below in the headscale server to add this machine to your network:
|
)
|
||||||
</p>
|
}
|
||||||
<code>headscale nodes register --user USERNAME --key {{.Key}}</code>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`))
|
|
||||||
|
|
||||||
type AuthProviderWeb struct {
|
type AuthProviderWeb struct {
|
||||||
serverURL string
|
serverURL string
|
||||||
|
@ -220,34 +221,14 @@ func (a *AuthProviderWeb) RegisterHandler(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var content bytes.Buffer
|
|
||||||
if err := registerWebAPITemplate.Execute(&content, registerWebAPITemplateConfig{
|
|
||||||
Key: machineKey.String(),
|
|
||||||
}); err != nil {
|
|
||||||
log.Error().
|
|
||||||
Str("func", "RegisterWebAPI").
|
|
||||||
Err(err).
|
|
||||||
Msg("Could not render register web API template")
|
|
||||||
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
||||||
writer.WriteHeader(http.StatusInternalServerError)
|
|
||||||
_, err = writer.Write([]byte("Could not render register web API template"))
|
|
||||||
if err != nil {
|
|
||||||
log.Error().
|
|
||||||
Caller().
|
|
||||||
Err(err).
|
|
||||||
Msg("Failed to write response")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
writer.WriteHeader(http.StatusOK)
|
writer.WriteHeader(http.StatusOK)
|
||||||
_, err = writer.Write(content.Bytes())
|
if _, err := writer.Write([]byte(registerWebHTML(machineKey.String()).Render())); err != nil {
|
||||||
if err != nil {
|
if _, err := writer.Write([]byte(templates.RegisterWeb(machineKey.String()).Render())); err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Caller().
|
Caller().
|
||||||
Err(err).
|
Err(err).
|
||||||
Msg("Failed to write response")
|
Msg("Failed to write response")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,49 +9,19 @@ import (
|
||||||
|
|
||||||
"github.com/gofrs/uuid/v5"
|
"github.com/gofrs/uuid/v5"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/juanfont/headscale/hscontrol/templates"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed templates/apple.html
|
|
||||||
var appleTemplate string
|
|
||||||
|
|
||||||
//go:embed templates/windows.html
|
|
||||||
var windowsTemplate string
|
|
||||||
|
|
||||||
// WindowsConfigMessage shows a simple message in the browser for how to configure the Windows Tailscale client.
|
// WindowsConfigMessage shows a simple message in the browser for how to configure the Windows Tailscale client.
|
||||||
func (h *Headscale) WindowsConfigMessage(
|
func (h *Headscale) WindowsConfigMessage(
|
||||||
writer http.ResponseWriter,
|
writer http.ResponseWriter,
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
) {
|
) {
|
||||||
winTemplate := template.Must(template.New("windows").Parse(windowsTemplate))
|
|
||||||
config := map[string]interface{}{
|
|
||||||
"URL": h.cfg.ServerURL,
|
|
||||||
}
|
|
||||||
|
|
||||||
var payload bytes.Buffer
|
|
||||||
if err := winTemplate.Execute(&payload, config); err != nil {
|
|
||||||
log.Error().
|
|
||||||
Str("handler", "WindowsRegConfig").
|
|
||||||
Err(err).
|
|
||||||
Msg("Could not render Windows index template")
|
|
||||||
|
|
||||||
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
||||||
writer.WriteHeader(http.StatusInternalServerError)
|
|
||||||
_, err := writer.Write([]byte("Could not render Windows index template"))
|
|
||||||
if err != nil {
|
|
||||||
log.Error().
|
|
||||||
Caller().
|
|
||||||
Err(err).
|
|
||||||
Msg("Failed to write response")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
writer.WriteHeader(http.StatusOK)
|
writer.WriteHeader(http.StatusOK)
|
||||||
_, err := writer.Write(payload.Bytes())
|
|
||||||
if err != nil {
|
if _, err := writer.Write([]byte(templates.Windows(h.cfg.ServerURL).Render())); err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Caller().
|
Caller().
|
||||||
Err(err).
|
Err(err).
|
||||||
|
@ -64,36 +34,10 @@ func (h *Headscale) AppleConfigMessage(
|
||||||
writer http.ResponseWriter,
|
writer http.ResponseWriter,
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
) {
|
) {
|
||||||
appleTemplate := template.Must(template.New("apple").Parse(appleTemplate))
|
|
||||||
|
|
||||||
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")
|
|
||||||
|
|
||||||
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
||||||
writer.WriteHeader(http.StatusInternalServerError)
|
|
||||||
_, err := writer.Write([]byte("Could not render Apple index template"))
|
|
||||||
if err != nil {
|
|
||||||
log.Error().
|
|
||||||
Caller().
|
|
||||||
Err(err).
|
|
||||||
Msg("Failed to write response")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
writer.WriteHeader(http.StatusOK)
|
writer.WriteHeader(http.StatusOK)
|
||||||
_, err := writer.Write(payload.Bytes())
|
|
||||||
if err != nil {
|
if _, err := writer.Write([]byte(templates.Apple(h.cfg.ServerURL).Render())); err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Caller().
|
Caller().
|
||||||
Err(err).
|
Err(err).
|
||||||
|
|
149
hscontrol/templates/apple.go
Normal file
149
hscontrol/templates/apple.go
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/chasefleming/elem-go"
|
||||||
|
"github.com/chasefleming/elem-go/attrs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Apple(url string) *elem.Element {
|
||||||
|
return HtmlStructure(
|
||||||
|
elem.Title(nil,
|
||||||
|
elem.Text("headscale - Apple")),
|
||||||
|
elem.Body(attrs.Props{
|
||||||
|
attrs.Style: bodyStyle.ToInline(),
|
||||||
|
},
|
||||||
|
headerOne("headscale: iOS configuration"),
|
||||||
|
headerTwo("GUI"),
|
||||||
|
elem.Ol(nil,
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text("Install the official Tailscale iOS client from the "),
|
||||||
|
elem.A(attrs.Props{attrs.Href: "https://apps.apple.com/app/tailscale/id1470499037"},
|
||||||
|
elem.Text("App store"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text("Open Tailscale and make sure you are "),
|
||||||
|
elem.I(nil, elem.Text("not ")),
|
||||||
|
elem.Text("logged in to any account"),
|
||||||
|
),
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text("Open Settings on the iOS device"),
|
||||||
|
),
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text(`Scroll down to the "third party apps" section, under "Game Center" or "TV Provider"`),
|
||||||
|
),
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text("Find Tailscale and select it"),
|
||||||
|
elem.Ul(nil,
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text(`If the iOS device was previously logged into Tailscale, switch the "Reset Keychain" toggle to "on"`),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text(fmt.Sprintf(`Enter "%s" under "Alternate Coordination Server URL"`,url)),
|
||||||
|
),
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text("Restart the app by closing it from the iOS app switcher, open the app and select the regular sign in option "),
|
||||||
|
elem.I(nil, elem.Text("(non-SSO)")),
|
||||||
|
elem.Text(". It should open up to the headscale authentication page."),
|
||||||
|
),
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text("Enter your credentials and log in. Headscale should now be working on your iOS device"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
headerOne("headscale: macOS configuration"),
|
||||||
|
headerTwo("Command line"),
|
||||||
|
elem.P(nil,
|
||||||
|
elem.Text("Use Tailscale's login command to add your profile:"),
|
||||||
|
),
|
||||||
|
elem.Pre(nil,
|
||||||
|
elem.Code(nil,
|
||||||
|
elem.Text(fmt.Sprintf("tailscale login --login-server %s",url)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
headerTwo("GUI"),
|
||||||
|
elem.Ol(nil,
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text("ALT + Click the Tailscale icon in the menu and hover over the Debug menu"),
|
||||||
|
),
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text(`Under "Custom Login Server", select "Add Account..."`),
|
||||||
|
),
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text(fmt.Sprintf(`Enter "%s" of the headscale instance and press "Add Account"`,url)),
|
||||||
|
),
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text(`Follow the login procedure in the browser`),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
headerTwo("Profiles"),
|
||||||
|
elem.P(nil,
|
||||||
|
elem.Text("Headscale can be set to the default server by installing a Headscale configuration profile:"),
|
||||||
|
),
|
||||||
|
elem.P(nil,
|
||||||
|
elem.A(attrs.Props{attrs.Href: "/apple/macos-app-store", attrs.Download: "headscale_macos.mobileconfig"},
|
||||||
|
elem.Text("macOS AppStore profile "),
|
||||||
|
),
|
||||||
|
elem.A(attrs.Props{attrs.Href: "/apple/macos-standalone", attrs.Download: "headscale_macos.mobileconfig"},
|
||||||
|
elem.Text("macOS Standalone profile"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
elem.Ol(nil,
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text("Download the profile, then open it. When it has been opened, there should be a notification that a profile can be installed"),
|
||||||
|
),
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text(`Open System Preferences and go to "Profiles"`),
|
||||||
|
),
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text(`Find and install the Headscale profile`),
|
||||||
|
),
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text(`Restart Tailscale.app and log in`),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
elem.P(nil, elem.Text("Or")),
|
||||||
|
elem.P(nil,
|
||||||
|
elem.Text("Use your terminal to configure the default setting for Tailscale by issuing:"),
|
||||||
|
),
|
||||||
|
elem.Ul(nil,
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text(`for app store client:`),
|
||||||
|
elem.Code(nil,
|
||||||
|
elem.Text(fmt.Sprintf(`defaults write io.tailscale.ipn.macos ControlURL %s`,url)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text(`for standalone client:`),
|
||||||
|
elem.Code(nil,
|
||||||
|
elem.Text(fmt.Sprintf(`defaults write io.tailscale.ipn.macsys ControlURL %s`,url)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
elem.P(nil,
|
||||||
|
elem.Text("Restart Tailscale.app and log in."),
|
||||||
|
),
|
||||||
|
headerThree("Caution"),
|
||||||
|
elem.P(nil,
|
||||||
|
elem.Text("You should always download and inspect the profile before installing it:"),
|
||||||
|
),
|
||||||
|
elem.Ul(nil,
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text(`for app store client: `),
|
||||||
|
elem.Code(nil,
|
||||||
|
elem.Text(fmt.Sprintf(`curl %s/apple/macos-app-store`,url)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
elem.Li(nil,
|
||||||
|
elem.Text(`for standalone client: `),
|
||||||
|
elem.Code(nil,
|
||||||
|
elem.Text(fmt.Sprintf(`curl %s/apple/macos-standalone`,url)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,131 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>headscale - Apple</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 40px auto;
|
|
||||||
max-width: 800px;
|
|
||||||
line-height: 1.5;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #444;
|
|
||||||
padding: 0 10px;
|
|
||||||
font-family: Sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3 {
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<h1>headscale: iOS configuration</h1>
|
|
||||||
<h2>GUI</h2>
|
|
||||||
<ol>
|
|
||||||
<li>
|
|
||||||
Install the official Tailscale iOS client from the
|
|
||||||
<a href="https://apps.apple.com/app/tailscale/id1470499037"
|
|
||||||
>App store</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Open Tailscale and make sure you are <i>not</i> logged in to any account
|
|
||||||
</li>
|
|
||||||
<li>Open Settings on the iOS device</li>
|
|
||||||
<li>
|
|
||||||
Scroll down to the "third party apps" section, under "Game Center" or
|
|
||||||
"TV Provider"
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Find Tailscale and select it
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
If the iOS device was previously logged into Tailscale, switch the
|
|
||||||
"Reset Keychain" toggle to "on"
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li>Enter "{{.URL}}" under "Alternate Coordination Server URL"</li>
|
|
||||||
<li>
|
|
||||||
Restart the app by closing it from the iOS app switcher, open the app
|
|
||||||
and select the regular sign in option <i>(non-SSO)</i>. It should open
|
|
||||||
up to the headscale authentication page.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Enter your credentials and log in. Headscale should now be working on
|
|
||||||
your iOS device
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
<h1>headscale: macOS configuration</h1>
|
|
||||||
<h2>Command line</h2>
|
|
||||||
<p>Use Tailscale's login command to add your profile:</p>
|
|
||||||
<pre><code>tailscale login --login-server {{.URL}}</code></pre>
|
|
||||||
<h2>GUI</h2>
|
|
||||||
<ol>
|
|
||||||
<li>
|
|
||||||
ALT + Click the Tailscale icon in the menu and hover over the Debug menu
|
|
||||||
</li>
|
|
||||||
<li>Under "Custom Login Server", select "Add Account..."</li>
|
|
||||||
<li>
|
|
||||||
Enter "{{.URL}}" of the headscale instance and press "Add Account"
|
|
||||||
</li>
|
|
||||||
<li>Follow the login procedure in the browser</li>
|
|
||||||
</ol>
|
|
||||||
<h2>Profiles</h2>
|
|
||||||
<p>
|
|
||||||
Headscale can be set to the default server by installing a Headscale
|
|
||||||
configuration profile:
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<a href="/apple/macos-app-store" download="headscale_macos.mobileconfig"
|
|
||||||
>macOS AppStore profile</a
|
|
||||||
>
|
|
||||||
<a href="/apple/macos-standalone" download="headscale_macos.mobileconfig"
|
|
||||||
>macOS Standalone profile</a
|
|
||||||
>
|
|
||||||
</p>
|
|
||||||
<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>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
for app store client:
|
|
||||||
<code>defaults write io.tailscale.ipn.macos ControlURL {{.URL}}</code>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
for standalone client:
|
|
||||||
<code>defaults write io.tailscale.ipn.macsys ControlURL {{.URL}}</code>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p>Restart Tailscale.app and log in.</p>
|
|
||||||
<h3>Caution</h3>
|
|
||||||
<p>
|
|
||||||
You should always download and inspect the profile before installing it:
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
for app store client: <code>curl {{.URL}}/apple/macos-app-store</code>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
for standalone client: <code>curl {{.URL}}/apple/macos-standalone</code>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
56
hscontrol/templates/general.go
Normal file
56
hscontrol/templates/general.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/chasefleming/elem-go"
|
||||||
|
"github.com/chasefleming/elem-go/attrs"
|
||||||
|
"github.com/chasefleming/elem-go/styles"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bodyStyle = styles.Props{
|
||||||
|
styles.Margin: "40px auto",
|
||||||
|
styles.MaxWidth: "800px",
|
||||||
|
styles.LineHeight: "1.5",
|
||||||
|
styles.FontSize: "16px",
|
||||||
|
styles.Color: "#444",
|
||||||
|
styles.Padding: "0 10px",
|
||||||
|
styles.FontFamily: "Sans-serif",
|
||||||
|
}
|
||||||
|
|
||||||
|
var headerStyle = styles.Props{
|
||||||
|
styles.LineHeight: "1.2",
|
||||||
|
}
|
||||||
|
|
||||||
|
func headerOne(text string) *elem.Element {
|
||||||
|
return elem.H1(attrs.Props{attrs.Style: headerStyle.ToInline()}, elem.Text(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
func headerTwo(text string) *elem.Element {
|
||||||
|
return elem.H2(attrs.Props{attrs.Style: headerStyle.ToInline()}, elem.Text(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
func headerThree(text string) *elem.Element {
|
||||||
|
return elem.H3(attrs.Props{attrs.Style: headerStyle.ToInline()}, elem.Text(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
func HtmlStructure(head, body *elem.Element) *elem.Element {
|
||||||
|
return elem.Html(nil,
|
||||||
|
elem.Head(
|
||||||
|
attrs.Props{
|
||||||
|
attrs.Lang: "en",
|
||||||
|
},
|
||||||
|
elem.Meta(attrs.Props{
|
||||||
|
attrs.Charset: "UTF-8",
|
||||||
|
}),
|
||||||
|
elem.Meta(attrs.Props{
|
||||||
|
attrs.HTTPequiv: "X-UA-Compatible",
|
||||||
|
attrs.Content: "IE=edge",
|
||||||
|
}),
|
||||||
|
elem.Meta(attrs.Props{
|
||||||
|
attrs.Name: "viewport",
|
||||||
|
attrs.Content: "width=device-width, initial-scale=1.0",
|
||||||
|
}),
|
||||||
|
head,
|
||||||
|
),
|
||||||
|
body,
|
||||||
|
)
|
||||||
|
}
|
34
hscontrol/templates/register_web.go
Normal file
34
hscontrol/templates/register_web.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/chasefleming/elem-go"
|
||||||
|
"github.com/chasefleming/elem-go/attrs"
|
||||||
|
"github.com/chasefleming/elem-go/styles"
|
||||||
|
)
|
||||||
|
|
||||||
|
var codeStyleRegisterWebAPI = styles.Props{
|
||||||
|
styles.Display: "block",
|
||||||
|
styles.Padding: "20px",
|
||||||
|
styles.Border: "1px solid #bbb",
|
||||||
|
styles.BackgroundColor: "#eee",
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterWeb(key string) *elem.Element {
|
||||||
|
return HtmlStructure(
|
||||||
|
elem.Title(nil, elem.Text("Registration - Headscale")),
|
||||||
|
elem.Body(attrs.Props{
|
||||||
|
attrs.Style: styles.Props{
|
||||||
|
styles.FontFamily: "sans",
|
||||||
|
}.ToInline(),
|
||||||
|
},
|
||||||
|
elem.H1(nil, elem.Text("headscale")),
|
||||||
|
elem.H2(nil, elem.Text("Machine registration")),
|
||||||
|
elem.P(nil, elem.Text("Run the command below in the headscale server to add this machine to your network: ")),
|
||||||
|
elem.Code(attrs.Props{attrs.Style: codeStyleRegisterWebAPI.ToInline()},
|
||||||
|
elem.Text(fmt.Sprintf("headscale nodes register --user USERNAME --key %s", key)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
38
hscontrol/templates/windows.go
Normal file
38
hscontrol/templates/windows.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package templates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/chasefleming/elem-go"
|
||||||
|
"github.com/chasefleming/elem-go/attrs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Windows(url string) *elem.Element {
|
||||||
|
return HtmlStructure(
|
||||||
|
elem.Title(nil,
|
||||||
|
elem.Text("headscale - Windows"),
|
||||||
|
),
|
||||||
|
elem.Body(attrs.Props{
|
||||||
|
attrs.Style : bodyStyle.ToInline(),
|
||||||
|
},
|
||||||
|
headerOne("headscale: Windows configuration"),
|
||||||
|
elem.P(nil,
|
||||||
|
elem.Text("Download "),
|
||||||
|
elem.A(attrs.Props{
|
||||||
|
attrs.Href: "https://tailscale.com/download/windows",
|
||||||
|
attrs.Rel: "noreferrer noopener",
|
||||||
|
attrs.Target: "_blank"},
|
||||||
|
elem.Text("Tailscale for Windows ")),
|
||||||
|
elem.Text("and install it."),
|
||||||
|
),
|
||||||
|
elem.P(nil,
|
||||||
|
elem.Text("Open a Command Prompt or Powershell and use Tailscale's login command to connect with headscale: "),
|
||||||
|
),
|
||||||
|
elem.Pre(nil,
|
||||||
|
elem.Code(nil,
|
||||||
|
elem.Text(fmt.Sprintf(`tailscale login --login-server %s`, url)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,45 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>headscale - Windows</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 40px auto;
|
|
||||||
max-width: 800px;
|
|
||||||
line-height: 1.5;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #444;
|
|
||||||
padding: 0 10px;
|
|
||||||
font-family: Sans-serif;
|
|
||||||
}
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3 {
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<h1>headscale: Windows configuration</h1>
|
|
||||||
<p>
|
|
||||||
Download
|
|
||||||
<a
|
|
||||||
href="https://tailscale.com/download/windows"
|
|
||||||
rel="noreferrer noopener"
|
|
||||||
target="_blank"
|
|
||||||
>Tailscale for Windows</a
|
|
||||||
>
|
|
||||||
and install it.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Open a Command Prompt or Powershell and use Tailscale's login command to
|
|
||||||
connect with headscale:
|
|
||||||
</p>
|
|
||||||
<pre><code>tailscale login --login-server {{.URL}}</code></pre>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Loading…
Reference in a new issue