From 0d9b952aeab95c59b28bcea42f719d06363b45b5 Mon Sep 17 00:00:00 2001
From: baude <bbaude@redhat.com>
Date: Mon, 16 Sep 2019 10:48:34 -0500
Subject: [PATCH] support non-standard ssh port for remote-client

when using the remote client, users may need to specify a non-standard
port for ssh connections.  we can do so on the command line and within
the remote-client configuration file.

Fixes: #3987

Signed-off-by: baude <bbaude@redhat.com>
---
 cmd/podman/cliconfig/config.go                   |  1 +
 cmd/podman/main.go                               |  5 +++++
 cmd/podman/main_local.go                         |  5 +++++
 cmd/podman/main_remote.go                        | 10 ++++++++++
 cmd/podman/remoteclientconfig/config.go          |  1 +
 cmd/podman/remoteclientconfig/configfile_test.go | 14 +++++++++++---
 docs/podman-remote.1.md                          |  4 ++++
 docs/podman-remote.conf.5.md                     |  4 ++++
 pkg/adapter/client.go                            |  2 +-
 pkg/adapter/client_unix.go                       |  8 ++++++--
 10 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index e0ce202ccb..b8796f9b30 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -41,6 +41,7 @@ type MainFlags struct {
 	VarlinkAddress       string
 	ConnectionName       string
 	RemoteConfigFilePath string
+	Port                 int
 }
 
 type AttachValues struct {
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index b83ccd9a5f..992dbe1d59 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -111,6 +111,11 @@ func before(cmd *cobra.Command, args []string) error {
 		return err
 	}
 
+	// check that global opts input is valid
+	if err := checkInput(); err != nil {
+		return err
+	}
+
 	//	Set log level; if not log-level is provided, default to error
 	logLevel := MainGlobalOpts.LogLevel
 	if logLevel == "" {
diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go
index 08d7ccaf4b..917096e179 100644
--- a/cmd/podman/main_local.go
+++ b/cmd/podman/main_local.go
@@ -267,3 +267,8 @@ func setUMask() {
 	// Be sure we can create directories with 0755 mode.
 	syscall.Umask(0022)
 }
+
+// checkInput can be used to verify any of the globalopt values
+func checkInput() error {
+	return nil
+}
diff --git a/cmd/podman/main_remote.go b/cmd/podman/main_remote.go
index a005e925c0..f617422e62 100644
--- a/cmd/podman/main_remote.go
+++ b/cmd/podman/main_remote.go
@@ -3,6 +3,7 @@
 package main
 
 import (
+	"github.com/pkg/errors"
 	"os/user"
 
 	"github.com/spf13/cobra"
@@ -18,6 +19,7 @@ func init() {
 	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.ConnectionName, "connection", "", "remote connection name")
 	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteConfigFilePath, "remote-config-path", "", "alternate path for configuration file")
 	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteUserName, "username", username, "username on the remote host")
+	rootCmd.PersistentFlags().IntVar(&MainGlobalOpts.Port, "port", 22, "port on remote host")
 	rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.RemoteHost, "remote-host", "", "remote host")
 	// TODO maybe we allow the altering of this for bridge connections?
 	// rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.VarlinkAddress, "varlink-address", adapter.DefaultAddress, "address of the varlink socket")
@@ -42,3 +44,11 @@ func setRLimits() error {
 }
 
 func setUMask() {}
+
+// checkInput can be used to verify any of the globalopt values
+func checkInput() error {
+	if MainGlobalOpts.Port < 0 || MainGlobalOpts.Port > 65536 {
+		return errors.Errorf("remote port must be between 0 and 65536")
+	}
+	return nil
+}
diff --git a/cmd/podman/remoteclientconfig/config.go b/cmd/podman/remoteclientconfig/config.go
index 01f293ec33..13880a8680 100644
--- a/cmd/podman/remoteclientconfig/config.go
+++ b/cmd/podman/remoteclientconfig/config.go
@@ -12,6 +12,7 @@ type RemoteConnection struct {
 	Destination string `toml:"destination"`
 	Username    string `toml:"username"`
 	IsDefault   bool   `toml:"default"`
+	Port        int    `toml:"port"`
 }
 
 // GetConfigFilePath is a simple helper to export the configuration file's
diff --git a/cmd/podman/remoteclientconfig/configfile_test.go b/cmd/podman/remoteclientconfig/configfile_test.go
index 66e0a46934..ea2224ea79 100644
--- a/cmd/podman/remoteclientconfig/configfile_test.go
+++ b/cmd/podman/remoteclientconfig/configfile_test.go
@@ -13,11 +13,13 @@ var goodConfig = `
 [connections.homer]
 destination = "192.168.1.1"
 username = "myuser"
+port = 22
 default = true
 
 [connections.bart]
 destination = "foobar.com"
 username = "root"
+port = 22
 `
 var noDest = `
 [connections]
@@ -26,9 +28,11 @@ var noDest = `
 destination = "192.168.1.1"
 username = "myuser"
 default = true
+port = 22
 
 [connections.bart]
 username = "root"
+port = 22
 `
 
 var noUser = `
@@ -36,6 +40,7 @@ var noUser = `
 
 [connections.homer]
 destination = "192.168.1.1"
+port = 22
 `
 
 func makeGoodResult() *RemoteConfig {
@@ -44,10 +49,12 @@ func makeGoodResult() *RemoteConfig {
 		Destination: "192.168.1.1",
 		Username:    "myuser",
 		IsDefault:   true,
+		Port:        22,
 	}
 	goodConnections["bart"] = RemoteConnection{
 		Destination: "foobar.com",
 		Username:    "root",
+		Port:        22,
 	}
 	var goodResult = RemoteConfig{
 		Connections: goodConnections,
@@ -59,6 +66,7 @@ func makeNoUserResult() *RemoteConfig {
 	var goodConnections = make(map[string]RemoteConnection)
 	goodConnections["homer"] = RemoteConnection{
 		Destination: "192.168.1.1",
+		Port:        22,
 	}
 	var goodResult = RemoteConfig{
 		Connections: goodConnections,
@@ -135,7 +143,7 @@ func TestRemoteConfig_GetDefault(t *testing.T) {
 		wantErr bool
 	}{
 		// A good toml should return the connection that is marked isDefault
-		{"good", fields{Connections: makeGoodResult().Connections}, &RemoteConnection{"192.168.1.1", "myuser", true}, false},
+		{"good", fields{Connections: makeGoodResult().Connections}, &RemoteConnection{"192.168.1.1", "myuser", true, 22}, false},
 		// If nothing is marked as isDefault and there is more than one connection, error should occur
 		{"nodefault", fields{Connections: noDefault}, nil, true},
 		// if nothing is marked as isDefault but there is only one connection, the one connection is considered the default
@@ -175,9 +183,9 @@ func TestRemoteConfig_GetRemoteConnection(t *testing.T) {
 		wantErr bool
 	}{
 		// Good connection
-		{"goodhomer", fields{Connections: makeGoodResult().Connections}, args{name: "homer"}, &RemoteConnection{"192.168.1.1", "myuser", true}, false},
+		{"goodhomer", fields{Connections: makeGoodResult().Connections}, args{name: "homer"}, &RemoteConnection{"192.168.1.1", "myuser", true, 22}, false},
 		// Good connection
-		{"goodbart", fields{Connections: makeGoodResult().Connections}, args{name: "bart"}, &RemoteConnection{"foobar.com", "root", false}, false},
+		{"goodbart", fields{Connections: makeGoodResult().Connections}, args{name: "bart"}, &RemoteConnection{"foobar.com", "root", false, 22}, false},
 		// Getting an unknown connection should result in error
 		{"noexist", fields{Connections: makeGoodResult().Connections}, args{name: "foobar"}, nil, true},
 		// Getting a connection when there are none should result in an error
diff --git a/docs/podman-remote.1.md b/docs/podman-remote.1.md
index 84042a8428..04010abaf2 100644
--- a/docs/podman-remote.1.md
+++ b/docs/podman-remote.1.md
@@ -35,6 +35,10 @@ Print usage statement
 
 Log messages above specified level: debug, info, warn, error (default), fatal or panic
 
+**--port**=*integer*
+
+Use an alternative port for the ssh connections.  The default port is 22
+
 **--remote-config-path**=*path*
 
 Alternate path for configuration file
diff --git a/docs/podman-remote.conf.5.md b/docs/podman-remote.conf.5.md
index 3e1cffb020..3c8a1a8017 100644
--- a/docs/podman-remote.conf.5.md
+++ b/docs/podman-remote.conf.5.md
@@ -22,6 +22,9 @@ of the user's remote connections.
   Denotes whether the connection is the default connection for the user.  The default connection
   is used when the user does not specify a destination or connection name to `podman`.
 
+**port** = int
+  Use an alternative port for the ssh connections.  The default port is 22.
+
 
 ## EXAMPLE
 
@@ -37,6 +40,7 @@ is designated as the default connection.
     [connections.host2]
     destination = "192.168.122.133"
     username = "fedora"
+    port = 2222
 ```
 
 ## FILES
diff --git a/pkg/adapter/client.go b/pkg/adapter/client.go
index da6ff5fd07..1805c758d3 100644
--- a/pkg/adapter/client.go
+++ b/pkg/adapter/client.go
@@ -35,7 +35,7 @@ func (r RemoteRuntime) RemoteEndpoint() (remoteEndpoint *Endpoint, err error) {
 		if len(r.cmd.RemoteUserName) < 1 {
 			return nil, errors.New("you must provide a username when providing a remote host name")
 		}
-		rc := remoteclientconfig.RemoteConnection{r.cmd.RemoteHost, r.cmd.RemoteUserName, false}
+		rc := remoteclientconfig.RemoteConnection{r.cmd.RemoteHost, r.cmd.RemoteUserName, false, r.cmd.Port}
 		remoteEndpoint, err = newBridgeConnection("", &rc, r.cmd.LogLevel)
 		//  if the user has a config file with connections in it
 	} else if len(remoteConfigConnections.Connections) > 0 {
diff --git a/pkg/adapter/client_unix.go b/pkg/adapter/client_unix.go
index 4781acd06d..a7bc7c1c0e 100644
--- a/pkg/adapter/client_unix.go
+++ b/pkg/adapter/client_unix.go
@@ -10,7 +10,11 @@ import (
 )
 
 func formatDefaultBridge(remoteConn *remoteclientconfig.RemoteConnection, logLevel string) string {
+	port := remoteConn.Port
+	if port == 0 {
+		port = 22
+	}
 	return fmt.Sprintf(
-		`ssh -T %s@%s -- /usr/bin/varlink -A \'/usr/bin/podman --log-level=%s varlink \\\$VARLINK_ADDRESS\' bridge`,
-		remoteConn.Username, remoteConn.Destination, logLevel)
+		`ssh -p %d -T %s@%s -- /usr/bin/varlink -A \'/usr/bin/podman --log-level=%s varlink \\\$VARLINK_ADDRESS\' bridge`,
+		port, remoteConn.Username, remoteConn.Destination, logLevel)
 }