Files
go-sip/internal/rpc/dispatcher_server.go
T

84 lines
3.1 KiB
Go

package rpc
import (
"context"
agentpb "git.ipao.vip/rogee/go-sip/gen/agent"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/status"
)
// DispatcherServer is the single Dispatcher-side AgentControl gRPC service.
// Upload RPCs and Agent-reported facts share this listener; the service is not
// an upload-only endpoint.
type dispatcherEventReporter interface {
ReportExecutionEvent(context.Context, *agentpb.ReportExecutionEventRequest) (*agentpb.ReportExecutionEventResponse, error)
}
type DispatcherServer struct {
agentpb.UnimplementedAgentControlServiceServer
uploads *DispatcherUploadServer
events dispatcherEventReporter
}
func NewDispatcherServer(uploads *DispatcherUploadServer, eventHandlers ...dispatcherEventReporter) *DispatcherServer {
var events dispatcherEventReporter
if len(eventHandlers) > 0 {
events = eventHandlers[0]
}
return &DispatcherServer{uploads: uploads, events: events}
}
func (s *DispatcherServer) RequestUpload(ctx context.Context, req *agentpb.RequestUploadRequest) (*agentpb.RequestUploadResponse, error) {
if s.uploads == nil {
return nil, status.Error(codes.Unimplemented, "Dispatcher upload handler is not configured")
}
return s.uploads.RequestUpload(ctx, req)
}
func (s *DispatcherServer) CompleteUpload(ctx context.Context, req *agentpb.CompleteUploadRequest) (*agentpb.CompleteUploadResponse, error) {
if s.uploads == nil {
return nil, status.Error(codes.Unimplemented, "Dispatcher upload handler is not configured")
}
return s.uploads.CompleteUpload(ctx, req)
}
func (s *DispatcherServer) ReportExecutionEvent(ctx context.Context, req *agentpb.ReportExecutionEventRequest) (*agentpb.ReportExecutionEventResponse, error) {
if s.events == nil {
return nil, status.Error(codes.Unimplemented, "Dispatcher execution-event receiver is not configured")
}
return s.events.ReportExecutionEvent(ctx, req)
}
func validateDispatcherPeer(ctx context.Context, meta *agentpb.RequestMeta, requirePeer bool, fingerprints, allowedAgentIDs map[string]struct{}) error {
if meta == nil {
return status.Error(codes.InvalidArgument, "request metadata is required")
}
if len(allowedAgentIDs) > 0 {
if _, ok := allowedAgentIDs[meta.AgentId]; !ok {
return status.Error(codes.PermissionDenied, "Agent identity is not allowed for Dispatcher RPCs")
}
}
if !requirePeer {
return nil
}
p, ok := peer.FromContext(ctx)
if !ok || p.AuthInfo == nil {
return status.Error(codes.Unauthenticated, "verified mTLS peer is required")
}
tlsInfo, ok := p.AuthInfo.(credentials.TLSInfo)
if !ok || len(tlsInfo.State.VerifiedChains) == 0 || len(tlsInfo.State.VerifiedChains[0]) == 0 {
return status.Error(codes.Unauthenticated, "verified mTLS peer is required")
}
if len(fingerprints) == 0 {
return status.Error(codes.PermissionDenied, "Dispatcher gRPC mTLS peer allowlist is not configured")
}
fingerprint := CertificateFingerprint(tlsInfo.State.VerifiedChains[0][0])
if _, allowed := fingerprints[fingerprint]; !allowed {
return status.Error(codes.PermissionDenied, "mTLS certificate is not allowed for Dispatcher RPCs")
}
return nil
}