Skip to content

Commit

Permalink
Allow TLS conn. between the client and the CA services
Browse files Browse the repository at this point in the history
Fixes FAB-392

Supporting TLS between the ACA client the rest of the CA services.

This feature requires changes to the configuration of the Membership
Services (YAML file), adding the support for two newly added (YAML)
configuration parameters under the security section of membersrvc.yaml.

To enable this feature (TLS connectivity), one needs to specify that
tls_enabled (=true) and a path to a PEM-encoded certificate file to
use. There is also the option to use the serverhostname override,
enforcing that the same TLS Server Certificate's Common Name to be used.

Change-Id: I64444d0e21615a47662ba1388cd6901ff787b408
Signed-off-by: ASHUTOSH KUMAR <ashutosh_kumar@hotmail.com>
Signed-off-by: JonathanLevi <jonathan@levi.name>
Signed-off-by: Gari Singh <gari.r.singh@gmail.com>
  • Loading branch information
ASHUTOSH KUMAR authored and mastersingh24 committed Oct 15, 2016
1 parent de2cade commit 925f4d9
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 8 deletions.
66 changes: 66 additions & 0 deletions docs/Setup/TLSSetup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
### Steps to enable TLS for all sever (ECA , ACA , TLSCA , TCA) and between ACA client to server communications.

1. Go to **memebersrvc.yaml** file under the fabric/membersrvc directory and edit security section, that is:
```
security:
serverhostoverride:
tls_enabled: false
client:
cert:
file:
```
To enable TLS between the ACA client and the rest of the CA Services set the `tls_enbabled` flag to `true`.

2. Next, set **serverhostoverride** field to match **CN** (Common Name) of TLS Server certificate. To extract the Common Name from TLS Server's certificate, for example using OpenSSL, you can use the following command:

```
openssl x509 -in <<certificate.crt -text -noout
```
where `certficate.crt` is the Server Certificate. If you have openssl installed on the machine and everything went well, you should expect an output of the form:

```
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
4f:39:0f:ac:7b:ce:2b:9f:28:57:52:4a:bb:94:a6:e5:9c:69:99:56
Signature Algorithm: ecdsa-with-SHA256
Issuer: C=US, ST=California, L=San Francisco, O=Internet Widgets, Inc., OU=WWW
Validity
Not Before: Aug 24 16:27:00 2016 GMT
Not After : Aug 24 16:27:00 2017 GMT
**Subject**: C=US, ST=California, L=San Francisco, O=example.com, **CN=www.example.com**
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
EC Public Key:
pub:
04:38:d2:62:75:4a:18:d9:f7:fe:6a:e7:df:32:e2:
15:0f:01:9c:1b:4f:dc:ff:22:97:5c:2a:d9:5c:c3:
a3:ef:e3:90:3b:3c:8a:d2:45:b1:60:11:94:5e:a7:
51:e8:e5:5d:be:38:39:da:66:e1:99:46:0c:d3:45:
3d:76:7e:b7:8c
ASN1 OID: prime256v1
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Subject Key Identifier:
E8:9C:86:81:59:D4:D7:76:43:C7:2E:92:88:30:1B:30:A5:B3:A4:5C
X509v3 Authority Key Identifier:
keyid:5E:33:AC:E0:9D:B9:F9:71:5F:1F:96:B5:84:85:35:BE:89:8C:35:C2
X509v3 Subject Alternative Name:
DNS:www.example.com
Signature Algorithm: ecdsa-with-SHA256
30:45:02:21:00:9f:7e:93:93:af:3d:cf:7b:77:f0:55:2d:57:
9d:a9:bf:b0:8c:9c:2e:cf:b2:b4:d8:de:f3:79:c7:66:7c:e7:
4d:02:20:7e:9b:36:d1:3a:df:e4:d2:d7:3b:9d:73:c7:61:a8:
2e:a5:b1:23:10:65:81:96:b1:3b:79:d4:a6:12:fe:f2:69
```

Now you can use that CN value (**www.example.com** above, for example) from the output and use it in the **serverhostoverride** field (under the security section of the membersrvc.yaml file)

3. Last, make sure that path to the corresponding TLS Server Certificate is specified under `security.client.cert.file`
114 changes: 111 additions & 3 deletions membersrvc/ca/client_grpc.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,132 @@
package ca

import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"io/ioutil"
"time"

pb "github.com/hyperledger/fabric/membersrvc/protos"
"google.golang.org/grpc"

"github.com/spf13/viper"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)

/** Performs Certificate type validation **/
/*
* Checks for valid Cert format type
* Cert expiration
*
*/
func isValidCertFormatted(certLocation string) bool {

var isvalidCert = false
certificate, err := ioutil.ReadFile(certLocation)
if err != nil {
return false
}
block, _ := pem.Decode(certificate)
if block == nil {
certificates, err := x509.ParseCertificates(certificate)
if err != nil {
caLogger.Error("Not a valid Certificate")
} else {
validCert := validateCert(certificates[0])
if !validCert {
caLogger.Error("Certificate has expired")
}
return validCert
}
} else {
certificates, err := x509.ParseCertificates(block.Bytes)
if err != nil {
caLogger.Error("Not a valid Certificate")
} else {
validCert := validateCert(certificates[0])
if !validCert {
caLogger.Error("Certificate has expired")
}
return validCert
}
}

return isvalidCert

}

/** Given the cert , it checks for expiry
* Does not check for revocation
*/
func validateCert(cert *x509.Certificate) bool {

notBefore := cert.NotBefore
notAfter := cert.NotAfter

currentTime := time.Now()
diffFromExpiry := notAfter.Sub(currentTime)
diffFromStart := currentTime.Sub(notBefore)

return ((diffFromExpiry > 0) && (diffFromStart > 0))

}

// NewClientTLSFromFile creates Client TLS connection credentials
// @certFile : TLS Server Certificate in PEM format
// @serverNameOverride : Common Name (CN) of the TLS Server Certificate
// returns Secure Transport Credentials
//
func NewClientTLSFromFile(certFile, serverNameOverride string) (credentials.TransportCredentials, error) {
caLogger.Debug("upgrading to TLS1.2")
b, err := ioutil.ReadFile(certFile)

if err != nil {
caLogger.Errorf("Certificate could not be found in the [%s] path", certFile)
return nil, err
}

if !isValidCertFormatted(certFile) {
return nil, nil
}

cp := x509.NewCertPool()

ok := cp.AppendCertsFromPEM(b)
if !ok {
caLogger.Error("credentials: failed to append certificates: ")
return nil, nil
}
return credentials.NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp, MinVersion: 0, MaxVersion: 0}), nil
}

//GetClientConn returns a connection to the server located on *address*.
func GetClientConn(address string, serverName string) (*grpc.ClientConn, error) {

caLogger.Debug("GetACAClient: using the given gRPC client connection to return a new ACA client")
var opts []grpc.DialOption
opts = append(opts, grpc.WithInsecure())

if viper.GetBool("security.tls_enabled") {
caLogger.Debug("TLS was enabled [security.tls_enabled == true]")

creds, err := NewClientTLSFromFile(viper.GetString("security.client.cert.file"), viper.GetString("security.serverhostoverride"))

if err != nil {
caLogger.Error("Could not establish TLS client connection in GetClientConn while getting creds:")
caLogger.Error(err)
return nil, err
}
opts = append(opts, grpc.WithTransportCredentials(creds))
} else {
caLogger.Debug("TLS was not enabled [security.tls_enabled == false]")
opts = append(opts, grpc.WithInsecure())
}
opts = append(opts, grpc.WithTimeout(time.Second*3))
return grpc.Dial(address, opts...)
}

//GetACAClient returns a client to Attribute Certificate Authority.
func GetACAClient() (*grpc.ClientConn, pb.ACAPClient, error) {
caLogger.Debug("GetACAClient: Trying to create a new ACA Client from the connection provided")
conn, err := GetClientConn(viper.GetString("aca.address"), viper.GetString("aca.server-name"))
if err != nil {
return nil, nil, err
Expand Down
20 changes: 16 additions & 4 deletions membersrvc/membersrvc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,26 @@ server:
file:

security:
# Can be 256 or 384
# Must be the same as in core.yaml
# Either 256 or 384 (note: must be the exact same value as specified in the core.yaml file)
level: 256

# Can be SHA2 or SHA3
# Must be the same as in core.yaml
# Either SHA2 or SHA3 (note: must be the exact same value as specified in the core.yaml file)
hashAlgorithm: SHA3

# The server host CN (Common Name) to be used (needs to match the TLS Server Certificate)
serverhostoverride:

# Boolean (true/false) value indicating whether TLS should be used between the client and
# the various CA services (ECA, TCA, TLSCA, ACA)
tls_enabled: false

# A PEM-encoded (X509 v3, Base64) certificate to use for establishing the TLS connection
# between the client and the ACA service
client:
cert:
file:


# Enabling/disabling different logging levels of the CA.
#
logging:
Expand Down
8 changes: 7 additions & 1 deletion membersrvc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,16 +85,22 @@ func main() {
runtime.GOMAXPROCS(viper.GetInt("server.gomaxprocs"))

var opts []grpc.ServerOption
if viper.GetString("server.tls.cert.file") != "" {

if viper.GetBool("security.tls_enabled") {
logger.Debug("TLS was enabled [security.tls_enabled == true]")
creds, err := credentials.NewServerTLSFromFile(viper.GetString("server.tls.cert.file"), viper.GetString("server.tls.key.file"))
if err != nil {
logger.Panic(err)
}
opts = []grpc.ServerOption{grpc.Creds(creds)}
} else {
logger.Debug("TLS was not enabled [security.tls_enabled == false]")
}

srv := grpc.NewServer(opts...)

if viper.GetBool("aca.enabled") {
logger.Debug("ACA was enabled [aca.enabled == true]")
aca.Start(srv)
}
eca.Start(srv)
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pages:
- Fabric Network Setup: Setup/Network-setup.md
- NodeSDK Setup: Setup/NodeSDK-setup.md
- CA Setup: Setup/ca-setup.md
- TLS Setup: Setup/TLSSetup.md
- Logging: Setup/logging-control.md

- APIs:
Expand Down

0 comments on commit 925f4d9

Please sign in to comment.