@@ -2,10 +2,18 @@ package grpc
22
33import (
44 "context"
5+ "crypto/ecdsa"
6+ "crypto/elliptic"
7+ "crypto/rand"
58 "crypto/tls"
9+ "crypto/x509"
10+ "crypto/x509/pkix"
11+ "encoding/pem"
612 "errors"
13+ "math/big"
714 "net"
815 "testing"
16+ "time"
917
1018 "github.com/stretchr/testify/assert"
1119 "github.com/stretchr/testify/mock"
@@ -209,3 +217,131 @@ func TestRPCs(t *testing.T) {
209217 })
210218 }
211219}
220+
221+ func TestMTLS (t * testing.T ) {
222+ var (
223+ qclient = & qmock.QueryClient {}
224+ com = testutil .CertificateOptionMocks (qclient )
225+ cod = testutil .CertificateOptionDomains ([]string {"localhost" , "127.0.0.1" })
226+ )
227+
228+ crt := testutil .Certificate (t , testutil .AccAddress (t ), com , cod )
229+
230+ qclient .EXPECT ().Certificates (mock .Anything , mock .Anything ).Return (& types.QueryCertificatesResponse {
231+ Certificates : types.CertificatesResponse {
232+ types.CertificateResponse {
233+ Certificate : types.Certificate {
234+ State : types .CertificateValid ,
235+ Cert : crt .PEM .Cert ,
236+ Pubkey : crt .PEM .Pub ,
237+ },
238+ Serial : crt .Serial .String (),
239+ },
240+ },
241+ }, nil )
242+
243+ cases := []struct {
244+ desc string
245+ cert func (* testing.T ) tls.Certificate
246+ errContains string
247+ }{
248+ {
249+ desc : "good cert" ,
250+ cert : func (* testing.T ) tls.Certificate {
251+ return testutil .Certificate (t , testutil .AccAddress (t ), com , cod ).Cert [0 ]
252+ },
253+ },
254+ {
255+ desc : "empty chain" ,
256+ cert : func (* testing.T ) tls.Certificate {
257+ return tls.Certificate {}
258+ },
259+ errContains : "empty chain" ,
260+ },
261+ {
262+ desc : "invalid subject" ,
263+ cert : func (t * testing.T ) tls.Certificate {
264+ t .Helper ()
265+
266+ priv , err := ecdsa .GenerateKey (elliptic .P256 (), rand .Reader )
267+ require .NoError (t , err )
268+
269+ template := x509.Certificate {
270+ SerialNumber : new (big.Int ).SetInt64 (time .Now ().UTC ().UnixNano ()),
271+ Subject : pkix.Name {
272+ CommonName : "badcert" ,
273+ },
274+ BasicConstraintsValid : true ,
275+ }
276+
277+ certDer , err := x509 .CreateCertificate (rand .Reader , & template , & template , priv .Public (), priv )
278+ require .NoError (t , err )
279+
280+ keyDer , err := x509 .MarshalPKCS8PrivateKey (priv )
281+ require .NoError (t , err )
282+
283+ certBytes := pem .EncodeToMemory (& pem.Block {
284+ Type : types .PemBlkTypeCertificate ,
285+ Bytes : certDer ,
286+ })
287+ privBytes := pem .EncodeToMemory (& pem.Block {
288+ Type : types .PemBlkTypeECPrivateKey ,
289+ Bytes : keyDer ,
290+ })
291+
292+ cert , err := tls .X509KeyPair (certBytes , privBytes )
293+ require .NoError (t , err )
294+
295+ return cert
296+ },
297+ errContains : "invalid certificate's subject" ,
298+ },
299+ }
300+
301+ for _ , c := range cases {
302+ c := c
303+
304+ t .Run (c .desc , func (t * testing.T ) {
305+ ctx , cancel := context .WithCancel (context .Background ())
306+ defer cancel ()
307+
308+ ctx = ContextWithQueryClient (ctx , qclient )
309+
310+ var (
311+ m mocks.Client
312+ mc mmocks.Client
313+ )
314+
315+ mc .EXPECT ().Submit (mock .Anything , mock .Anything , mock .Anything ).Return (nil )
316+ m .EXPECT ().Manifest ().Return (& mc )
317+
318+ s := newServer (ctx , crt .Cert , & m )
319+ defer s .Stop ()
320+
321+ l , err := net .Listen ("tcp" , ":0" )
322+ require .NoError (t , err )
323+
324+ go func () {
325+ require .NoError (t , s .Serve (l ))
326+ }()
327+
328+ tlsConfig := tls.Config {
329+ InsecureSkipVerify : true ,
330+ Certificates : []tls.Certificate {c .cert (t )},
331+ }
332+
333+ conn , err := grpc .DialContext (ctx , l .Addr ().String (),
334+ grpc .WithTransportCredentials (credentials .NewTLS (& tlsConfig )))
335+ require .NoError (t , err )
336+
337+ defer conn .Close ()
338+
339+ _ , err = leasev1 .NewLeaseRPCClient (conn ).SendManifest (ctx , & leasev1.SendManifestRequest {})
340+ if c .errContains != "" {
341+ assert .ErrorContains (t , err , c .errContains )
342+ } else {
343+ assert .NoError (t , err )
344+ }
345+ })
346+ }
347+ }
0 commit comments