feat: view and enable routes on machines

This commit is contained in:
Johan Siebens
2022-05-15 07:10:05 +02:00
parent 52aa221cd0
commit 3aceacbc8d
9 changed files with 860 additions and 98 deletions
+88
View File
@@ -7,6 +7,7 @@ import (
"github.com/muesli/coral"
"github.com/nleeper/goment"
"github.com/rodaine/table"
"inet.af/netaddr"
"strings"
)
@@ -19,6 +20,8 @@ func machineCommands() *coral.Command {
command.AddCommand(deleteMachineCommand())
command.AddCommand(listMachinesCommand())
command.AddCommand(getMachineRoutesCommand())
command.AddCommand(setMachineRoutesCommand())
return command
}
@@ -109,3 +112,88 @@ func listMachinesCommand() *coral.Command {
return command
}
func getMachineRoutesCommand() *coral.Command {
command := &coral.Command{
Use: "get-routes",
Short: "Show the routes of a machine",
SilenceUsage: true,
}
var machineID uint64
var target = Target{}
target.prepareCommand(command)
command.Flags().Uint64Var(&machineID, "machine-id", 0, "")
command.RunE = func(command *coral.Command, args []string) error {
grpcClient, c, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
req := api.GetMachineRoutesRequest{MachineId: machineID}
resp, err := grpcClient.GetMachineRoutes(context.Background(), &req)
if err != nil {
return err
}
tbl := table.New("ROUTE", "ALLOWED")
for _, r := range resp.Routes {
tbl.AddRow(r.Advertised, r.Allowed)
}
tbl.Print()
return nil
}
return command
}
func setMachineRoutesCommand() *coral.Command {
command := &coral.Command{
Use: "set-routes",
Short: "Enable routes of a machine",
SilenceUsage: true,
}
var machineID uint64
var allowedIps []string
var target = Target{}
target.prepareCommand(command)
command.Flags().Uint64Var(&machineID, "machine-id", 0, "")
command.Flags().StringSliceVar(&allowedIps, "allowed-ips", []string{}, "")
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
var prefixes []netaddr.IPPrefix
for _, r := range allowedIps {
p, err := netaddr.ParseIPPrefix(r)
if err != nil {
return err
}
prefixes = append(prefixes, p)
}
req := api.SetMachineRoutesRequest{MachineId: machineID, AllowedIps: allowedIps}
resp, err := client.SetMachineRoutes(context.Background(), &req)
if err != nil {
return err
}
tbl := table.New("ROUTE", "ALLOWED")
for _, r := range resp.Routes {
tbl.AddRow(r.Advertised, r.Allowed)
}
tbl.Print()
return nil
}
return command
}
+40
View File
@@ -26,6 +26,7 @@ type Machine struct {
HostInfo HostInfo
Endpoints Endpoints
AllowIPs AllowIPs
IPv4 IP
IPv6 IP
@@ -56,6 +57,15 @@ func (m *Machine) HasTag(tag string) bool {
return false
}
func (m *Machine) IsAllowedIP(i netaddr.IPPrefix) bool {
for _, t := range m.AllowIPs {
if t.Overlaps(i) {
return true
}
}
return false
}
type IP struct {
*netaddr.IP
}
@@ -81,6 +91,36 @@ func (i IP) Value() (driver.Value, error) {
return i.String(), nil
}
type AllowIPs []netaddr.IPPrefix
func (hi *AllowIPs) Scan(destination interface{}) error {
switch value := destination.(type) {
case []byte:
return json.Unmarshal(value, hi)
default:
return fmt.Errorf("unexpected data type %T", destination)
}
}
func (hi AllowIPs) Value() (driver.Value, error) {
bytes, err := json.Marshal(hi)
return bytes, err
}
// GormDataType gorm common data type
func (AllowIPs) GormDataType() string {
return "json"
}
// GormDBDataType gorm db data type
func (AllowIPs) GormDBDataType(db *gorm.DB, field *schema.Field) string {
switch db.Dialector.Name() {
case "sqlite":
return "JSON"
}
return ""
}
type HostInfo tailcfg.Hostinfo
func (hi *HostInfo) Scan(destination interface{}) error {
+2
View File
@@ -116,6 +116,8 @@ func ToNode(m *domain.Machine, connected bool) (*tailcfg.Node, error) {
allowedIPs = append(allowedIPs, ipv6)
}
allowedIPs = append(allowedIPs, m.AllowIPs...)
var derp string
if hostinfo.NetInfo != nil {
derp = fmt.Sprintf("127.3.3.40:%d", hostinfo.NetInfo.PreferredDERP)
+76
View File
@@ -7,6 +7,7 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
"inet.af/netaddr"
)
func (s *Service) ListMachines(ctx context.Context, req *api.ListMachinesRequest) (*api.ListMachinesResponse, error) {
@@ -75,3 +76,78 @@ func (s *Service) DeleteMachine(ctx context.Context, req *api.DeleteMachineReque
return &api.DeleteMachineResponse{}, nil
}
func (s *Service) GetMachineRoutes(ctx context.Context, req *api.GetMachineRoutesRequest) (*api.GetMachineRoutesResponse, error) {
m, err := s.repository.GetMachine(ctx, req.MachineId)
if err != nil {
return nil, err
}
if m == nil {
return nil, status.Error(codes.NotFound, "machine does not exist")
}
var routes []*api.RoutableIP
for _, r := range m.HostInfo.RoutableIPs {
routes = append(routes, &api.RoutableIP{
Advertised: r.String(),
Allowed: m.IsAllowedIP(r),
})
}
response := api.GetMachineRoutesResponse{
Routes: routes,
}
return &response, nil
}
func (s *Service) SetMachineRoutes(ctx context.Context, req *api.SetMachineRoutesRequest) (*api.GetMachineRoutesResponse, error) {
m, err := s.repository.GetMachine(ctx, req.MachineId)
if err != nil {
return nil, err
}
if m == nil {
return nil, status.Error(codes.NotFound, "machine does not exist")
}
var allowedIps []netaddr.IPPrefix
for _, r := range req.AllowedIps {
prefix, err := netaddr.ParseIPPrefix(r)
if err != nil {
return nil, err
}
allowedIps = append(allowedIps, prefix)
}
m.AllowIPs = allowedIps
if err := s.repository.SaveMachine(ctx, m); err != nil {
return nil, err
}
s.brokers(m.TailnetID).SignalPeerUpdated(m.ID)
var routes []*api.RoutableIP
for _, r := range m.HostInfo.RoutableIPs {
routes = append(routes, &api.RoutableIP{
Advertised: r.String(),
Allowed: m.IsAllowedIP(r),
})
}
response := api.GetMachineRoutesResponse{
Routes: routes,
}
return &response, nil
}
func mapIp(ip []netaddr.IPPrefix) []string {
var x = []string{}
for _, i := range ip {
x = append(x, i.String())
}
return x
}