Skip to content

Commit

Permalink
Add gossip integration test for renewing expired cert
Browse files Browse the repository at this point in the history
-Test renew cert before expiration (passes)
-Test renew cert after expiration (previously failed)

Signed-off-by: David Enyeart <[email protected]>
  • Loading branch information
denyeart committed Jan 20, 2025
1 parent a4b9db2 commit 8fb837e
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 0 deletions.
126 changes: 126 additions & 0 deletions integration/gossip/gossip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
package gossip

import (
"crypto/ecdsa"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
"path/filepath"
Expand Down Expand Up @@ -285,8 +289,130 @@ var _ = Describe("Gossip State Transfer and Membership", func() {
assertPeerMembershipUpdate(network, peer1Org1, []*nwo.Peer{peer0Org2, peer1Org2}, nwprocs, expectedMsgFromExpirationCallback)
})
})

It("updates membership for a peer with a renewed certificate", func() {
network.Bootstrap()
orderer := network.Orderer("orderer")
nwprocs.ordererRunner = network.OrdererRunner(orderer)
nwprocs.ordererProcess = ifrit.Invoke(nwprocs.ordererRunner)
Eventually(nwprocs.ordererProcess.Ready(), network.EventuallyTimeout).Should(BeClosed())

peer0Org1 := network.Peer("Org1", "peer0")
peer0Org2 := network.Peer("Org2", "peer0")

By("bringing up a peer in each organization")
startPeers(nwprocs, false, peer0Org1, peer0Org2)

channelparticipation.JoinOrdererAppChannel(network, "testchannel", orderer, nwprocs.ordererRunner)

By("joining peers to channel")
network.JoinChannel(channelName, orderer, peer0Org1, peer0Org2)

By("verifying membership of both peers")
bothPeers := []*nwo.Peer{peer0Org1, peer0Org2}
network.VerifyMembership(bothPeers, channelName)

fmt.Println("===MEMBERSHIP VERIFIED===")
time.Sleep(5 * time.Second)

By("stopping, renewing peer0Org2 certificate before expiration, and restarting")
stopPeers(nwprocs, peer0Org2)
renewPeerCertificate(network, peer0Org2, time.Now().Add(time.Minute))

fmt.Println("===STOPPED AND RENEWED peer0Org2, RESTARTING, CHECK FOR PKI-ID REPLACEMENT===")
time.Sleep(5 * time.Second)

startPeers(nwprocs, false, peer0Org2)

By("ensuring that peer0Org1 replaces peer0Org2 PKI-ID")
peer0Org1Runner := nwprocs.peerRunners[peer0Org1.ID()]
Eventually(peer0Org1Runner.Err(), network.EventuallyTimeout).Should(gbytes.Say("changed its PKI-ID from"))

fmt.Println("===PKI-ID REPLACED, WAIT FOR MEMBERSHIP===")
time.Sleep(5 * time.Second)

By("verifying membership after cert renewed")
network.VerifyMembership(bothPeers, channelName)

fmt.Println("===MEMBERSHIP VERIFIED WITH RENEWED CERT, WAIT FOR CERT TO EXPIRE AND THEN RENEW AGAIN ===")
time.Sleep(5 * time.Second)

By("waiting for cert to expire within a minute")
Eventually(peer0Org1Runner.Err(), network.EventuallyTimeout).Should(gbytes.Say("gossipping peer identity expired"))

By("stopping, renewing peer0Org2 certificate again after its expiration, restarting")
stopPeers(nwprocs, peer0Org2)
renewPeerCertificate(network, peer0Org2, time.Now().Add(time.Hour))

fmt.Println("===STOPPED AND RENEWED peer0Org2 AGAIN AFTER EXPIRATION, RESTARTING, CHECK FOR PKI-ID REPLACEMENT AGAIN===")
time.Sleep(5 * time.Second)

startPeers(nwprocs, false, peer0Org2)

By("ensuring that peer0Org1 replaces peer0Org2 PKI-ID after it expired")
Eventually(peer0Org1Runner.Err(), network.EventuallyTimeout).Should(gbytes.Say("changed its PKI-ID from"))
})
})

// renewPeerCertificate renews the certificate with a given expirationTime and re-writes it to the peer's signcert directory
func renewPeerCertificate(network *nwo.Network, peer *nwo.Peer, expirationTime time.Time) {
peerDomain := network.Organization(peer.Organization).Domain

peerCAKeyPath := filepath.Join(network.RootDir, "crypto", "peerOrganizations", peerDomain, "ca", "priv_sk")
peerCAKey, err := os.ReadFile(peerCAKeyPath)
Expect(err).NotTo(HaveOccurred())

peerCACertPath := filepath.Join(network.RootDir, "crypto", "peerOrganizations", peerDomain, "ca", fmt.Sprintf("ca.%s-cert.pem", peerDomain))
peerCACert, err := os.ReadFile(peerCACertPath)
Expect(err).NotTo(HaveOccurred())

peerCertPath := filepath.Join(network.PeerLocalMSPDir(peer), "signcerts", fmt.Sprintf("peer0.%s-cert.pem", peerDomain))
peerCert, err := os.ReadFile(peerCertPath)
Expect(err).NotTo(HaveOccurred())

renewedCert, _ := expireCertificate(peerCert, peerCACert, peerCAKey, expirationTime)
err = os.WriteFile(peerCertPath, renewedCert, 0o600)
fmt.Println(peerCertPath)
Expect(err).NotTo(HaveOccurred())
}

// expireCertificate re-creates and re-signs a certificate with a new expirationTime
func expireCertificate(certPEM, caCertPEM, caKeyPEM []byte, expirationTime time.Time) (expiredcertPEM []byte, earlyMadeCACertPEM []byte) {
keyAsDER, _ := pem.Decode(caKeyPEM)
caKeyWithoutType, err := x509.ParsePKCS8PrivateKey(keyAsDER.Bytes)
Expect(err).NotTo(HaveOccurred())
caKey := caKeyWithoutType.(*ecdsa.PrivateKey)

caCertAsDER, _ := pem.Decode(caCertPEM)
caCert, err := x509.ParseCertificate(caCertAsDER.Bytes)
Expect(err).NotTo(HaveOccurred())

certAsDER, _ := pem.Decode(certPEM)
cert, err := x509.ParseCertificate(certAsDER.Bytes)
Expect(err).NotTo(HaveOccurred())

cert.Raw = nil
caCert.Raw = nil
// The certificate was made 1 minute ago (1 hour doesn't work since cert will be before original CA cert NotBefore time)
cert.NotBefore = time.Now().Add((-1) * time.Minute)
// As well as the CA certificate
caCert.NotBefore = time.Now().Add((-1) * time.Minute)
// The certificate expires now
cert.NotAfter = expirationTime

// The CA signs the certificate
certBytes, err := x509.CreateCertificate(rand.Reader, cert, caCert, cert.PublicKey, caKey)
Expect(err).NotTo(HaveOccurred())

// The CA signs its own certificate
caCertBytes, err := x509.CreateCertificate(rand.Reader, caCert, caCert, caCert.PublicKey, caKey)
Expect(err).NotTo(HaveOccurred())

expiredcertPEM = pem.EncodeToMemory(&pem.Block{Bytes: certBytes, Type: "CERTIFICATE"})
earlyMadeCACertPEM = pem.EncodeToMemory(&pem.Block{Bytes: caCertBytes, Type: "CERTIFICATE"})
return
}

func runTransactions(n *nwo.Network, orderer *nwo.Orderer, peer *nwo.Peer, chaincodeName string, channelID string) {
for i := 0; i < 5; i++ {
sess, err := n.PeerUserSession(peer, "User1", commands.ChaincodeInvoke{
Expand Down
1 change: 1 addition & 0 deletions integration/nwo/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -1211,6 +1211,7 @@ func (n *Network) ConfigTxGen(command Command) (*gexec.Session, error) {
func (n *Network) Discover(command Command) (*gexec.Session, error) {
cmd := NewCommand(n.Components.Discover(), command)
cmd.Args = append(cmd.Args, "--peerTLSCA", n.CACertsBundlePath())
cmd.Env = []string{"FABRIC_LOGGING_SPEC=info:grpc=warn"} // suppress chatty grpc info messages
return n.StartSession(cmd, command.SessionName())
}

Expand Down

0 comments on commit 8fb837e

Please sign in to comment.