diff --git a/core/deliverservice/testdata/core.yaml b/core/deliverservice/testdata/core.yaml index b9d03bf2fee..54d48244763 100644 --- a/core/deliverservice/testdata/core.yaml +++ b/core/deliverservice/testdata/core.yaml @@ -441,13 +441,11 @@ peer: # library: /etc/hyperledger/fabric/plugin/escc.so handlers: authFilters: - - - name: DefaultAuth - - - name: ExpirationCheck # This filter checks identity x509 certificate expiration + - name: DefaultAuth + - name: ExpirationCheck # This filter checks identity x509 certificate expiration + - name: TimeWindowCheck # This filter checks Timestamp in TimeWindow decorators: - - - name: DefaultDecorator + - name: DefaultDecorator endorsers: escc: name: DefaultEndorsement diff --git a/core/handlers/auth/filter/timewindow.go b/core/handlers/auth/filter/timewindow.go new file mode 100644 index 00000000000..72780db8492 --- /dev/null +++ b/core/handlers/auth/filter/timewindow.go @@ -0,0 +1,69 @@ +/* +Copyright IBM Corp, SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package filter + +import ( + "context" + "time" + + "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric/core/handlers/auth" + "github.com/hyperledger/fabric/protoutil" + "github.com/pkg/errors" +) + +// NewTimeWindowCheckFilter creates a new Filter that checks timewindow expiration +func NewTimeWindowCheckFilter(timeWindow time.Duration) auth.Filter { + return &timewindowCheckFilter{ + timeWindow: timeWindow, + } +} + +type timewindowCheckFilter struct { + next peer.EndorserServer + timeWindow time.Duration +} + +// Init initializes the Filter with the next EndorserServer +func (f *timewindowCheckFilter) Init(next peer.EndorserServer) { + f.next = next +} + +func validateTimewindowProposal(signedProp *peer.SignedProposal, timeWindow time.Duration) error { + prop, err := protoutil.UnmarshalProposal(signedProp.ProposalBytes) + if err != nil { + return errors.Wrap(err, "failed parsing proposal") + } + + hdr, err := protoutil.UnmarshalHeader(prop.Header) + if err != nil { + return errors.Wrap(err, "failed parsing header") + } + + chdr, err := protoutil.UnmarshalChannelHeader(hdr.ChannelHeader) + if err != nil { + return errors.Wrap(err, "failed parsing channel header") + } + + timeProposal := chdr.Timestamp.AsTime().UTC() + now := time.Now().UTC() + + if timeProposal.Add(timeWindow).Before(now) || timeProposal.Add(-timeWindow).After(now) { + return errors.Errorf("request unauthorized due to incorrect timestamp %s, peer time %s, peer.authentication.timewindow %s", + timeProposal.Format(time.RFC3339), now.Format(time.RFC3339), timeWindow.String()) + } + + return nil +} + +// ProcessProposal processes a signed proposal +func (f *timewindowCheckFilter) ProcessProposal(ctx context.Context, signedProp *peer.SignedProposal) (*peer.ProposalResponse, error) { + if err := validateTimewindowProposal(signedProp, f.timeWindow); err != nil { + return nil, err + } + return f.next.ProcessProposal(ctx, signedProp) +} diff --git a/core/handlers/auth/filter/timewindow_test.go b/core/handlers/auth/filter/timewindow_test.go new file mode 100644 index 00000000000..1aa4b0662c3 --- /dev/null +++ b/core/handlers/auth/filter/timewindow_test.go @@ -0,0 +1,64 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package filter + +import ( + "context" + "testing" + "time" + + "github.com/golang/protobuf/proto" + "github.com/hyperledger/fabric-protos-go/common" + "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric/protoutil" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func createSignedProposalForCheckTimeWindow(t *testing.T, tt time.Time) *peer.SignedProposal { + sHdr := protoutil.MakeSignatureHeader(createX509Identity(t, "notExpiredCert.pem"), nil) + hdr := protoutil.MakePayloadHeader(&common.ChannelHeader{ + Timestamp: timestamppb.New(tt), + }, sHdr) + hdrBytes, err := proto.Marshal(hdr) + require.NoError(t, err) + prop := &peer.Proposal{ + Header: hdrBytes, + } + propBytes, err := proto.Marshal(prop) + require.NoError(t, err) + return &peer.SignedProposal{ + ProposalBytes: propBytes, + } +} + +func TestTimeWindowCheckFilter(t *testing.T) { + nextEndorser := &mockEndorserServer{} + auth := NewTimeWindowCheckFilter(time.Minute * 15) + auth.Init(nextEndorser) + + now := time.Now() + + // Scenario I: Not expired timestamp + sp := createSignedProposalForCheckTimeWindow(t, now) + _, err := auth.ProcessProposal(context.Background(), sp) + require.NoError(t, err) + require.True(t, nextEndorser.invoked) + nextEndorser.invoked = false + + // Scenario II: Expired timestamp before + sp = createSignedProposalForCheckTimeWindow(t, now.Add(-time.Minute*30)) + _, err = auth.ProcessProposal(context.Background(), sp) + require.Contains(t, err.Error(), "request unauthorized due to incorrect timestamp") + require.False(t, nextEndorser.invoked) + + // Scenario III: Expired timestamp after + sp = createSignedProposalForCheckTimeWindow(t, now.Add(time.Minute*30)) + _, err = auth.ProcessProposal(context.Background(), sp) + require.Contains(t, err.Error(), "request unauthorized due to incorrect timestamp") + require.False(t, nextEndorser.invoked) +} diff --git a/core/handlers/library/config.go b/core/handlers/library/config.go index 6161788eb00..06b00a3c1be 100644 --- a/core/handlers/library/config.go +++ b/core/handlers/library/config.go @@ -7,6 +7,8 @@ package library import ( + "time" + "github.com/mitchellh/mapstructure" "github.com/spf13/viper" ) @@ -14,10 +16,11 @@ import ( // Config configures the factory methods // and plugins for the registry type Config struct { - AuthFilters []*HandlerConfig `yaml:"authFilters"` - Decorators []*HandlerConfig `yaml:"decorators"` - Endorsers PluginMapping `yaml:"endorsers"` - Validators PluginMapping `yaml:"validators"` + AuthFilters []*HandlerConfig `yaml:"authFilters"` + Decorators []*HandlerConfig `yaml:"decorators"` + Endorsers PluginMapping `yaml:"endorsers"` + Validators PluginMapping `yaml:"validators"` + AuthenticationTimeWindow time.Duration `yaml:"authenticationTimeWindow"` } // PluginMapping stores a map between chaincode id to plugin config @@ -54,10 +57,18 @@ func LoadConfig() (Config, error) { validators[k] = &HandlerConfig{Name: name, Library: library} } + authenticationTimeWindow := viper.GetDuration("peer.authentication.timewindow") + if authenticationTimeWindow == 0 { + defaultTimeWindow := 15 * time.Minute + logger.Warningf("`peer.authentication.timewindow` not set; defaulting to %s", defaultTimeWindow) + authenticationTimeWindow = defaultTimeWindow + } + return Config{ - AuthFilters: authFilters, - Decorators: decorators, - Endorsers: endorsers, - Validators: validators, + AuthFilters: authFilters, + Decorators: decorators, + Endorsers: endorsers, + Validators: validators, + AuthenticationTimeWindow: authenticationTimeWindow, }, nil } diff --git a/core/handlers/library/config_test.go b/core/handlers/library/config_test.go index ff36983dbe4..1729d807b93 100644 --- a/core/handlers/library/config_test.go +++ b/core/handlers/library/config_test.go @@ -10,6 +10,7 @@ import ( "bytes" "strings" "testing" + "time" "github.com/spf13/viper" "github.com/stretchr/testify/require" @@ -24,6 +25,8 @@ peer: library: /path/to/default.so - name: ExpirationCheck library: /path/to/expiration.so + - name: TimeWindowCheck + library: /path/to/timewindow.so decorators: - name: DefaultDecorator library: /path/to/decorators.so @@ -49,6 +52,7 @@ peer: AuthFilters: []*HandlerConfig{ {Name: "DefaultAuth", Library: "/path/to/default.so"}, {Name: "ExpirationCheck", Library: "/path/to/expiration.so"}, + {Name: "TimeWindowCheck", Library: "/path/to/timewindow.so"}, }, Decorators: []*HandlerConfig{ {Name: "DefaultDecorator", Library: "/path/to/decorators.so"}, @@ -59,6 +63,7 @@ peer: Validators: PluginMapping{ "vscc": &HandlerConfig{Name: "DefaultValidation", Library: "/path/to/vscc.so"}, }, + AuthenticationTimeWindow: 900000000000, } require.EqualValues(t, expect, actual) } @@ -96,10 +101,11 @@ peer: require.NotNil(t, actual) expect := Config{ - AuthFilters: nil, - Decorators: nil, - Endorsers: PluginMapping{"escc": &HandlerConfig{Name: "DefaultEndorsement", Library: "/path/to/foo"}}, - Validators: PluginMapping{"vscc": &HandlerConfig{Name: "DefaultValidation"}}, + AuthFilters: nil, + Decorators: nil, + Endorsers: PluginMapping{"escc": &HandlerConfig{Name: "DefaultEndorsement", Library: "/path/to/foo"}}, + Validators: PluginMapping{"vscc": &HandlerConfig{Name: "DefaultValidation"}}, + AuthenticationTimeWindow: time.Minute * 15, } require.EqualValues(t, expect, actual) diff --git a/core/handlers/library/library.go b/core/handlers/library/library.go index 3021f60a434..251e2273a4f 100644 --- a/core/handlers/library/library.go +++ b/core/handlers/library/library.go @@ -7,6 +7,8 @@ SPDX-License-Identifier: Apache-2.0 package library import ( + "time" + "github.com/hyperledger/fabric/core/handlers/auth" "github.com/hyperledger/fabric/core/handlers/auth/filter" "github.com/hyperledger/fabric/core/handlers/decoration" @@ -19,7 +21,9 @@ import ( // HandlerLibrary is used to assert // how to create the various handlers -type HandlerLibrary struct{} +type HandlerLibrary struct { + authenticationTimeWindow time.Duration +} // DefaultAuth creates a default auth.Filter // that doesn't do any access control checks - simply @@ -36,6 +40,12 @@ func (r *HandlerLibrary) ExpirationCheck() auth.Filter { return filter.NewExpirationCheckFilter() } +// TimeWindowCheck is an auth filter which blocks requests +// from identities with Timestamp not in window +func (r *HandlerLibrary) TimeWindowCheck() auth.Filter { + return filter.NewTimeWindowCheckFilter(r.authenticationTimeWindow) +} + // DefaultDecorator creates a default decorator // that doesn't do anything with the input, simply // returns the input as output. diff --git a/core/handlers/library/registry.go b/core/handlers/library/registry.go index 5f469f32023..5ae76baf3a3 100644 --- a/core/handlers/library/registry.go +++ b/core/handlers/library/registry.go @@ -10,6 +10,7 @@ import ( "fmt" "reflect" "sync" + "time" "github.com/hyperledger/fabric-lib-go/common/flogging" "github.com/hyperledger/fabric/core/handlers/auth" @@ -47,10 +48,11 @@ const ( ) type registry struct { - filters []auth.Filter - decorators []decoration.Decorator - endorsers map[string]endorsement2.PluginFactory - validators map[string]validation.PluginFactory + filters []auth.Filter + decorators []decoration.Decorator + endorsers map[string]endorsement2.PluginFactory + validators map[string]validation.PluginFactory + authenticationTimeWindow time.Duration } var ( @@ -63,8 +65,9 @@ var ( func InitRegistry(c Config) Registry { once.Do(func() { reg = registry{ - endorsers: make(map[string]endorsement2.PluginFactory), - validators: make(map[string]validation.PluginFactory), + endorsers: make(map[string]endorsement2.PluginFactory), + validators: make(map[string]validation.PluginFactory), + authenticationTimeWindow: c.AuthenticationTimeWindow, } reg.loadHandlers(c) }) @@ -100,7 +103,9 @@ func (r *registry) evaluateModeAndLoad(c *HandlerConfig, handlerType HandlerType // loadCompiled loads a statically compiled handler func (r *registry) loadCompiled(handlerFactory string, handlerType HandlerType, extraArgs ...string) { - registryMD := reflect.ValueOf(&HandlerLibrary{}) + registryMD := reflect.ValueOf(&HandlerLibrary{ + authenticationTimeWindow: r.authenticationTimeWindow, + }) o := registryMD.MethodByName(handlerFactory) if !o.IsValid() { diff --git a/integration/e2e/filter_proposal_test.go b/integration/e2e/filter_proposal_test.go new file mode 100644 index 00000000000..8d8f7e73dc3 --- /dev/null +++ b/integration/e2e/filter_proposal_test.go @@ -0,0 +1,250 @@ +/* +Copyright IBM Corp All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package e2e + +import ( + "context" + "crypto/rand" + "os" + "path/filepath" + "syscall" + "time" + + docker "github.com/fsouza/go-dockerclient" + "github.com/golang/protobuf/proto" + "github.com/hyperledger/fabric-protos-go/common" + "github.com/hyperledger/fabric-protos-go/gateway" + "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric/integration/channelparticipation" + "github.com/hyperledger/fabric/integration/nwo" + "github.com/hyperledger/fabric/protoutil" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/pkg/errors" + "github.com/tedsuo/ifrit" + ginkgomon "github.com/tedsuo/ifrit/ginkgomon_v2" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/timestamppb" +) + +var _ = Describe("FilterProposalTimeWindow", func() { + var ( + testDir string + client *docker.Client + network *nwo.Network + chaincode nwo.Chaincode + ordererRunner *ginkgomon.Runner + ordererProcess, peerProcess ifrit.Process + ) + + BeforeEach(func() { + var err error + testDir, err = os.MkdirTemp("", "e2e") + Expect(err).NotTo(HaveOccurred()) + + client, err = docker.NewClientFromEnv() + Expect(err).NotTo(HaveOccurred()) + + chaincode = nwo.Chaincode{ + Name: "mycc", + Version: "0.0", + Path: components.Build("github.com/hyperledger/fabric/integration/chaincode/simple/cmd"), + Lang: "binary", + PackageFile: filepath.Join(testDir, "simplecc.tar.gz"), + Ctor: `{"Args":["init","a","100","b","200"]}`, + SignaturePolicy: `AND ('Org1MSP.member','Org2MSP.member')`, + Sequence: "1", + InitRequired: true, + Label: "my_prebuilt_chaincode", + } + }) + + AfterEach(func() { + if ordererProcess != nil { + ordererProcess.Signal(syscall.SIGTERM) + Eventually(ordererProcess.Wait(), network.EventuallyTimeout).Should(Receive()) + } + + if peerProcess != nil { + peerProcess.Signal(syscall.SIGTERM) + Eventually(peerProcess.Wait(), network.EventuallyTimeout).Should(Receive()) + } + + if network != nil { + network.Cleanup() + } + os.RemoveAll(testDir) + }) + + BeforeEach(func() { + network = nwo.New(nwo.BasicEtcdRaft(), testDir, client, StartPort(), components) + network.GenerateConfigTree() + network.Bootstrap() + + // Start all the fabric processes + ordererRunner, ordererProcess, peerProcess = network.StartSingleOrdererNetwork("orderer") + + By("getting the orderer by name") + orderer := network.Orderer("orderer") + + By("setting up the channel") + channelparticipation.JoinOrdererJoinPeersAppChannel(network, "testchannel", orderer, ordererRunner) + + By("enabling new lifecycle capabilities") + nwo.EnableCapabilities(network, "testchannel", "Application", "V2_5", orderer, network.Peer("Org1", "peer0"), network.Peer("Org2", "peer0")) + + By("deploying the chaincode") + nwo.DeployChaincode(network, "testchannel", orderer, chaincode) + + By("getting the client peer by name") + peer := network.Peer("Org1", "peer0") + + RunQueryInvokeQuery(network, orderer, peer, "testchannel") + }) + + It("filters proposal", func() { + By("normal time") + err := endorser(network, timestamppb.Now()) + Expect(err).NotTo(HaveOccurred()) + + By("add 30 minute") + err = endorser(network, timestamppb.New(time.Now().Add(time.Minute*30))) + rpcErr := status.Convert(err) + Expect(rpcErr.Message()).To(Equal("failed to collect enough transaction endorsements, see attached details for more info")) + Expect(len(rpcErr.Details())).To(BeNumerically(">", 0)) + Expect(rpcErr.Details()[0].(*gateway.ErrorDetail).Message).To(ContainSubstring("request unauthorized due to incorrect timestamp")) + + By("sub 30 minute") + err = endorser(network, timestamppb.New(time.Now().Add(-time.Minute*30))) + rpcErr = status.Convert(err) + Expect(rpcErr.Message()).To(Equal("failed to collect enough transaction endorsements, see attached details for more info")) + Expect(len(rpcErr.Details())).To(BeNumerically(">", 0)) + Expect(rpcErr.Details()[0].(*gateway.ErrorDetail).Message).To(ContainSubstring("request unauthorized due to incorrect timestamp")) + }) +}) + +func endorser(network *nwo.Network, needTime *timestamppb.Timestamp) error { + peerOrg1 := network.Peer("Org1", "peer0") + signingIdentity := network.PeerUserSigner(peerOrg1, "User1") + creator, err := signingIdentity.Serialize() + Expect(err).NotTo(HaveOccurred()) + + conn := network.PeerClientConn(peerOrg1) + gatewayClient := gateway.NewGatewayClient(conn) + + invocationSpec := &peer.ChaincodeInvocationSpec{ + ChaincodeSpec: &peer.ChaincodeSpec{ + Type: peer.ChaincodeSpec_NODE, + ChaincodeId: &peer.ChaincodeID{Name: "mycc"}, + Input: &peer.ChaincodeInput{Args: [][]byte{[]byte("invoke"), []byte("a"), []byte("b"), []byte("10")}}, + }, + } + + proposal, transactionID, err := createChaincodeProposalWithTransient( + common.HeaderType_ENDORSER_TRANSACTION, + "testchannel", + invocationSpec, + creator, + nil, + needTime, + ) + Expect(err).NotTo(HaveOccurred()) + + proposedTransaction, err := protoutil.GetSignedProposal(proposal, signingIdentity) + Expect(err).NotTo(HaveOccurred()) + + mspid := network.Organization(peerOrg1.Organization).MSPID + + endorseRequest := &gateway.EndorseRequest{ + TransactionId: transactionID, + ChannelId: "testchannel", + ProposedTransaction: proposedTransaction, + EndorsingOrganizations: []string{mspid}, + } + + ctx, cancel := context.WithTimeout(context.Background(), network.EventuallyTimeout) + defer cancel() + _, err = gatewayClient.Endorse(ctx, endorseRequest) + + return err +} + +// createChaincodeProposalWithTransient creates a proposal from given input +// It returns the proposal and the transaction id associated to the proposal +func createChaincodeProposalWithTransient(typ common.HeaderType, channelID string, cis *peer.ChaincodeInvocationSpec, creator []byte, transientMap map[string][]byte, needTime *timestamppb.Timestamp) (*peer.Proposal, string, error) { + // generate a random nonce + nonce, err := getRandomNonce() + if err != nil { + return nil, "", err + } + + // compute txid + txid := protoutil.ComputeTxID(nonce, creator) + + return createChaincodeProposalWithTxIDNonceAndTransient(txid, typ, channelID, cis, nonce, creator, transientMap, needTime) +} + +func getRandomNonce() ([]byte, error) { + key := make([]byte, 24) + + _, err := rand.Read(key) + if err != nil { + return nil, errors.Wrap(err, "error getting random bytes") + } + return key, nil +} + +// createChaincodeProposalWithTxIDNonceAndTransient creates a proposal from +// given input +func createChaincodeProposalWithTxIDNonceAndTransient(txid string, typ common.HeaderType, channelID string, cis *peer.ChaincodeInvocationSpec, nonce, creator []byte, transientMap map[string][]byte, needTime *timestamppb.Timestamp) (*peer.Proposal, string, error) { + ccHdrExt := &peer.ChaincodeHeaderExtension{ChaincodeId: cis.ChaincodeSpec.ChaincodeId} + ccHdrExtBytes, err := proto.Marshal(ccHdrExt) + if err != nil { + return nil, "", errors.Wrap(err, "error marshaling ChaincodeHeaderExtension") + } + + cisBytes, err := proto.Marshal(cis) + if err != nil { + return nil, "", errors.Wrap(err, "error marshaling ChaincodeInvocationSpec") + } + + ccPropPayload := &peer.ChaincodeProposalPayload{Input: cisBytes, TransientMap: transientMap} + ccPropPayloadBytes, err := proto.Marshal(ccPropPayload) + if err != nil { + return nil, "", errors.Wrap(err, "error marshaling ChaincodeProposalPayload") + } + + hdr := &common.Header{ + ChannelHeader: protoutil.MarshalOrPanic( + &common.ChannelHeader{ + Type: int32(typ), + TxId: txid, + Timestamp: needTime, + ChannelId: channelID, + Extension: ccHdrExtBytes, + Epoch: 0, + }, + ), + SignatureHeader: protoutil.MarshalOrPanic( + &common.SignatureHeader{ + Nonce: nonce, + Creator: creator, + }, + ), + } + + hdrBytes, err := proto.Marshal(hdr) + if err != nil { + return nil, "", err + } + + prop := &peer.Proposal{ + Header: hdrBytes, + Payload: ccPropPayloadBytes, + } + return prop, txid, nil +} diff --git a/integration/nwo/template/core_template.go b/integration/nwo/template/core_template.go index 35f615d84c1..1747c49e196 100644 --- a/integration/nwo/template/core_template.go +++ b/integration/nwo/template/core_template.go @@ -123,6 +123,7 @@ peer: authFilters: - name: DefaultAuth - name: ExpirationCheck + - name: TimeWindowCheck decorators: - name: DefaultDecorator endorsers: diff --git a/internal/peer/node/start.go b/internal/peer/node/start.go index 633001b38f7..4fde38d723a 100644 --- a/internal/peer/node/start.go +++ b/internal/peer/node/start.go @@ -834,26 +834,6 @@ func serve(args []string) error { discprotos.RegisterDiscoveryServer(peerServer.Server(), discoveryService) } - if coreConfig.GatewayOptions.Enabled { - if coreConfig.DiscoveryEnabled { - logger.Info("Starting peer with Gateway enabled") - - gatewayServer := gateway.CreateServer( - serverEndorser, - discoveryService, - peerInstance, - &serverConfig.SecOpts, - aclProvider, - coreConfig.LocalMSPID, - coreConfig.GatewayOptions, - builtinSCCs, - ) - gatewayprotos.RegisterGatewayServer(peerServer.Server(), gatewayServer) - } else { - logger.Warning("Discovery service must be enabled for embedded gateway") - } - } - logger.Infof("Starting peer with ID=[%s], network ID=[%s], address=[%s]", coreConfig.PeerID, coreConfig.NetworkID, coreConfig.PeerAddress) // Get configuration before starting go routines to avoid @@ -913,6 +893,26 @@ func serve(args []string) error { // Register the Endorser server pb.RegisterEndorserServer(peerServer.Server(), auth) + if coreConfig.GatewayOptions.Enabled { + if coreConfig.DiscoveryEnabled { + logger.Info("Starting peer with Gateway enabled") + + gatewayServer := gateway.CreateServer( + auth, + discoveryService, + peerInstance, + &serverConfig.SecOpts, + aclProvider, + coreConfig.LocalMSPID, + coreConfig.GatewayOptions, + builtinSCCs, + ) + gatewayprotos.RegisterGatewayServer(peerServer.Server(), gatewayServer) + } else { + logger.Warning("Discovery service must be enabled for embedded gateway") + } + } + // register the snapshot server snapshotSvc := &snapshotgrpc.SnapshotService{LedgerGetter: peerInstance, ACLProvider: aclProvider} pb.RegisterSnapshotServer(peerServer.Server(), snapshotSvc) diff --git a/orderer/common/cluster/testdata/blockverification/core.yaml b/orderer/common/cluster/testdata/blockverification/core.yaml index d4c0a03e5e3..fe0f32975c5 100644 --- a/orderer/common/cluster/testdata/blockverification/core.yaml +++ b/orderer/common/cluster/testdata/blockverification/core.yaml @@ -448,13 +448,11 @@ peer: # library: /etc/hyperledger/fabric/plugin/escc.so handlers: authFilters: - - - name: DefaultAuth - - - name: ExpirationCheck # This filter checks identity x509 certificate expiration + - name: DefaultAuth + - name: ExpirationCheck # This filter checks identity x509 certificate expiration + - name: TimeWindowCheck # This filter checks Timestamp in TimeWindow decorators: - - - name: DefaultDecorator + - name: DefaultDecorator endorsers: escc: name: DefaultEndorsement diff --git a/sampleconfig/core.yaml b/sampleconfig/core.yaml index 3d3569c14d4..1049bdfdfa5 100644 --- a/sampleconfig/core.yaml +++ b/sampleconfig/core.yaml @@ -313,6 +313,8 @@ peer: authentication: # the acceptable difference between the current server time and the # client's time as specified in a client request message + # this value is used for delivery service and + # endorsement service (if the authFilter is enabled) timewindow: 15m # Path on the file system where peer will store data (eg ledger). This @@ -454,13 +456,11 @@ peer: # library: /etc/hyperledger/fabric/plugin/escc.so handlers: authFilters: - - - name: DefaultAuth - - - name: ExpirationCheck # This filter checks identity x509 certificate expiration + - name: DefaultAuth + - name: ExpirationCheck # This filter checks identity x509 certificate expiration + - name: TimeWindowCheck # This filter checks the timestamp of an proposal request with the peer.authentication.timewindow parameter from core.yaml decorators: - - - name: DefaultDecorator + - name: DefaultDecorator endorsers: escc: name: DefaultEndorsement