github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/internal/enforcer/applicationproxy/serviceregistry/serviceregistry.go (about)

     1  package serviceregistry
     2  
     3  import (
     4  	"crypto/x509"
     5  	"fmt"
     6  	"net"
     7  	"sync"
     8  
     9  	"go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/applicationproxy/common"
    10  	"go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/applicationproxy/servicecache"
    11  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/auth"
    12  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/pucontext"
    13  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/secrets"
    14  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/urisearch"
    15  	"go.aporeto.io/enforcerd/trireme-lib/policy"
    16  	"go.aporeto.io/tg/tglib"
    17  )
    18  
    19  // ServiceContext includes all the all the service related information
    20  // for dependent services. It is indexed by the PU ID and a PU can
    21  // easily retrieve all the state with a simple lookup. Note, that
    22  // there is one ServiceContext for every PU.
    23  type ServiceContext struct {
    24  	PU        *policy.PUInfo
    25  	PUContext *pucontext.PUContext
    26  	RootCA    [][]byte
    27  
    28  	// Dependent Services are services that are consumed by the PU.
    29  	// The dependent service cache is only accessible internally,
    30  	// so that all types are properly converted.
    31  	dependentServiceCache *servicecache.ServiceCache
    32  }
    33  
    34  // DependentServiceData are the data that are held for each service
    35  // in the dependentServiceCache.
    36  type DependentServiceData struct {
    37  	// Used for authorization
    38  	APICache *urisearch.APICache
    39  	// Used by the protomux to find the right service type.
    40  	ServiceType common.ListenerType
    41  	// ServiceObject is the original service object.
    42  	ServiceObject *policy.ApplicationService
    43  }
    44  
    45  // PortContext includes all the needed associations to refer to a service by port.
    46  // For incoming connections the only available information is the IP/port
    47  // pair of the original request and we use this to map the connection and
    48  // request to a port. For network services we have additional state data
    49  // such as the authorizers. Note that there is one PortContext for every
    50  // service of every PU.
    51  type PortContext struct {
    52  	ID                 string
    53  	Type               common.ListenerType
    54  	Service            *policy.ApplicationService
    55  	Authorizer         *auth.Processor
    56  	PUContext          *pucontext.PUContext
    57  	TargetPort         int
    58  	ClientTrustedRoots *x509.CertPool
    59  }
    60  
    61  // Registry is a service registry. It maintains all the state information
    62  // and provides a simple API to retrieve the data. The registry always
    63  // locks and allows multi-threading.
    64  type Registry struct {
    65  	indexByName map[string]*ServiceContext
    66  	indexByPort *servicecache.ServiceCache
    67  	sync.Mutex
    68  }
    69  
    70  var instance = Registry{
    71  	indexByName: map[string]*ServiceContext{},
    72  	indexByPort: servicecache.NewTable(),
    73  }
    74  
    75  //Instance returns the service registry instance
    76  func Instance() *Registry {
    77  	return &instance
    78  }
    79  
    80  // Register registers a new service with the registry. If the service
    81  // already exists it updates the service with the new information, otherwise
    82  // it creates a new service.
    83  func (r *Registry) Register(
    84  	puID string,
    85  	pu *policy.PUInfo,
    86  	puContext *pucontext.PUContext,
    87  	secrets secrets.Secrets,
    88  ) (*ServiceContext, error) {
    89  
    90  	r.Lock()
    91  	defer r.Unlock()
    92  
    93  	sctx := &ServiceContext{
    94  		PU:                    pu,
    95  		PUContext:             puContext,
    96  		dependentServiceCache: servicecache.NewTable(),
    97  		RootCA:                [][]byte{},
    98  	}
    99  
   100  	// Delete all old references first. Since the registry is locked
   101  	// nobody will be affected.
   102  	r.indexByPort.DeleteByID(puID, true)
   103  	r.indexByPort.DeleteByID(puID, false)
   104  
   105  	if err := r.updateDependentServices(sctx); err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	if err := r.updateExposedServices(sctx, secrets); err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	r.indexByName[puID] = sctx
   114  
   115  	return sctx, nil
   116  }
   117  
   118  // buildExposedServices builds the caches for the exposed services. It assumes that an authorization
   119  func (r *Registry) updateExposedServices(sctx *ServiceContext, secrets secrets.Secrets) error {
   120  
   121  	for _, service := range sctx.PU.Policy.ExposedServices() {
   122  		if service.Type != policy.ServiceHTTP && service.Type != policy.ServiceTCP {
   123  			continue
   124  		}
   125  		if err := r.updateExposedPortAssociations(sctx, service, secrets); err != nil {
   126  			return err
   127  		}
   128  	}
   129  
   130  	return nil
   131  }
   132  
   133  // Unregister unregisters a pu from the registry.
   134  func (r *Registry) Unregister(puID string) error {
   135  	r.Lock()
   136  	defer r.Unlock()
   137  
   138  	delete(r.indexByName, puID)
   139  	r.indexByPort.DeleteByID(puID, true)
   140  	r.indexByPort.DeleteByID(puID, false)
   141  	return nil
   142  }
   143  
   144  // RetrieveServiceByID retrieves a service by the PU ID. Returns error if not found.
   145  func (r *Registry) RetrieveServiceByID(id string) (*ServiceContext, error) {
   146  	r.Lock()
   147  	defer r.Unlock()
   148  
   149  	svc, ok := r.indexByName[id]
   150  	if !ok {
   151  		return nil, fmt.Errorf("Service not found: %s", id)
   152  	}
   153  
   154  	return svc, nil
   155  }
   156  
   157  // RetrieveExposedServiceContext retrieves a service by the provided IP and or port. This
   158  // is called by the network side of processing to find the context.
   159  func (r *Registry) RetrieveExposedServiceContext(ip net.IP, port int, host string) (*PortContext, error) {
   160  	r.Lock()
   161  	defer r.Unlock()
   162  
   163  	data := r.indexByPort.Find(ip, port, host, true)
   164  	if data == nil {
   165  		return nil, fmt.Errorf("Service information not found: %s %d %s", ip.String(), port, host)
   166  	}
   167  
   168  	portContext, ok := data.(*PortContext)
   169  	if !ok {
   170  		return nil, fmt.Errorf("Internal server error")
   171  	}
   172  
   173  	return portContext, nil
   174  }
   175  
   176  // RetrieveDependentServiceDataByIDAndNetwork will return the service data that match the given
   177  // PU and the given IP/port information.
   178  func (r *Registry) RetrieveDependentServiceDataByIDAndNetwork(id string, ip net.IP, port int, host string) (*ServiceContext, *DependentServiceData, error) {
   179  	sctx, err := r.RetrieveServiceByID(id)
   180  	if err != nil {
   181  		return nil, nil, fmt.Errorf("Services for PU %s not found: %s", id, err)
   182  	}
   183  	data := sctx.dependentServiceCache.Find(ip, port, "", false)
   184  	if data == nil {
   185  		return nil, nil, fmt.Errorf("Service not found for this PU: %s", id)
   186  	}
   187  	serviceData, ok := data.(*DependentServiceData)
   188  	if !ok {
   189  		return nil, nil, fmt.Errorf("Internal server error - bad data types")
   190  	}
   191  	return sctx, serviceData, nil
   192  }
   193  
   194  // updateExposedPortAssociations will insert the association between a port
   195  // and a service in the global exposed service cache. This is  needed
   196  // for all incoming connections, so that can determine both the type
   197  // of proxy as well the correct policy for this connection. This
   198  // association cannot have overlaps.
   199  func (r *Registry) updateExposedPortAssociations(sctx *ServiceContext, service *policy.ApplicationService, secrets secrets.Secrets) error {
   200  
   201  	// Do All the basic validations first.
   202  	if service.PrivateNetworkInfo == nil {
   203  		return fmt.Errorf("Private network is required for exposed services")
   204  	}
   205  	port, err := service.PrivateNetworkInfo.Ports.SinglePort()
   206  	if err != nil {
   207  		return fmt.Errorf("Multi-port is not supported for exposed services: %s", err)
   208  	}
   209  	if service.PublicNetworkInfo != nil {
   210  		if _, err := service.PublicNetworkInfo.Ports.SinglePort(); err != nil {
   211  			return fmt.Errorf("Multi-port is not supported for public network services: %s", err)
   212  		}
   213  	}
   214  
   215  	// Find any existing state and get the authorizer. We do not want
   216  	// to re-initialize the authorizer for every policy update.
   217  	authProcessor, err := r.createOrUpdateAuthProcessor(sctx, service, secrets)
   218  	if err != nil {
   219  		return err
   220  	}
   221  
   222  	clientCAs := x509.NewCertPool()
   223  	if (service.UserAuthorizationType == policy.UserAuthorizationMutualTLS || service.UserAuthorizationType == policy.UserAuthorizationJWT) &&
   224  		len(service.MutualTLSTrustedRoots) > 0 {
   225  		if !clientCAs.AppendCertsFromPEM(service.MutualTLSTrustedRoots) {
   226  			return fmt.Errorf("Unable to process client CAs")
   227  		}
   228  	}
   229  
   230  	// Add the new references.
   231  	if err := r.indexByPort.Add(
   232  		service.PrivateNetworkInfo,
   233  		sctx.PU.ContextID,
   234  		&PortContext{
   235  			ID:                 sctx.PU.ContextID,
   236  			Service:            service,
   237  			TargetPort:         int(port),
   238  			Type:               serviceTypeToNetworkListenerType(service.Type, false),
   239  			Authorizer:         authProcessor,
   240  			ClientTrustedRoots: clientCAs,
   241  			PUContext:          sctx.PUContext,
   242  		},
   243  		true,
   244  	); err != nil {
   245  		return fmt.Errorf("Possible port overlap: %s", err)
   246  	}
   247  
   248  	if service.PublicNetworkInfo != nil {
   249  		if err := r.indexByPort.Add(
   250  			service.PublicNetworkInfo,
   251  			sctx.PU.ContextID,
   252  			&PortContext{
   253  				ID:                 sctx.PU.ContextID,
   254  				Service:            service,
   255  				TargetPort:         int(port),
   256  				Type:               serviceTypeToNetworkListenerType(service.Type, service.PublicServiceTLSType == policy.ServiceTLSTypeNone),
   257  				Authorizer:         authProcessor,
   258  				ClientTrustedRoots: clientCAs,
   259  				PUContext:          sctx.PUContext,
   260  			},
   261  			true,
   262  		); err != nil {
   263  			return fmt.Errorf("Possible port overlap with public services: %s", err)
   264  		}
   265  	}
   266  
   267  	return nil
   268  }
   269  
   270  func updateDependentService(service *policy.ApplicationService, sctx *ServiceContext) error {
   271  
   272  	if len(service.CACert) != 0 {
   273  		sctx.RootCA = append(sctx.RootCA, service.CACert)
   274  	}
   275  
   276  	serviceData := &DependentServiceData{
   277  		ServiceType:   serviceTypeToApplicationListenerType(service.Type),
   278  		ServiceObject: service,
   279  	}
   280  	if service.Type == policy.ServiceHTTP {
   281  		serviceData.APICache = urisearch.NewAPICache(service.HTTPRules, service.ID, service.External)
   282  	}
   283  
   284  	if err := sctx.dependentServiceCache.Add(
   285  		service.NetworkInfo,
   286  		sctx.PU.ContextID,
   287  		serviceData,
   288  		false,
   289  	); err != nil {
   290  		return fmt.Errorf("Possible overlap in dependent services: %s", err)
   291  	}
   292  
   293  	return nil
   294  }
   295  
   296  // UpdateDependentServicesByID will the dependent services for the ID
   297  func (r *Registry) UpdateDependentServicesByID(id string) error {
   298  
   299  	sctx, err := r.RetrieveServiceByID(id)
   300  	if err != nil {
   301  		return fmt.Errorf("Services for PU %s not found: %s", id, err)
   302  	}
   303  
   304  	r.Lock()
   305  	sctx.dependentServiceCache = servicecache.NewTable()
   306  	err = r.updateDependentServices(sctx)
   307  	r.Unlock()
   308  
   309  	return err
   310  }
   311  
   312  // updateDependentService will update all the information in the
   313  // ServiceContext for the dependent services.
   314  func (r *Registry) updateDependentServices(sctx *ServiceContext) error {
   315  
   316  	for _, service := range sctx.PU.Policy.DependentServices() {
   317  		if err := updateDependentService(service, sctx); err != nil {
   318  			return err
   319  		}
   320  	}
   321  
   322  	return nil
   323  }
   324  
   325  func (r *Registry) createOrUpdateAuthProcessor(sctx *ServiceContext, service *policy.ApplicationService, secrets secrets.Secrets) (*auth.Processor, error) {
   326  
   327  	var cert *x509.Certificate
   328  	if len(service.FallbackJWTAuthorizationCert) > 0 {
   329  		var err error
   330  		cert, err = tglib.ParseCertificate([]byte(service.FallbackJWTAuthorizationCert))
   331  		if err != nil {
   332  			return nil, err
   333  		}
   334  	}
   335  
   336  	portContext, _ := r.indexByPort.FindListeningServicesForPU(sctx.PU.ContextID)
   337  	var authProcessor *auth.Processor
   338  	if portContext != nil {
   339  		existingPortCtx, ok := portContext.(*PortContext)
   340  		if !ok {
   341  			return nil, fmt.Errorf("Internal error - unusable data structure")
   342  		}
   343  		authProcessor = existingPortCtx.Authorizer
   344  		authProcessor.UpdateSecrets(secrets, cert)
   345  	} else {
   346  		authProcessor = auth.NewProcessor(secrets, cert)
   347  	}
   348  
   349  	authProcessor.AddOrUpdateService(
   350  		urisearch.NewAPICache(service.HTTPRules, service.ID, false),
   351  		service.UserAuthorizationType,
   352  		service.UserAuthorizationHandler,
   353  		service.UserTokenToHTTPMappings,
   354  	)
   355  
   356  	return authProcessor, nil
   357  }
   358  
   359  func serviceTypeToNetworkListenerType(serviceType policy.ServiceType, noTLS bool) common.ListenerType {
   360  	switch serviceType {
   361  	case policy.ServiceHTTP:
   362  		if noTLS {
   363  			return common.HTTPNetwork
   364  		}
   365  		return common.HTTPSNetwork
   366  	default:
   367  		return common.TCPNetwork
   368  	}
   369  }
   370  
   371  func serviceTypeToApplicationListenerType(serviceType policy.ServiceType) common.ListenerType {
   372  	switch serviceType {
   373  	case policy.ServiceHTTP:
   374  		return common.HTTPApplication
   375  	default:
   376  		return common.TCPApplication
   377  	}
   378  }