mirror of https://github.com/coder/coder.git
273 lines
6.8 KiB
Go
273 lines
6.8 KiB
Go
package tailnet
|
|
|
|
import (
|
|
"net/netip"
|
|
|
|
"github.com/google/uuid"
|
|
"golang.org/x/xerrors"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
"tailscale.com/tailcfg"
|
|
"tailscale.com/types/key"
|
|
|
|
"github.com/coder/coder/v2/tailnet/proto"
|
|
)
|
|
|
|
func UUIDToByteSlice(u uuid.UUID) []byte {
|
|
b := [16]byte(u)
|
|
o := make([]byte, 16)
|
|
copy(o, b[:]) // copy so that we can't mutate the original
|
|
return o
|
|
}
|
|
|
|
func NodeToProto(n *Node) (*proto.Node, error) {
|
|
k, err := n.Key.MarshalBinary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
disco, err := n.DiscoKey.MarshalText()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
derpForcedWebsocket := make(map[int32]string)
|
|
for i, s := range n.DERPForcedWebsocket {
|
|
derpForcedWebsocket[int32(i)] = s
|
|
}
|
|
addresses := make([]string, len(n.Addresses))
|
|
for i, prefix := range n.Addresses {
|
|
s, err := prefix.MarshalText()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
addresses[i] = string(s)
|
|
}
|
|
allowedIPs := make([]string, len(n.AllowedIPs))
|
|
for i, prefix := range n.AllowedIPs {
|
|
s, err := prefix.MarshalText()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
allowedIPs[i] = string(s)
|
|
}
|
|
return &proto.Node{
|
|
Id: int64(n.ID),
|
|
AsOf: timestamppb.New(n.AsOf),
|
|
Key: k,
|
|
Disco: string(disco),
|
|
PreferredDerp: int32(n.PreferredDERP),
|
|
DerpLatency: n.DERPLatency,
|
|
DerpForcedWebsocket: derpForcedWebsocket,
|
|
Addresses: addresses,
|
|
AllowedIps: allowedIPs,
|
|
Endpoints: n.Endpoints,
|
|
}, nil
|
|
}
|
|
|
|
func ProtoToNode(p *proto.Node) (*Node, error) {
|
|
k := key.NodePublic{}
|
|
err := k.UnmarshalBinary(p.GetKey())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
disco := key.DiscoPublic{}
|
|
err = disco.UnmarshalText([]byte(p.GetDisco()))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
derpForcedWebsocket := make(map[int]string)
|
|
for i, s := range p.GetDerpForcedWebsocket() {
|
|
derpForcedWebsocket[int(i)] = s
|
|
}
|
|
addresses := make([]netip.Prefix, len(p.GetAddresses()))
|
|
for i, prefix := range p.GetAddresses() {
|
|
err = addresses[i].UnmarshalText([]byte(prefix))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
allowedIPs := make([]netip.Prefix, len(p.GetAllowedIps()))
|
|
for i, prefix := range p.GetAllowedIps() {
|
|
err = allowedIPs[i].UnmarshalText([]byte(prefix))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return &Node{
|
|
ID: tailcfg.NodeID(p.GetId()),
|
|
AsOf: p.GetAsOf().AsTime(),
|
|
Key: k,
|
|
DiscoKey: disco,
|
|
PreferredDERP: int(p.GetPreferredDerp()),
|
|
DERPLatency: p.GetDerpLatency(),
|
|
DERPForcedWebsocket: derpForcedWebsocket,
|
|
Addresses: addresses,
|
|
AllowedIPs: allowedIPs,
|
|
Endpoints: p.Endpoints,
|
|
}, nil
|
|
}
|
|
|
|
func OnlyNodeUpdates(resp *proto.CoordinateResponse) ([]*Node, error) {
|
|
nodes := make([]*Node, 0, len(resp.GetPeerUpdates()))
|
|
for _, pu := range resp.GetPeerUpdates() {
|
|
if pu.Kind != proto.CoordinateResponse_PeerUpdate_NODE {
|
|
continue
|
|
}
|
|
n, err := ProtoToNode(pu.Node)
|
|
if err != nil {
|
|
return nil, xerrors.Errorf("failed conversion from protobuf: %w", err)
|
|
}
|
|
nodes = append(nodes, n)
|
|
}
|
|
return nodes, nil
|
|
}
|
|
|
|
func SingleNodeUpdate(id uuid.UUID, node *Node, reason string) (*proto.CoordinateResponse, error) {
|
|
p, err := NodeToProto(node)
|
|
if err != nil {
|
|
return nil, xerrors.Errorf("node failed conversion to protobuf: %w", err)
|
|
}
|
|
return &proto.CoordinateResponse{
|
|
PeerUpdates: []*proto.CoordinateResponse_PeerUpdate{
|
|
{
|
|
Kind: proto.CoordinateResponse_PeerUpdate_NODE,
|
|
Id: UUIDToByteSlice(id),
|
|
Node: p,
|
|
Reason: reason,
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func DERPMapToProto(derpMap *tailcfg.DERPMap) *proto.DERPMap {
|
|
if derpMap == nil {
|
|
return nil
|
|
}
|
|
|
|
regionScore := make(map[int64]float64)
|
|
if derpMap.HomeParams != nil {
|
|
for k, v := range derpMap.HomeParams.RegionScore {
|
|
regionScore[int64(k)] = v
|
|
}
|
|
}
|
|
|
|
regions := make(map[int64]*proto.DERPMap_Region, len(derpMap.Regions))
|
|
for regionID, region := range derpMap.Regions {
|
|
regions[int64(regionID)] = DERPRegionToProto(region)
|
|
}
|
|
|
|
return &proto.DERPMap{
|
|
HomeParams: &proto.DERPMap_HomeParams{
|
|
RegionScore: regionScore,
|
|
},
|
|
Regions: regions,
|
|
}
|
|
}
|
|
|
|
func DERPRegionToProto(region *tailcfg.DERPRegion) *proto.DERPMap_Region {
|
|
if region == nil {
|
|
return nil
|
|
}
|
|
|
|
regionNodes := make([]*proto.DERPMap_Region_Node, len(region.Nodes))
|
|
for i, node := range region.Nodes {
|
|
regionNodes[i] = DERPNodeToProto(node)
|
|
}
|
|
|
|
return &proto.DERPMap_Region{
|
|
RegionId: int64(region.RegionID),
|
|
EmbeddedRelay: region.EmbeddedRelay,
|
|
RegionCode: region.RegionCode,
|
|
RegionName: region.RegionName,
|
|
Avoid: region.Avoid,
|
|
|
|
Nodes: regionNodes,
|
|
}
|
|
}
|
|
|
|
func DERPNodeToProto(node *tailcfg.DERPNode) *proto.DERPMap_Region_Node {
|
|
if node == nil {
|
|
return nil
|
|
}
|
|
|
|
return &proto.DERPMap_Region_Node{
|
|
Name: node.Name,
|
|
RegionId: int64(node.RegionID),
|
|
HostName: node.HostName,
|
|
CertName: node.CertName,
|
|
Ipv4: node.IPv4,
|
|
Ipv6: node.IPv6,
|
|
StunPort: int32(node.STUNPort),
|
|
StunOnly: node.STUNOnly,
|
|
DerpPort: int32(node.DERPPort),
|
|
InsecureForTests: node.InsecureForTests,
|
|
ForceHttp: node.ForceHTTP,
|
|
StunTestIp: node.STUNTestIP,
|
|
CanPort_80: node.CanPort80,
|
|
}
|
|
}
|
|
|
|
func DERPMapFromProto(derpMap *proto.DERPMap) *tailcfg.DERPMap {
|
|
if derpMap == nil {
|
|
return nil
|
|
}
|
|
|
|
regionScore := make(map[int]float64, len(derpMap.HomeParams.RegionScore))
|
|
for k, v := range derpMap.HomeParams.RegionScore {
|
|
regionScore[int(k)] = v
|
|
}
|
|
|
|
regions := make(map[int]*tailcfg.DERPRegion, len(derpMap.Regions))
|
|
for regionID, region := range derpMap.Regions {
|
|
regions[int(regionID)] = DERPRegionFromProto(region)
|
|
}
|
|
|
|
return &tailcfg.DERPMap{
|
|
HomeParams: &tailcfg.DERPHomeParams{
|
|
RegionScore: regionScore,
|
|
},
|
|
Regions: regions,
|
|
}
|
|
}
|
|
|
|
func DERPRegionFromProto(region *proto.DERPMap_Region) *tailcfg.DERPRegion {
|
|
if region == nil {
|
|
return nil
|
|
}
|
|
|
|
regionNodes := make([]*tailcfg.DERPNode, len(region.Nodes))
|
|
for i, node := range region.Nodes {
|
|
regionNodes[i] = DERPNodeFromProto(node)
|
|
}
|
|
|
|
return &tailcfg.DERPRegion{
|
|
RegionID: int(region.RegionId),
|
|
EmbeddedRelay: region.EmbeddedRelay,
|
|
RegionCode: region.RegionCode,
|
|
RegionName: region.RegionName,
|
|
Avoid: region.Avoid,
|
|
|
|
Nodes: regionNodes,
|
|
}
|
|
}
|
|
|
|
func DERPNodeFromProto(node *proto.DERPMap_Region_Node) *tailcfg.DERPNode {
|
|
if node == nil {
|
|
return nil
|
|
}
|
|
|
|
return &tailcfg.DERPNode{
|
|
Name: node.Name,
|
|
RegionID: int(node.RegionId),
|
|
HostName: node.HostName,
|
|
CertName: node.CertName,
|
|
IPv4: node.Ipv4,
|
|
IPv6: node.Ipv6,
|
|
STUNPort: int(node.StunPort),
|
|
STUNOnly: node.StunOnly,
|
|
DERPPort: int(node.DerpPort),
|
|
InsecureForTests: node.InsecureForTests,
|
|
ForceHTTP: node.ForceHttp,
|
|
STUNTestIP: node.StunTestIp,
|
|
CanPort80: node.CanPort_80,
|
|
}
|
|
}
|