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 }