Skip to content

Commit fcbbbe7

Browse files
committed
[FAB-7490] Mutual TLS support for CLI
The CLI was not working properly with mutual TLS enabled. In order to add support for client certficates, the code needed to be partially refactored by adding peer and orderer client instances. This code should be useful in the future as part of any additional CLI rework. Change-Id: Ifa19c26bf3aff887c5279a8f721e0425ec49732e Signed-off-by: Gari Singh <gari.r.singh@gmail.com>
1 parent b4a1ec8 commit fcbbbe7

29 files changed

+900
-232
lines changed

core/comm/client.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@ type GRPCClient interface {
3535
// SetServerRootCAs sets the list of authorities used to verify server
3636
// certificates based on a list of PEM-encoded X509 certificate authorities
3737
SetServerRootCAs(clientRoots [][]byte) error
38-
// NewConnection returns a grpc.ClientConn for the target address
39-
NewConnection(address string) (*grpc.ClientConn, error)
38+
// NewConnection returns a grpc.ClientConn for the target address and
39+
// overrides the server name used to verify the hostname on the
40+
// certificate returned by a server when using TLS
41+
NewConnection(address string, serverNameOverride string) (*grpc.ClientConn, error)
4042
}
4143

4244
type grpcClient struct {
@@ -185,8 +187,10 @@ func (client *grpcClient) SetServerRootCAs(serverRoots [][]byte) error {
185187
return nil
186188
}
187189

188-
// NewConnection returns a grpc.ClientConn for the target address
189-
func (client *grpcClient) NewConnection(address string) (
190+
// NewConnection returns a grpc.ClientConn for the target address and
191+
// overrides the server name used to verify the hostname on the
192+
// certificate returned by a server when using TLS
193+
func (client *grpcClient) NewConnection(address string, serverNameOverride string) (
190194
*grpc.ClientConn, error) {
191195

192196
var dialOpts []grpc.DialOption
@@ -197,6 +201,7 @@ func (client *grpcClient) NewConnection(address string) (
197201
// SetServerRootCAs / SetMaxRecvMsgSize / SetMaxSendMsgSize
198202
// to take effect on a per connection basis
199203
if client.tlsConfig != nil {
204+
client.tlsConfig.ServerName = serverNameOverride
200205
dialOpts = append(dialOpts,
201206
grpc.WithTransportCredentials(
202207
credentials.NewTLS(client.tlsConfig)))

core/comm/client_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ func TestNewConnection_Timeout(t *testing.T) {
135135
config := comm.ClientConfig{
136136
Timeout: 1 * time.Second}
137137
client, err := comm.NewGRPCClient(config)
138-
conn, err := client.NewConnection(testAddress)
138+
conn, err := client.NewConnection(testAddress, "")
139139
assert.Contains(t, err.Error(), "context deadline exceeded")
140140
t.Log(err)
141141
assert.Nil(t, conn)
@@ -292,7 +292,7 @@ func TestNewConnection(t *testing.T) {
292292
t.Fatalf("error creating client for test: %v", err)
293293
}
294294
conn, err := client.NewConnection(fmt.Sprintf("localhost:%d",
295-
test.clientPort))
295+
test.clientPort), "")
296296
if test.success {
297297
assert.NoError(t, err)
298298
assert.NotNil(t, conn)
@@ -348,7 +348,7 @@ func TestSetServerRootCAs(t *testing.T) {
348348

349349
// initial config should work
350350
t.Log("running initial good config")
351-
conn, err := client.NewConnection(address)
351+
conn, err := client.NewConnection(address, "")
352352
assert.NoError(t, err)
353353
assert.NotNil(t, conn)
354354
if conn != nil {
@@ -360,15 +360,15 @@ func TestSetServerRootCAs(t *testing.T) {
360360
err = client.SetServerRootCAs([][]byte{})
361361
assert.NoError(t, err)
362362
// now connection should fail
363-
_, err = client.NewConnection(address)
363+
_, err = client.NewConnection(address, "")
364364
assert.Error(t, err)
365365

366366
// good root cert
367367
t.Log("running good config")
368368
err = client.SetServerRootCAs([][]byte{[]byte(caPEM)})
369369
assert.NoError(t, err)
370370
// now connection should succeed again
371-
conn, err = client.NewConnection(address)
371+
conn, err = client.NewConnection(address, "")
372372
assert.NoError(t, err)
373373
assert.NotNil(t, conn)
374374
conn.Close()
@@ -442,7 +442,7 @@ func TestSetMessageSize(t *testing.T) {
442442
if test.maxSendSize > 0 {
443443
client.SetMaxSendMsgSize(test.maxSendSize)
444444
}
445-
conn, err := client.NewConnection(address)
445+
conn, err := client.NewConnection(address, "")
446446
assert.NoError(t, err)
447447
defer conn.Close()
448448
// create service client from conn

peer/chaincode/chaincode.go

+5-10
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,8 @@ const (
2424
var logger = flogging.MustGetLogger("chaincodeCmd")
2525

2626
func addFlags(cmd *cobra.Command) {
27+
common.AddOrdererFlags(cmd)
2728
flags := cmd.PersistentFlags()
28-
29-
flags.StringVarP(&orderingEndpoint, "orderer", "o", "", "Ordering service endpoint")
30-
flags.BoolVarP(&tls, "tls", "", false, "Use TLS when communicating with the orderer endpoint")
31-
flags.StringVarP(&caFile, "cafile", "", "", "Path to file containing PEM-encoded trusted certificate(s) for the ordering endpoint")
3229
flags.StringVarP(&transient, "transient", "", "", "Transient map of arguments in JSON encoding")
3330
}
3431

@@ -64,18 +61,16 @@ var (
6461
escc string
6562
vscc string
6663
policyMarshalled []byte
67-
orderingEndpoint string
68-
tls bool
69-
caFile string
7064
transient string
7165
collectionsConfigFile string
7266
collectionConfigBytes []byte
7367
)
7468

7569
var chaincodeCmd = &cobra.Command{
76-
Use: chainFuncName,
77-
Short: fmt.Sprint(shortDes),
78-
Long: fmt.Sprint(longDes),
70+
Use: chainFuncName,
71+
Short: fmt.Sprint(shortDes),
72+
Long: fmt.Sprint(longDes),
73+
PersistentPreRun: common.SetOrdererEnv,
7974
}
8075

8176
var flags *pflag.FlagSet

peer/chaincode/common.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
putils "github.com/hyperledger/fabric/protos/utils"
2727
"github.com/pkg/errors"
2828
"github.com/spf13/cobra"
29+
"github.com/spf13/viper"
2930
"golang.org/x/net/context"
3031
)
3132

@@ -315,7 +316,7 @@ func InitCmdFactory(isEndorserRequired, isOrdererRequired bool) (*ChaincodeCmdFa
315316

316317
var broadcastClient common.BroadcastClient
317318
if isOrdererRequired {
318-
if len(orderingEndpoint) == 0 {
319+
if len(common.OrderingEndpoint) == 0 {
319320
orderingEndpoints, err := common.GetOrdererEndpointOfChainFnc(channelID, signer, endorserClient)
320321
if err != nil {
321322
return nil, fmt.Errorf("Error getting (%s) orderer endpoint: %s", channelID, err)
@@ -324,10 +325,11 @@ func InitCmdFactory(isEndorserRequired, isOrdererRequired bool) (*ChaincodeCmdFa
324325
return nil, fmt.Errorf("Error no orderer endpoint got for %s", channelID)
325326
}
326327
logger.Infof("Get chain(%s) orderer endpoint: %s", channelID, orderingEndpoints[0])
327-
orderingEndpoint = orderingEndpoints[0]
328+
// override viper env
329+
viper.Set("orderer.address", orderingEndpoints[0])
328330
}
329331

330-
broadcastClient, err = common.GetBroadcastClientFnc(orderingEndpoint, tls, caFile)
332+
broadcastClient, err = common.GetBroadcastClientFnc()
331333

332334
if err != nil {
333335
return nil, fmt.Errorf("Error getting broadcast client: %s", err)

peer/chaincode/flags_test.go

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
Copyright IBM Corp. 2016-2017 All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package chaincode
8+
9+
import (
10+
"testing"
11+
12+
"github.com/spf13/cobra"
13+
"github.com/spf13/viper"
14+
"github.com/stretchr/testify/assert"
15+
)
16+
17+
func TestOrdererFlags(t *testing.T) {
18+
19+
var (
20+
ca = "root.crt"
21+
key = "client.key"
22+
cert = "client.crt"
23+
endpoint = "orderer.example.com:7050"
24+
sn = "override.example.com"
25+
)
26+
27+
testCmd := &cobra.Command{
28+
Use: "test",
29+
Run: func(cmd *cobra.Command, args []string) {
30+
t.Logf("rootcert: %s", viper.GetString("orderer.tls.rootcert.file"))
31+
assert.Equal(t, ca, viper.GetString("orderer.tls.rootcert.file"))
32+
assert.Equal(t, key, viper.GetString("orderer.tls.clientKey.file"))
33+
assert.Equal(t, cert, viper.GetString("orderer.tls.clientCert.file"))
34+
assert.Equal(t, endpoint, viper.GetString("orderer.address"))
35+
assert.Equal(t, sn, viper.GetString("orderer.tls.serverhostoverride"))
36+
assert.Equal(t, true, viper.GetBool("orderer.tls.enabled"))
37+
assert.Equal(t, true, viper.GetBool("orderer.tls.clientAuthRequired"))
38+
}}
39+
40+
runCmd := Cmd(nil)
41+
42+
runCmd.AddCommand(testCmd)
43+
44+
runCmd.SetArgs([]string{"test", "--cafile", ca, "--keyfile", key,
45+
"--certfile", cert, "--orderer", endpoint, "--tls", "--clientauth",
46+
"--ordererTLSHostnameOverride", sn})
47+
err := runCmd.Execute()
48+
assert.NoError(t, err)
49+
50+
// check env one more time
51+
t.Logf("address: %s", viper.GetString("orderer.address"))
52+
assert.Equal(t, ca, viper.GetString("orderer.tls.rootcert.file"))
53+
assert.Equal(t, key, viper.GetString("orderer.tls.clientKey.file"))
54+
assert.Equal(t, cert, viper.GetString("orderer.tls.clientCert.file"))
55+
assert.Equal(t, endpoint, viper.GetString("orderer.address"))
56+
assert.Equal(t, sn, viper.GetString("orderer.tls.serverhostoverride"))
57+
assert.Equal(t, true, viper.GetBool("orderer.tls.enabled"))
58+
assert.Equal(t, true, viper.GetBool("orderer.tls.clientAuthRequired"))
59+
}

peer/chaincode/invoke_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,15 @@ func TestInvokeCmd(t *testing.T) {
117117
common.GetOrdererEndpointOfChainFnc = func(chainID string, signer msp.SigningIdentity, endorserClient pb.EndorserClient) ([]string, error) {
118118
return []string{"localhost:9999"}, nil
119119
}
120-
common.GetBroadcastClientFnc = func(orderingEndpoint string, tlsEnabled bool, caFile string) (common.BroadcastClient, error) {
120+
common.GetBroadcastClientFnc = func() (common.BroadcastClient, error) {
121121
return nil, errors.New("error")
122122
}
123123
err = cmd.Execute()
124124
assert.Error(t, err)
125125

126126
// Success case
127127
t.Logf("Start success case")
128-
common.GetBroadcastClientFnc = func(orderingEndpoint string, tlsEnabled bool, caFile string) (common.BroadcastClient, error) {
128+
common.GetBroadcastClientFnc = func() (common.BroadcastClient, error) {
129129
return mockCF.BroadcastClient, nil
130130
}
131131
err = cmd.Execute()

peer/channel/channel.go

+12-45
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,9 @@ import (
2323
"github.com/hyperledger/fabric/common/flogging"
2424
"github.com/hyperledger/fabric/msp"
2525
"github.com/hyperledger/fabric/peer/common"
26-
ab "github.com/hyperledger/fabric/protos/orderer"
2726
pb "github.com/hyperledger/fabric/protos/peer"
2827
"github.com/spf13/cobra"
2928
"github.com/spf13/pflag"
30-
"golang.org/x/net/context"
31-
"google.golang.org/grpc"
32-
"google.golang.org/grpc/credentials"
3329
)
3430

3531
const (
@@ -55,13 +51,9 @@ var (
5551
genesisBlockPath string
5652

5753
// create related variables
58-
channelID string
59-
channelTxFile string
60-
orderingEndpoint string
61-
tls bool
62-
caFile string
63-
ordererTLSHostnameOverride string
64-
timeout int
54+
channelID string
55+
channelTxFile string
56+
timeout int
6557
)
6658

6759
// Cmd returns the cobra command for Node
@@ -81,12 +73,7 @@ func Cmd(cf *ChannelCmdFactory) *cobra.Command {
8173

8274
// AddFlags adds flags for create and join
8375
func AddFlags(cmd *cobra.Command) {
84-
flags := cmd.PersistentFlags()
85-
86-
flags.StringVarP(&orderingEndpoint, "orderer", "o", "", "Ordering service endpoint")
87-
flags.BoolVarP(&tls, "tls", "", false, "Use TLS when communicating with the orderer endpoint")
88-
flags.StringVarP(&caFile, "cafile", "", "", "Path to file containing PEM-encoded trusted certificate(s) for the ordering endpoint")
89-
flags.StringVarP(&ordererTLSHostnameOverride, "ordererTLSHostnameOverride", "", "", "The hostname override to use when validating the TLS connection to the orderer.")
76+
common.AddOrdererFlags(cmd)
9077
}
9178

9279
var flags *pflag.FlagSet
@@ -117,9 +104,10 @@ func attachFlags(cmd *cobra.Command, names []string) {
117104
}
118105

119106
var channelCmd = &cobra.Command{
120-
Use: channelFuncName,
121-
Short: fmt.Sprint(shortDes),
122-
Long: fmt.Sprint(longDes),
107+
Use: channelFuncName,
108+
Short: fmt.Sprint(shortDes),
109+
Long: fmt.Sprint(longDes),
110+
PersistentPreRun: common.SetOrdererEnv,
123111
}
124112

125113
type BroadcastClientFactory func() (common.BroadcastClient, error)
@@ -145,7 +133,7 @@ func InitCmdFactory(isEndorserRequired EndorserRequirement, isOrdererRequired Or
145133
}
146134

147135
cmdFact.BroadcastFactory = func() (common.BroadcastClient, error) {
148-
return common.GetBroadcastClientFnc(orderingEndpoint, tls, caFile)
136+
return common.GetBroadcastClientFnc()
149137
}
150138

151139
//for join and list, we need the endorser as well
@@ -158,34 +146,13 @@ func InitCmdFactory(isEndorserRequired EndorserRequirement, isOrdererRequired Or
158146

159147
//for create and fetch, we need the orderer as well
160148
if isOrdererRequired {
161-
if len(strings.Split(orderingEndpoint, ":")) != 2 {
162-
return nil, fmt.Errorf("Ordering service endpoint %s is not valid or missing", orderingEndpoint)
163-
}
164-
165-
var opts []grpc.DialOption
166-
// check for TLS
167-
if tls {
168-
if caFile != "" {
169-
creds, err := credentials.NewClientTLSFromFile(caFile, ordererTLSHostnameOverride)
170-
if err != nil {
171-
return nil, fmt.Errorf("Error connecting to %s due to %s", orderingEndpoint, err)
172-
}
173-
opts = append(opts, grpc.WithTransportCredentials(creds))
174-
}
175-
} else {
176-
opts = append(opts, grpc.WithInsecure())
149+
if len(strings.Split(common.OrderingEndpoint, ":")) != 2 {
150+
return nil, fmt.Errorf("ordering service endpoint %s is not valid or missing", common.OrderingEndpoint)
177151
}
178-
conn, err := grpc.Dial(orderingEndpoint, opts...)
152+
cmdFact.DeliverClient, err = newDeliverClient(channelID)
179153
if err != nil {
180154
return nil, err
181155
}
182-
183-
client, err := ab.NewAtomicBroadcastClient(conn).Deliver(context.TODO())
184-
if err != nil {
185-
return nil, fmt.Errorf("Error connecting due to %s", err)
186-
}
187-
188-
cmdFact.DeliverClient = newDeliverClient(conn, client, channelID)
189156
}
190157
logger.Infof("Endorser and orderer connections initialized")
191158
return cmdFact, nil

0 commit comments

Comments
 (0)