mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01: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] Network changes are relied to the nodes | ||||
| - [x] ~~Multiuser~~ Namespace support | ||||
| - [x] Basic routing (advertise & accept)  | ||||
| - [ ] Share nodes between ~~users~~ namespaces | ||||
| - [ ] Node registration via pre-auth keys | ||||
| - [ ] ACLs | ||||
|  | ||||
							
								
								
									
										72
									
								
								app.go
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								app.go
									
									
									
									
									
								
							| @ -1,12 +1,15 @@ | ||||
| package headscale | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"sync" | ||||
| 
 | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/jinzhu/gorm/dialects/postgres" | ||||
| 	"inet.af/netaddr" | ||||
| 	"tailscale.com/tailcfg" | ||||
| 	"tailscale.com/wgengine/wgcfg" | ||||
| ) | ||||
| @ -113,3 +116,72 @@ func (h *Headscale) RegisterMachine(key string, namespace string) error { | ||||
| 	fmt.Println("Machine registered 🎉") | ||||
| 	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{ | ||||
| 	Use:   "list", | ||||
| 	Short: "Creates a new namespace", | ||||
| 	Short: "List all the namespaces", | ||||
| 	Run: func(cmd *cobra.Command, args []string) { | ||||
| 		h, err := getHeadscaleApp() | ||||
| 		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() { | ||||
| 	viper.SetConfigName("config") | ||||
| 	viper.AddConfigPath(".") | ||||
| @ -136,6 +185,10 @@ func main() { | ||||
| 	namespaceCmd.AddCommand(createNamespaceCmd) | ||||
| 	namespaceCmd.AddCommand(listNamespacesCmd) | ||||
| 
 | ||||
| 	headscaleCmd.AddCommand(nodeCmd) | ||||
| 	nodeCmd.AddCommand(listRoutesCmd) | ||||
| 	nodeCmd.AddCommand(enableRouteCmd) | ||||
| 
 | ||||
| 	if err := headscaleCmd.Execute(); err != nil { | ||||
| 		fmt.Println(err) | ||||
| 		os.Exit(-1) | ||||
|  | ||||
							
								
								
									
										64
									
								
								machine.go
									
									
									
									
									
								
							
							
						
						
									
										64
									
								
								machine.go
									
									
									
									
									
								
							| @ -8,6 +8,7 @@ import ( | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/davecgh/go-spew/spew" | ||||
| 	"github.com/jinzhu/gorm/dialects/postgres" | ||||
| 	"inet.af/netaddr" | ||||
| 	"tailscale.com/tailcfg" | ||||
| @ -29,8 +30,9 @@ type Machine struct { | ||||
| 	LastSeen   *time.Time | ||||
| 	Expiry     *time.Time | ||||
| 
 | ||||
| 	HostInfo  postgres.Jsonb | ||||
| 	Endpoints postgres.Jsonb | ||||
| 	HostInfo      postgres.Jsonb | ||||
| 	Endpoints     postgres.Jsonb | ||||
| 	EnabledRoutes postgres.Jsonb | ||||
| 
 | ||||
| 	CreatedAt time.Time | ||||
| 	UpdatedAt time.Time | ||||
| @ -64,14 +66,34 @@ func (m Machine) toNode() (*tailcfg.Node, error) { | ||||
| 	} | ||||
| 
 | ||||
| 	addrs := []netaddr.IPPrefix{} | ||||
| 	allowedIPs := []netaddr.IPPrefix{} | ||||
| 
 | ||||
| 	ip, err := netaddr.ParseIPPrefix(fmt.Sprintf("%s/32", m.IPAddress)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	addrs = append(addrs, ip)           // missing the ipv6 ?
 | ||||
| 	allowedIPs = append(allowedIPs, ip) // looks like the client expect this
 | ||||
| 	addrs = append(addrs, ip) // missing the ipv6 ?
 | ||||
| 
 | ||||
| 	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{} | ||||
| 	if len(m.Endpoints.RawMessage) != 0 { | ||||
| @ -126,6 +148,7 @@ func (m Machine) toNode() (*tailcfg.Node, error) { | ||||
| 		MachineAuthorized: m.Registered, | ||||
| 	} | ||||
| 
 | ||||
| 	spew.Dump(n) | ||||
| 	// n.Key.MarshalText()
 | ||||
| 	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 }) | ||||
| 	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