Regalis Technologies log

Regalis Technologies

technical blog about GNU/Linux, C++, security, electronics

Mon 02 January 2023 in ssh by Regalis

Last update: Sat 11 February 2023

Configuring the SSH client

This part of the SSH series will cover the configuration of the OpenSSH client.

Configuration sources

The ssh client obtains configuration data from the following sources (in the following order):

The client will use the first obtained value for each parameter. Configured options will be used not only by the ssh itself, but by a lot of other tools as well. That is, among others: scp, sftp, sshfs, git, ansible and any other tool which uses the OpenSSH library/suite.

Format of the configuration files

The configuration file contains key-value pairs, one per line.

The ssh_config(5) manual page reference the keys as keywords and values as arguments. The keywords are case-insensitive, arguments are case-sensitive.

There are two special keywords which restricts all the following declarations to be used only when the specified conditions are satisfied. These are a Host and a Match keywords. Any declarations following these keywords will be applied up to the next Host or Match keyword or until the end of file.

Here is a small example:

1
2
3
4
5
Host host1.lab.regalis.tech
    User regalis
    Port 2222
    IdentitiesOnly yes
    IdentityFile ~/.ssh/id_rsa_xyz

Assuming the user placed a content in the ~/.ssh/config file, it is now possible to use ssh like this:

1
$ ssh host1.lab.regalis.tech

Instead of:

1
$ ssh regalis@host1.lab.regalis.tech -p 2222 -i ~/.ssh/id_rsa_xyz -o IdentitiesOnly=yes

Commonly used options

Here is a list of some commonly used options:

For a complete list of supported options - please consult the ssh_config(5) manual page.

Examples

I have prepared the few real life examples illustrating commonly used SSH scenarios. I will start with basic examples and continue extending them with more use cases.

Using different logins/usernames for different groups of servers

Let's say we have multiple groups of servers (for example production, staging and testing) and we use our personal username (for example mkowalski) for production and staging servers, and the test username for the group of servers in testing environment. We can prepare the following configuration file:

1
2
3
4
5
Host *.prod.regalis.tech *.staging.regalis.tech
    User mkowalski

Host *.testing.regalis.tech
    User test

Now we can use the SSH client without bothering with the username:

1
2
$ ssh srv01.prod.regalis.tech
$ scp srv01.testing.regalis.tech:/etc/dnsmasq.conf srv01.staging.regalis.tech:

Configuring jump hosts

We can use a configuration file to enable smooth, transparent use of jump hosts. Let's say our production and staging group of servers needs to be accessed through the jump.regalis.tech host, and the testing group of servers through the dc01.regalis.tech host. What's more, both jump hosts accepts only the public key authentication. We can extend our configuration file like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Host jump.regalis.tech dc01.regalis.tech
    User mkowalski
    IdentitiesOnly yes
    IdentityFile ~/.ssh/id_rsa_mkowalski_regalis_tech

Host *.prod.regalis.tech *.staging.regalis.tech
    User mkowalski
    ProxyJump jump.regalis.tech

Host *.testing.regalis.tech
    User test
    ProxyJump dc01.regalis.tech

Now you can use the SSH just like before but the SSH client will automatically use the appropriate jump host, for example you can copy a local file to the srv01.prod.regalis.tech by simply using scp like this:

1
$ scp my_file srv01.prod.regalis.tech:

In the example above, the ssh client will first make a SSH connection to the configuration-specified jump host and then establish a TCP forwarding to the target host from there.

Please note: this operation does not require to start a shell on the jump host, what we need is just a secure port forwarding.

The user must authenticate to both the jump host and the target host. Depending on the authentication methods - this may result in several separate password prompts.

Using aliases

Sometimes it is easier to refer to a host by a short alias rather then by a long FQDN or IP address. The SSH provides this functionality, we can use this by combining a Host keyword with a Hostname option:

1
2
3
4
5
6
Host jump-host
    Hostname jump01.infra.regalis.tech
    (...)

Host bastion
    Hostname jump02.infra.regalis.tech

Now you can simply use the following commands:

1
2
3
$ ssh bastion
$ sftp bastion
$ ssh -J bastion web01.infra.example.com

The -J bastion option will use bastion (defined in the configuration file) as a jump host while connecting to the web01.infra.example.com.

SSH hosts autocompletion

GNU Bash has a powerful hosts autocompletion feature (available in most GNU/Linux distributions as a separate package, usually called bash-completion) which covers not only already visited hosts (from the known hosts files) but also user-defined aliases.

Configuring local dynamic application-level port forwarding (SOCKS proxy)

It is very common that some of the network services are available only from the specified locations (for example from the jump host or from a specified server inside the testing environment). What is more, the user may want to contact these kind of services using a HTTP(S) client - curl or what is sometimes more important - a full featured web browser like Firefox.

Well, this is exactly the case for the local dynamic application-level port forwarding feature of SSH.

Given the following infrastructure, let's consider the user on the local station want to access HTTP servers on the 10.0.0.2, 10.0.0.3 and the app.fakedomain.com from the local web browser:

A diagram illustrating five hosts: local, dc01, webapp, 10.0.0.2, 10.0.0.3 and app. The local station is connected to the dc01, the dc01 host is connected to the webapp. The rest of hosts are connected to the webapp.

It this scenario, we need to configure the SSH to connect to the webapp.testing.regalis.tech via the jump host dc01.regalis.tech and act as a SOCKS proxy server. Again, we don't need to execute any command on both the jump host and the target host.

All user connections made via the proxy server will be forwarded through the secure channel (provided by the SSH connections) and handled on the remote end (in our case on the webapp.testing.regalis.tech).

The user may use the following command to accomplish this task:

1
$ ssh -J dc01.regalis.tech -D 9999 -N webapp.testing.regalis.tech

However in real world scenarios, it will be inconvinient to provide separate usernames and port numbers for each connection. That is why we can extend our configuration file and make it similar to this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
Host jump.regalis.tech dc01.regalis.tech
    User mkowalski
    IdentitiesOnly yes
    IdentityFile ~/.ssh/id_rsa_mkowalski_regalis_tech

Host *.prod.regalis.tech *.staging.regalis.tech
    User mkowalski
    ProxyJump jump.regalis.tech

Host *.testing.regalis.tech
    User test
    ProxyJump dc01.regalis.tech

Host webapp-proxy
    Hostname webapp.testing.regalis.tech
    CanonicalizeHostname yes
    DynamicForward 9999
    ExitOnForwardFailure yes
    SessionType none

Please note the CanonicalizeHostname option - this is required when we need to force SSH to re-read the configuration file and apply additional configuration options for the real hostname provided with the Hostname option.

The SessionType option has been set to none to prevent the execution of a remote command (the ssh client will not start an interactive shell).

The ExitOnForwardFailure option will cause the SSH to terminate the connection if it cannot set up the requested dynamic port forwarding.

Now we can simply use the following command:

1
$ ssh webapp-proxy

The SOCKS proxy will be available at localhost:9999, we can use it like this:

1
2
3
$ export ALL_PROXY=socks://localhost:9999
$ curl 10.0.0.2
$ curl app.fakedomain.com

Note about hostnames resolution

Please note that the hostname resolution will be performed on the remote end, so a local station is not required to be able to resolve app.fakedomain.com.

You can even start a dedicated web browser that will make all connections through the proxy:

1
$ chromium --proxy-server=socks://localhost:9999

More information about the port forwardings will be available in the dedicated article from the series about Secure Shell Protocol.

Debugging configuration

You can execute the SSH client with the -v flag to see what options are beeing used:

1
2
3
4
5
$ ssh -v webapp-proxy
(...)
debug1: /home/regalis/.ssh/config line 3: Applying options for *
debug1: /home/regalis/.ssh/config line 64: Applying options for webapp-proxy
(...)

Temporarily disabling the configuration file

You can use -F option to specify an alternative configuration file, if set to none - no configuration files will be read.

1
2
3
$ ssh -F none lab.regalis.tech

$ ssh -F /dev/null lab.regalis.tech