Skip to content

Commit

Permalink
[FAB-6805] Mutual TLS
Browse files Browse the repository at this point in the history
This patch enables TLS client authentication. The existing
system cert pool configuration is merged into the new
TLSCerts configuration.

Change-Id: Ia9eacabf8a88f046028ca544b0ab6a99b65a5ea6
Signed-off-by: Emir Heidinger <[email protected]>
Signed-off-by: Troy Ronda <[email protected]>
Signed-off-by: Aleksandar Likic <[email protected]>
  • Loading branch information
emirsh authored and Aleksandar Likic committed Dec 14, 2017
1 parent 3651028 commit b99661a
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 b99661a

Please sign in to comment.