From 9fb1c89f818791668f997569e609208357e2ad17 Mon Sep 17 00:00:00 2001 From: Anis Elleuch Date: Fri, 30 Sep 2016 07:42:37 +0100 Subject: [PATCH] Add TLS encryption capability to RPC clients (#2789) --- cmd/auth-rpc-client.go | 3 ++- cmd/control-heal-main.go | 1 + cmd/control-lock-main.go | 1 + cmd/control-shutdown-main.go | 1 + cmd/lock-rpc-server.go | 2 +- cmd/namespace-lock.go | 3 ++- cmd/net-rpc-client.go | 50 ++++++++++++++++++++++++++++++------ cmd/storage-rpc-client.go | 1 + 8 files changed, 51 insertions(+), 11 deletions(-) diff --git a/cmd/auth-rpc-client.go b/cmd/auth-rpc-client.go index 89f2bd967..dbaaaee3e 100644 --- a/cmd/auth-rpc-client.go +++ b/cmd/auth-rpc-client.go @@ -83,6 +83,7 @@ func isRPCTokenValid(tokenStr string) bool { type authConfig struct { accessKey string // Username for the server. secretKey string // Password for the server. + secureConn bool // Ask for a secured connection address string // Network address path of RPC server. path string // Network path for HTTP dial. loginMethod string // RPC service name for authenticating using JWT @@ -104,7 +105,7 @@ func newAuthClient(cfg *authConfig) *AuthRPCClient { // Save the config. config: cfg, // 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. isLoggedIn: false, } diff --git a/cmd/control-heal-main.go b/cmd/control-heal-main.go index 96d8c8035..056bf4bea 100644 --- a/cmd/control-heal-main.go +++ b/cmd/control-heal-main.go @@ -87,6 +87,7 @@ func healControl(ctx *cli.Context) { authCfg := &authConfig{ accessKey: serverConfig.GetCredential().AccessKeyID, secretKey: serverConfig.GetCredential().SecretAccessKey, + secureConn: parsedURL.Scheme == "https", address: parsedURL.Host, path: path.Join(reservedBucket, controlPath), loginMethod: "Controller.LoginHandler", diff --git a/cmd/control-lock-main.go b/cmd/control-lock-main.go index 508b5a15c..2c8fba060 100644 --- a/cmd/control-lock-main.go +++ b/cmd/control-lock-main.go @@ -126,6 +126,7 @@ func lockControl(c *cli.Context) { authCfg := &authConfig{ accessKey: serverConfig.GetCredential().AccessKeyID, secretKey: serverConfig.GetCredential().SecretAccessKey, + secureConn: parsedURL.Scheme == "https", address: parsedURL.Host, path: path.Join(reservedBucket, controlPath), loginMethod: "Controller.LoginHandler", diff --git a/cmd/control-shutdown-main.go b/cmd/control-shutdown-main.go index 28b6d9457..806be942a 100644 --- a/cmd/control-shutdown-main.go +++ b/cmd/control-shutdown-main.go @@ -66,6 +66,7 @@ func shutdownControl(c *cli.Context) { authCfg := &authConfig{ accessKey: serverConfig.GetCredential().AccessKeyID, secretKey: serverConfig.GetCredential().SecretAccessKey, + secureConn: parsedURL.Scheme == "https", address: parsedURL.Host, path: path.Join(reservedBucket, controlPath), loginMethod: "Controller.LoginHandler", diff --git a/cmd/lock-rpc-server.go b/cmd/lock-rpc-server.go index 493d3b351..837bc41ea 100644 --- a/cmd/lock-rpc-server.go +++ b/cmd/lock-rpc-server.go @@ -294,7 +294,7 @@ func (l *lockServer) lockMaintenance(interval time.Duration) { // Validate if long lived locks are indeed clean. for _, nlrip := range nlripLongLived { // 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 diff --git a/cmd/namespace-lock.go b/cmd/namespace-lock.go index 4b3cdae0e..4545857a9 100644 --- a/cmd/namespace-lock.go +++ b/cmd/namespace-lock.go @@ -45,7 +45,8 @@ func initDsyncNodes(disks []string, port int) error { accessKey: cred.AccessKeyID, secretKey: cred.SecretAccessKey, // Construct a new dsync server addr. - address: disk[:idx] + ":" + serverPort, + secureConn: isSSL(), + address: disk[:idx] + ":" + serverPort, // Construct a new rpc path for the disk. path: pathutil.Join(lockRPCPath, disk[idx+1:]), loginMethod: "Dsync.LoginHandler", diff --git a/cmd/net-rpc-client.go b/cmd/net-rpc-client.go index b2e0500b6..f2d7fab2b 100644 --- a/cmd/net-rpc-client.go +++ b/cmd/net-rpc-client.go @@ -17,7 +17,12 @@ package cmd import ( + "bufio" + "crypto/tls" "errors" + "io" + "net" + "net/http" "net/rpc" "sync" ) @@ -28,15 +33,17 @@ type RPCClient struct { rpcPrivate *rpc.Client node string rpcPath string + secureConn bool } // 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 // connect happens. -func newClient(node, rpcPath string) *RPCClient { +func newClient(node, rpcPath string, secureConn bool) *RPCClient { return &RPCClient{ - node: node, - rpcPath: rpcPath, + node: node, + rpcPath: rpcPath, + secureConn: secureConn, } } @@ -63,14 +70,41 @@ func (rpcClient *RPCClient) dialRPCClient() (*rpc.Client, error) { if 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 { return nil, err - } else if rpc == nil { - return nil, errors.New("No valid RPC Client created after dial") } - rpcClient.rpcPrivate = rpc - return rpcClient.rpcPrivate, 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") + } + rpcClient.rpcPrivate = rpc + 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. diff --git a/cmd/storage-rpc-client.go b/cmd/storage-rpc-client.go index 09251d1eb..c578c5fe9 100644 --- a/cmd/storage-rpc-client.go +++ b/cmd/storage-rpc-client.go @@ -107,6 +107,7 @@ func newRPCClient(networkPath string) (StorageAPI, error) { rpcClient := newAuthClient(&authConfig{ accessKey: cred.AccessKeyID, secretKey: cred.SecretAccessKey, + secureConn: isSSL(), address: rpcAddr, path: rpcPath, loginMethod: "Storage.LoginHandler",