Use Peer.ID instead of Peer.Key as peer identifier (#664)

Replace Peer.Key as internal identifier with a randomly generated Peer.ID 
in the Management service.
Every group now references peers by ID instead of a public key.
Every route now references peers by ID instead of a public key.
FileStore does store.json file migration on startup by generating Peer.ID and replacing
all Peer.Key identifier references .
This commit is contained in:
Misha Bragin
2023-02-03 10:33:28 +01:00
committed by GitHub
parent 9e408b5bbc
commit 9adadfade4
22 changed files with 485 additions and 359 deletions

View File

@@ -96,10 +96,16 @@ func (h *Groups) UpdateGroupHandler(w http.ResponseWriter, r *http.Request) {
return
}
var peers []string
if req.Peers == nil {
peers = make([]string, 0)
} else {
peers = *req.Peers
}
group := server.Group{
ID: groupID,
Name: *req.Name,
Peers: peerIPsToKeys(account, req.Peers),
Peers: peers,
}
if err := h.accountManager.SaveGroup(account.Id, user.Id, &group); err != nil {
@@ -191,10 +197,9 @@ func (h *Groups) PatchGroupHandler(w http.ResponseWriter, r *http.Request) {
Values: peerKeys,
})
case api.GroupPatchOperationOpAdd:
peerKeys := peerIPsToKeys(account, &patch.Value)
operations = append(operations, server.GroupUpdateOperation{
Type: server.InsertPeersToGroup,
Values: peerKeys,
Values: patch.Value,
})
default:
util.WriteError(status.Errorf(status.InvalidArgument,
@@ -237,10 +242,16 @@ func (h *Groups) CreateGroupHandler(w http.ResponseWriter, r *http.Request) {
return
}
var peers []string
if req.Peers == nil {
peers = make([]string, 0)
} else {
peers = *req.Peers
}
group := server.Group{
ID: xid.New().String(),
Name: req.Name,
Peers: peerIPsToKeys(account, req.Peers),
Peers: peers,
}
err = h.accountManager.SaveGroup(account.Id, user.Id, &group)
@@ -359,7 +370,7 @@ func toGroupResponse(account *server.Account, group *server.Group) *api.Group {
continue
}
peerResp := api.PeerMinimum{
Id: peer.IP.String(),
Id: peer.ID,
Name: peer.Name,
}
cache[pid] = peerResp

View File

@@ -22,8 +22,8 @@ import (
)
var TestPeers = map[string]*server.Peer{
"A": {Key: "A", IP: net.ParseIP("100.100.100.100")},
"B": {Key: "B", IP: net.ParseIP("200.200.200.200")},
"A": {Key: "A", ID: "peer-A-ID", IP: net.ParseIP("100.100.100.100")},
"B": {Key: "B", ID: "peer-B-ID", IP: net.ParseIP("200.200.200.200")},
}
func initGroupTestData(user *server.User, groups ...*server.Group) *Groups {
@@ -269,8 +269,8 @@ func TestWriteGroup(t *testing.T) {
Id: "id-existed",
PeersCount: 2,
Peers: []api.PeerMinimum{
{Id: "100.100.100.100"},
{Id: "200.200.200.200"}},
{Id: "peer-A-ID"},
{Id: "peer-B-ID"}},
},
},
}

View File

@@ -27,7 +27,7 @@ func NewPeers(accountManager server.AccountManager, authAudience string) *Peers
}
}
func (h *Peers) updatePeer(account *server.Account, user *server.User, peer *server.Peer, w http.ResponseWriter, r *http.Request) {
func (h *Peers) updatePeer(account *server.Account, user *server.User, peerID string, w http.ResponseWriter, r *http.Request) {
req := &api.PutApiPeersIdJSONBody{}
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
@@ -35,8 +35,8 @@ func (h *Peers) updatePeer(account *server.Account, user *server.User, peer *ser
return
}
update := &server.Peer{Key: peer.Key, SSHEnabled: req.SshEnabled, Name: req.Name}
peer, err = h.accountManager.UpdatePeer(account.Id, user.Id, update)
update := &server.Peer{ID: peerID, SSHEnabled: req.SshEnabled, Name: req.Name}
peer, err := h.accountManager.UpdatePeer(account.Id, user.Id, update)
if err != nil {
util.WriteError(err, w)
return
@@ -45,8 +45,8 @@ func (h *Peers) updatePeer(account *server.Account, user *server.User, peer *ser
util.WriteJSONObject(w, toPeerResponse(peer, account, dnsDomain))
}
func (h *Peers) deletePeer(accountID, userID string, peer *server.Peer, w http.ResponseWriter, r *http.Request) {
_, err := h.accountManager.DeletePeer(accountID, peer.Key, userID)
func (h *Peers) deletePeer(accountID, userID string, peerID string, w http.ResponseWriter) {
_, err := h.accountManager.DeletePeer(accountID, peerID, userID)
if err != nil {
util.WriteError(err, w)
return
@@ -62,31 +62,19 @@ func (h *Peers) HandlePeer(w http.ResponseWriter, r *http.Request) {
return
}
vars := mux.Vars(r)
peerId := vars["id"] //effectively peer IP address
if len(peerId) == 0 {
peerID := vars["id"]
if len(peerID) == 0 {
util.WriteError(status.Errorf(status.InvalidArgument, "invalid peer ID"), w)
return
}
peer, err := h.accountManager.GetPeerByIP(account.Id, peerId)
if err != nil {
util.WriteError(err, w)
return
}
dnsDomain := h.accountManager.GetDNSDomain()
switch r.Method {
case http.MethodDelete:
h.deletePeer(account.Id, user.Id, peer, w, r)
h.deletePeer(account.Id, user.Id, peerID, w)
return
case http.MethodPut:
h.updatePeer(account, user, peer, w, r)
h.updatePeer(account, user, peerID, w, r)
return
case http.MethodGet:
util.WriteJSONObject(w, toPeerResponse(peer, account, dnsDomain))
return
default:
util.WriteError(status.Errorf(status.NotFound, "unknown METHOD"), w)
}
@@ -132,7 +120,7 @@ func toPeerResponse(peer *server.Peer, account *server.Account, dnsDomain string
}
groupsChecked[group.ID] = struct{}{}
for _, pk := range group.Peers {
if pk == peer.Key {
if pk == peer.ID {
info := api.GroupMinimum{
Id: group.ID,
Name: group.Name,
@@ -149,7 +137,7 @@ func toPeerResponse(peer *server.Peer, account *server.Account, dnsDomain string
fqdn = peer.DNSLabel
}
return &api.Peer{
Id: peer.IP.String(),
Id: peer.ID,
Name: peer.Name,
Ip: peer.IP.String(),
Connected: peer.Status.Connected,

View File

@@ -45,7 +45,7 @@ func (h *Routes) GetAllRoutesHandler(w http.ResponseWriter, r *http.Request) {
}
apiRoutes := make([]*api.Route, 0)
for _, r := range routes {
apiRoutes = append(apiRoutes, toRouteResponse(account, r))
apiRoutes = append(apiRoutes, toRouteResponse(r))
}
util.WriteJSONObject(w, apiRoutes)
@@ -85,7 +85,7 @@ func (h *Routes) CreateRouteHandler(w http.ResponseWriter, r *http.Request) {
return
}
resp := toRouteResponse(account, newRoute)
resp := toRouteResponse(newRoute)
util.WriteJSONObject(w, &resp)
}
@@ -126,16 +126,6 @@ func (h *Routes) UpdateRouteHandler(w http.ResponseWriter, r *http.Request) {
return
}
peerKey := req.Peer
if req.Peer != "" {
peer := account.GetPeerByIP(req.Peer)
if peer == nil {
util.WriteError(status.Errorf(status.NotFound, "peer %s not found", req.Peer), w)
return
}
peerKey = peer.Key
}
if utf8.RuneCountInString(req.NetworkId) > route.MaxNetIDChar || req.NetworkId == "" {
util.WriteError(status.Errorf(status.InvalidArgument,
"identifier should be between 1 and %d", route.MaxNetIDChar), w)
@@ -148,7 +138,7 @@ func (h *Routes) UpdateRouteHandler(w http.ResponseWriter, r *http.Request) {
NetID: req.NetworkId,
NetworkType: prefixType,
Masquerade: req.Masquerade,
Peer: peerKey,
Peer: req.Peer,
Metric: req.Metric,
Description: req.Description,
Enabled: req.Enabled,
@@ -161,7 +151,7 @@ func (h *Routes) UpdateRouteHandler(w http.ResponseWriter, r *http.Request) {
return
}
resp := toRouteResponse(account, newRoute)
resp := toRouteResponse(newRoute)
util.WriteJSONObject(w, &resp)
}
@@ -245,18 +235,9 @@ func (h *Routes) PatchRouteHandler(w http.ResponseWriter, r *http.Request) {
"value field only accepts 1 value, got %d", len(patch.Value)), w)
return
}
peerValue := patch.Value
if patch.Value[0] != "" {
peer, err := h.accountManager.GetPeerByIP(account.Id, patch.Value[0])
if err != nil {
util.WriteError(err, w)
return
}
peerValue = []string{peer.Key}
}
operations = append(operations, server.RouteUpdateOperation{
Type: server.UpdateRoutePeer,
Values: peerValue,
Values: patch.Value,
})
case api.RoutePatchOperationPathMetric:
if patch.Op != api.RoutePatchOperationOpReplace {
@@ -305,13 +286,13 @@ func (h *Routes) PatchRouteHandler(w http.ResponseWriter, r *http.Request) {
}
}
route, err := h.accountManager.UpdateRoute(account.Id, routeID, operations)
root, err := h.accountManager.UpdateRoute(account.Id, routeID, operations)
if err != nil {
util.WriteError(err, w)
return
}
resp := toRouteResponse(account, route)
resp := toRouteResponse(root)
util.WriteJSONObject(w, &resp)
}
@@ -361,25 +342,16 @@ func (h *Routes) GetRouteHandler(w http.ResponseWriter, r *http.Request) {
return
}
util.WriteJSONObject(w, toRouteResponse(account, foundRoute))
util.WriteJSONObject(w, toRouteResponse(foundRoute))
}
func toRouteResponse(account *server.Account, serverRoute *route.Route) *api.Route {
var peerIP string
if serverRoute.Peer != "" {
peer, found := account.Peers[serverRoute.Peer]
if !found {
panic("peer ID not found")
}
peerIP = peer.IP.String()
}
func toRouteResponse(serverRoute *route.Route) *api.Route {
return &api.Route{
Id: serverRoute.ID,
Description: serverRoute.Description,
NetworkId: serverRoute.NetID,
Enabled: serverRoute.Enabled,
Peer: peerIP,
Peer: serverRoute.Peer,
Network: serverRoute.Network.String(),
NetworkType: serverRoute.NetworkType.String(),
Masquerade: serverRoute.Masquerade,

View File

@@ -24,8 +24,9 @@ import (
const (
existingRouteID = "existingRouteID"
notFoundRouteID = "notFoundRouteID"
existingPeerID = "100.64.0.100"
notFoundPeerID = "100.64.0.200"
existingPeerIP = "100.64.0.100"
existingPeerID = "peer-id"
notFoundPeerID = "nonExistingPeer"
existingPeerKey = "existingPeerKey"
testAccountID = "test_id"
existingGroupID = "testGroup"
@@ -47,9 +48,10 @@ var testingAccount = &server.Account{
Id: testAccountID,
Domain: "hotmail.com",
Peers: map[string]*server.Peer{
existingPeerKey: {
existingPeerID: {
Key: existingPeerKey,
IP: netip.MustParseAddr(existingPeerID).AsSlice(),
IP: netip.MustParseAddr(existingPeerIP).AsSlice(),
ID: existingPeerID,
},
},
Users: map[string]*server.User{
@@ -66,18 +68,15 @@ func initRoutesTestData() *Routes {
}
return nil, status.Errorf(status.NotFound, "route with ID %s not found", routeID)
},
CreateRouteFunc: func(accountID string, network, peerIP, description, netID string, masquerade bool, metric int, groups []string, enabled bool, _ string) (*route.Route, error) {
peer := testingAccount.GetPeerByIP(peerIP)
if peer == nil {
return nil, status.Errorf(status.NotFound, "peer %s not found", peerIP)
CreateRouteFunc: func(accountID string, network, peerID, description, netID string, masquerade bool, metric int, groups []string, enabled bool, _ string) (*route.Route, error) {
if peerID == notFoundPeerID {
return nil, status.Errorf(status.InvalidArgument, "peer with ID %s not found", peerID)
}
networkType, p, _ := route.ParseNetwork(network)
return &route.Route{
ID: existingRouteID,
NetID: netID,
Peer: peer.Key,
Peer: peerID,
Network: p,
NetworkType: networkType,
Description: description,
@@ -86,7 +85,10 @@ func initRoutesTestData() *Routes {
Groups: groups,
}, nil
},
SaveRouteFunc: func(_, _ string, _ *route.Route) error {
SaveRouteFunc: func(_, _ string, r *route.Route) error {
if r.Peer == notFoundPeerID {
return status.Errorf(status.InvalidArgument, "peer with ID %s not found", r.Peer)
}
return nil
},
DeleteRouteFunc: func(_ string, routeID string, _ string) error {
@@ -119,6 +121,9 @@ func initRoutesTestData() *Routes {
routeToUpdate.NetID = operation.Values[0]
case server.UpdateRoutePeer:
routeToUpdate.Peer = operation.Values[0]
if routeToUpdate.Peer == notFoundPeerID {
return nil, status.Errorf(status.InvalidArgument, "peer with ID %s not found", routeToUpdate.Peer)
}
case server.UpdateRouteMetric:
routeToUpdate.Metric, _ = strconv.Atoi(operation.Values[0])
case server.UpdateRouteMasquerade:
@@ -166,7 +171,7 @@ func TestRoutesHandlers(t *testing.T) {
requestPath: "/api/routes/" + existingRouteID,
expectedStatus: http.StatusOK,
expectedBody: true,
expectedRoute: toRouteResponse(testingAccount, baseExistingRoute),
expectedRoute: toRouteResponse(baseExistingRoute),
},
{
name: "Get Not Existing Route",
@@ -212,7 +217,7 @@ func TestRoutesHandlers(t *testing.T) {
requestType: http.MethodPost,
requestPath: "/api/routes",
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\",\"groups\":[\"%s\"]}", notFoundPeerID, existingGroupID)),
expectedStatus: http.StatusNotFound,
expectedStatus: http.StatusUnprocessableEntity,
expectedBody: false,
},
{
@@ -263,7 +268,7 @@ func TestRoutesHandlers(t *testing.T) {
requestType: http.MethodPut,
requestPath: "/api/routes/" + existingRouteID,
requestBody: bytes.NewBufferString(fmt.Sprintf("{\"Description\":\"Post\",\"Network\":\"192.168.0.0/16\",\"network_id\":\"awesomeNet\",\"Peer\":\"%s\",\"groups\":[\"%s\"]}", notFoundPeerID, existingGroupID)),
expectedStatus: http.StatusNotFound,
expectedStatus: http.StatusUnprocessableEntity,
expectedBody: false,
},
{
@@ -326,7 +331,7 @@ func TestRoutesHandlers(t *testing.T) {
requestType: http.MethodPatch,
requestPath: "/api/routes/" + existingRouteID,
requestBody: bytes.NewBufferString(fmt.Sprintf("[{\"op\":\"replace\",\"path\":\"peer\",\"value\":[\"%s\"]}]", notFoundPeerID)),
expectedStatus: http.StatusNotFound,
expectedStatus: http.StatusUnprocessableEntity,
expectedBody: false,
},
{