mirror of
https://github.com/juanfont/headscale.git
synced 2024-11-27 12:05:26 +00:00
Added basic routes functionality
This commit is contained in:
parent
db75cd39a7
commit
1fad8e6e5b
@ -8,6 +8,7 @@ An open source implementation of the Tailscale coordination server.
|
|||||||
- [x] Node registration through the web flow
|
- [x] Node registration through the web flow
|
||||||
- [x] Network changes are relied to the nodes
|
- [x] Network changes are relied to the nodes
|
||||||
- [x] ~~Multiuser~~ Namespace support
|
- [x] ~~Multiuser~~ Namespace support
|
||||||
|
- [x] Basic routing (advertise & accept)
|
||||||
- [ ] Share nodes between ~~users~~ namespaces
|
- [ ] Share nodes between ~~users~~ namespaces
|
||||||
- [ ] Node registration via pre-auth keys
|
- [ ] Node registration via pre-auth keys
|
||||||
- [ ] ACLs
|
- [ ] ACLs
|
||||||
|
72
app.go
72
app.go
@ -1,12 +1,15 @@
|
|||||||
package headscale
|
package headscale
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/jinzhu/gorm/dialects/postgres"
|
||||||
|
"inet.af/netaddr"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/wgengine/wgcfg"
|
"tailscale.com/wgengine/wgcfg"
|
||||||
)
|
)
|
||||||
@ -113,3 +116,72 @@ func (h *Headscale) RegisterMachine(key string, namespace string) error {
|
|||||||
fmt.Println("Machine registered 🎉")
|
fmt.Println("Machine registered 🎉")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Headscale) ListNodeRoutes(namespace string, nodeName string) error {
|
||||||
|
m, err := h.GetMachine(namespace, nodeName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
hi, err := m.GetHostInfo()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(hi.RoutableIPs)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Headscale) EnableNodeRoute(namespace string, nodeName string, routeStr string) error {
|
||||||
|
m, err := h.GetMachine(namespace, nodeName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hi, err := m.GetHostInfo()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
route, err := netaddr.ParseIPPrefix(routeStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rIP := range hi.RoutableIPs {
|
||||||
|
if rIP == route {
|
||||||
|
db, err := h.db()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Cannot open DB: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
routes, _ := json.Marshal([]string{routeStr}) // TODO: only one for the time being, so overwriting the rest
|
||||||
|
m.EnabledRoutes = postgres.Jsonb{RawMessage: json.RawMessage(routes)}
|
||||||
|
db.Save(&m)
|
||||||
|
db.Close()
|
||||||
|
|
||||||
|
peers, _ := h.getPeers(*m)
|
||||||
|
h.pollMu.Lock()
|
||||||
|
for _, p := range *peers {
|
||||||
|
if pUp, ok := h.clientsPolling[uint64(p.ID)]; ok {
|
||||||
|
pUp <- []byte{}
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h.pollMu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Could not find routable range")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func eqCIDRs(a, b []netaddr.IPPrefix) bool {
|
||||||
|
if len(a) != len(b) || ((a == nil) != (b == nil)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, v := range a {
|
||||||
|
if v != b[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
@ -102,7 +102,7 @@ var createNamespaceCmd = &cobra.Command{
|
|||||||
|
|
||||||
var listNamespacesCmd = &cobra.Command{
|
var listNamespacesCmd = &cobra.Command{
|
||||||
Use: "list",
|
Use: "list",
|
||||||
Short: "Creates a new namespace",
|
Short: "List all the namespaces",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
h, err := getHeadscaleApp()
|
h, err := getHeadscaleApp()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -120,6 +120,55 @@ var listNamespacesCmd = &cobra.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var nodeCmd = &cobra.Command{
|
||||||
|
Use: "node",
|
||||||
|
Short: "Manage the nodes of Headscale",
|
||||||
|
}
|
||||||
|
|
||||||
|
var listRoutesCmd = &cobra.Command{
|
||||||
|
Use: "list-routes NAMESPACE NODE",
|
||||||
|
Short: "List the routes exposed by this node",
|
||||||
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) < 2 {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
err = h.ListNodeRoutes(args[0], args[1])
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var enableRouteCmd = &cobra.Command{
|
||||||
|
Use: "enable-route",
|
||||||
|
Short: "Allows exposing a route declared by this node to the rest of the nodes",
|
||||||
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) < 3 {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
err = h.EnableNodeRoute(args[0], args[1], args[2])
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
viper.SetConfigName("config")
|
viper.SetConfigName("config")
|
||||||
viper.AddConfigPath(".")
|
viper.AddConfigPath(".")
|
||||||
@ -136,6 +185,10 @@ func main() {
|
|||||||
namespaceCmd.AddCommand(createNamespaceCmd)
|
namespaceCmd.AddCommand(createNamespaceCmd)
|
||||||
namespaceCmd.AddCommand(listNamespacesCmd)
|
namespaceCmd.AddCommand(listNamespacesCmd)
|
||||||
|
|
||||||
|
headscaleCmd.AddCommand(nodeCmd)
|
||||||
|
nodeCmd.AddCommand(listRoutesCmd)
|
||||||
|
nodeCmd.AddCommand(enableRouteCmd)
|
||||||
|
|
||||||
if err := headscaleCmd.Execute(); err != nil {
|
if err := headscaleCmd.Execute(); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
|
64
machine.go
64
machine.go
@ -8,6 +8,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/jinzhu/gorm/dialects/postgres"
|
"github.com/jinzhu/gorm/dialects/postgres"
|
||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
@ -29,8 +30,9 @@ type Machine struct {
|
|||||||
LastSeen *time.Time
|
LastSeen *time.Time
|
||||||
Expiry *time.Time
|
Expiry *time.Time
|
||||||
|
|
||||||
HostInfo postgres.Jsonb
|
HostInfo postgres.Jsonb
|
||||||
Endpoints postgres.Jsonb
|
Endpoints postgres.Jsonb
|
||||||
|
EnabledRoutes postgres.Jsonb
|
||||||
|
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
@ -64,14 +66,34 @@ func (m Machine) toNode() (*tailcfg.Node, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addrs := []netaddr.IPPrefix{}
|
addrs := []netaddr.IPPrefix{}
|
||||||
allowedIPs := []netaddr.IPPrefix{}
|
|
||||||
|
|
||||||
ip, err := netaddr.ParseIPPrefix(fmt.Sprintf("%s/32", m.IPAddress))
|
ip, err := netaddr.ParseIPPrefix(fmt.Sprintf("%s/32", m.IPAddress))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
addrs = append(addrs, ip) // missing the ipv6 ?
|
addrs = append(addrs, ip) // missing the ipv6 ?
|
||||||
allowedIPs = append(allowedIPs, ip) // looks like the client expect this
|
|
||||||
|
allowedIPs := []netaddr.IPPrefix{}
|
||||||
|
allowedIPs = append(allowedIPs, ip)
|
||||||
|
|
||||||
|
routesStr := []string{}
|
||||||
|
if len(m.EnabledRoutes.RawMessage) != 0 {
|
||||||
|
allwIps, err := m.EnabledRoutes.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(allwIps, &routesStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, aip := range routesStr {
|
||||||
|
ip, err := netaddr.ParseIPPrefix(aip)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
allowedIPs = append(allowedIPs, ip)
|
||||||
|
}
|
||||||
|
|
||||||
endpoints := []string{}
|
endpoints := []string{}
|
||||||
if len(m.Endpoints.RawMessage) != 0 {
|
if len(m.Endpoints.RawMessage) != 0 {
|
||||||
@ -126,6 +148,7 @@ func (m Machine) toNode() (*tailcfg.Node, error) {
|
|||||||
MachineAuthorized: m.Registered,
|
MachineAuthorized: m.Registered,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spew.Dump(n)
|
||||||
// n.Key.MarshalText()
|
// n.Key.MarshalText()
|
||||||
return &n, nil
|
return &n, nil
|
||||||
}
|
}
|
||||||
@ -156,3 +179,32 @@ func (h *Headscale) getPeers(m Machine) (*[]*tailcfg.Node, error) {
|
|||||||
sort.Slice(peers, func(i, j int) bool { return peers[i].ID < peers[j].ID })
|
sort.Slice(peers, func(i, j int) bool { return peers[i].ID < peers[j].ID })
|
||||||
return &peers, nil
|
return &peers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Headscale) GetMachine(namespace string, name string) (*Machine, error) {
|
||||||
|
machines, err := h.ListMachinesInNamespace(namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range *machines {
|
||||||
|
if m.Name == name {
|
||||||
|
return &m, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Machine) GetHostInfo() (*tailcfg.Hostinfo, error) {
|
||||||
|
hostinfo := tailcfg.Hostinfo{}
|
||||||
|
if len(m.HostInfo.RawMessage) != 0 {
|
||||||
|
hi, err := m.HostInfo.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(hi, &hostinfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &hostinfo, nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user