84 lines
3.1 KiB
Go
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
|
|
}
|