SSH through a proxy server in one command line

This post shows you how to SSH through a proxy server.

Prerequisites

The local client is required to have OpenBSD netcat installed.

There are 2 versions of netcat. Traditional netcat and OpenBSD netcat. This tutorial explicitly requires OpenBSD netcat. Traditional netcat won't work. See their difference here.

In macOS:

# nc -h
usage: nc [-46AacCDdEFhklMnOortUuvz] [-K tc] [-b boundif] [-i interval] [-p source_port]
	  [--apple-recv-anyif] [--apple-awdl-unres]
	  [--apple-boundif ifbound]
	  [--apple-no-cellular] [--apple-no-expensive]
	  [--apple-no-flowadv] [--apple-tcp-timeout conntimo]
	  [--apple-tcp-keepalive keepidle] [--apple-tcp-keepintvl keepintvl]
	  [--apple-tcp-keepcnt keepcnt] [--apple-tclass tclass]
	  [--tcp-adp-rtimo num_probes] [--apple-initcoproc-allow]
	  [--apple-tcp-adp-wtimo num_probes]
	  [--setsockopt-later] [--apple-no-connectx]
	  [--apple-delegate-pid pid] [--apple-delegate-uuid uuid]
	  [--apple-kao] [--apple-ext-bk-idle]
	  [--apple-netsvctype svc] [---apple-nowakefromsleep]
	  [--apple-notify-ack] [--apple-sockev]
	  [--apple-tos tos] [--apple-tos-cmsg]
	  [-s source_ip_address] [-w timeout] [-X proxy_version]
	  [-x proxy_address[:port]] [hostname] [port[s]]
	Command Summary:
	-4                            Use IPv4
	-6                            Use IPv6
	-A                            Set SO_RECV_ANYIF on socket
	--apple-recv-anyif
	-a                            Set SO_AWDL_UNRESTRICTED on socket
	--apple-awdl-unres
	-b ifbound                    Bind socket to interface
	--apple-boundif ifbound
	-C                            Don't use cellular connection
	-c                            Send CRLF as line-ending
	--apple-no-cellular
	-D                            Enable the debug socket option
	-d                            Detach from stdin
	-E                            Don't use expensive interfaces
	--apple-no-expensive
	-F                            Do not use flow advisory (flow adv enabled by default)
	--apple-no-flowadv
	-G conntimo                   Connection timeout in seconds
	--apple-tcp-timeout conntimo
	-H keepidle                   Initial idle timeout in seconds
	--apple-tcp-keepalive keepidle
	-h                            This help text
	-I keepintvl                  Interval for repeating idle timeouts in seconds
	--apple-tcp-keepintvl keepintvl
	-i secs                       Delay interval for lines sent, ports scanned
	-J keepcnt                    Number of times to repeat idle timeout
	--apple-tcp-keepcnt keepcnt
	-K tclass                     Specify traffic class
	--apple-tclass tclass
	-k                            Keep inbound sockets open for multiple connects
	-L num_probes                 Number of probes to send before generating a read timeout event
	--tcp-adp-rtimo num_probes
	-l                            Listen mode, for inbound connects
	-m                            Set SO_INTCOPROC_ALLOW on socket
	--apple-initcoproc-allow
	-N num_probes                 Number of probes to send before generating a write timeout event
	--apple-tcp-adp-wtimo num_probes
	-o                            Issue socket options after connect/bind
	-n                            Suppress name/port resolutions
	--setsockopt-later
	-O                            Use old-style connect instead of connectx
	--apple-no-connectx
	--apple-delegate-pid pid      Set socket as delegate using pid
	-p port                       Specify local port for remote connects (cannot use with -l)
	-r                            Randomize remote ports
	-s addr                       Local source address
	-t                            Answer TELNET negotiation
	-U                            Use UNIX domain socket
	-u                            UDP mode
	-v                            Verbose
	-w secs                       Timeout for connects and final net reads
	-X proto                      Proxy protocol: "4", "5" (SOCKS) or "connect"
	-x addr[:port]                Specify proxy address and port
	-z                            Zero-I/O mode [used for scanning]
	--apple-delegate-uuid uuid    Set socket as delegate using uuid
	--apple-ecn mode              Set the ECN mode
	--apple-ext-bk-idle           Extended background idle time
	--apple-kao                   Set keep alive offload
	--apple-netsvctype            Set the network service type
	--apple-nowakefromsleep n      No wake from sleep (when n >= 2 generate KEV_SOCKET_CLOSED)
	--apple-notify-ack            Receive events when data gets acknowledged
	--apple-sockev                Receive and print socket events
	--apple-tos tos               Set the IP_TOS or IPV6_TCLASS option
	--apple-tos-cmsg              Set the IP_TOS or IPV6_TCLASS option via cmsg
	--apple-no-reuseport          Do not use the SO_REUSPORT socket option
Port numbers can be individual or ranges: lo-hi [inclusive]

In Linux, with OpenBSD netcat:

# nc -h    
OpenBSD netcat (Debian patchlevel 1)
usage: nc [-46CDdFhklNnrStUuvZz] [-I length] [-i interval] [-M ttl]
	  [-m minttl] [-O length] [-P proxy_username] [-p source_port]
	  [-q seconds] [-s sourceaddr] [-T keyword] [-V rtable] [-W recvlimit]
	  [-w timeout] [-X proxy_protocol] [-x proxy_address[:port]]
	  [destination] [port]
	Command Summary:
		-4		Use IPv4
		-6		Use IPv6
		-b		Allow broadcast
		-C		Send CRLF as line-ending
		-D		Enable the debug socket option
		-d		Detach from stdin
		-F		Pass socket fd
		-h		This help text
		-I length	TCP receive buffer length
		-i interval	Delay interval for lines sent, ports scanned
		-k		Keep inbound sockets open for multiple connects
		-l		Listen mode, for inbound connects
		-M ttl		Outgoing TTL / Hop Limit
		-m minttl	Minimum incoming TTL / Hop Limit
		-N		Shutdown the network socket after EOF on stdin
		-n		Suppress name/port resolutions
		-O length	TCP send buffer length
		-P proxyuser	Username for proxy authentication
		-p port		Specify local port for remote connects
		-q secs		quit after EOF on stdin and delay of secs
		-r		Randomize remote ports
		-S		Enable the TCP MD5 signature option
		-s sourceaddr	Local source address
		-T keyword	TOS value
		-t		Answer TELNET negotiation
		-U		Use UNIX domain socket
		-u		UDP mode
		-V rtable	Specify alternate routing table
		-v		Verbose
		-W recvlimit	Terminate after receiving a number of packets
		-w timeout	Timeout for connects and final net reads
		-X proto	Proxy protocol: "4", "5" (SOCKS) or "connect"
		-x addr[:port]	Specify proxy address and port
		-Z		DCCP mode
		-z		Zero-I/O mode [used for scanning]
	Port numbers can be individual or ranges: lo-hi [inclusive]

In Linux, with GNU netcat:

# nc --help   
GNU netcat 0.7.1, a rewrite of the famous networking tool.
Basic usages:
connect to somewhere:  nc [options] hostname port [port] ...
listen for inbound:    nc -l -p port [options] [hostname] [port] ...
tunnel to somewhere:   nc -L hostname:port -p port [options]

Mandatory arguments to long options are mandatory for short options too.
Options:
  -c, --close                close connection on EOF from stdin
  -e, --exec=PROGRAM         program to exec after connect
  -g, --gateway=LIST         source-routing hop point[s], up to 8
  -G, --pointer=NUM          source-routing pointer: 4, 8, 12, ...
  -h, --help                 display this help and exit
  -i, --interval=SECS        delay interval for lines sent, ports scanned
  -l, --listen               listen mode, for inbound connects
  -L, --tunnel=ADDRESS:PORT  forward local port to remote address
  -n, --dont-resolve         numeric-only IP addresses, no DNS
  -o, --output=FILE          output hexdump traffic to FILE (implies -x)
  -p, --local-port=NUM       local port number
  -r, --randomize            randomize local and remote ports
  -s, --source=ADDRESS       local source address (ip or hostname)
  -t, --tcp                  TCP mode (default)
  -T, --telnet               answer using TELNET negotiation
  -u, --udp                  UDP mode
  -v, --verbose              verbose (use twice to be more verbose)
  -V, --version              output version information and exit
  -x, --hexdump              hexdump incoming and outgoing traffic
  -w, --wait=SECS            timeout for connects and final net reads
  -z, --zero                 zero-I/O mode (used for scanning)

Remote port number can also be specified as range.  Example: '1-1024'

Install netcat

In Ubuntu: use netcat-openbsd (not netcat-traditional)

sudo apt install -yq netcat-openbsd

In Arch Linux: use openbsd-netcat (not netcat)

sudo pacman -Sy openbsd-netcat

(Optional) Start the proxy server at local

If you already have a proxy server running, you can skip this section.
To use an existing remote server, called proxy.server with ssh access as a SOCKS proxy, use the following command line.

ssh -D 12345 -CqNT proxy.server
  • -D sock channel port specifier
  • -C: compress data
  • -q: quiet mode, no warning
  • -N: open SSH connection with no command to be executed
  • -T: do not allocate a TTY-terminal, save a tiny amount of memory

For more reliable command options in practice (such as -o "ServerAliveInterval 30" -o "ServerAliveCountMax 100" flag, autossh, etc.), check this post.

This command opens a SOCKS server at localhost at port 12345. Note: this port must be available at the local client.

Use the SSH command through the SOCKS proxy server

To ssh to a proxied server at proxied.server, through a SOCKS v5 proxy server localhost at port 12345, use the following command line:

ssh -o ProxyCommand="nc -X 5 -x localhost:12345 %h %p" proxied.server

If you are using the GNU version of netcat (while we require the OpenBSD version), there will be an error.

nc: invalid option -- 'X'
Try `nc --help' for more information.
kex_exchange_identification: Connection closed by remote host
Connection closed by UNKNOWN port 65535

To fix it, you can install the netcat OpenBSD version.