Skip to content

Commit

Permalink
Merge "[FAB-6805] Mutual TLS"
Browse files Browse the repository at this point in the history
  • Loading branch information
Aleksandar Likic authored and Gerrit Code Review committed Dec 14, 2017
2 parents a00fd98 + b99661a commit 987d9fd
Show file tree
Hide file tree
Showing 27 changed files with 475 additions and 209 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -344,4 +344,5 @@ clean:
-$(GO_CMD) clean
-rm -Rf /tmp/enroll_user /tmp/msp /tmp/keyvaluestore /tmp/hfc-kvs /tmp/state
-rm -f integration-report.xml report.xml
-FIXTURE_PROJECT_NAME=$(FIXTURE_PROJECT_NAME) DOCKER_REMOVE_FORCE=$(FIXTURE_DOCKER_REMOVE_FORCE) $(TEST_SCRIPTS_PATH)/clean_integration.sh
-FIXTURE_PROJECT_NAME=$(FIXTURE_PROJECT_NAME) DOCKER_REMOVE_FORCE=$(FIXTURE_DOCKER_REMOVE_FORCE) $(TEST_SCRIPTS_PATH)/clean_integration.sh

2 changes: 2 additions & 0 deletions api/apiconfig/configprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ SPDX-License-Identifier: Apache-2.0
package apiconfig

import (
"crypto/tls"
"crypto/x509"
"time"
)
Expand Down Expand Up @@ -48,6 +49,7 @@ type Config interface {
KeyStorePath() string
CAKeyStorePath() string
CryptoConfigPath() string
TLSClientCerts() ([]tls.Certificate, error)
}

// TimeoutType enumerates the different types of outgoing connections
Expand Down
299 changes: 157 additions & 142 deletions api/apiconfig/mocks/mockconfig.gen.go

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion api/apiconfig/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ type ClientConfig struct {
Logging LoggingType
CryptoConfig CCType
TLS TLSType
TLSCerts MutualTLSConfig

// currently not used by GO-SDK
CredentialStore CredentialStoreType
}
Expand Down Expand Up @@ -136,7 +138,7 @@ type TLSConfig struct {
// MutualTLSConfig Mutual TLS configurations
type MutualTLSConfig struct {
Pem []string
// Certfiles root certificates for TLS validation (Comma serparated path list)
// Certfiles root certificates for TLS validation (Comma separated path list)
Path string
// Client client TLS information
Client struct {
Expand Down
45 changes: 23 additions & 22 deletions api/apifabca/mocks/mockfabriccaclient.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 7 additions & 6 deletions api/apitxn/mocks/mockapitxn.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions pkg/config/comm/comm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package comm

import (
"crypto/tls"

"github.com/hyperledger/fabric-sdk-go/api/apiconfig"
"github.com/hyperledger/fabric-sdk-go/pkg/errors"
)

// TLSConfig returns the appropriate config for TLS including the root CAs,
// certs for mutual TLS, and server host override
func TLSConfig(certificate string, serverhostoverride string, config apiconfig.Config) (*tls.Config, error) {
certPool, _ := config.TLSCACertPool("")

if len(certificate) == 0 && (certPool == nil || len(certPool.Subjects()) == 0) {
return nil, errors.New("certificate is required")
}

tlsCaCertPool, err := config.TLSCACertPool(certificate)
if err != nil {
return nil, err
}

clientCerts, err := config.TLSClientCerts()
if err != nil {
return nil, errors.Errorf("Error loading cert/key pair for TLS client credentials: %v", err)
}

return &tls.Config{RootCAs: tlsCaCertPool, Certificates: clientCerts, ServerName: serverhostoverride}, nil
}
41 changes: 38 additions & 3 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package config

import (
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"io/ioutil"
Expand Down Expand Up @@ -125,7 +126,7 @@ func initConfigWithCmdRoot(configFile string, cmdRootPrefix string) (*Config, er

func getCertPool(myViper *viper.Viper) (*x509.CertPool, error) {
tlsCertPool := x509.NewCertPool()
if myViper.GetBool("client.systemcertpool") == true {
if myViper.GetBool("client.tlsCerts.systemCertPool") == true {
var err error
if tlsCertPool, err = x509.SystemCertPool(); err != nil {
return nil, err
Expand Down Expand Up @@ -173,6 +174,11 @@ func (c *Config) Client() (*apiconfig.ClientConfig, error) {
return nil, err
}
client := config.Client

client.TLSCerts.Path = substPathVars(client.TLSCerts.Path)
client.TLSCerts.Client.Keyfile = substPathVars(client.TLSCerts.Client.Keyfile)
client.TLSCerts.Client.Certfile = substPathVars(client.TLSCerts.Client.Certfile)

return &client, nil
}

Expand Down Expand Up @@ -438,7 +444,7 @@ func (c *Config) OrderersConfig() ([]apiconfig.OrdererConfig, error) {

if orderer.TLSCACerts.Path != "" {
orderer.TLSCACerts.Path = substPathVars(orderer.TLSCACerts.Path)
} else if len(orderer.TLSCACerts.Pem) == 0 && c.configViper.GetBool("client.systemcertpool") == false {
} else if len(orderer.TLSCACerts.Pem) == 0 && c.configViper.GetBool("client.tlsCerts.systemCertPool") == false {
errors.Errorf("Orderer has no certs configured. Make sure TLSCACerts.Pem or TLSCACerts.Path is set for %s", orderer.URL)
}

Expand Down Expand Up @@ -697,7 +703,7 @@ func (c *Config) verifyPeerConfig(p apiconfig.PeerConfig, peerName string, tlsEn
if p.EventURL == "" {
return errors.Errorf("event URL does not exist or empty for peer %s", peerName)
}
if tlsEnabled && len(p.TLSCACerts.Pem) == 0 && p.TLSCACerts.Path == "" && c.configViper.GetBool("client.systemcertpool") == false {
if tlsEnabled && len(p.TLSCACerts.Pem) == 0 && p.TLSCACerts.Path == "" && c.configViper.GetBool("client.tlsCerts.systemCertPool") == false {
return errors.Errorf("tls.certificate does not exist or empty for peer %s", peerName)
}
return nil
Expand Down Expand Up @@ -810,6 +816,35 @@ func (c *Config) CryptoConfigPath() string {
return substPathVars(c.configViper.GetString("client.cryptoconfig.path"))
}

// TLSClientCerts loads the client's certs for mutual TLS
// It checks the config for embedded pem files before looking for cert files
func (c *Config) TLSClientCerts() ([]tls.Certificate, error) {
config, err := c.NetworkConfig()
if err != nil {
return nil, err
}

clientConfig := config.Client
var clientCerts tls.Certificate

if clientConfig.TLSCerts.Client.CertPem != "" {
clientCerts, err = tls.X509KeyPair([]byte(clientConfig.TLSCerts.Client.CertPem), []byte(clientConfig.TLSCerts.Client.KeyPem))
if err != nil {
return nil, errors.Errorf("Error loading cert/key pair as TLS client credentials: %v", err)
}

} else if clientConfig.TLSCerts.Client.Certfile != "" {
clientConfig.TLSCerts.Client.Keyfile = substPathVars(clientConfig.TLSCerts.Client.Keyfile)
clientConfig.TLSCerts.Client.Certfile = substPathVars(clientConfig.TLSCerts.Client.Certfile)
clientCerts, err = tls.LoadX509KeyPair(clientConfig.TLSCerts.Client.Certfile, clientConfig.TLSCerts.Client.Keyfile)
if err != nil {
return nil, errors.Errorf("Error loading cert/key pair as TLS client credentials: %v", err)
}
}

return []tls.Certificate{clientCerts}, nil
}

// loadCAKey
func loadCAKey(rawData []byte) (*x509.Certificate, error) {
block, _ := pem.Decode(rawData)
Expand Down
5 changes: 3 additions & 2 deletions pkg/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ client:
#library: "/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so, /usr/lib/softhsm/libsofthsm2.so ,/usr/lib/s390x-linux-gnu/softhsm/libsofthsm2.so, /usr/lib/powerpc64le-linux-gnu/softhsm/libsofthsm2.so, /usr/local/Cellar/softhsm/2.1.0/lib/softhsm/libsofthsm2.so"
library: "add BCCSP library here"

# [Optional]. Use system certificate pool when connecting to peers, orderers (for negotiating TLS) Default: false
# systemcertpool: true
#tlsCerts:
# [Optional]. Use system certificate pool when connecting to peers, orderers (for negotiating TLS) Default: false
#systemCertPool: true

#
# [Optional]. But most apps would have this section so that channel objects can be constructed
Expand Down
2 changes: 1 addition & 1 deletion pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,7 @@ func TestSystemCertPoolEnabled(t *testing.T) {
t.Fatal("System Cert Pool not loaded even though it is enabled")
}

// Org2 'mychannel' peer is missing cert + pem (it should not fail when systemcertpool enabled)
// Org2 'mychannel' peer is missing cert + pem (it should not fail when systemCertPool enabled)
_, err = c.ChannelPeers("mychannel")
if err != nil {
t.Fatalf("Should have skipped verifying ca cert + pem: %s", err)
Expand Down
5 changes: 3 additions & 2 deletions pkg/config/testdata/config_test_pem.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,9 @@ client:
ephemeral: false
level: 256

# [Optional]. Use system certificate pool when connecting to peers, orderers (for negotiating TLS) Default: false
systemcertpool: true
tlsCerts:
# [Optional]. Use system certificate pool when connecting to peers, orderers (for negotiating TLS) Default: false
systemCertPool: true

#
# [Optional]. But most apps would have this section so that channel objects can be constructed
Expand Down
6 changes: 6 additions & 0 deletions pkg/fabric-ca-client/mocks/mockconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ SPDX-License-Identifier: Apache-2.0
package mocks

import (
"crypto/tls"
"crypto/x509"
"time"

Expand Down Expand Up @@ -201,3 +202,8 @@ func (c *MockConfig) SecurityProviderLabel() string {
func (c *MockConfig) IsSecurityEnabled() bool {
return false
}

// TLSClientCerts ...
func (c *MockConfig) TLSClientCerts() ([]tls.Certificate, error) {
return nil, nil
}
9 changes: 5 additions & 4 deletions pkg/fabric-client/events/consumer/consumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (

apiconfig "github.com/hyperledger/fabric-sdk-go/api/apiconfig"
fab "github.com/hyperledger/fabric-sdk-go/api/apifabclient"
"github.com/hyperledger/fabric-sdk-go/pkg/config/comm"
"github.com/hyperledger/fabric-sdk-go/pkg/config/urlutil"
"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer"
ehpb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer"
Expand Down Expand Up @@ -59,16 +60,16 @@ func NewEventsClient(client fab.FabricClient, peerAddress string, certificate st
}

//newEventsClientConnectionWithAddress Returns a new grpc.ClientConn to the configured local PEER.
func newEventsClientConnectionWithAddress(peerAddress string, certificate string, serverhostoverride string, config apiconfig.Config) (*grpc.ClientConn, error) {
func newEventsClientConnectionWithAddress(peerAddress string, certificate string, serverHostOverride string, config apiconfig.Config) (*grpc.ClientConn, error) {
var opts []grpc.DialOption
opts = append(opts, grpc.WithTimeout(config.TimeoutOrDefault(apiconfig.EventHub)))
if urlutil.IsTLSEnabled(peerAddress) {
tlsCaCertPool, err := config.TLSCACertPool(certificate)
tlsConfig, err := comm.TLSConfig(certificate, serverHostOverride, config)
if err != nil {
return nil, err
}
creds := credentials.NewClientTLSFromCert(tlsCaCertPool, serverhostoverride)
opts = append(opts, grpc.WithTransportCredentials(creds))

opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
} else {
opts = append(opts, grpc.WithInsecure())
}
Expand Down
Loading

0 comments on commit 987d9fd

Please sign in to comment.