fix(backend): enforce port range in federation runtime commands

The federationRuntimeCommand handler forwarded AddService/UpdateService
commands from consumers to provider nodes without validating that the
port in the service payload falls within the share's allowed port range.
This allowed consumers to use any port on shared nodes, bypassing the
provider's port_range_start/port_range_end restrictions.

Add port extraction and validation in federationRuntimeCommand for
service commands, rejecting requests with ports outside the allowed
range with a 403 error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
sagitchu
2026-02-15 11:22:50 +08:00
parent 04ce125416
commit 77dbd719ed
2 changed files with 166 additions and 0 deletions
@@ -7,6 +7,7 @@ import (
"net"
"net/http"
"sort"
"strconv"
"strings"
"sync"
"time"
@@ -1232,6 +1233,13 @@ func (h *Handler) federationRuntimeCommand(w http.ResponseWriter, r *http.Reques
return
}
if isFederationServiceCommand(cmd) {
if err := validateFederationCommandPorts(share, req.Data); err != nil {
response.WriteJSON(w, response.Err(403, err.Error()))
return
}
}
res, err := h.sendNodeCommand(share.NodeID, cmd, req.Data, false, false)
if err != nil {
response.WriteJSON(w, response.ErrDefault(err.Error()))
@@ -1249,6 +1257,55 @@ func isFederationRuntimeCommandAllowed(commandType string) bool {
}
}
func isFederationServiceCommand(commandType string) bool {
switch strings.ToLower(strings.TrimSpace(commandType)) {
case "addservice", "updateservice":
return true
default:
return false
}
}
func validateFederationCommandPorts(share *sqlite.PeerShare, data interface{}) error {
if share == nil || (share.PortRangeStart <= 0 && share.PortRangeEnd <= 0) {
return nil
}
dataMap, ok := data.(map[string]interface{})
if !ok {
return nil
}
services, ok := dataMap["services"]
if !ok {
return nil
}
serviceList, ok := services.([]interface{})
if !ok {
return nil
}
for _, svc := range serviceList {
svcMap, ok := svc.(map[string]interface{})
if !ok {
continue
}
addr, ok := svcMap["addr"].(string)
if !ok || addr == "" {
continue
}
_, portStr, err := net.SplitHostPort(addr)
if err != nil {
continue
}
port, err := strconv.Atoi(portStr)
if err != nil || port <= 0 {
continue
}
if port < share.PortRangeStart || port > share.PortRangeEnd {
return fmt.Errorf("port %d out of allowed range %d-%d", port, share.PortRangeStart, share.PortRangeEnd)
}
}
return nil
}
func (h *Handler) pickPeerSharePort(share *sqlite.PeerShare, requestedPort int) (int, error) {
if share == nil {
return 0, fmt.Errorf("share not found")