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 }