github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/pkg/accessstrategy/cmp_mtls_access_strategy.go (about) 1 package accessstrategy 2 3 import ( 4 "context" 5 "crypto/tls" 6 "net/http" 7 8 "github.com/kyma-incubator/compass/components/director/pkg/log" 9 10 "github.com/pkg/errors" 11 12 "github.com/kyma-incubator/compass/components/director/pkg/certloader" 13 ) 14 15 // HTTPRoundTripper missing godoc 16 type HTTPRoundTripper interface { 17 RoundTrip(*http.Request) (*http.Response, error) 18 Clone() HTTPRoundTripper 19 GetTransport() *http.Transport 20 } 21 22 const tenantHeader = "Tenant_Id" 23 24 type cmpMTLSAccessStrategyExecutor struct { 25 certCache certloader.Cache 26 tenantProviderFunc func(ctx context.Context) (string, error) 27 externalClientCertSecretName string 28 extSvcClientCertSecretName string 29 } 30 31 // NewCMPmTLSAccessStrategyExecutor creates a new Executor for the CMP mTLS Access Strategy 32 func NewCMPmTLSAccessStrategyExecutor(certCache certloader.Cache, tenantProviderFunc func(ctx context.Context) (string, error), externalClientCertSecretName, extSvcClientCertSecretName string) *cmpMTLSAccessStrategyExecutor { 33 return &cmpMTLSAccessStrategyExecutor{ 34 certCache: certCache, 35 tenantProviderFunc: tenantProviderFunc, 36 externalClientCertSecretName: externalClientCertSecretName, 37 extSvcClientCertSecretName: extSvcClientCertSecretName, 38 } 39 } 40 41 // Execute performs the access strategy's specific execution logic 42 func (as *cmpMTLSAccessStrategyExecutor) Execute(ctx context.Context, baseClient *http.Client, documentURL, tnt string) (*http.Response, error) { 43 clientCerts := as.certCache.Get() 44 if clientCerts == nil { 45 return nil, errors.New("did not find client certificate in the cache") 46 } 47 tr := &http.Transport{} 48 if baseClient.Transport != nil { 49 switch v := baseClient.Transport.(type) { 50 case *http.Transport: 51 tr = v.Clone() 52 case HTTPRoundTripper: 53 tr = v.GetTransport().Clone() 54 default: 55 return nil, errors.New("unsupported transport type") 56 } 57 } 58 59 tr.TLSClientConfig.Certificates = []tls.Certificate{*clientCerts[as.externalClientCertSecretName]} 60 61 client := &http.Client{ 62 Timeout: baseClient.Timeout, 63 Transport: tr, 64 } 65 66 req, err := http.NewRequest("GET", documentURL, nil) 67 if err != nil { 68 return nil, err 69 } 70 71 // if it's not request to global registry && the webhook is associated with app template use the local tenant id as header 72 if as.tenantProviderFunc != nil && len(tnt) > 0 { 73 localTenantID, err := as.tenantProviderFunc(ctx) 74 if err != nil { 75 return nil, err 76 } 77 78 req.Header.Set(tenantHeader, localTenantID) 79 } else { 80 req.Header.Set(tenantHeader, tnt) 81 } 82 83 resp, err := client.Do(req) 84 if err != nil || resp.StatusCode >= http.StatusBadRequest { 85 if len(clientCerts) != 2 { 86 return nil, errors.Errorf("There must be exactly 2 certificates in the cert cache. Actual number of certificates: %d", len(clientCerts)) 87 } 88 log.C(ctx).Info("Failed to execute request with initial mtls certificate. Will retry with backup certificate...") 89 tr.TLSClientConfig.Certificates = []tls.Certificate{*clientCerts[as.extSvcClientCertSecretName]} 90 client.Transport = tr 91 return client.Do(req) 92 } 93 return resp, err 94 }