mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-11-13 00:14:22 +08:00
fadc2b6c12
* fix(relay): authenticate endpoints and fail closed on missing routes * fix(tunnel): retain chains until receiver replacement is acknowledged * docs: record final relay security review and validation
126 lines
3.5 KiB
Go
126 lines
3.5 KiB
Go
package handler
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"go-backend/internal/http/client"
|
|
"go-backend/internal/relaysecurity"
|
|
"go-backend/internal/store/repo"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func (h *Handler) resolveTunnelRelayAuth(tx *gorm.DB, state *tunnelCreateState, create bool) error {
|
|
if state.Type != 2 || state.RelayAuthMode == relaysecurity.Legacy {
|
|
return nil
|
|
}
|
|
if state.RelayAuthMode != relaysecurity.Required {
|
|
return fmt.Errorf("unsupported relay authentication mode")
|
|
}
|
|
for i := range state.OutNodes {
|
|
c, err := h.repo.RelayEndpointCredentials(tx, state.TunnelID, state.OutNodes[i].NodeID, 3, create)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
state.OutNodes[i].RelayAuth = c
|
|
}
|
|
for hop := range state.ChainHops {
|
|
for i := range state.ChainHops[hop] {
|
|
c, err := h.repo.RelayEndpointCredentials(tx, state.TunnelID, state.ChainHops[hop][i].NodeID, 2, create)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
state.ChainHops[hop][i].RelayAuth = c
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *Handler) resolveBestExitRelayAuth(tunnelID int64, targets []tunnelRuntimeNode) error {
|
|
tunnel, err := h.repo.GetTunnelRecord(tunnelID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tunnel == nil {
|
|
return fmt.Errorf("tunnel not found")
|
|
}
|
|
return h.resolveTunnelRelayAuth(nil, &tunnelCreateState{TunnelID: tunnelID, Type: tunnel.Type, RelayAuthMode: tunnel.RelayAuthMode, OutNodes: targets}, false)
|
|
}
|
|
|
|
func relayAuthVersion(c *relaysecurity.Credentials) int {
|
|
if c == nil {
|
|
return 0
|
|
}
|
|
return c.Version
|
|
}
|
|
func decodePersistedRelayAuth(raw string) (*relaysecurity.Credentials, error) {
|
|
if raw == "" {
|
|
return nil, nil
|
|
}
|
|
var c relaysecurity.Credentials
|
|
if err := json.Unmarshal([]byte(raw), &c); err != nil {
|
|
return nil, fmt.Errorf("invalid persisted relay authentication")
|
|
}
|
|
if err := c.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return &c, nil
|
|
}
|
|
func validateFederationRelayAuth(persisted string, c *relaysecurity.Credentials, targets []federationRuntimeTarget) error {
|
|
old, err := decodePersistedRelayAuth(persisted)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if old != nil && (c == nil || *old != *c) {
|
|
return fmt.Errorf("authenticated relay cannot be downgraded or rotated by reapply")
|
|
}
|
|
if c != nil {
|
|
if err := c.Validate(); err != nil {
|
|
return err
|
|
}
|
|
for _, target := range targets {
|
|
if err := target.RelayAuth.Validate(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
} else {
|
|
for _, target := range targets {
|
|
if target.RelayAuth != nil {
|
|
if err := target.RelayAuth.Validate(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *Handler) preflightFederationRelayAuth(state *tunnelCreateState, domain string) error {
|
|
if state.RelayAuthMode != relaysecurity.Required || state.Type != 2 {
|
|
return nil
|
|
}
|
|
for _, node := range state.Nodes {
|
|
if node == nil || node.IsRemote != 1 {
|
|
continue
|
|
}
|
|
info, err := client.NewFederationClient().Connect(node.RemoteURL, node.RemoteToken, domain)
|
|
if err != nil {
|
|
return fmt.Errorf("relay authentication capability check failed for remote node %d", node.ID)
|
|
}
|
|
if info.RelayAuthVersion != relaysecurity.Version {
|
|
return fmt.Errorf("remote node %d does not support required relay authentication", node.ID)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func decodeRuntimeRelayAuth(runtime *repo.PeerShareRuntime) (*relaysecurity.Credentials, error) {
|
|
c, err := decodePersistedRelayAuth(runtime.RelayAuth)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if runtime.RelayAuthVersion != relayAuthVersion(c) {
|
|
return nil, fmt.Errorf("persisted relay authentication version or credentials unavailable")
|
|
}
|
|
return c, nil
|
|
}
|