Refactor RDP proxy handling and update related tests

This commit is contained in:
2026-05-17 20:38:35 +03:00
parent 8e9402580f
commit d551e57fd5
172 changed files with 22117 additions and 2509 deletions
+148 -162
View File
@@ -11,6 +11,7 @@ import (
"io"
"net/http"
"os"
"path/filepath"
"reflect"
"sort"
"strconv"
@@ -33,6 +34,13 @@ type Module struct {
vpnClientDiagnosticHub *vpnClientDiagnosticHub
}
const (
adminRuntimeProjectionRequestSchema = "rap.web_ingress.control_api_projection_request.v1"
adminRuntimeProjectionResponseSchema = "rap.web_ingress.control_api_projection_response.v1"
adminRuntimeProjectionBodySchema = "rap.control_api.admin_runtime_projection.v1"
adminRuntimeManifestSchema = "rap.web_ingress.ui_manifest.v1"
)
func NewModule(deps module.Dependencies, verifiers ...*authority.Verifier) *Module {
store := NewPostgresStore(deps.Infra.DB, verifiers...)
if deps.Config.Secret.EncryptionKeyBase64 != "" {
@@ -52,6 +60,7 @@ func (m *Module) Name() string {
}
func (m *Module) RegisterRoutes(router chi.Router) {
router.Get("/downloads/{fileName}", m.downloadReleaseFile)
router.Route("/clusters", func(r chi.Router) {
r.Get("/", m.listClusters)
r.Post("/", m.createCluster)
@@ -90,6 +99,7 @@ func (m *Module) RegisterRoutes(router chi.Router) {
r.Put("/{clusterID}/nodes/{nodeID}/workloads/{serviceType}/desired", m.setDesiredWorkload)
r.Post("/{clusterID}/nodes/{nodeID}/workloads/{serviceType}/status", m.reportWorkloadStatus)
r.Get("/{clusterID}/nodes/{nodeID}/workloads/status", m.listWorkloadStatuses)
r.Post("/{clusterID}/nodes/{nodeID}/admin-runtime/projection", m.projectAdminRuntime)
r.Get("/{clusterID}/mesh/links", m.listMeshLinks)
r.Post("/{clusterID}/mesh/links", m.reportMeshLink)
r.Get("/{clusterID}/mesh/route-intents", m.listRouteIntents)
@@ -97,14 +107,6 @@ func (m *Module) RegisterRoutes(router chi.Router) {
r.Post("/{clusterID}/mesh/route-intents/{routeIntentID}/expire", m.expireRouteIntent)
r.Post("/{clusterID}/mesh/route-intents/{routeIntentID}/disable", m.disableRouteIntent)
r.Get("/{clusterID}/mesh/qos-policies", m.listQoSPolicies)
r.Get("/{clusterID}/fabric/entry-points", m.listFabricEntryPoints)
r.Post("/{clusterID}/fabric/entry-points", m.createFabricEntryPoint)
r.Get("/{clusterID}/fabric/entry-points/{entryPointID}/nodes", m.listFabricEntryPointNodes)
r.Put("/{clusterID}/fabric/entry-points/{entryPointID}/nodes/{nodeID}", m.setFabricEntryPointNode)
r.Get("/{clusterID}/fabric/egress-pools", m.listFabricEgressPools)
r.Post("/{clusterID}/fabric/egress-pools", m.createFabricEgressPool)
r.Get("/{clusterID}/fabric/egress-pools/{egressPoolID}/nodes", m.listFabricEgressPoolNodes)
r.Put("/{clusterID}/fabric/egress-pools/{egressPoolID}/nodes/{nodeID}", m.setFabricEgressPoolNode)
r.Get("/{clusterID}/fabric/service-channels/route-feedback", m.listFabricServiceChannelRouteFeedback)
r.Post("/{clusterID}/fabric/service-channels/route-feedback/expire", m.expireFabricServiceChannelRouteFeedback)
r.Get("/{clusterID}/fabric/service-channels/rebuild-attempts", m.listFabricServiceChannelRouteRebuildAttempts)
@@ -172,6 +174,24 @@ func (m *Module) RegisterRoutes(router chi.Router) {
router.Put("/fabric/testing-flags", m.upsertFabricTestingFlag)
}
func (m *Module) downloadReleaseFile(w http.ResponseWriter, r *http.Request) {
fileName := filepath.Base(strings.TrimSpace(chi.URLParam(r, "fileName")))
if fileName == "" || fileName == "." || fileName != strings.TrimSpace(chi.URLParam(r, "fileName")) {
http.NotFound(w, r)
return
}
releaseDir := strings.TrimSpace(os.Getenv("RAP_RELEASE_DIR"))
if releaseDir == "" {
releaseDir = "/tmp/rap-release"
}
path := filepath.Join(releaseDir, fileName)
if _, err := os.Stat(path); err != nil {
http.NotFound(w, r)
return
}
http.ServeFile(w, r, path)
}
func (m *Module) listClusters(w http.ResponseWriter, r *http.Request) {
items, err := m.service.ListClusters(r.Context(), r.URL.Query().Get("actor_user_id"))
if writeServiceError(w, err) {
@@ -239,6 +259,126 @@ func (m *Module) updateCluster(w http.ResponseWriter, r *http.Request) {
httpx.WriteJSON(w, http.StatusOK, map[string]any{"cluster": item})
}
func (m *Module) projectAdminRuntime(w http.ResponseWriter, r *http.Request) {
clusterID := chi.URLParam(r, "clusterID")
nodeID := chi.URLParam(r, "nodeID")
var payload struct {
SchemaVersion string `json:"schema_version"`
Method string `json:"method"`
Path string `json:"path"`
Query string `json:"query"`
Host string `json:"host"`
Scope string `json:"scope"`
ServiceClass string `json:"service_class"`
ObservedAt string `json:"observed_at"`
}
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
httpx.WriteError(w, http.StatusBadRequest, "invalid admin runtime projection payload")
return
}
if strings.TrimSpace(payload.SchemaVersion) != adminRuntimeProjectionRequestSchema {
httpx.WriteError(w, http.StatusBadRequest, "invalid admin runtime projection schema")
return
}
method := strings.ToUpper(strings.TrimSpace(payload.Method))
path := strings.TrimSpace(payload.Path)
if method != http.MethodGet && method != http.MethodHead {
httpx.WriteJSON(w, http.StatusOK, adminRuntimeProjectionResponse(http.StatusForbidden, "blocked", "control_api_mutation_rejected", nil))
return
}
if path == "" {
path = "/"
}
if !strings.HasPrefix(path, "/") {
path = "/" + path
}
scope := strings.TrimSpace(payload.Scope)
serviceClass := normalizeFabricServiceClass(payload.ServiceClass)
if !isAllowedAdminRuntimeProjectionScope(scope, serviceClass) {
httpx.WriteJSON(w, http.StatusOK, adminRuntimeProjectionResponse(http.StatusForbidden, "blocked", "control_api_projection_scope_rejected", nil))
return
}
body := map[string]any{
"schema_version": adminRuntimeProjectionBodySchema,
"cluster_id": clusterID,
"node_id": nodeID,
"scope": scope,
"service_class": serviceClass,
"path": path,
"query": payload.Query,
"host": payload.Host,
"projection": "read_only",
"audit_required": true,
}
if path == "/ui-manifest" || strings.HasSuffix(path, "/ui-manifest") {
body["manifest"] = adminRuntimeManifest(scope, serviceClass)
httpx.WriteJSON(w, http.StatusOK, adminRuntimeProjectionResponse(http.StatusOK, "ready", "ui_manifest_ready", body))
return
}
if path == "/healthz" || path == "/readyz" {
httpx.WriteJSON(w, http.StatusOK, adminRuntimeProjectionResponse(http.StatusOK, "ready", "admin_runtime_projection_ready", body))
return
}
httpx.WriteJSON(w, http.StatusOK, adminRuntimeProjectionResponse(http.StatusNotImplemented, "blocked", "control_api_projection_not_implemented", body))
}
func adminRuntimeProjectionResponse(statusCode int, status string, reason string, body map[string]any) map[string]any {
raw, _ := json.Marshal(body)
return map[string]any{
"schema_version": adminRuntimeProjectionResponseSchema,
"status": status,
"reason": reason,
"status_code": statusCode,
"headers": map[string]string{
"Content-Type": "application/json",
},
"body": json.RawMessage(raw),
}
}
func isAllowedAdminRuntimeProjectionScope(scope string, serviceClass string) bool {
switch serviceClass {
case FabricServiceClassPlatformAdmin:
return scope == "platform"
case FabricServiceClassClusterAdmin:
return scope == "cluster"
case FabricServiceClassOrganization:
return scope == "organization"
case FabricServiceClassUserPortal:
return scope == "user" || scope == "organization"
default:
return false
}
}
func adminRuntimeManifest(scope string, serviceClass string) map[string]any {
sections := []string{"status"}
actions := []string{"read_status"}
switch strings.TrimSpace(serviceClass) {
case FabricServiceClassPlatformAdmin:
sections = []string{"clusters", "nodes", "roles", "fabric", "workloads", "audit"}
actions = []string{"read_platform_summary", "read_cluster_summaries", "read_node_status"}
case FabricServiceClassClusterAdmin:
sections = []string{"cluster", "nodes", "fabric", "workloads", "audit"}
actions = []string{"read_cluster_summary", "read_node_status"}
case FabricServiceClassOrganization:
sections = []string{"organization", "sessions", "resources", "audit"}
actions = []string{"read_organization_summary", "read_sessions"}
case FabricServiceClassUserPortal:
sections = []string{"profile", "sessions", "resources"}
actions = []string{"read_profile", "read_sessions"}
}
return map[string]any{
"schema_version": adminRuntimeManifestSchema,
"scope": scope,
"service_class": serviceClass,
"sections": sections,
"allowed_actions": actions,
"mutation_enabled": false,
"projection_binding": "control_api_read_only",
}
}
func (m *Module) listClusterNodes(w http.ResponseWriter, r *http.Request) {
items, err := m.service.ListClusterNodes(r.Context(), r.URL.Query().Get("actor_user_id"), chi.URLParam(r, "clusterID"))
if writeServiceError(w, err) {
@@ -1073,160 +1213,6 @@ func (m *Module) listQoSPolicies(w http.ResponseWriter, r *http.Request) {
httpx.WriteJSON(w, http.StatusOK, map[string]any{"qos_policies": items})
}
func (m *Module) listFabricEntryPoints(w http.ResponseWriter, r *http.Request) {
items, err := m.service.ListFabricEntryPoints(r.Context(), r.URL.Query().Get("actor_user_id"), chi.URLParam(r, "clusterID"))
if writeServiceError(w, err) {
return
}
httpx.WriteJSON(w, http.StatusOK, map[string]any{"entry_points": items})
}
func (m *Module) createFabricEntryPoint(w http.ResponseWriter, r *http.Request) {
var payload struct {
ActorUserID string `json:"actor_user_id"`
Name string `json:"name"`
Status string `json:"status"`
EndpointType string `json:"endpoint_type"`
PublicEndpoint *string `json:"public_endpoint"`
Policy json.RawMessage `json:"policy"`
Metadata json.RawMessage `json:"metadata"`
}
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
httpx.WriteError(w, http.StatusBadRequest, "invalid fabric entry point payload")
return
}
item, err := m.service.CreateFabricEntryPoint(r.Context(), CreateFabricEntryPointInput{
ActorUserID: payload.ActorUserID,
ClusterID: chi.URLParam(r, "clusterID"),
Name: payload.Name,
Status: payload.Status,
EndpointType: payload.EndpointType,
PublicEndpoint: payload.PublicEndpoint,
Policy: payload.Policy,
Metadata: payload.Metadata,
})
if writeServiceError(w, err) {
return
}
httpx.WriteJSON(w, http.StatusCreated, map[string]any{"entry_point": item})
}
func (m *Module) setFabricEntryPointNode(w http.ResponseWriter, r *http.Request) {
var payload struct {
ActorUserID string `json:"actor_user_id"`
Status string `json:"status"`
Priority int `json:"priority"`
Metadata json.RawMessage `json:"metadata"`
}
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
httpx.WriteError(w, http.StatusBadRequest, "invalid fabric entry point node payload")
return
}
item, err := m.service.SetFabricEntryPointNode(r.Context(), SetFabricEntryPointNodeInput{
ActorUserID: payload.ActorUserID,
ClusterID: chi.URLParam(r, "clusterID"),
EntryPointID: chi.URLParam(r, "entryPointID"),
NodeID: chi.URLParam(r, "nodeID"),
Status: payload.Status,
Priority: payload.Priority,
Metadata: payload.Metadata,
})
if writeServiceError(w, err) {
return
}
httpx.WriteJSON(w, http.StatusOK, map[string]any{"entry_point_node": item})
}
func (m *Module) listFabricEntryPointNodes(w http.ResponseWriter, r *http.Request) {
items, err := m.service.ListFabricEntryPointNodes(
r.Context(),
r.URL.Query().Get("actor_user_id"),
chi.URLParam(r, "clusterID"),
chi.URLParam(r, "entryPointID"),
)
if writeServiceError(w, err) {
return
}
httpx.WriteJSON(w, http.StatusOK, map[string]any{"entry_point_nodes": items})
}
func (m *Module) listFabricEgressPools(w http.ResponseWriter, r *http.Request) {
items, err := m.service.ListFabricEgressPools(r.Context(), r.URL.Query().Get("actor_user_id"), chi.URLParam(r, "clusterID"))
if writeServiceError(w, err) {
return
}
httpx.WriteJSON(w, http.StatusOK, map[string]any{"egress_pools": items})
}
func (m *Module) createFabricEgressPool(w http.ResponseWriter, r *http.Request) {
var payload struct {
ActorUserID string `json:"actor_user_id"`
Name string `json:"name"`
Status string `json:"status"`
Description *string `json:"description"`
RouteScope json.RawMessage `json:"route_scope"`
Policy json.RawMessage `json:"policy"`
Metadata json.RawMessage `json:"metadata"`
}
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
httpx.WriteError(w, http.StatusBadRequest, "invalid fabric egress pool payload")
return
}
item, err := m.service.CreateFabricEgressPool(r.Context(), CreateFabricEgressPoolInput{
ActorUserID: payload.ActorUserID,
ClusterID: chi.URLParam(r, "clusterID"),
Name: payload.Name,
Status: payload.Status,
Description: payload.Description,
RouteScope: payload.RouteScope,
Policy: payload.Policy,
Metadata: payload.Metadata,
})
if writeServiceError(w, err) {
return
}
httpx.WriteJSON(w, http.StatusCreated, map[string]any{"egress_pool": item})
}
func (m *Module) setFabricEgressPoolNode(w http.ResponseWriter, r *http.Request) {
var payload struct {
ActorUserID string `json:"actor_user_id"`
Status string `json:"status"`
Priority int `json:"priority"`
Metadata json.RawMessage `json:"metadata"`
}
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
httpx.WriteError(w, http.StatusBadRequest, "invalid fabric egress pool node payload")
return
}
item, err := m.service.SetFabricEgressPoolNode(r.Context(), SetFabricEgressPoolNodeInput{
ActorUserID: payload.ActorUserID,
ClusterID: chi.URLParam(r, "clusterID"),
EgressPoolID: chi.URLParam(r, "egressPoolID"),
NodeID: chi.URLParam(r, "nodeID"),
Status: payload.Status,
Priority: payload.Priority,
Metadata: payload.Metadata,
})
if writeServiceError(w, err) {
return
}
httpx.WriteJSON(w, http.StatusOK, map[string]any{"egress_pool_node": item})
}
func (m *Module) listFabricEgressPoolNodes(w http.ResponseWriter, r *http.Request) {
items, err := m.service.ListFabricEgressPoolNodes(
r.Context(),
r.URL.Query().Get("actor_user_id"),
chi.URLParam(r, "clusterID"),
chi.URLParam(r, "egressPoolID"),
)
if writeServiceError(w, err) {
return
}
httpx.WriteJSON(w, http.StatusOK, map[string]any{"egress_pool_nodes": items})
}
func (m *Module) issueFabricServiceChannelLease(w http.ResponseWriter, r *http.Request) {
var payload struct {
ActorUserID string `json:"actor_user_id"`