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 }