headscale now has a CLI - registration of machines occurs there

This commit is contained in:
Juan Font Alonso 2021-02-21 01:30:03 +01:00
parent ff7db34b5e
commit b1d06f3ffd
5 changed files with 127 additions and 35 deletions

36
app.go
View file

@ -3,6 +3,7 @@ package headscale
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
@ -65,3 +66,38 @@ func (h *Headscale) Serve() error {
err := r.Run(h.cfg.Addr) err := r.Run(h.cfg.Addr)
return err return err
} }
func (h *Headscale) RegisterMachine(key string) error {
mKey, err := wgcfg.ParseHexKey(key)
if err != nil {
log.Printf("Cannot parse client key: %s", err)
return err
}
db, err := h.db()
if err != nil {
log.Printf("Cannot open DB: %s", err)
return err
}
defer db.Close()
m := Machine{}
if db.First(&m, "machine_key = ?", mKey.HexString()).RecordNotFound() {
log.Printf("Cannot find machine with machine key: %s", mKey.Base64())
return err
}
if m.isAlreadyRegistered() {
fmt.Println("This machine already registered")
return nil
}
ip, err := h.getAvailableIP()
if err != nil {
log.Println(err)
return err
}
m.IPAddress = ip.String()
m.Registered = true
db.Save(&m)
fmt.Println("Machine registered 🎉")
return nil
}

View file

@ -1,16 +1,72 @@
package main package main
import ( import (
"fmt"
"io" "io"
"log" "log"
"os" "os"
"github.com/juanfont/headscale" "github.com/juanfont/headscale"
"github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
) )
const version = "0.1"
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version.",
Long: "The version of headscale.",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(version)
},
}
var headscaleCmd = &cobra.Command{
Use: "headscale",
Short: "headscale - a Tailscale control server",
Long: fmt.Sprintf(`
headscale is an open source implementation of the Tailscale control server
Juan Font Alonso <juanfontalonso@gmail.com> - 2021
https://gitlab.com/juanfont/headscale`),
}
var serveCmd = &cobra.Command{
Use: "serve",
Short: "Launches the headscale server",
Args: func(cmd *cobra.Command, args []string) error {
return nil
},
Run: func(cmd *cobra.Command, args []string) {
h, err := getHeadscaleApp()
if err != nil {
log.Fatalf("Error initializing: %s", err)
}
h.Serve()
},
}
var registerCmd = &cobra.Command{
Use: "register machineID",
Short: "Registers a machine to your network",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Missing parameters")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
h, err := getHeadscaleApp()
if err != nil {
log.Fatalf("Error initializing: %s", err)
}
h.RegisterMachine(args[0])
},
}
func main() { func main() {
viper.SetConfigName("config") viper.SetConfigName("config")
viper.AddConfigPath(".") viper.AddConfigPath(".")
@ -20,6 +76,18 @@ func main() {
log.Fatalf("Fatal error config file: %s \n", err) log.Fatalf("Fatal error config file: %s \n", err)
} }
headscaleCmd.AddCommand(versionCmd)
headscaleCmd.AddCommand(serveCmd)
headscaleCmd.AddCommand(registerCmd)
if err := headscaleCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(-1)
}
}
func getHeadscaleApp() (*headscale.Headscale, error) {
derpMap, err := loadDerpMap(viper.GetString("derp_map_path")) derpMap, err := loadDerpMap(viper.GetString("derp_map_path"))
if err != nil { if err != nil {
log.Printf("Could not load DERP servers map file: %s", err) log.Printf("Could not load DERP servers map file: %s", err)
@ -39,9 +107,9 @@ func main() {
} }
h, err := headscale.NewHeadscale(cfg) h, err := headscale.NewHeadscale(cfg)
if err != nil { if err != nil {
log.Fatalln(err) return nil, err
} }
h.Serve() return h, nil
} }
func loadDerpMap(path string) (*tailcfg.DERPMap, error) { func loadDerpMap(path string) (*tailcfg.DERPMap, error) {

1
go.mod
View file

@ -6,6 +6,7 @@ require (
github.com/gin-gonic/gin v1.6.3 github.com/gin-gonic/gin v1.6.3
github.com/jinzhu/gorm v1.9.16 github.com/jinzhu/gorm v1.9.16
github.com/klauspost/compress v1.11.7 github.com/klauspost/compress v1.11.7
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee
github.com/spf13/viper v1.7.1 github.com/spf13/viper v1.7.1
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
gopkg.in/yaml.v2 v2.2.8 gopkg.in/yaml.v2 v2.2.8

2
go.sum
View file

@ -232,6 +232,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
@ -380,6 +381,7 @@ github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee h1:GQkkv3XSnxhAMjdq2wLfEnptEVr+2BNvmHizILHn+d4=
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=

View file

@ -262,41 +262,26 @@ func (h *Headscale) RegisterWebAPI(c *gin.Context) {
c.String(http.StatusBadRequest, "Wrong params") c.String(http.StatusBadRequest, "Wrong params")
return return
} }
mKey, err := wgcfg.ParseHexKey(mKeyStr)
if err != nil {
log.Printf("Cannot parse client key: %s", err)
c.String(http.StatusInternalServerError, "Sad!")
return
}
db, err := h.db()
if err != nil {
log.Printf("Cannot open DB: %s", err)
c.String(http.StatusInternalServerError, ":(")
return
}
defer db.Close()
m := Machine{}
if db.First(&m, "machine_key = ?", mKey.HexString()).RecordNotFound() {
log.Printf("Cannot find machine with machine key: %s", mKey.Base64())
c.String(http.StatusNotFound, "Sad!")
return
}
if !m.isAlreadyRegistered() { c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(fmt.Sprintf(`
ip, err := h.getAvailableIP() <html>
if err != nil { <body>
log.Println(err) <h1>headscale</h1>
c.String(http.StatusInternalServerError, "Upsy dupsy") <p>
return Run the command below in the headscale server to add this machine to your network:
} </p>
m.IPAddress = ip.String()
m.Registered = true // very naive 😱
db.Save(&m)
c.JSON(http.StatusOK, gin.H{"msg": "Ook"}) <p>
return <code>
} <b>headscale register %s</b>
c.JSON(http.StatusOK, gin.H{"msg": "Eek"}) </code>
</p>
</body>
</html>
`, mKeyStr)))
return
} }
func (h *Headscale) handleNewServer(c *gin.Context, db *gorm.DB, idKey wgcfg.Key, req tailcfg.RegisterRequest) { func (h *Headscale) handleNewServer(c *gin.Context, db *gorm.DB, idKey wgcfg.Key, req tailcfg.RegisterRequest) {