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  }