Skip to content

Commit

Permalink
generate TLS test certs with SAN for go1.15 (#325)
Browse files Browse the repository at this point in the history
* generate TLS test certs with SAN for go1.15

Fixes #324. The certificates need to have SAN fields
instead of relying on the CommonName due to
X.509 CommonName deprecation in go1.15.
The certificate is generated on the fly and cached in
github.com/grpc-ecosystem/go-grpc-middleware/testing/testcert
utility package.

Reference: https://golang.org/doc/go1.15#commonname

* move test cert generation to interceptor_suite.go
  • Loading branch information
dmitris authored Sep 8, 2020
1 parent 7a5efaa commit a0dd2b9
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 133 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ go:
- 1.11.x
- 1.12.x
- 1.13.x
- 1.14.x
- 1.15.x

env:
global:
Expand Down
12 changes: 0 additions & 12 deletions testing/certs/gen_cert.sh

This file was deleted.

24 changes: 0 additions & 24 deletions testing/certs/localhost.crt

This file was deleted.

54 changes: 0 additions & 54 deletions testing/certs/localhost.go

This file was deleted.

27 changes: 0 additions & 27 deletions testing/certs/localhost.key

This file was deleted.

86 changes: 70 additions & 16 deletions testing/interceptor_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ package grpc_testing

import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"flag"
"math/big"
"net"
"path"
"runtime"
"time"

"github.com/grpc-ecosystem/go-grpc-middleware/testing/certs"
pb_testproto "github.com/grpc-ecosystem/go-grpc-middleware/testing/testproto"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
Expand All @@ -22,12 +25,10 @@ import (

var (
flagTls = flag.Bool("use_tls", true, "whether all gRPC middleware tests should use tls")
)

func getTestingCertsPath() string {
_, callerPath, _, _ := runtime.Caller(0)
return path.Join(path.Dir(callerPath), "certs")
}
certPEM []byte
keyPEM []byte
)

// InterceptorTestSuite is a testify/Suite that starts a gRPC PingService server and a client.
type InterceptorTestSuite struct {
Expand All @@ -52,22 +53,31 @@ func (s *InterceptorTestSuite) SetupSuite() {
s.serverRunning = make(chan bool)

s.serverAddr = "127.0.0.1:0"

var err error
certPEM, keyPEM, err = generateCertAndKey([]string{"localhost", "example.com"})
if err != nil {
s.T().Fatalf("unable to generate test certificate/key: " + err.Error())
}
go func() {
for {
var err error
s.ServerListener, err = net.Listen("tcp", s.serverAddr)
if err != nil {
s.T().Fatalf("unable to listen on address %s: %v", s.serverAddr, err)
}
s.serverAddr = s.ServerListener.Addr().String()
require.NoError(s.T(), err, "must be able to allocate a port for serverListener")
if *flagTls {
localhostCert, err := tls.X509KeyPair(certs.LocalhostCert, certs.LocalhostKey)
require.NoError(s.T(), err, "failed loading server credentials for localhostCert")
creds := credentials.NewServerTLSFromCert(&localhostCert)
cert, err := tls.X509KeyPair(certPEM, keyPEM)
if err != nil {
s.T().Fatalf("unable to load test TLS certificate: %v", err)
}
creds := credentials.NewServerTLSFromCert(&cert)
s.ServerOpts = append(s.ServerOpts, grpc.Creds(creds))
}
// This is the point where we hook up the interceptor
s.Server = grpc.NewServer(s.ServerOpts...)
// Crete a service of the instantiator hasn't provided one.
// Create a service of the instantiator hasn't provided one.
if s.TestService == nil {
s.TestService = &TestPingService{T: s.T()}
}
Expand Down Expand Up @@ -104,9 +114,11 @@ func (s *InterceptorTestSuite) RestartServer(delayedStart time.Duration) <-chan
func (s *InterceptorTestSuite) NewClient(dialOpts ...grpc.DialOption) pb_testproto.TestServiceClient {
newDialOpts := append(dialOpts, grpc.WithBlock())
if *flagTls {
creds, err := credentials.NewClientTLSFromFile(
path.Join(getTestingCertsPath(), "localhost.crt"), "localhost")
require.NoError(s.T(), err, "failed reading client credentials for localhost.crt")
cp := x509.NewCertPool()
if !cp.AppendCertsFromPEM(certPEM) {
s.T().Fatal("failed to append certificate")
}
creds := credentials.NewTLS(&tls.Config{ServerName: "localhost", RootCAs: cp})
newDialOpts = append(newDialOpts, grpc.WithTransportCredentials(creds))
} else {
newDialOpts = append(newDialOpts, grpc.WithInsecure())
Expand Down Expand Up @@ -143,3 +155,45 @@ func (s *InterceptorTestSuite) TearDownSuite() {
s.clientConn.Close()
}
}

// generateCertAndKey copied from https:/johanbrandhorst/certify/blob/master/issuers/vault/vault_suite_test.go#L255
// with minor modifications.
func generateCertAndKey(san []string) ([]byte, []byte, error) {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, err
}
notBefore := time.Now()
notAfter := notBefore.Add(time.Hour)
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return nil, nil, err
}
template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: "Certify Test Cert",
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
DNSNames: san,
}
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, priv.Public(), priv)
if err != nil {
return nil, nil, err
}
certOut := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: derBytes,
})
keyOut := pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(priv),
})

return certOut, keyOut, nil
}

0 comments on commit a0dd2b9

Please sign in to comment.