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

     1  package ca_chain
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"encoding/pem"
     8  	"errors"
     9  	"io"
    10  	"testing"
    11  
    12  	"github.com/sirupsen/logrus"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  const (
    18  	testCACert = `-----BEGIN CERTIFICATE-----
    19  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
    49  -----END CERTIFICATE-----`
    50  	testCert = `-----BEGIN CERTIFICATE-----
    51  MIIEEjCCAfoCFBhRTszftYHtN+HOfbU/q3zvYBYOMA0GCSqGSIb3DQEBCwUAMFUx
    52  CzAJBgNVBAYTAlVTMQowCAYDVQQIDAEgMQowCAYDVQQKDAEgMQowCAYDVQQLDAEg
    53  MRAwDgYDVQQDDAdUZXN0IENBMRAwDgYJKoZIhvcNAQkBFgEgMCAXDTE5MTAxODA2
    54  MDA1MloYDzIxMTkwOTI0MDYwMDUyWjA0MQswCQYDVQQGEwJVUzEKMAgGA1UECAwB
    55  IDEKMAgGA1UECgwBIDENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQAD
    56  ggEPADCCAQoCggEBALc0+Xo61c0xCvebNg1OJl4iXC5blzGlbDfejWKn7266g+UU
    57  Z3xscCDWMNruojd+7EbkQmAyUtdGifNw+xIHyNA/jiyIsB3KteN84X+toA4mjY1t
    58  SpqlNMOUW0EZ9f0KZNn4GZnA/TyFWI3EC4gOcJyuuL7YfE7Qu1e3LeBwDcRYpJ3W
    59  Zw1k3+aClC1N7iTPEP9scr64+KA0d5xIkrtl5t8qiSR8Tn+JLPygGre0G0hhIZeH
    60  pfPQWX6iILbJMgPnbPmCivklkyUIE8WHh2qGbOGaO3LVKSS6/YfOshw4g/RQyusI
    61  Ii65iXnFa/VvRY2dkn5w9EehZzbT8kQa7U39NwkCAwEAATANBgkqhkiG9w0BAQsF
    62  AAOCAgEAMAfp7FRBHm9t4byRfWrUYblI7eQOlcixXHSPc16VX93HTsNWwZV1EBiO
    63  GWcTRcts5FQr9HWGHVukQ+4iXLWtb/Og+hHrjyLmOGvx7sgPeHuyWB89npSABden
    64  rpMHPePMzsO/YTw1QuYJOijNYpLCL83YWk62DCSGwQ2HO1KKLDw3suBHudV80cHV
    65  nav7Q0VW+iA+3apdrgediCHCtc6PQDHPzdrXQSVA+OF2itX3Xhc6Mm3dn4D3Hhqo
    66  WYJNeI0naNHTguoKFYdJHHjv07nX+1I+CAk6kjEv17VEKsU7SjhOizLYdtb9OrOS
    67  gnQ6KTkPfCeIlK2PNguwxgeLBNYQyTnUxr1QxgVkKFsBfwFV4hq9podEbjrgUSu1
    68  KZSdU7u7WMCjLYpyC5kbRmd/Qkdo/45wifomJNP3/16NSNZ0gatKVUJ6q6UjRsZl
    69  3va4QcB3QuNtGiQZqEuc/+KM21MSvC8cC/bIOaKZlWbKtEV+tsbuIIhng0opJrEw
    70  +5ZqVqrwIVjbsGaw/NPROth/XDJp5jzpwxnf5HDQhLV04sfdN9IRw005WC+l0f19
    71  iG9V6qslKJvNR8A8A+RqvyfIJ0gjNzVLQHrZyTsEbC62w1IcxkBG7lR6W7ZCXal1
    72  RSKf+3OIln1a6DKx+zEzL20uwW5L/5l3FsLwwvOLybX4mAhiyxY=
    73  -----END CERTIFICATE-----`
    74  
    75  	// the same as testCert, but encoded with PKCS7
    76  	testCertPKCS7 = `-----BEGIN PKCS7-----
    77  MIIEQwYJKoZIhvcNAQcCoIIENDCCBDACAQExADALBgkqhkiG9w0BBwGgggQWMIIE
    78  EjCCAfoCFBhRTszftYHtN+HOfbU/q3zvYBYOMA0GCSqGSIb3DQEBCwUAMFUxCzAJ
    79  BgNVBAYTAlVTMQowCAYDVQQIDAEgMQowCAYDVQQKDAEgMQowCAYDVQQLDAEgMRAw
    80  DgYDVQQDDAdUZXN0IENBMRAwDgYJKoZIhvcNAQkBFgEgMCAXDTE5MTAxODA2MDA1
    81  MloYDzIxMTkwOTI0MDYwMDUyWjA0MQswCQYDVQQGEwJVUzEKMAgGA1UECAwBIDEK
    82  MAgGA1UECgwBIDENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEP
    83  ADCCAQoCggEBALc0+Xo61c0xCvebNg1OJl4iXC5blzGlbDfejWKn7266g+UUZ3xs
    84  cCDWMNruojd+7EbkQmAyUtdGifNw+xIHyNA/jiyIsB3KteN84X+toA4mjY1tSpql
    85  NMOUW0EZ9f0KZNn4GZnA/TyFWI3EC4gOcJyuuL7YfE7Qu1e3LeBwDcRYpJ3WZw1k
    86  3+aClC1N7iTPEP9scr64+KA0d5xIkrtl5t8qiSR8Tn+JLPygGre0G0hhIZeHpfPQ
    87  WX6iILbJMgPnbPmCivklkyUIE8WHh2qGbOGaO3LVKSS6/YfOshw4g/RQyusIIi65
    88  iXnFa/VvRY2dkn5w9EehZzbT8kQa7U39NwkCAwEAATANBgkqhkiG9w0BAQsFAAOC
    89  AgEAMAfp7FRBHm9t4byRfWrUYblI7eQOlcixXHSPc16VX93HTsNWwZV1EBiOGWcT
    90  Rcts5FQr9HWGHVukQ+4iXLWtb/Og+hHrjyLmOGvx7sgPeHuyWB89npSABdenrpMH
    91  PePMzsO/YTw1QuYJOijNYpLCL83YWk62DCSGwQ2HO1KKLDw3suBHudV80cHVnav7
    92  Q0VW+iA+3apdrgediCHCtc6PQDHPzdrXQSVA+OF2itX3Xhc6Mm3dn4D3HhqoWYJN
    93  eI0naNHTguoKFYdJHHjv07nX+1I+CAk6kjEv17VEKsU7SjhOizLYdtb9OrOSgnQ6
    94  KTkPfCeIlK2PNguwxgeLBNYQyTnUxr1QxgVkKFsBfwFV4hq9podEbjrgUSu1KZSd
    95  U7u7WMCjLYpyC5kbRmd/Qkdo/45wifomJNP3/16NSNZ0gatKVUJ6q6UjRsZl3va4
    96  QcB3QuNtGiQZqEuc/+KM21MSvC8cC/bIOaKZlWbKtEV+tsbuIIhng0opJrEw+5Zq
    97  VqrwIVjbsGaw/NPROth/XDJp5jzpwxnf5HDQhLV04sfdN9IRw005WC+l0f19iG9V
    98  6qslKJvNR8A8A+RqvyfIJ0gjNzVLQHrZyTsEbC62w1IcxkBG7lR6W7ZCXal1RSKf
    99  +3OIln1a6DKx+zEzL20uwW5L/5l3FsLwwvOLybX4mAhiyxahADEA
   100  -----END PKCS7-----`
   101  
   102  	testCertPubKey = `-----BEGIN PUBLIC KEY-----
   103  MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtzT5ejrVzTEK95s2DU4m
   104  XiJcLluXMaVsN96NYqfvbrqD5RRnfGxwINYw2u6iN37sRuRCYDJS10aJ83D7EgfI
   105  0D+OLIiwHcq143zhf62gDiaNjW1KmqU0w5RbQRn1/Qpk2fgZmcD9PIVYjcQLiA5w
   106  nK64vth8TtC7V7ct4HANxFikndZnDWTf5oKULU3uJM8Q/2xyvrj4oDR3nEiSu2Xm
   107  3yqJJHxOf4ks/KAat7QbSGEhl4el89BZfqIgtskyA+ds+YKK+SWTJQgTxYeHaoZs
   108  4Zo7ctUpJLr9h86yHDiD9FDK6wgiLrmJecVr9W9FjZ2SfnD0R6FnNtPyRBrtTf03
   109  CQIDAQAB
   110  -----END PUBLIC KEY-----`
   111  )
   112  
   113  func TestDefaultBuilder_BuildChainFromTLSConnectionState(t *testing.T) {
   114  	testError := errors.New("test-error")
   115  
   116  	block, _ := pem.Decode([]byte(testCert))
   117  	testCertificate, err := x509.ParseCertificate(block.Bytes)
   118  	require.NoError(t, err)
   119  
   120  	block, _ = pem.Decode([]byte(testCACert))
   121  	testCACertificate, err := x509.ParseCertificate(block.Bytes)
   122  	require.NoError(t, err)
   123  
   124  	tests := map[string]struct {
   125  		chains              [][]*x509.Certificate
   126  		setupResolverMock   func(t *testing.T) (resolver, func())
   127  		expectedError       string
   128  		expectedChainLength int
   129  	}{
   130  		"no chains": {
   131  			chains:              [][]*x509.Certificate{},
   132  			expectedChainLength: 0,
   133  		},
   134  		"empty chain": {
   135  			chains:              [][]*x509.Certificate{{}},
   136  			expectedChainLength: 0,
   137  		},
   138  		"error on chain resolving": {
   139  			chains: [][]*x509.Certificate{{testCertificate}},
   140  			setupResolverMock: func(t *testing.T) (resolver, func()) {
   141  				mock := new(mockResolver)
   142  				cleanup := func() {
   143  					mock.AssertExpectations(t)
   144  				}
   145  
   146  				mock.
   147  					On("Resolve", []*x509.Certificate{testCertificate}).
   148  					Return(nil, testError).
   149  					Once()
   150  
   151  				return mock, cleanup
   152  			},
   153  			expectedError:       "error while fetching certificates into the CA Chain: couldn't resolve certificates chain from the leaf certificate: test-error",
   154  			expectedChainLength: 0,
   155  		},
   156  		"certificates chain prepared properly": {
   157  			chains: [][]*x509.Certificate{{testCertificate}},
   158  			setupResolverMock: func(t *testing.T) (resolver, func()) {
   159  				mock := new(mockResolver)
   160  				cleanup := func() {
   161  					mock.AssertExpectations(t)
   162  				}
   163  
   164  				mock.
   165  					On("Resolve", []*x509.Certificate{testCertificate}).
   166  					Return([]*x509.Certificate{testCertificate, testCACertificate}, nil).
   167  					Once()
   168  
   169  				return mock, cleanup
   170  			},
   171  			expectedChainLength: 2,
   172  		},
   173  	}
   174  
   175  	for tn, tc := range tests {
   176  		t.Run(tn, func(t *testing.T) {
   177  			var err error
   178  
   179  			builder := NewBuilder(logrus.StandardLogger()).(*defaultBuilder)
   180  
   181  			if tc.setupResolverMock != nil {
   182  				resolver, cleanup := tc.setupResolverMock(t)
   183  				defer cleanup()
   184  
   185  				builder.resolver = resolver
   186  			}
   187  
   188  			TLS := new(tls.ConnectionState)
   189  			TLS.VerifiedChains = tc.chains
   190  
   191  			err = builder.BuildChainFromTLSConnectionState(TLS)
   192  
   193  			if tc.expectedError != "" {
   194  				assert.EqualError(t, err, tc.expectedError)
   195  				return
   196  			}
   197  
   198  			assert.NoError(t, err)
   199  			assert.Len(t, builder.certificates, tc.expectedChainLength)
   200  		})
   201  	}
   202  }
   203  
   204  func TestDefaultBuilder_addCertificate(t *testing.T) {
   205  	block, _ := pem.Decode([]byte(testCert))
   206  	testCertificate, err := x509.ParseCertificate(block.Bytes)
   207  	require.NoError(t, err)
   208  
   209  	b := NewBuilder(logrus.StandardLogger()).(*defaultBuilder)
   210  	b.addCertificate(testCertificate)
   211  	b.addCertificate(testCertificate)
   212  
   213  	require.Len(t, b.certificates, 1)
   214  	assert.Equal(t, testCertificate, b.certificates[0])
   215  }
   216  
   217  func TestDefaultBuilder_String(t *testing.T) {
   218  	testError := errors.New("test-error")
   219  
   220  	block, _ := pem.Decode([]byte(testCert))
   221  	testCertificate, err := x509.ParseCertificate(block.Bytes)
   222  	require.NoError(t, err)
   223  
   224  	tests := map[string]struct {
   225  		encodePEMMock        pemEncoder
   226  		expectedOutput       string
   227  		expectedLogToContain []string
   228  	}{
   229  		"encoding error": {
   230  			encodePEMMock: func(out io.Writer, b *pem.Block) error {
   231  				return testError
   232  			},
   233  			expectedOutput: "",
   234  			expectedLogToContain: []string{
   235  				"error=test-error",
   236  				`msg="Failed to encode certificate from chain"`,
   237  			},
   238  		},
   239  		"encoding succeeded": {
   240  			encodePEMMock: func(out io.Writer, b *pem.Block) error {
   241  				assert.Equal(t, pemTypeCertificate, b.Type)
   242  				assert.Equal(t, testCertificate.Raw, b.Bytes)
   243  
   244  				buf := bytes.NewBufferString(testCert)
   245  
   246  				_, err := io.Copy(out, buf)
   247  				require.NoError(t, err)
   248  
   249  				return nil
   250  			},
   251  			expectedOutput: testCert,
   252  		},
   253  	}
   254  
   255  	for tn, tc := range tests {
   256  		t.Run(tn, func(t *testing.T) {
   257  			out := new(bytes.Buffer)
   258  
   259  			logger := logrus.New()
   260  			logger.Out = out
   261  
   262  			b := NewBuilder(logger).(*defaultBuilder)
   263  			b.encodePEM = tc.encodePEMMock
   264  
   265  			b.addCertificate(testCertificate)
   266  			assert.Equal(t, tc.expectedOutput, b.String())
   267  
   268  			output := out.String()
   269  
   270  			if len(tc.expectedLogToContain) < 1 {
   271  				assert.Empty(t, output)
   272  				return
   273  			}
   274  
   275  			for _, part := range tc.expectedLogToContain {
   276  				assert.Contains(t, output, part)
   277  			}
   278  		})
   279  	}
   280  }