SSH - the Secure SHell

mail

Extra configuration directives for ~/.ssh/authorized_keys

from="192.168.15.*" ssh-ecdsa sshKey
accept the key only for clients coming from the 192.168.15/24 network :
from="!*.public.ssi.gouv.fr, *.ssi.gouv.fr" ssh-ecdsa sshKey
accept the key only for clients coming from ssi.gouv.fr but not public.ssi.gouv.fr
no-agent-forwarding ssh-ecdsa sshKey
disable the agent forwarding for the specified key
command="someCommand" ssh-ed25519 sshKey
will execute someCommand automatically when the corresponding key is used for authentication. Extra command/arguments supplied by the user (if any) will be ignored.
mail

sshd hardening

Server configuration and security settings (sources : ANSSI, 1)

In the configuration file :

Use SSH protocol version 2 only (version 1 has flaws allowing MitM attacks) : this is not necessary since support of the version 1 of the protocol has been removed (source). There remains only one supported version : the version 2, and no need for a Protocol configuration directive anymore.
Protocol 2
Deny root login :
PermitRootLogin no
Disable password authentication (source) :
Disable port forwarding (unless this is explicitly required, by X2Go, for instance ) :
AllowTcpForwarding no
Disable X11 forwarding (which could allow : key logging, sending signals to applications, redirecting display) :
X11Forwarding no
ForwardX11Trusted no
Define ACL (or consider PAM) :
  • whitelist users : AllowUsers kevin stuart bob
  • whitelist users and the client IP address : AllowUsers admin@192.168.17/24
  • whitelist groups / IP addresses : AllowGroups developers wheel@192.168.17/24
  • blacklist users : DenyUsers root alice bob
  • blacklist groups : DenyGroups others
  • To explicitly blacklist all the existing local users (except kevin, stuart and bob), you can quickly generate the list with :
    cut -d: -f1 /etc/passwd | grep -Ev 'kevin|stuart|bob' | tr '\n' ' '
Check directories / key files permissions of the user side before opening a session :
StrictModes yes
Automatically logout idle users after n seconds of inactivity :
ClientAliveInterval n
ClientAliveCountMax 0
Ignore ~/.rhosts and ~/.shosts files (if they exist) :
IgnoreRhosts yes
Disable host-based authentication :
HostbasedAuthentication no
Disable empty passwords :
PermitEmptyPasswords no
Login restrictions :
MaxAuthTries 2
LoginGraceTime 30
Allow strong ciphers only (source)
  • add the directives below to /etc/ssh/sshd_config
  • or (even better) :
    1. add the directives to /etc/ssh/sshd_config.d/cyphers.conf
    2. make sure it's "included" by /etc/ssh/sshd_config with a line such as :
      Include /etc/ssh/sshd_config.d/*.conf
  • KexAlgorithms curve25519-sha256, curve25519-sha256@libssh.org, diffie-hellman-group16-sha512, diffie-hellman-group18-sha512, diffie-hellman-group-exchange-sha256
    Ciphers chacha20-poly1305@openssh.com, aes256-gcm@openssh.com, aes128-gcm@openssh.com, aes256-ctr, aes192-ctr, aes128-ctr
    MACs hmac-sha2-256-etm@openssh.com, hmac-sha2-512-etm@openssh.com, umac-128-etm@openssh.com
    HostKeyAlgorithms ssh-ed25519, ssh-ed25519-cert-v01@openssh.com, sk-ssh-ed25519@openssh.com, sk-ssh-ed25519-cert-v01@openssh.com, rsa-sha2-256, rsa-sha2-512, rsa-sha2-256-cert-v01@openssh.com, rsa-sha2-512-cert-v01@openssh.com
By default sshd listens on all available interfaces and IP addresses on the system. You can limit this with :
Port 443
ListenAddress 192.168.1.5
Or :
ListenAddress 192.168.1.5:443
Changing the port sshd listens on does NOT make it more secure. It will only lower the number of port scans and make your logs lighter.
Disable compression (source) :
Compression no

Filter sshd port (defaults to TCP 22) (See NetFilter) :

iptables -I INPUT -s 192.168.1.0/24 -m state --state NEW -p tcp --dport 22 -j ACCEPT

Rate-limit incoming connections :

#!/bin/bash
interface=eth1
sshPort=22
iptables -I INPUT -p tcp --dport $sshPort -i $interface -m state --state NEW -m recent --set
iptables -I INPUT -p tcp --dport $sshPort -i $interface -m state --state NEW -m recent --update --seconds 60 --hitcount 5 -j DROP
Or consider fail2ban to block brute force attacks (if you don't mind it eating your CPU...).

Use TCP Wrappers :

It's a host-based networking ACL system, used to filter network access to Internet.
Edit /etc/hosts.allow to allow SSH only from 192.168.1.2 and 172.16.23.12 :
sshd : 192.168.1.2 172.16.23.12

Last but not least :

  • analyze logs (logwatch, logcheck)
  • keep software up-to-date (apt upgrade & al.)

Test the configuration :

  • test mode : sshd -t
  • extended test mode : sshd -T
mail

SSH keys types

Key type Length [bits] Comment
min. default max.
DSA exactly 1024 Deprecated since OpenSSH 7.0, should not be used anymore (source, details)
ECDSA ssh-keygen -b determines the key length by selecting from one of three elliptic curve sizes: 256, 384 or 521 bits. All other values will fail.
  • Is ECDSA obsolete/deprecated?
  • key strength depends on the actual randomness of the number generator
  • The 521 bits elliptic curve size is not a typo, it's _really_ 521 (not 512 as I thought , details)
Ed25519 keys have a fixed length and ssh-keygen -b flag will be ignored
  • probably the strongest mathematically (and also the fastest), but not yet widely supported
  • stronger encryption (password-protection) of the private key by default than other key types
RSA 1024 2048
  • Generally, 2048 bits is considered sufficient.
  • The best bet if you can't use Ed25519.

  • If you need more security than RSA-2048 offers, the way to go would be to switch to elliptic curve cryptography — not to continue using RSA. (source)
  • If supported, ECDSA should be used instead of RSA.
  • Virtual machines and machines having a short uptime have not enough entropy to benefit from cryptographically-acceptable randomness and generate strong keys.
mail

known_hosts

The known_hosts file exists in 2 flavors : Both contain public host keys for all known hosts.

known_hosts format (source) :

clear text

  • Entries are space-separated
  • Lines starting with # and empty lines are considered as comments and ignored
Fields :
  1. markers : one of @cert-authority or @revoked
  2. hostnames :
    • comma-separated list of patterns, including wildcards ? or *, negation !, or stuff like : [hostname]:nonStandardPortNumber
    • lines starting with a | : 1 hashed host name only, no wildcards
  3. keyType : taken directly from the host public key
  4. base64-encoded key : taken directly from the host public key
  5. comment : for humans only, field ignored

hashed

Alternately, hostnames may be stored in a hashed form which hides host names and addresses should the file's contents be disclosed.
  • hashed hostnames start with a | character
  • only one hashed hostname may appear on a single line
  • none of the above negation or wildcard operators may be applied

~/.ssh/known_hosts snippet :

example.com,12.34.56.78                                ssh-rsa             AAAAB3NzaC1yc2EAAAABIEYsdlLJizHhbn2mUjvSAHQqZETYP8
[87.65.43.21]:443                                        ssh-ed25519         AAAAC3NzaC1lZDI1NTE5AAAAIJ/ycvU99wR2V5R0DoRFUKLJu5gHHzbTeclrKCNazPF6
[myServer]:443                                           ssh-ed25519         AAAAC3NzaC1lZDI1NTE5AAAAIJ/ycvU99wR2V5R0DoRFUKLJu5gHHzbTeclrKCNazPF6
otherServer,10.200.2.133                               ssh-ed25519         AAAAC3NzaC1lZDI1NTE5AAAAIFJedssLBKbi/JNVr+D/LjBPQxz/lh0QWp/9Gqq1lQE4

<----------------------- hostname -------------------------> <---- keyType ----> <------------------------ host public key -------------------------------> <- comment ->

|1|grfd/OaC6DWyOMR7SnNf9B/16VA=|2uogKOBT5cudir2KPB7osUhcGw0= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItboumF44X/ijdaL6wPg9EGXbDKS9Euljb1QzNZ+URKQDHNQfvDZI= foo bar baz
|1|dsRkLZMgihkLV7iIYaOj+c2E9tQ=|jv1y+ZlW6V5Ur2+ZokGAZqx01Ks= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbLCnAw+yUtX4Kk200cPqrHaujTP0k9HfMD0ePH35GNI6cw8Kbs4= hello world
|1|19DOvudcdXmK3NyfRa3bKdWDF4k=|NpVnuE2D07Je9aa5i/TUXz5ZAnE= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbLCnAw+yUtX4Kk200cPqrHaujTP0k9HfMD0ePH35GNI6cw8Kbs4= ga bu zo meu
mail

How can an SSH connection succeed / fail intermittently with no error message despite verbose mode ?

Situation

Details

Looks like you've done nothing wrong. The problem must be :

Solution

There are chances you actually shot your own foot with hardened security rules like this .
mail

ssh-agent

Setup :

apt install openssh-client

Starting ssh-agent :

  • Was it automatically started at login :
    ps aux | grep [s]sh-agent
    bob      982  0.0  0.0  11088   328 ?        Ss   Oct08   0:00 /usr/bin/ssh-agent startxfce4
  • env | grep SSH_
    	SSH_AUTH_SOCK=/tmp/ssh-Nmp81HTtfifX/agent.3012
    	SSH_AGENT_PID=3082
    
    check the agent is running :
    	echo $SSH_AUTH_SOCK
    	/!\ not '$SSH_AGENT_SOCK' as said in the doc

Manage private keys :

  • add a private key :
    ssh-add ~/.ssh/myPrivateKey
  • list known keys :
    ssh-add -l
    2048 SHA256:cMdVUp8pTJGbLYS6Ngrl5odnFYGhoBXGY1p8pTJGbLTk+drlkgE /home/kevin/.ssh/myPrivateKey (RSA)
  • delete all keys :
    ssh-add -D
Looks like under some conditions, ssh-agent forgets keys after a reboot (don't know whether this is context-specific or the default behavior. Details). Anyway, to permanently add keys to the agent, no need for
ssh-add myKey
in ~/.bashrc. Instead, use AddKeysToAgent.
run the agent manually (should not be necessary) :
	eval `ssh-agent`
	==> read why we "eval" instead of "running" it : https://unix.stackexchange.com/questions/622700/why-do-we-run-ssh-agent-with-eval

kill the agent :
	ssh-agent -k
mail

SSH client configuration

Configuration files :

/etc/ssh/ssh_config
system-wide settings
~/.ssh/config
personal user configuration
~/.ssh/known_hosts
local cache of host (i.e. server) keys
~/.ssh/authorized_keys
list of public keys that will be accepted for key-based logins
Usage of ~/.ssh/authorized_keys2 for SSH protocol 2 is deprecated since 2001 (sources : 1, 2).

Configuration directives :

Flag Default Usage
BindInterface Use the address of the specified interface on the local machine as the source address of the connection
This feature was introduced by OpenSSH 7.7. As of June 2019, it must be installed —on Debian Stretch— via the testing repository (details, sources : 1, 2).
Compression no Specifies whether to use compression
ControlMaster Share multiple sessions over a single network connection. Values :
  • no : default
  • yes : enable multiplexed connections by re-using a master connection. Connect normally (i.e. open a new connection) if using the master connection fails.
  • ask : as above + requires confirmation via ssh-askpass
  • auto : use master connection, create it if missing
  • autoask : as above + ask confirmation like the ask option
ControlPath Specify
  • the path to the control socket used for connection sharing as described in ControlMaster
  • or none to disable connection sharing
ControlPersist Used with ControlMaster to specify how the master connection should remain open in the background :
  • no (default) : do not leave the master connection open in the background (waiting for future client connections) and close it as soon as the initial client connection is closed
  • yes or 0 : leave the master connection open in the background forever (or until closed explicitly)
  • ns : the master connection stays open in the background up to n seconds of inactivity
HashKnownHosts no hash host names and addresses when they are added to ~/.ssh/known_hosts so that they do not visually reveal identifying information if the file's contents are disclosed.
Include someFile
  • include the specified configuration file(s)
  • multiple pathnames may be specified and each pathname may contain wildcards (such as ~)
  • files without absolute paths are assumed to be in :
    • ~/.ssh if included in a user configuration file
    • /etc/ssh if included from the system configuration file
    For this reason, Included configuration files related to SSHFS mounts declared in /etc/fstab should have an absolute path.
  • Include may appear inside a Match or Host block to perform conditional inclusion
LogLevel LEVEL (explicit)
ProxyCommand Specifies the command to use to connect to the server, typically when there is an intermediate hop between the SSH client and server (examples : 1, 2, 3)
ProxyJump Specify one or more jump proxies :
  • syntax :
  • Multiple proxies may be separated by comma characters and will be visited sequentially.
  • SSH will connect to the target host by first making a ssh connection to the specified jump host and then establishing a TCP forwarding to the ultimate target from there.
  • ProxyCommand vs ProxyJump
  • OpenSSH/Cookbook/Proxies and Jump Hosts
  • ProxyJump competes with ProxyCommand : whichever is specified 1st wins.
  • configuration for the destination host (either supplied via the command-line or the configuration file) is not generally applied to jump hosts. ~/.ssh/config should be used if specific configuration is required for jump hosts.
  • ProxyJump came with OpenSSH 7.3 (2016-08-01). It is absolutely stunning that so many hosts are not up-to-date and still lack this feature today .
ServerAliveInterval n send keepalive messages every n seconds when the connection is idle (details). Defaults to 0 (i.e. disabled).
StrictHostKeyChecking ask
If you're using temporary VMs and want to avoid polluting ~/.ssh/known_hosts with irrelevant host keys, see UserKnownHostsFile instead.
  • yes
    • never automatically add host keys to ~/.ssh/known_hosts
    • refuse to connect to hosts whose host key has changed
    • provide maximum protection against man-in-the-middle (MITM) attacks, though it can be annoying when /etc/ssh/ssh_known_hosts is poorly maintained or when connections to new hosts are frequently made. This option forces the user to manually add all new hosts.
  • accept-new
    • automatically add new host keys to ~/.ssh/known_hosts
    • refuse to connect to hosts whose host key has changed
  • no or off
    • automatically add new host keys to ~/.ssh/known_hosts
    • allow connections to hosts whose host key has changed (some restrictions, though)
  • ask
    • prompt the user before adding new host keys to ~/.ssh/known_hosts
    • refuse to connect to hosts whose host key has changed
UserKnownHostsFile someFile ~/.ssh/known_hosts specify one or more (space-separated) files to use for the user host key database
To connect to an host without registering its host key into ~/.ssh/known_hosts (useful with VMs having a short lifespan) :
  • ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null myServer
  • The StrictHostKeyChecking=no part disables the prompt :
    The authenticity of host 'myServer (12.34.56.78)' can't be established.
    ECDSA key fingerprint is SHA256:JNRU2lkrMUH77Rt6DBdJbY5HwZ6JSMAY3oBmrENprFk.
    Are you sure you want to continue connecting (yes/no)?

ProxyCommand vs ProxyJump :

ProxyJump is intended as an equivalent / replacement / shorthand for ProxyCommand, with a simpler syntax (details and examples).
You probably have seen things like these (source) :
  • oldest ProxyCommand :
    Host sshHost
    	ProxyCommand ssh sshProxy nc %h %p 2> /dev/null
  • newer ProxyCommand with the -W flag :
    Host sshHost
    	ProxyCommand ssh sshProxy -W %h:%p
  • ProxyJump :
    Host sshHost
    	ProxyJump sshProxy

Example

A mashup of useful options :

Host HAL		this can be an alias, an IP address, a negated expression or a pattern (trick about patterns lists)

	HostName realHostname		if you specified an alias above
	Port 443				Port to connect to on the remote host. Defaults to 22
	User DrEinsteinVonBrainstorm
	IdentityFile ~/.ssh/myPrivateKey

	ProxyCommand ssh -e none hop exec nc -w 5 %h %p		Go through hop host. If hop is an HTTP proxy, read this.

	AddKeysToAgent yes			details
	ForwardAgent yes
Patterns lists are comma-separated (,), but when they apply to the Host directive, they are whitespace-separated.

My basic default configuration :

Host *				this is a valid statement (details : 1, 2)
	ServerAliveInterval 120	Keep SSH sessions alive
	LogLevel QUIET		hide the connection banner
If SSH ignores your ~/.ssh/config file, be more explicit with -F.
mail

sshd configuration

sshd supports SSH1 and SSH2 protocols, but SSH1 is not secure, so it mustn't be used !

Existing connections can survive sshd restart (source) :

Configuration files :

File Purpose
/etc/ssh/sshd_config This is the main configuration file (Configuration directives). sshd can't start if this file is unavailable.
/etc/motd welcome message to be displayed after a successful login, either "normal" login or via SSH (or grep Banner /etc/ssh/sshd_config )
~/.ssh/authorized_keys list of public keys. This can also hold some configuration directives.
/etc/nologin If this file exists, sshd refuses to let anyone except root log in.
/etc/hosts.allow
/etc/hosts.deny
ACL that should be enforced by tcp-wrappers are defined here.

Server configuration directives (source) :

Directive Default value Usage
AcceptEnv myEnvironmentVariable Declare environment variables that can be set by the SSH client (aka whitelist . A single AcceptEnv directive can declare several variables, separated by a space character.
AllowGroups all SPACE-separated list of group names / pattern. Only the matching ones are allowed to log in.
AllowTcpForwarding yes Specifies whether TCP forwarding is permitted.
Disabling TCP forwarding does not improve security unless users are also denied shell access, as they can always install their own forwarders.
When SSH forwarding is still needed, it is possible to enable it to specified users only (source) :
AllowTcpForwarding no
Match User stuart
AllowTcpForwarding yes
AllowUsers all SPACE-separated list of user names or user / user@host patterns. Only the matching ones are allowed to log in.
AuthenticationMethods Any comma-separated list of authentication methods that must be successfully completed for a user to be granted access
Banner points to a banner file that can be found anywhere. The content of this banner is displayed during an SSH login, between the login name and password input.
ChallengeResponseAuthentication yes whether challenge-response authentication —the keyboard-interactive authentication scheme defined in RFC-4256— is enabled. This keyboard-interactive authentication scheme could, in theory, ask a user any number of multi-faceted questions. In practice it often asks only for the user's password.
This directive has been deprecated by KbdInteractiveAuthentication
ClientAliveCountMax n Allow at most n unanswered requests sent by ClientAliveInterval before disconnecting the client.
The ClientAliveInterval timer is reset after sending every request.
ClientAliveInterval n Start a timer for n seconds before requesting client response over the encrypted channel (this has nothing to do with KeepAlive, which is a TCP setting).
0 disables this function.
Compression value yes Specifies whether compression is enabled after the user has authenticated successfully. value must be :
  • yes
  • delayed (a legacy synonym for yes)
  • no
DenyGroups empty
  • Login is disallowed for users belonging to groups that match one of the names / patterns.
  • By default, login is allowed for all groups.
  • The allow/deny users directives are processed in the following order:
    1. DenyGroups
    2. AllowGroups
DenyUsers empty
  • Login is disallowed for user names that match one of the patterns.
  • By default, login is allowed for all users.
  • The allow/deny users directives are processed in the following order:
    1. DenyUsers
    2. AllowUsers
GatewayPorts no Specifies whether remote hosts are allowed to connect to ports forwarded for the client (details)
HostbasedAuthentication no Specifies whether rhosts or /etc/hosts.equiv authentication together with successful public key client host authentication is allowed
IgnoreRhosts yes whether to ignore per-user .rhosts and .shosts files (SSH can emulate the obsolete rsh with these) during HostbasedAuthentication. The system-wide /etc/hosts.equiv and /etc/shosts.equiv are still used regardless of this setting.
KbdInteractiveAuthentication yes whether to allow keyboard-interactive authentication
ChallengeResponseAuthentication is a deprecated alias for this
ListenAddress Specifies the local addresses sshd should listen on
LoginGraceTime duration 120 seconds
  • The server disconnects after duration if the user has not successfully logged in.
  • 0 = no time limit
  • duration format
Match Introduces a conditional block. If all of the criteria on the Match line are satisfied :
  • the keywords on the following lines override those set in the global section of the config file
  • until :
    • either another Match line
    • or the end of the file
  • If a keyword appears in multiple Match blocks that are satisfied, only the first instance of the keyword is applied.
MaxAuthTries 6 Specifies the maximum number of authentication attempts permitted per connection. Once the number of failures reaches half this value, additional failures are logged.
MaxSessions 10
  • Specifies the maximum number of open sessions permitted per network connection (each shell, login, SFTP, , counts as 1 session)
  • Multiple sessions may be established by clients that support connection multiplexing
  • special values :
    • 1 : disable session multiplexing
    • 0 : prevent all shell, login and subsystem sessions (SFTP, ...) while still permitting forwarding
MaxStartups 10:30:100
  • Specifies the maximum number of concurrent unauthenticated connections to sshd. Additional incoming connections will be dropped until for a connection (which frees 1 slot for an incoming unauthenticated connection)
  • Random early drop can be enabled by specifying three colon-separated values min:rate%:max. If the current number of unauthenticated connections is :
    • n < min : no connection attempt is refused
    • min ≤ n < max :
      • there is a rate% chance for the connection to be refused when n = min
      • the risk increases linearly to 100% when n = max
    • n ≥ max : all connection attempts are refused
PasswordAuthentication yes Whether password authentication is allowed
PerSourceMaxStartups n none
  • Allows n unauthenticated connections from a given source address (none = no limit).
  • This limit is applied in addition to MaxStartups, whichever is lower.
PermitEmptyPasswords no When PasswordAuthentication is allowed, it specifies whether the server allows login to accounts with empty password strings.
PermitRootLogin value prohibit-password
  • yes : root can log in using ssh
  • prohibit-password or without-password : password and keyboard-interactive authentication are disabled for root
  • forced-commands-only : root login with public key authentication will be allowed, but only if the command option has been specified (which may be useful for taking remote backups even if root login is normally not allowed). All other authentication methods are disabled for root.
  • no : root is not allowed to log in
PubkeyAuthentication yes Whether public key authentication is allowed
StrictModes yes Specifies whether sshd should check file modes and ownership of the user's files and home directory before accepting login. This is normally desirable because novices sometimes accidentally leave their directory or files world-writable.
Subsystem empty Configures an external subsystem (e.g. file transfer daemon)
UseDNS Specifies whether sshd should look up the remote host name and check that the resolved host name for the remote IP address maps back to the very same IP address. Set this to no to disable server lookups.
UsePAM no Enables the PAM interface. Details :
UsePrivilegeSeparation This option is deprecated since OpenSSH 7.5 (source)
X11DisplayOffset n Specifies the first display number available for sshd's X11 forwarding. This prevents sshd from interfering with real X11 servers. Defaults to 10.
X11Forwarding no whether X11 forwarding is permitted
disabling X11 forwarding does not prevent users from forwarding X11 traffic, as users can always install their own forwarders

Advanced Features

  • log on a remote host as a specified user :
    ssh bob@sshServer
  • receive display of remote application :
    1. log in remote host :
      ssh -X stuart@sshServer
    2. start remote application
  • execute a remote application on its own (remote) display :
    1. log in remote host :
      ssh kevin@sshServer
    2. mangle its DISPLAY environment variable to use the local display :
      export DISPLAY=:0.0
    3. then start remote application
  • copy a file ("push" mode) :
    scp path/to/file sshServer:/remote/destination/directory
  • copy a file ("pull" mode) :
    scp sshServer:/path/to/file local/destination/directory
  • copy files with scp as a specific user on the remote system :
    scp /path/to/file bob@sshServer:/remote/destination/directory
  • Preserve file rights and timestamp while using scp : use the -p flag :
    scp -p
mail

SSH tunneling

Local and Remote tunnel types :

What is local or remote is the tunnel entrance.
Whether you need to setup a local or a remote tunnel depends on the traffic direction :
Local tunnel Remote tunnel
where is the resource server located ? away from :
  • my workstation
  • my LAN
on :
  • my workstation
  • my LAN
where is the resource client located ? on : away from :
  • my workstation
  • my LAN
traffic direction
  • outgoing
  • forward a local port to a remote host
  • incoming
  • forward a remote port to a local host
  • If the client is on a machine of the LAN which is not my workstation, just set up the tunnel from there .
  • The main difference between local and remote tunnels is the traffic direction.

Notes about what's next :

workstation
  • is the machine where I type commands
  • is an ssh client
sshServer
  • is where sshd is running
  • for simplicity, we'll assume it's listening on all interfaces on port TCP 22

Local tunnels

Before diving into the details, let's focus on the command line :
ssh sshServer -L sourceHost:sourcePort:destinationHost:destinationPort
    |<---+---->|     |<---------+----------->| |<--------------+--------------->|
         |                      |                              +-------------------> tunnel exit, as seen from sshServer
         |                      +--------------------------------------------------> tunnel entrance, as seen from workstation
         +-------------------------------------------------------------------------> where we're connecting to via ssh
  • sourceHost :
    • since we're dealing with a local tunnel, this refers to workstation, either via
      • one of its interfaces connected to the LAN
      • or via localhost / 127.0.0.1
    • when omitted or set to *, it means "ALL interfaces"
  • destinationHost must be understood as seen from sshServer :
    • this can be
      • an IP address sourceHost can not reach
      • a hostname sourceHost can not resolve
    • when set to 127.0.0.1, it refers to sshServer itself

A local tunnel, where destinationHost is sshServer :

SSH tunnel - local - with destinationHost == sshServer

A local tunnel, where destinationHost is another host :

SSH tunnel - local - with destinationHost != sshServer

Remote tunnels

As we did with local tunnels, let's have a look at the command line :
ssh sshServer -R sourceHost:sourcePort destinationHost:destinationPort
    |<---+---->|     |<----------+---------->| |<--------------+--------------->|
         |                       |                             +-------------------> tunnel exit, as seen from workstation
         |                       +-------------------------------------------------> tunnel entrance, as seen from sshServer
         +-------------------------------------------------------------------------> where we're connecting to via ssh

A remote tunnel, where destinationHost is workstation :

SSH tunnel - remote - with destinationHost == workstation

A remote tunnel, where destinationHost is another host :

SSH tunnel - remote - with destinationHost != workstation

Notes

  • When opening a tunnel, you'll be prompted for the password of the user account used to log in sshServer.
    • If successful, a shell opens on sshServer with this user's credentials.
    • The tunnel stays up as long as this shell is active; to close it, just exit.
  • To make sure the tunnel is up :
    netstat -a | grep ssh
    In netstat output, connections listed as TIME_WAIT are waiting for the protocol session end (=timeout).
  • Whatever the tunnel type, sshd only accepts connections from the loopback address, so that only local programs can use the tunnel (for security reasons). To workaround this, edit /etc/ssh/sshd_config and set GatewayPorts to yes. (details)