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

     1  package ca_chain
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"encoding/hex"
     8  	"encoding/pem"
     9  	"fmt"
    10  	"io"
    11  	"strings"
    12  
    13  	"github.com/sirupsen/logrus"
    14  )
    15  
    16  const (
    17  	pemTypeCertificate = "CERTIFICATE"
    18  )
    19  
    20  type pemEncoder func(out io.Writer, b *pem.Block) error
    21  
    22  type Builder interface {
    23  	fmt.Stringer
    24  
    25  	BuildChainFromTLSConnectionState(TLS *tls.ConnectionState) error
    26  }
    27  
    28  func NewBuilder(logger logrus.FieldLogger) Builder {
    29  	logger = logger.
    30  		WithField("context", "certificate-chain-build")
    31  
    32  	return &defaultBuilder{
    33  		certificates:     make([]*x509.Certificate, 0),
    34  		seenCertificates: make(map[string]bool, 0),
    35  		resolver: newChainResolver(
    36  			newURLResolver(logger),
    37  			newVerifyResolver(logger),
    38  		),
    39  		encodePEM: pem.Encode,
    40  		logger:    logger,
    41  	}
    42  }
    43  
    44  type defaultBuilder struct {
    45  	certificates     []*x509.Certificate
    46  	seenCertificates map[string]bool
    47  
    48  	resolver  resolver
    49  	encodePEM pemEncoder
    50  
    51  	logger logrus.FieldLogger
    52  }
    53  
    54  func (b *defaultBuilder) BuildChainFromTLSConnectionState(TLS *tls.ConnectionState) error {
    55  	for _, verifiedChain := range TLS.VerifiedChains {
    56  		b.logger.
    57  			WithField("chain-leaf", fmt.Sprintf("%v", verifiedChain)).
    58  			Debug("Processing chain")
    59  		err := b.fetchCertificatesFromVerifiedChain(verifiedChain)
    60  		if err != nil {
    61  			return fmt.Errorf("error while fetching certificates into the CA Chain: %v", err)
    62  		}
    63  	}
    64  
    65  	return nil
    66  }
    67  
    68  func (b *defaultBuilder) fetchCertificatesFromVerifiedChain(verifiedChain []*x509.Certificate) error {
    69  	var err error
    70  
    71  	if len(verifiedChain) < 1 {
    72  		return nil
    73  	}
    74  
    75  	verifiedChain, err = b.resolver.Resolve(verifiedChain)
    76  	if err != nil {
    77  		return fmt.Errorf("couldn't resolve certificates chain from the leaf certificate: %v", err)
    78  	}
    79  
    80  	for _, certificate := range verifiedChain {
    81  		b.addCertificate(certificate)
    82  	}
    83  
    84  	return nil
    85  }
    86  
    87  func (b *defaultBuilder) addCertificate(certificate *x509.Certificate) {
    88  	signature := hex.EncodeToString(certificate.Signature)
    89  	if b.seenCertificates[signature] {
    90  		return
    91  	}
    92  
    93  	b.seenCertificates[signature] = true
    94  	b.certificates = append(b.certificates, certificate)
    95  }
    96  
    97  func (b *defaultBuilder) String() string {
    98  	out := bytes.NewBuffer(nil)
    99  	for _, certificate := range b.certificates {
   100  		err := b.encodePEM(out, &pem.Block{Type: pemTypeCertificate, Bytes: certificate.Raw})
   101  		if err != nil {
   102  			b.logger.
   103  				WithError(err).
   104  				Warning("Failed to encode certificate from chain")
   105  		}
   106  	}
   107  
   108  	return strings.TrimSpace(out.String())
   109  }