package service import ( "context" "crypto/tls" "encoding/json" "fmt" "net" "mbase/app/handler" "mbase/app/operator" "mbase/pkg/logger" "mbase/pkg/netacl" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/metadata" "google.golang.org/grpc/peer" ) type ServiceConfig struct { Handler *handler.Handler Logic *operator.Logic NetACL *netacl.NetACL Portnum uint32 Address string Protocol string Hostname string X509Cert []byte X509Key []byte } type Service struct { gsrv *grpc.Server hand *handler.Handler lg *operator.Logic log *logger.Logger nacl *netacl.NetACL portnum uint32 address string protocol string hostname string username string password string x509Cert []byte x509Key []byte } func NewService(conf *ServiceConfig) *Service { svc := Service{ hand: conf.Handler, lg: conf.Logic, nacl: conf.NetACL, portnum: conf.Portnum, address: conf.Address, protocol: conf.Protocol, hostname: conf.Hostname, x509Cert: conf.X509Cert, x509Key: conf.X509Key, } svc.log = logger.NewLogger("gservice") return &svc } func (svc *Service) Run() error { var err error svc.log.Infof("Service run") listenSpec := fmt.Sprintf("%s:%d", svc.address, svc.portnum) listener, err := net.Listen(svc.protocol, listenSpec) if err != nil { return err } tlsCert, err := tls.X509KeyPair(svc.x509Cert, svc.x509Key) if err != nil { return err } tlsConfig := tls.Config{ Certificates: []tls.Certificate{tlsCert}, ClientAuth: tls.NoClientCert, InsecureSkipVerify: true, } tlsCredentials := credentials.NewTLS(&tlsConfig) if err != nil { return err } interceptors := []grpc.UnaryServerInterceptor{ svc.accessInterceptor, svc.logInterceptor, } gsrvOpts := []grpc.ServerOption{ grpc.Creds(tlsCredentials), grpc.ChainUnaryInterceptor(interceptors...), } svc.gsrv = grpc.NewServer(gsrvOpts...) svc.hand.Register(svc.gsrv) svc.log.Infof("Service listening at %v", listener.Addr()) err = svc.gsrv.Serve(listener) if err != nil { return err } return err } func (svc *Service) accessInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { peerMeta, _ := peer.FromContext(ctx) host, _, err := net.SplitHostPort(peerMeta.Addr.String()) if err != nil { return nil, err } addressEnabled, _ := svc.nacl.AddressIsEnabled(host) if err != nil { return nil, err } if addressEnabled { svc.log.Warningf("Enable access from %s", host) return handler(ctx, req) } svc.log.Warningf("Disable access from %s", host) return nil, fmt.Errorf("Access disabled by network ACL") } func (svc *Service) logInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { meta, _ := metadata.FromIncomingContext(ctx) peerMeta, _ := peer.FromContext(ctx) svc.log.Infof("User %v called %v from %s", meta["username"], info.FullMethod, peerMeta.Addr.String()) return handler(ctx, req) } func (svc *Service) debugInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { var err error reqBinary, err := json.Marshal(req) requestString := "" if err == nil { requestString = string(reqBinary) } svc.log.Debugf("Called method: %v with params %v", info.FullMethod, requestString) return handler(ctx, req) } func (svc *Service) Stop() { svc.log.Infof("Stopping service") svc.gsrv.GracefulStop() }