Skip to content

Commit

Permalink
[FAB-18188] Log orderer and peer cert expiration date upon startup (h…
Browse files Browse the repository at this point in the history
…yperledger#1804)

FAB-17000 added a warning if expiration is within a week.

This change additionally info logs the future enrollment, tls server, and tls client
cert expiration dates upon startup, regardless of whether they will expire within a week or not.

Signed-off-by: David Enyeart <enyeart@us.ibm.com>
  • Loading branch information
denyeart committed Aug 26, 2020
1 parent f6a7dc3 commit c8ec1e0
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 42 deletions.
17 changes: 10 additions & 7 deletions common/crypto/expiration.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,35 +39,35 @@ func certExpirationTime(pemBytes []byte) time.Time {
return cert.NotAfter
}

// WarnFunc notifies a warning happened with the given format, and can be replaced with Warnf of a logger.
type WarnFunc func(format string, args ...interface{})
// MessageFunc notifies a message happened with the given format, and can be replaced with Warnf or Infof of a logger.
type MessageFunc func(format string, args ...interface{})

// Scheduler invokes f after d time, and can be replaced with time.AfterFunc.
type Scheduler func(d time.Duration, f func()) *time.Timer

// TrackExpiration warns a week before one of the certificates expires
func TrackExpiration(tls bool, serverCert []byte, clientCertChain [][]byte, sIDBytes []byte, warn WarnFunc, now time.Time, s Scheduler) {
func TrackExpiration(tls bool, serverCert []byte, clientCertChain [][]byte, sIDBytes []byte, info MessageFunc, warn MessageFunc, now time.Time, s Scheduler) {
sID := &msp.SerializedIdentity{}
if err := proto.Unmarshal(sIDBytes, sID); err != nil {
return
}

trackCertExpiration(sID.IdBytes, "enrollment", warn, now, s)
trackCertExpiration(sID.IdBytes, "enrollment", info, warn, now, s)

if !tls {
return
}

trackCertExpiration(serverCert, "server TLS", warn, now, s)
trackCertExpiration(serverCert, "server TLS", info, warn, now, s)

if len(clientCertChain) == 0 || len(clientCertChain[0]) == 0 {
return
}

trackCertExpiration(clientCertChain[0], "client TLS", warn, now, s)
trackCertExpiration(clientCertChain[0], "client TLS", info, warn, now, s)
}

func trackCertExpiration(rawCert []byte, certRole string, warn WarnFunc, now time.Time, sched Scheduler) {
func trackCertExpiration(rawCert []byte, certRole string, info MessageFunc, warn MessageFunc, now time.Time, sched Scheduler) {
expirationTime := certExpirationTime(rawCert)
if expirationTime.IsZero() {
// If the certificate expiration time cannot be classified, return.
Expand All @@ -82,6 +82,8 @@ func trackCertExpiration(rawCert []byte, certRole string, warn WarnFunc, now tim
return
}

info("The %s certificate will expire on %s", certRole, expirationTime)

if timeLeftUntilExpiration < oneWeek {
days := timeLeftUntilExpiration / (time.Hour * 24)
hours := (timeLeftUntilExpiration - (days * time.Hour * 24)) / time.Hour
Expand All @@ -94,4 +96,5 @@ func trackCertExpiration(rawCert []byte, certRole string, warn WarnFunc, now tim
sched(timeLeftUntilOneWeekBeforeExpiration, func() {
warn("The %s certificate will expire within one week", certRole)
})

}
97 changes: 62 additions & 35 deletions common/crypto/expiration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"testing"
"time"

Expand All @@ -18,6 +19,7 @@ import (
"github.com/hyperledger/fabric/common/crypto/tlsgen"
"github.com/hyperledger/fabric/protoutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestX509CertExpiresAt(t *testing.T) {
Expand Down Expand Up @@ -87,73 +89,90 @@ func TestTrackExpiration(t *testing.T) {
IdBytes: tlsCert.Cert,
})

shouldNotBeInvoked := func(format string, args ...interface{}) {
warnShouldNotBeInvoked := func(format string, args ...interface{}) {
t.Fatalf(format, args...)
}

var formattedWarning string
shouldBeInvoked := func(format string, args ...interface{}) {
warnShouldBeInvoked := func(format string, args ...interface{}) {
formattedWarning = fmt.Sprintf(format, args...)
}

var formattedInfo string
infoShouldBeInvoked := func(format string, args ...interface{}) {
formattedInfo = fmt.Sprintf(format, args...)
}

for _, testCase := range []struct {
description string
tls bool
serverCert []byte
clientCertChain [][]byte
sIDBytes []byte
warn WarnFunc
now time.Time
expectedWarn string
description string
tls bool
serverCert []byte
clientCertChain [][]byte
sIDBytes []byte
info MessageFunc
warn MessageFunc
now time.Time
expectedInfoPrefix string
expectedWarn string
}{
{
description: "No TLS, enrollment cert isn't valid logs a warning",
warn: shouldNotBeInvoked,
warn: warnShouldNotBeInvoked,
sIDBytes: []byte{1, 2, 3},
},
{
description: "No TLS, enrollment cert expires soon",
sIDBytes: signingIdentity,
warn: shouldBeInvoked,
now: monthBeforeExpiration,
expectedWarn: "The enrollment certificate will expire within one week",
description: "No TLS, enrollment cert expires soon",
sIDBytes: signingIdentity,
info: infoShouldBeInvoked,
warn: warnShouldBeInvoked,
now: monthBeforeExpiration,
expectedInfoPrefix: "The enrollment certificate will expire on",
expectedWarn: "The enrollment certificate will expire within one week",
},
{
description: "TLS, server cert expires soon",
warn: shouldBeInvoked,
now: monthBeforeExpiration,
tls: true,
serverCert: tlsCert.Cert,
expectedWarn: "The server TLS certificate will expire within one week",
description: "TLS, server cert expires soon",
info: infoShouldBeInvoked,
warn: warnShouldBeInvoked,
now: monthBeforeExpiration,
tls: true,
serverCert: tlsCert.Cert,
expectedInfoPrefix: "The server TLS certificate will expire on",
expectedWarn: "The server TLS certificate will expire within one week",
},
{
description: "TLS, server cert expires really soon",
warn: shouldBeInvoked,
now: twoDaysBeforeExpiration,
tls: true,
serverCert: tlsCert.Cert,
expectedWarn: "The server TLS certificate expires within 2 days and 12 hours",
description: "TLS, server cert expires really soon",
info: infoShouldBeInvoked,
warn: warnShouldBeInvoked,
now: twoDaysBeforeExpiration,
tls: true,
serverCert: tlsCert.Cert,
expectedInfoPrefix: "The server TLS certificate will expire on",
expectedWarn: "The server TLS certificate expires within 2 days and 12 hours",
},
{
description: "TLS, server cert has expired",
warn: shouldBeInvoked,
info: infoShouldBeInvoked,
warn: warnShouldBeInvoked,
now: expirationTime.Add(time.Hour),
tls: true,
serverCert: tlsCert.Cert,
expectedWarn: "The server TLS certificate has expired",
},
{
description: "TLS, client cert expires soon",
warn: shouldBeInvoked,
now: monthBeforeExpiration,
tls: true,
clientCertChain: [][]byte{tlsCert.Cert},
expectedWarn: "The client TLS certificate will expire within one week",
description: "TLS, client cert expires soon",
info: infoShouldBeInvoked,
warn: warnShouldBeInvoked,
now: monthBeforeExpiration,
tls: true,
clientCertChain: [][]byte{tlsCert.Cert},
expectedInfoPrefix: "The client TLS certificate will expire on",
expectedWarn: "The client TLS certificate will expire within one week",
},
} {
t.Run(testCase.description, func(t *testing.T) {
defer func() {
formattedWarning = ""
formattedInfo = ""
}()

fakeTimeAfter := func(duration time.Duration, f func()) *time.Timer {
Expand All @@ -168,15 +187,23 @@ func TestTrackExpiration(t *testing.T) {
testCase.serverCert,
testCase.clientCertChain,
testCase.sIDBytes,
testCase.info,
testCase.warn,
testCase.now,
fakeTimeAfter)

if testCase.expectedInfoPrefix != "" {
require.True(t, strings.HasPrefix(formattedInfo, testCase.expectedInfoPrefix))
} else {
require.Empty(t, formattedInfo)
}

if testCase.expectedWarn != "" {
assert.Equal(t, testCase.expectedWarn, formattedWarning)
} else {
assert.Empty(t, formattedWarning)
}

})
}
}
1 change: 1 addition & 0 deletions internal/peer/node/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ func serve(args []string) error {
serverConfig.SecOpts.Certificate,
cs.GetClientCertificate().Certificate,
signingIdentityBytes,
expirationLogger.Infof,
expirationLogger.Warnf, // This can be used to piggyback a metric event in the future
time.Now(),
time.AfterFunc)
Expand Down
1 change: 1 addition & 0 deletions orderer/common/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ func Main() {
serverConfig.SecOpts.Certificate,
[][]byte{clusterClientConfig.SecOpts.Certificate},
identityBytes,
expirationLogger.Infof,
expirationLogger.Warnf, // This can be used to piggyback a metric event in the future
time.Now(),
time.AfterFunc)
Expand Down

0 comments on commit c8ec1e0

Please sign in to comment.