Skip to content

Commit 837fc68

Browse files
committed
[FAB-3950] support conc generateCert invocations
usage of gossip/comm/crypto.go:GenerateCertificates is prone to race conditions problems if it's used with the same parameters for file names. The method is used only in test code, but the tests run concurrently. Because the function that invokes GenerateCertificates usually uses the same file name as parameter and adds defer os.Remove() on the file names, and as a result - if it's used concurrently you may have a race condition in which one goroutine deletes the files that the other goroutine uses to read from. I changed the code to do both generation, removal and loading in the same method. The generation uses a random file name so concurrent invocations can now be used. Change-Id: I6742b4ca0347bcc868f3df5a429c34bd8098d505 Signed-off-by: Yacov Manevich <[email protected]>
1 parent eaf7f4d commit 837fc68

File tree

5 files changed

+47
-92
lines changed

5 files changed

+47
-92
lines changed

gossip/comm/comm_impl.go

+12-32
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"errors"
2323
"fmt"
2424
"net"
25-
"os"
2625
"reflect"
2726
"sync"
2827
"sync/atomic"
@@ -620,39 +619,20 @@ func createGRPCLayer(port int) (*grpc.Server, net.Listener, api.PeerSecureDialOp
620619
var serverOpts []grpc.ServerOption
621620
var dialOpts []grpc.DialOption
622621

623-
keyFileName := fmt.Sprintf("key.%d.pem", util.RandomUInt64())
624-
certFileName := fmt.Sprintf("cert.%d.pem", util.RandomUInt64())
622+
cert := GenerateCertificatesOrPanic()
623+
returnedCertHash = certHashFromRawCert(cert.Certificate[0])
625624

626-
defer os.Remove(keyFileName)
627-
defer os.Remove(certFileName)
628-
629-
err = GenerateCertificates(keyFileName, certFileName)
630-
if err == nil {
631-
cert, err := tls.LoadX509KeyPair(certFileName, keyFileName)
632-
if err != nil {
633-
panic(err)
634-
}
635-
636-
if len(cert.Certificate) == 0 {
637-
panic(errors.New("Certificate chain is nil"))
638-
}
639-
640-
returnedCertHash = certHashFromRawCert(cert.Certificate[0])
641-
642-
tlsConf := &tls.Config{
643-
Certificates: []tls.Certificate{cert},
644-
ClientAuth: tls.RequestClientCert,
645-
InsecureSkipVerify: true,
646-
}
647-
serverOpts = append(serverOpts, grpc.Creds(credentials.NewTLS(tlsConf)))
648-
ta := credentials.NewTLS(&tls.Config{
649-
Certificates: []tls.Certificate{cert},
650-
InsecureSkipVerify: true,
651-
})
652-
dialOpts = append(dialOpts, grpc.WithTransportCredentials(&authCreds{tlsCreds: ta}))
653-
} else {
654-
dialOpts = append(dialOpts, grpc.WithInsecure())
625+
tlsConf := &tls.Config{
626+
Certificates: []tls.Certificate{cert},
627+
ClientAuth: tls.RequestClientCert,
628+
InsecureSkipVerify: true,
655629
}
630+
serverOpts = append(serverOpts, grpc.Creds(credentials.NewTLS(tlsConf)))
631+
ta := credentials.NewTLS(&tls.Config{
632+
Certificates: []tls.Certificate{cert},
633+
InsecureSkipVerify: true,
634+
})
635+
dialOpts = append(dialOpts, grpc.WithTransportCredentials(&authCreds{tlsCreds: ta}))
656636

657637
listenAddress := fmt.Sprintf("%s:%d", "", port)
658638
ll, err = net.Listen("tcp", listenAddress)

gossip/comm/comm_test.go

+6-25
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,7 @@ func newCommInstance(port int, sec api.MessageCryptoService) (Comm, error) {
111111

112112
func handshaker(endpoint string, comm Comm, t *testing.T, sigMutator func([]byte) []byte, pkiIDmutator func([]byte) []byte, mutualTLS bool) <-chan proto.ReceivedMessage {
113113
c := &commImpl{}
114-
err := GenerateCertificates("key.pem", "cert.pem")
115-
assert.NoError(t, err, "%v", err)
116-
defer os.Remove("cert.pem")
117-
defer os.Remove("key.pem")
118-
cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
119-
assert.NoError(t, err, "%v", err)
114+
cert := GenerateCertificatesOrPanic()
120115
tlsCfg := &tls.Config{
121116
InsecureSkipVerify: true,
122117
}
@@ -286,28 +281,19 @@ func TestBasic(t *testing.T) {
286281

287282
func TestProdConstructor(t *testing.T) {
288283
t.Parallel()
289-
keyFileName := fmt.Sprintf("key.%d.pem", util.RandomUInt64())
290-
certFileName := fmt.Sprintf("cert.%d.pem", util.RandomUInt64())
291-
292-
GenerateCertificates(keyFileName, certFileName)
293-
cert, _ := tls.LoadX509KeyPair(certFileName, keyFileName)
294-
os.Remove(keyFileName)
295-
os.Remove(certFileName)
284+
peerIdentity := GenerateCertificatesOrPanic()
296285
srv, lsnr, dialOpts, certHash := createGRPCLayer(20000)
297286
defer srv.Stop()
298287
defer lsnr.Close()
299-
comm1, _ := NewCommInstance(srv, &cert, identity.NewIdentityMapper(naiveSec), []byte("localhost:20000"), dialOpts)
288+
comm1, _ := NewCommInstance(srv, &peerIdentity, identity.NewIdentityMapper(naiveSec), []byte("localhost:20000"), dialOpts)
300289
comm1.(*commImpl).selfCertHash = certHash
301290
go srv.Serve(lsnr)
302291

303-
GenerateCertificates(keyFileName, certFileName)
304-
cert, _ = tls.LoadX509KeyPair(certFileName, keyFileName)
305-
os.Remove(keyFileName)
306-
os.Remove(certFileName)
292+
peerIdentity = GenerateCertificatesOrPanic()
307293
srv, lsnr, dialOpts, certHash = createGRPCLayer(30000)
308294
defer srv.Stop()
309295
defer lsnr.Close()
310-
comm2, _ := NewCommInstance(srv, &cert, identity.NewIdentityMapper(naiveSec), []byte("localhost:30000"), dialOpts)
296+
comm2, _ := NewCommInstance(srv, &peerIdentity, identity.NewIdentityMapper(naiveSec), []byte("localhost:30000"), dialOpts)
311297
comm2.(*commImpl).selfCertHash = certHash
312298
go srv.Serve(lsnr)
313299
defer comm1.Stop()
@@ -350,12 +336,7 @@ func TestCloseConn(t *testing.T) {
350336
defer comm1.Stop()
351337
acceptChan := comm1.Accept(acceptAll)
352338

353-
err := GenerateCertificates("key.pem", "cert.pem")
354-
assert.NoError(t, err, "%v", err)
355-
defer os.Remove("cert.pem")
356-
defer os.Remove("key.pem")
357-
cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
358-
assert.NoError(t, err, "%v", err)
339+
cert := GenerateCertificatesOrPanic()
359340
tlsCfg := &tls.Config{
360341
InsecureSkipVerify: true,
361342
Certificates: []tls.Certificate{cert},

gossip/comm/crypto.go

+25-7
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,15 @@ import (
2323
"crypto/tls"
2424
"crypto/x509"
2525
"encoding/pem"
26+
"errors"
27+
"fmt"
2628
"math/big"
2729
"net"
2830
"os"
2931
"time"
3032

3133
"github.com/hyperledger/fabric/common/util"
34+
gutil "github.com/hyperledger/fabric/gossip/util"
3235
"golang.org/x/net/context"
3336
"google.golang.org/grpc/credentials"
3437
"google.golang.org/grpc/peer"
@@ -43,15 +46,20 @@ func writeFile(filename string, keyType string, data []byte) error {
4346
return pem.Encode(f, &pem.Block{Type: keyType, Bytes: data})
4447
}
4548

46-
func GenerateCertificates(privKeyFile string, certKeyFile string) error {
49+
func GenerateCertificatesOrPanic() tls.Certificate {
50+
privKeyFile := fmt.Sprintf("key.%d.priv", gutil.RandomUInt64())
51+
certKeyFile := fmt.Sprintf("cert.%d.pub", gutil.RandomUInt64())
52+
53+
defer os.Remove(privKeyFile)
54+
defer os.Remove(certKeyFile)
4755
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
4856
if err != nil {
49-
return err
57+
panic(err)
5058
}
5159

5260
sn, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
5361
if err != nil {
54-
return err
62+
panic(err)
5563
}
5664
template := x509.Certificate{
5765
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
@@ -60,18 +68,28 @@ func GenerateCertificates(privKeyFile string, certKeyFile string) error {
6068
}
6169
rawBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
6270
if err != nil {
63-
return err
71+
panic(err)
6472
}
6573
err = writeFile(certKeyFile, "CERTIFICATE", rawBytes)
6674
if err != nil {
67-
return err
75+
panic(err)
6876
}
6977
privBytes, err := x509.MarshalECPrivateKey(privateKey)
7078
if err != nil {
71-
return err
79+
panic(err)
7280
}
7381
err = writeFile(privKeyFile, "EC PRIVATE KEY", privBytes)
74-
return err
82+
if err != nil {
83+
panic(err)
84+
}
85+
cert, err := tls.LoadX509KeyPair(certKeyFile, privKeyFile)
86+
if err != nil {
87+
panic(err)
88+
}
89+
if len(cert.Certificate) == 0 {
90+
panic(errors.New("Certificate chain is empty"))
91+
}
92+
return cert
7593
}
7694

7795
func certHashFromRawCert(rawCert []byte) []byte {

gossip/comm/crypto_test.go

+3-15
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"crypto/tls"
2121
"fmt"
2222
"net"
23-
"os"
2423
"sync"
2524
"testing"
2625
"time"
@@ -84,27 +83,16 @@ func (s *gossipTestServer) Ping(context.Context, *proto.Empty) (*proto.Empty, er
8483
}
8584

8685
func TestCertificateExtraction(t *testing.T) {
87-
err := GenerateCertificates("key.pem", "cert.pem")
88-
defer os.Remove("cert.pem")
89-
defer os.Remove("key.pem")
90-
assert.NoError(t, err, "%v", err)
91-
serverCert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
92-
assert.NoError(t, err, "%v", err)
93-
94-
srv := createTestServer(t, &serverCert)
86+
cert := GenerateCertificatesOrPanic()
87+
srv := createTestServer(t, &cert)
9588
defer srv.stop()
9689

97-
GenerateCertificates("key2.pem", "cert2.pem")
98-
defer os.Remove("cert2.pem")
99-
defer os.Remove("key2.pem")
100-
clientCert, err := tls.LoadX509KeyPair("cert2.pem", "key2.pem")
90+
clientCert := GenerateCertificatesOrPanic()
10191
clientCertHash := certHashFromRawCert(clientCert.Certificate[0])
102-
assert.NoError(t, err)
10392
ta := credentials.NewTLS(&tls.Config{
10493
Certificates: []tls.Certificate{clientCert},
10594
InsecureSkipVerify: true,
10695
})
107-
assert.NoError(t, err, "%v", err)
10896
ac := &authCreds{tlsCreds: ta}
10997
assert.Equal(t, "1.2", ac.Info().SecurityVersion)
11098
assert.Equal(t, "tls", ac.Info().SecurityProtocol)

gossip/gossip/anchor_test.go

+1-13
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"fmt"
2222
"io"
2323
"net"
24-
"os"
2524
"sync"
2625
"sync/atomic"
2726
"testing"
@@ -107,18 +106,7 @@ func newPeerMock(port int, expectedMsgs2Receive int, t *testing.T, msgAssertions
107106
}
108107

109108
func newGRPCServerWithTLS() (*grpc.Server, []byte) {
110-
// TODO: For this change set I simply exported the function, but I think that we should
111-
// Instead have a function that returns a TLS gRPC server from the comm package instead,
112-
// and then we could remove the boilerplate code below both from this file and from the
113-
// comm test code.
114-
// However, it needs to be attended in a subsequent change set rather than this.
115-
comm.GenerateCertificates("key.pem", "cert.pem")
116-
defer os.Remove("cert.pem")
117-
defer os.Remove("key.pem")
118-
cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
119-
if err != nil {
120-
panic(err)
121-
}
109+
cert := comm.GenerateCertificatesOrPanic()
122110
tlsConf := &tls.Config{
123111
Certificates: []tls.Certificate{cert},
124112
ClientAuth: tls.RequestClientCert,

0 commit comments

Comments
 (0)