Add TLS encryption capability to RPC clients (#2789)

This commit is contained in:
Anis Elleuch 2016-09-30 07:42:37 +01:00 committed by Harshavardhana
parent 1e6afac3bd
commit 9fb1c89f81
8 changed files with 51 additions and 11 deletions

View File

@ -83,6 +83,7 @@ func isRPCTokenValid(tokenStr string) bool {
type authConfig struct { type authConfig struct {
accessKey string // Username for the server. accessKey string // Username for the server.
secretKey string // Password for the server. secretKey string // Password for the server.
secureConn bool // Ask for a secured connection
address string // Network address path of RPC server. address string // Network address path of RPC server.
path string // Network path for HTTP dial. path string // Network path for HTTP dial.
loginMethod string // RPC service name for authenticating using JWT loginMethod string // RPC service name for authenticating using JWT
@ -104,7 +105,7 @@ func newAuthClient(cfg *authConfig) *AuthRPCClient {
// Save the config. // Save the config.
config: cfg, config: cfg,
// Initialize a new reconnectable rpc client. // Initialize a new reconnectable rpc client.
rpc: newClient(cfg.address, cfg.path), rpc: newClient(cfg.address, cfg.path, cfg.secureConn),
// Allocated auth client not logged in yet. // Allocated auth client not logged in yet.
isLoggedIn: false, isLoggedIn: false,
} }

View File

@ -87,6 +87,7 @@ func healControl(ctx *cli.Context) {
authCfg := &authConfig{ authCfg := &authConfig{
accessKey: serverConfig.GetCredential().AccessKeyID, accessKey: serverConfig.GetCredential().AccessKeyID,
secretKey: serverConfig.GetCredential().SecretAccessKey, secretKey: serverConfig.GetCredential().SecretAccessKey,
secureConn: parsedURL.Scheme == "https",
address: parsedURL.Host, address: parsedURL.Host,
path: path.Join(reservedBucket, controlPath), path: path.Join(reservedBucket, controlPath),
loginMethod: "Controller.LoginHandler", loginMethod: "Controller.LoginHandler",

View File

@ -126,6 +126,7 @@ func lockControl(c *cli.Context) {
authCfg := &authConfig{ authCfg := &authConfig{
accessKey: serverConfig.GetCredential().AccessKeyID, accessKey: serverConfig.GetCredential().AccessKeyID,
secretKey: serverConfig.GetCredential().SecretAccessKey, secretKey: serverConfig.GetCredential().SecretAccessKey,
secureConn: parsedURL.Scheme == "https",
address: parsedURL.Host, address: parsedURL.Host,
path: path.Join(reservedBucket, controlPath), path: path.Join(reservedBucket, controlPath),
loginMethod: "Controller.LoginHandler", loginMethod: "Controller.LoginHandler",

View File

@ -66,6 +66,7 @@ func shutdownControl(c *cli.Context) {
authCfg := &authConfig{ authCfg := &authConfig{
accessKey: serverConfig.GetCredential().AccessKeyID, accessKey: serverConfig.GetCredential().AccessKeyID,
secretKey: serverConfig.GetCredential().SecretAccessKey, secretKey: serverConfig.GetCredential().SecretAccessKey,
secureConn: parsedURL.Scheme == "https",
address: parsedURL.Host, address: parsedURL.Host,
path: path.Join(reservedBucket, controlPath), path: path.Join(reservedBucket, controlPath),
loginMethod: "Controller.LoginHandler", loginMethod: "Controller.LoginHandler",

View File

@ -294,7 +294,7 @@ func (l *lockServer) lockMaintenance(interval time.Duration) {
// Validate if long lived locks are indeed clean. // Validate if long lived locks are indeed clean.
for _, nlrip := range nlripLongLived { for _, nlrip := range nlripLongLived {
// Initialize client based on the long live locks. // Initialize client based on the long live locks.
c := newClient(nlrip.lri.node, nlrip.lri.rpcPath) c := newClient(nlrip.lri.node, nlrip.lri.rpcPath, isSSL())
var expired bool var expired bool

View File

@ -45,6 +45,7 @@ func initDsyncNodes(disks []string, port int) error {
accessKey: cred.AccessKeyID, accessKey: cred.AccessKeyID,
secretKey: cred.SecretAccessKey, secretKey: cred.SecretAccessKey,
// Construct a new dsync server addr. // Construct a new dsync server addr.
secureConn: isSSL(),
address: disk[:idx] + ":" + serverPort, address: disk[:idx] + ":" + serverPort,
// Construct a new rpc path for the disk. // Construct a new rpc path for the disk.
path: pathutil.Join(lockRPCPath, disk[idx+1:]), path: pathutil.Join(lockRPCPath, disk[idx+1:]),

View File

@ -17,7 +17,12 @@
package cmd package cmd
import ( import (
"bufio"
"crypto/tls"
"errors" "errors"
"io"
"net"
"net/http"
"net/rpc" "net/rpc"
"sync" "sync"
) )
@ -28,15 +33,17 @@ type RPCClient struct {
rpcPrivate *rpc.Client rpcPrivate *rpc.Client
node string node string
rpcPath string rpcPath string
secureConn bool
} }
// newClient constructs a RPCClient object with node and rpcPath initialized. // newClient constructs a RPCClient object with node and rpcPath initialized.
// It _doesn't_ connect to the remote endpoint. See Call method to see when the // It _doesn't_ connect to the remote endpoint. See Call method to see when the
// connect happens. // connect happens.
func newClient(node, rpcPath string) *RPCClient { func newClient(node, rpcPath string, secureConn bool) *RPCClient {
return &RPCClient{ return &RPCClient{
node: node, node: node,
rpcPath: rpcPath, rpcPath: rpcPath,
secureConn: secureConn,
} }
} }
@ -63,14 +70,41 @@ func (rpcClient *RPCClient) dialRPCClient() (*rpc.Client, error) {
if rpcClient.rpcPrivate != nil { if rpcClient.rpcPrivate != nil {
return rpcClient.rpcPrivate, nil return rpcClient.rpcPrivate, nil
} }
rpc, err := rpc.DialHTTPPath("tcp", rpcClient.node, rpcClient.rpcPath)
var err error
var conn net.Conn
if rpcClient.secureConn {
conn, err = tls.Dial("tcp", rpcClient.node, &tls.Config{})
} else {
conn, err = net.Dial("tcp", rpcClient.node)
}
if err != nil { if err != nil {
return nil, err return nil, err
} else if rpc == nil { }
io.WriteString(conn, "CONNECT "+rpcClient.rpcPath+" HTTP/1.0\n\n")
// Require successful HTTP response
// before switching to RPC protocol.
resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "CONNECT"})
if err == nil && resp.Status == "200 Connected to Go RPC" {
rpc := rpc.NewClient(conn)
if rpc == nil {
return nil, errors.New("No valid RPC Client created after dial") return nil, errors.New("No valid RPC Client created after dial")
} }
rpcClient.rpcPrivate = rpc rpcClient.rpcPrivate = rpc
return rpcClient.rpcPrivate, nil return rpc, nil
}
if err == nil {
err = errors.New("unexpected HTTP response: " + resp.Status)
}
conn.Close()
return nil, &net.OpError{
Op: "dial-http",
Net: rpcClient.node + " " + rpcClient.rpcPath,
Addr: nil,
Err: err,
}
} }
// Call makes a RPC call to the remote endpoint using the default codec, namely encoding/gob. // Call makes a RPC call to the remote endpoint using the default codec, namely encoding/gob.

View File

@ -107,6 +107,7 @@ func newRPCClient(networkPath string) (StorageAPI, error) {
rpcClient := newAuthClient(&authConfig{ rpcClient := newAuthClient(&authConfig{
accessKey: cred.AccessKeyID, accessKey: cred.AccessKeyID,
secretKey: cred.SecretAccessKey, secretKey: cred.SecretAccessKey,
secureConn: isSSL(),
address: rpcAddr, address: rpcAddr,
path: rpcPath, path: rpcPath,
loginMethod: "Storage.LoginHandler", loginMethod: "Storage.LoginHandler",