github.com/secure-build/gitlab-runner@v12.5.0+incompatible/helpers/tls/ca_chain/resolver_url.go (about)

     1  // Inspired by https://github.com/zakjan/cert-chain-resolver/blob/master/certUtil/chain.go
     2  // which is licensed on a MIT license.
     3  //
     4  // Shout out to Jan Žák (http://zakjan.cz) original author of `certUtil` package and other
     5  // contributors who updated it!
     6  
     7  package ca_chain
     8  
     9  import (
    10  	"crypto/x509"
    11  	"fmt"
    12  
    13  	"github.com/sirupsen/logrus"
    14  )
    15  
    16  const defaultURLResolverLoopLimit = 15
    17  
    18  type fetcher func(url string) ([]byte, error)
    19  type decoder func(data []byte) (*x509.Certificate, error)
    20  
    21  type urlResolver struct {
    22  	logger  logrus.FieldLogger
    23  	fetcher fetcher
    24  	decoder decoder
    25  
    26  	loopLimit int
    27  }
    28  
    29  func newURLResolver(logger logrus.FieldLogger) resolver {
    30  	return &urlResolver{
    31  		logger:    logger,
    32  		fetcher:   fetchRemoteCertificate,
    33  		decoder:   decodeCertificate,
    34  		loopLimit: defaultURLResolverLoopLimit,
    35  	}
    36  }
    37  
    38  func (r *urlResolver) Resolve(certs []*x509.Certificate) ([]*x509.Certificate, error) {
    39  	if len(certs) < 1 {
    40  		return nil, nil
    41  	}
    42  
    43  	loop := 0
    44  	for {
    45  		loop++
    46  		if loop >= r.loopLimit {
    47  			r.
    48  				logger.
    49  				Warning("urlResolver loop limit exceeded; exiting the loop")
    50  
    51  			break
    52  		}
    53  
    54  		certificate := certs[len(certs)-1]
    55  		log := prepareCertificateLogger(r.logger, certificate)
    56  
    57  		if certificate.IssuingCertificateURL == nil {
    58  			log.Debug("Certificate doesn't provide parent URL: exiting the loop")
    59  			break
    60  		}
    61  
    62  		newCert, err := r.fetchIssuerCertificate(certificate)
    63  		if err != nil {
    64  			return nil, fmt.Errorf("error while fetching issuer certificate: %v", err)
    65  		}
    66  
    67  		certs = append(certs, newCert)
    68  
    69  		if isSelfSigned(newCert) {
    70  			log.Debug("Fetched issuer certificate is a ROOT certificate so exiting the loop")
    71  			break
    72  		}
    73  	}
    74  
    75  	return certs, nil
    76  }
    77  
    78  func (r *urlResolver) fetchIssuerCertificate(cert *x509.Certificate) (*x509.Certificate, error) {
    79  	log := prepareCertificateLogger(r.logger, cert).
    80  		WithField("method", "fetchIssuerCertificate")
    81  
    82  	issuerURL := cert.IssuingCertificateURL[0]
    83  
    84  	data, err := r.fetcher(issuerURL)
    85  	if err != nil {
    86  		log.
    87  			WithError(err).
    88  			WithField("issuerURL", issuerURL).
    89  			Warning("Remote certificate fetching error")
    90  
    91  		return nil, fmt.Errorf("remote fetch failure: %v", err)
    92  	}
    93  
    94  	newCert, err := r.decoder(data)
    95  	if err != nil {
    96  		log.
    97  			WithError(err).
    98  			Warning("Certificate decoding error")
    99  
   100  		return nil, fmt.Errorf("decoding failure: %v", err)
   101  	}
   102  
   103  	preparePrefixedCertificateLogger(log, newCert, "newCert").
   104  		Debug("Appending the certificate to the chain")
   105  
   106  	return newCert, nil
   107  }