github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/internal/enforcer/envoyauthorizer/envoyauthorizerenforcer.go (about) 1 package envoyauthorizer 2 3 import ( 4 "context" 5 "crypto/tls" 6 "crypto/x509" 7 "fmt" 8 "sync" 9 "time" 10 11 "go.aporeto.io/enforcerd/trireme-lib/collector" 12 "go.aporeto.io/enforcerd/trireme-lib/common" 13 "go.aporeto.io/enforcerd/trireme-lib/controller/constants" 14 "go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/applicationproxy/serviceregistry" 15 enforcerconstants "go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/constants" 16 "go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/envoyauthorizer/envoyproxy" 17 "go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/metadata" 18 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/ebpf" 19 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/fqconfig" 20 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/packettracing" 21 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/pucontext" 22 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/secrets" 23 "go.aporeto.io/enforcerd/trireme-lib/controller/runtime" 24 "go.aporeto.io/enforcerd/trireme-lib/policy" 25 "go.aporeto.io/enforcerd/trireme-lib/utils/cache" 26 "go.uber.org/zap" 27 ) 28 29 // Enforcer implements the Enforcer interface as an envoy authorizer 30 // and starts envoy external authz filter gRPC servers for enforcement. 31 type Enforcer struct { 32 mode constants.ModeType 33 collector collector.EventCollector 34 externalIPCacheTimeout time.Duration 35 secrets secrets.Secrets 36 tokenIssuer common.ServiceTokenIssuer 37 38 puContexts cache.DataStore 39 clients cache.DataStore 40 systemCAPool *x509.CertPool 41 42 metadata *metadata.Client 43 sync.RWMutex 44 } 45 46 // envoyAuthzServers, envoy servers used my enforcer 47 type envoyServers struct { 48 ingress *envoyproxy.AuthServer 49 egress *envoyproxy.AuthServer 50 sds *envoyproxy.SdsServer 51 } 52 53 // NewEnvoyAuthorizerEnforcer creates a new envoy authorizer 54 func NewEnvoyAuthorizerEnforcer(mode constants.ModeType, eventCollector collector.EventCollector, externalIPCacheTimeout time.Duration, secrets secrets.Secrets, tokenIssuer common.ServiceTokenIssuer) (*Enforcer, error) { 55 // abort if this is not the right mode 56 if mode != constants.RemoteContainerEnvoyAuthorizer && mode != constants.LocalEnvoyAuthorizer { 57 return nil, fmt.Errorf("enforcer mode type must be either RemoteContainerEnvoyAuthorizer or LocalEnvoyAuthorizer, got: %d", mode) 58 } 59 zap.L().Info("Creating Envoy Authorizer Enforcer") 60 // same logic as in the nfqdatapath 61 if externalIPCacheTimeout <= 0 { 62 var err error 63 externalIPCacheTimeout, err = time.ParseDuration(enforcerconstants.DefaultExternalIPTimeout) 64 if err != nil { 65 externalIPCacheTimeout = time.Second 66 } 67 } 68 69 // same logic as in app proxy 70 systemPool, err := x509.SystemCertPool() 71 if err != nil { 72 return nil, err 73 } 74 75 if ok := systemPool.AppendCertsFromPEM(secrets.CertAuthority()); !ok { 76 return nil, fmt.Errorf("error while adding provided CA") 77 } 78 // TODO: systemPool needs the same treatment as the AppProxy and a `processCertificateUpdates` and `expandCAPool` implementation as well 79 return &Enforcer{ 80 mode: mode, 81 collector: eventCollector, 82 externalIPCacheTimeout: externalIPCacheTimeout, 83 secrets: secrets, 84 tokenIssuer: tokenIssuer, 85 puContexts: cache.NewCache("puContexts"), 86 clients: cache.NewCache("clients"), 87 // auth: apiauth.New(puContexts, registry, secrets), 88 // metadata: metadata.NewClient(puContext, registry, tokenIssuer), 89 }, nil 90 } 91 92 // Secrets implements the LockedSecrets 93 func (e *Enforcer) Secrets() (secrets.Secrets, func()) { 94 e.RLock() 95 return e.secrets, e.RUnlock 96 } 97 98 // Enforce starts enforcing policies for the given policy.PUInfo. 99 // here we do the following: 100 // 1. create a new PU always and instantiate a new apiAuth, as we want to be as stateless as possible. 101 // 2. create a PUcontext as this will be used in auth code. 102 // 3. If envoy servers are not present then create all 3 envoy servers. 103 // 4. If the servers are already present under policy update then update the service certs. 104 func (e *Enforcer) Enforce(ctx context.Context, contextID string, puInfo *policy.PUInfo) error { 105 e.Lock() 106 defer e.Unlock() 107 108 zap.L().Debug("Enforce for the envoy for pu", zap.String("puID", contextID)) 109 // here we 1st need to create a PuContext, as the PU context will derive the 110 // serviceCtxt which will be used by the authorizer to determine the policyInfo. 111 112 pu, err := pucontext.NewPU(contextID, puInfo, nil, e.externalIPCacheTimeout) 113 if err != nil { 114 return fmt.Errorf("error creating new pu: %s", err) 115 } 116 // Add the puContext to the cache as we need to later while serving the requests. 117 e.puContexts.AddOrUpdate(contextID, pu) 118 119 sctx, err := serviceregistry.Instance().Register(contextID, puInfo, pu, e.secrets) 120 if err != nil { 121 return fmt.Errorf("policy conflicts detected: %s", err) 122 } 123 124 caPool := e.expandCAPool(sctx.RootCA) 125 126 // now instantiate the apiAuth and metadata 127 // create a new server if it doesn't exist yet 128 if _, err := e.clients.Get(contextID); err != nil { 129 zap.L().Debug("creating new auth and sds servers", zap.String("puID", contextID)) 130 ingressServer, err := envoyproxy.NewExtAuthzServer(contextID, e.puContexts, e.collector, envoyproxy.IngressDirection, e.secrets, e.tokenIssuer) 131 if err != nil { 132 zap.L().Error("Cannot create and run IngressServer", zap.Error(err)) 133 return err 134 } 135 136 egressServer, err := envoyproxy.NewExtAuthzServer(contextID, e.puContexts, e.collector, envoyproxy.EgressDirection, e.secrets, e.tokenIssuer) 137 if err != nil { 138 zap.L().Error("Cannot create and run EgressServer", zap.Error(err)) 139 ingressServer.Stop() 140 return err 141 } 142 sdsServer, err := envoyproxy.NewSdsServer(contextID, puInfo, caPool, e.secrets) 143 if err != nil { 144 zap.L().Error("Cannot create and run SdsServer", zap.Error(err)) 145 return err 146 } 147 // Add the EnvoyServers to our cache 148 if err := e.clients.Add(contextID, &envoyServers{ingress: ingressServer, egress: egressServer, sds: sdsServer}); err != nil { 149 ingressServer.Stop() 150 egressServer.Stop() 151 sdsServer.Stop() 152 return err 153 } 154 155 } else { 156 // we have this client already, this is only a policy update 157 zap.L().Debug("handling policy update for envoy servers", zap.String("puID", contextID)) 158 // For updates we need to update the certificates if we have new ones. Otherwise 159 // we return. There is nothing else to do in case of policy update. 160 // this required for the Envoy servers. 161 if c, cerr := e.clients.Get(contextID); cerr == nil { 162 _, perr := e.processCertificateUpdates(puInfo, c.(*envoyServers), caPool) 163 if perr != nil { 164 zap.L().Error("unable to update certificates for services", zap.Error(perr)) 165 return perr 166 } 167 return nil 168 } 169 } 170 171 return nil 172 } 173 174 // processCertificateUpdates processes the certificate information and updates 175 // the servers. 176 func (e *Enforcer) processCertificateUpdates(puInfo *policy.PUInfo, server *envoyServers, caPool *x509.CertPool) (bool, error) { 177 178 // If there are certificates provided, we will need to update them for the 179 // services. If the certificates are nil, we ignore them. 180 certPEM, keyPEM, caPEM := puInfo.Policy.ServiceCertificates() 181 if certPEM == "" || keyPEM == "" { 182 return false, nil 183 } 184 185 // Process any updates on the cert pool 186 if caPEM != "" { 187 if !caPool.AppendCertsFromPEM([]byte(caPEM)) { 188 zap.L().Warn("Failed to add Services CA") 189 } 190 } 191 192 // Create the TLS certificate 193 tlsCert, err := tls.X509KeyPair([]byte(certPEM), []byte(keyPEM)) 194 if err != nil { 195 return false, fmt.Errorf("Invalid certificates: %s", err) 196 } 197 // Here update the enforcer secrets because we are using the LockedSecrets. 198 // Also, send a update event to the SDS server so it can send a new cert to the envoy Sidecar. 199 // // update all the server certs, the Write lock has already been acquired by the Enforce function, so no need to lock again. 200 server.ingress.UpdateSecrets(&tlsCert, caPool, e.secrets, certPEM, keyPEM) 201 server.egress.UpdateSecrets(&tlsCert, caPool, e.secrets, certPEM, keyPEM) 202 server.sds.UpdateSecrets(&tlsCert, caPool, e.secrets, certPEM, keyPEM) 203 204 if e.metadata != nil { 205 e.metadata.UpdateSecrets([]byte(certPEM), []byte(keyPEM)) 206 } 207 return true, nil 208 } 209 210 func (e *Enforcer) expandCAPool(externalCAs [][]byte) *x509.CertPool { 211 systemPool, err := x509.SystemCertPool() 212 if err != nil { 213 zap.L().Error("cannot process system pool", zap.Error(err)) 214 return e.systemCAPool 215 } 216 if ok := systemPool.AppendCertsFromPEM(e.secrets.CertAuthority()); !ok { 217 zap.L().Error("cannot appen system CA", zap.Error(err)) 218 return e.systemCAPool 219 } 220 for _, ca := range externalCAs { 221 if ok := systemPool.AppendCertsFromPEM(ca); !ok { 222 zap.L().Error("cannot append external service ca", zap.String("CA", string(ca))) 223 } 224 } 225 return systemPool 226 } 227 228 // Unenforce stops enforcing policy for the given IP. 229 func (e *Enforcer) Unenforce(ctx context.Context, contextID string) error { 230 e.Lock() 231 defer e.Unlock() 232 233 // stop the authz servers 234 rawAuthzServers, err := e.clients.Get(contextID) 235 if err != nil { 236 return err 237 } 238 239 server := rawAuthzServers.(*envoyServers) 240 shutdownCtx, shutdownCtxCancel := context.WithTimeout(ctx, time.Second*10) 241 defer shutdownCtxCancel() 242 243 var wg sync.WaitGroup 244 shutdownCh := make(chan struct{}) 245 wg.Add(3) 246 go func() { 247 server.ingress.GracefulStop() 248 wg.Done() 249 }() 250 go func() { 251 server.egress.GracefulStop() 252 wg.Done() 253 }() 254 go func() { 255 server.sds.GracefulStop() 256 wg.Done() 257 }() 258 go func() { 259 wg.Wait() 260 shutdownCh <- struct{}{} 261 }() 262 263 select { 264 case <-shutdownCtx.Done(): 265 zap.L().Warn("Graceful shutdown of envoy server did not finish in time. Shutting down hard now...", zap.String("puID", contextID), zap.Error(shutdownCtx.Err())) 266 var wg sync.WaitGroup 267 wg.Add(3) 268 go func() { 269 server.ingress.Stop() 270 wg.Done() 271 }() 272 go func() { 273 server.egress.Stop() 274 wg.Done() 275 }() 276 go func() { 277 server.sds.Stop() 278 wg.Done() 279 }() 280 wg.Wait() 281 case <-shutdownCh: 282 } 283 284 if err := e.puContexts.RemoveWithDelay(contextID, 10*time.Second); err != nil { 285 zap.L().Debug("Unable to remove PU context from cache", zap.String("puID", contextID), zap.Error(err)) 286 } 287 288 return nil 289 } 290 291 // UpdateSecrets -- updates the secrets of running enforcers managed by trireme. Remote enforcers will get the secret updates with the next policy push 292 func (e *Enforcer) UpdateSecrets(secrets secrets.Secrets) error { 293 e.Lock() 294 defer e.Unlock() 295 e.secrets = secrets 296 return nil 297 } 298 299 // SetTargetNetworks is unimplemented in the envoy authorizer 300 func (e *Enforcer) SetTargetNetworks(cfg *runtime.Configuration) error { 301 return nil 302 } 303 304 // SetLogLevel is unimplemented in the envoy authorizer 305 func (e *Enforcer) SetLogLevel(level constants.LogLevel) error { 306 return nil 307 } 308 309 // CleanUp is unimplemented in the envoy authorizer 310 func (e *Enforcer) CleanUp() error { 311 return nil 312 } 313 314 // Run is unimplemented in the envoy authorizer 315 func (e *Enforcer) Run(ctx context.Context) error { 316 return nil 317 } 318 319 // GetBPFObject is unimplemented in the envoy authorizer 320 func (e *Enforcer) GetBPFObject() ebpf.BPFModule { 321 return nil 322 } 323 324 // GetServiceMeshType is unimplemented in the envoy authorizer 325 func (e *Enforcer) GetServiceMeshType() policy.ServiceMesh { 326 return policy.None 327 } 328 329 // GetFilterQueue is unimplemented in the envoy authorizer 330 func (e *Enforcer) GetFilterQueue() fqconfig.FilterQueue { 331 return nil 332 } 333 334 // EnableDatapathPacketTracing is unimplemented in the envoy authorizer 335 func (e *Enforcer) EnableDatapathPacketTracing(ctx context.Context, contextID string, direction packettracing.TracingDirection, interval time.Duration) error { 336 return nil 337 } 338 339 // EnableIPTablesPacketTracing is unimplemented in the envoy authorizer 340 func (e *Enforcer) EnableIPTablesPacketTracing(ctx context.Context, contextID string, interval time.Duration) error { 341 return nil 342 } 343 344 // Ping is unimplemented in the envoy authorizer 345 func (e *Enforcer) Ping(ctx context.Context, contextID string, pingConfig *policy.PingConfig) error { 346 return nil 347 } 348 349 // DebugCollect is unimplemented in the envoy authorizer 350 func (e *Enforcer) DebugCollect(ctx context.Context, contextID string, debugConfig *policy.DebugConfig) error { 351 return nil 352 }