github.com/true-sqn/fabric@v2.1.1+incompatible/core/chaincode/accesscontrol/access_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package accesscontrol
     8  
     9  import (
    10  	"context"
    11  	"crypto/tls"
    12  	"crypto/x509"
    13  	"fmt"
    14  	"net"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/golang/protobuf/proto"
    19  	pb "github.com/hyperledger/fabric-protos-go/peer"
    20  	"github.com/hyperledger/fabric/common/crypto/tlsgen"
    21  	"github.com/hyperledger/fabric/common/flogging/floggingtest"
    22  	"github.com/stretchr/testify/assert"
    23  	"go.uber.org/zap/zapcore"
    24  	"google.golang.org/grpc"
    25  	"google.golang.org/grpc/credentials"
    26  )
    27  
    28  type ccSrv struct {
    29  	l              net.Listener
    30  	grpcSrv        *grpc.Server
    31  	t              *testing.T
    32  	expectedCCname string
    33  }
    34  
    35  func (cs *ccSrv) Register(stream pb.ChaincodeSupport_RegisterServer) error {
    36  	msg, err := stream.Recv()
    37  	if err != nil {
    38  		return err
    39  	}
    40  
    41  	// First message is a register message
    42  	assert.Equal(cs.t, pb.ChaincodeMessage_REGISTER.String(), msg.Type.String())
    43  	// And its chaincode name is the expected one
    44  	chaincodeID := &pb.ChaincodeID{}
    45  	err = proto.Unmarshal(msg.Payload, chaincodeID)
    46  	if err != nil {
    47  		return err
    48  	}
    49  	assert.Equal(cs.t, cs.expectedCCname, chaincodeID.Name)
    50  	// Subsequent messages are just echoed back
    51  	for {
    52  		msg, _ = stream.Recv()
    53  		if err != nil {
    54  			return err
    55  		}
    56  		err = stream.Send(msg)
    57  		if err != nil {
    58  			return err
    59  		}
    60  	}
    61  }
    62  
    63  func (cs *ccSrv) stop() {
    64  	cs.grpcSrv.Stop()
    65  	cs.l.Close()
    66  }
    67  
    68  func createTLSService(t *testing.T, ca tlsgen.CA, host string) *grpc.Server {
    69  	keyPair, err := ca.NewServerCertKeyPair(host)
    70  	assert.NoError(t, err)
    71  	cert, err := tls.X509KeyPair(keyPair.Cert, keyPair.Key)
    72  	assert.NoError(t, err)
    73  	tlsConf := &tls.Config{
    74  		Certificates: []tls.Certificate{cert},
    75  		ClientAuth:   tls.RequireAndVerifyClientCert,
    76  		ClientCAs:    x509.NewCertPool(),
    77  	}
    78  	tlsConf.ClientCAs.AppendCertsFromPEM(ca.CertBytes())
    79  	return grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsConf)))
    80  }
    81  
    82  func newCCServer(t *testing.T, port int, expectedCCname string, withTLS bool, ca tlsgen.CA) *ccSrv {
    83  	var s *grpc.Server
    84  	if withTLS {
    85  		s = createTLSService(t, ca, "localhost")
    86  	} else {
    87  		s = grpc.NewServer()
    88  	}
    89  
    90  	l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", "", port))
    91  	assert.NoError(t, err, "%v", err)
    92  	return &ccSrv{
    93  		t:              t,
    94  		expectedCCname: expectedCCname,
    95  		l:              l,
    96  		grpcSrv:        s,
    97  	}
    98  }
    99  
   100  type ccClient struct {
   101  	conn   *grpc.ClientConn
   102  	stream pb.ChaincodeSupport_RegisterClient
   103  }
   104  
   105  func newClient(t *testing.T, port int, cert *tls.Certificate, peerCACert []byte) (*ccClient, error) {
   106  	tlsCfg := &tls.Config{
   107  		RootCAs: x509.NewCertPool(),
   108  	}
   109  
   110  	tlsCfg.RootCAs.AppendCertsFromPEM(peerCACert)
   111  	if cert != nil {
   112  		tlsCfg.Certificates = []tls.Certificate{*cert}
   113  	}
   114  	tlsOpts := grpc.WithTransportCredentials(credentials.NewTLS(tlsCfg))
   115  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   116  	defer cancel()
   117  	conn, err := grpc.DialContext(ctx, fmt.Sprintf("localhost:%d", port), tlsOpts, grpc.WithBlock())
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	chaincodeSupportClient := pb.NewChaincodeSupportClient(conn)
   122  	stream, err := chaincodeSupportClient.Register(context.Background())
   123  	assert.NoError(t, err)
   124  	return &ccClient{
   125  		conn:   conn,
   126  		stream: stream,
   127  	}, nil
   128  }
   129  
   130  func (c *ccClient) close() {
   131  	c.conn.Close()
   132  }
   133  
   134  func (c *ccClient) sendMsg(msg *pb.ChaincodeMessage) {
   135  	c.stream.Send(msg)
   136  }
   137  
   138  func (c *ccClient) recv() *pb.ChaincodeMessage {
   139  	msgs := make(chan *pb.ChaincodeMessage, 1)
   140  	go func() {
   141  		msg, _ := c.stream.Recv()
   142  		if msg != nil {
   143  			msgs <- msg
   144  		}
   145  	}()
   146  	select {
   147  	case <-time.After(time.Second):
   148  		return nil
   149  	case msg := <-msgs:
   150  		return msg
   151  	}
   152  }
   153  
   154  func TestAccessControl(t *testing.T) {
   155  	backupTTL := ttl
   156  	defer func() {
   157  		ttl = backupTTL
   158  	}()
   159  	ttl = time.Second * 3
   160  
   161  	oldLogger := logger
   162  	l, recorder := floggingtest.NewTestLogger(t, floggingtest.AtLevel(zapcore.InfoLevel))
   163  	logger = l
   164  	defer func() { logger = oldLogger }()
   165  
   166  	chaincodeID := &pb.ChaincodeID{Name: "example02"}
   167  	payload, err := proto.Marshal(chaincodeID)
   168  	assert.NoError(t, err)
   169  	registerMsg := &pb.ChaincodeMessage{
   170  		Type:    pb.ChaincodeMessage_REGISTER,
   171  		Payload: payload,
   172  	}
   173  	putStateMsg := &pb.ChaincodeMessage{
   174  		Type: pb.ChaincodeMessage_PUT_STATE,
   175  	}
   176  
   177  	ca, _ := tlsgen.NewCA()
   178  	srv := newCCServer(t, 7052, "example02", true, ca)
   179  	auth := NewAuthenticator(ca)
   180  	pb.RegisterChaincodeSupportServer(srv.grpcSrv, auth.Wrap(srv))
   181  	go srv.grpcSrv.Serve(srv.l)
   182  	defer srv.stop()
   183  
   184  	// Create an attacker without a TLS certificate
   185  	_, err = newClient(t, 7052, nil, ca.CertBytes())
   186  	assert.Error(t, err)
   187  	assert.Contains(t, err.Error(), "context deadline exceeded")
   188  
   189  	// Create an attacker with its own TLS certificate
   190  	maliciousCA, _ := tlsgen.NewCA()
   191  	keyPair, err := maliciousCA.NewClientCertKeyPair()
   192  	assert.NoError(t, err)
   193  	cert, err := tls.X509KeyPair(keyPair.Cert, keyPair.Key)
   194  	assert.NoError(t, err)
   195  	_, err = newClient(t, 7052, &cert, ca.CertBytes())
   196  	assert.Error(t, err)
   197  	assert.Contains(t, err.Error(), "context deadline exceeded")
   198  
   199  	// Create a chaincode for example01 that tries to impersonate example02
   200  	kp, err := auth.Generate("example01")
   201  	assert.NoError(t, err)
   202  	cert, err = tls.X509KeyPair(kp.Cert, kp.Key)
   203  	assert.NoError(t, err)
   204  	mismatchedShim, err := newClient(t, 7052, &cert, ca.CertBytes())
   205  	assert.NoError(t, err)
   206  	defer mismatchedShim.close()
   207  	mismatchedShim.sendMsg(registerMsg)
   208  	mismatchedShim.sendMsg(putStateMsg)
   209  	// Mismatched chaincode didn't get back anything
   210  	assert.Nil(t, mismatchedShim.recv())
   211  	assertLogContains(t, recorder, "with given certificate hash", "belongs to a different chaincode")
   212  
   213  	// Create the real chaincode that its cert is generated by us that should pass the security checks
   214  	kp, err = auth.Generate("example02")
   215  	assert.NoError(t, err)
   216  	cert, err = tls.X509KeyPair(kp.Cert, kp.Key)
   217  	assert.NoError(t, err)
   218  	realCC, err := newClient(t, 7052, &cert, ca.CertBytes())
   219  	assert.NoError(t, err)
   220  	defer realCC.close()
   221  	realCC.sendMsg(registerMsg)
   222  	realCC.sendMsg(putStateMsg)
   223  	echoMsg := realCC.recv()
   224  	// The real chaincode should be echoed back its message
   225  	assert.NotNil(t, echoMsg)
   226  	assert.Equal(t, pb.ChaincodeMessage_PUT_STATE, echoMsg.Type)
   227  	// Log should not complain about anything
   228  	assert.Empty(t, recorder.Messages())
   229  
   230  	// Create the real chaincode that its cert is generated by us
   231  	// but one that the first message sent by it isn't a register message.
   232  	// The second message that is sent is a register message but it's "too late"
   233  	// and the stream is already denied.
   234  	kp, err = auth.Generate("example02")
   235  	assert.NoError(t, err)
   236  	cert, err = tls.X509KeyPair(kp.Cert, kp.Key)
   237  	assert.NoError(t, err)
   238  	confusedCC, err := newClient(t, 7052, &cert, ca.CertBytes())
   239  	assert.NoError(t, err)
   240  	defer confusedCC.close()
   241  	confusedCC.sendMsg(putStateMsg)
   242  	confusedCC.sendMsg(registerMsg)
   243  	confusedCC.sendMsg(putStateMsg)
   244  	assert.Nil(t, confusedCC.recv())
   245  	assertLogContains(t, recorder, "expected a ChaincodeMessage_REGISTER message")
   246  
   247  	// Create a real chaincode, that its cert was generated by us
   248  	// but it sends a malformed first message
   249  	kp, err = auth.Generate("example02")
   250  	assert.NoError(t, err)
   251  	cert, err = tls.X509KeyPair(kp.Cert, kp.Key)
   252  	assert.NoError(t, err)
   253  	malformedMessageCC, err := newClient(t, 7052, &cert, ca.CertBytes())
   254  	assert.NoError(t, err)
   255  	defer malformedMessageCC.close()
   256  	// Save old payload
   257  	originalPayload := registerMsg.Payload
   258  	registerMsg.Payload = append(registerMsg.Payload, 0)
   259  	malformedMessageCC.sendMsg(registerMsg)
   260  	malformedMessageCC.sendMsg(putStateMsg)
   261  	assert.Nil(t, malformedMessageCC.recv())
   262  	assertLogContains(t, recorder, "Failed unmarshaling message")
   263  	// Recover old payload
   264  	registerMsg.Payload = originalPayload
   265  
   266  	// Create a real chaincode, that its cert was generated by us
   267  	// but have it reconnect only after too much time.
   268  	// This tests a use case where the CC's cert has been expired
   269  	// and the CC has been compromised. We don't want it to be able
   270  	// to reconnect to us.
   271  	kp, err = auth.Generate("example02")
   272  	assert.NoError(t, err)
   273  	cert, err = tls.X509KeyPair(kp.Cert, kp.Key)
   274  	assert.NoError(t, err)
   275  	lateCC, err := newClient(t, 7052, &cert, ca.CertBytes())
   276  	assert.NoError(t, err)
   277  	defer lateCC.close()
   278  	time.Sleep(ttl + time.Second*2)
   279  	lateCC.sendMsg(registerMsg)
   280  	lateCC.sendMsg(putStateMsg)
   281  	echoMsg = lateCC.recv()
   282  	assert.Nil(t, echoMsg)
   283  	assertLogContains(t, recorder, "with given certificate hash", "not found in registry")
   284  }
   285  
   286  func assertLogContains(t *testing.T, r *floggingtest.Recorder, ss ...string) {
   287  	defer r.Reset()
   288  	for _, s := range ss {
   289  		assert.NotEmpty(t, r.MessagesContaining(s))
   290  	}
   291  }