SSH
Secure Shell (SSH) is a method to securely communicate between computers. This page will discuss SSH under the context of the OpenSSH server.
Theory & Cryptography
SSH supports different key exchange algorithms, ciphers, and message authentication codes (MACs). The server and client negotiates and chooses a set of these algorithms that are supported by both and then proceeds with the key exchange.
Key Exchange
SSH handles key exchange using one of two ways: Diffie-Hellman and Elliptic Curve Diffie-Hellman. A shared secret is generated between the client and server for every session which will be used as the key, thereby providing forward secrecy.
Diffie-Hellman works with a multiplicative group of integers modulo a prime. Its security is based on the hardness of the discrete logarithm problem.
Elliptic Curve Diffie-Hellman works with elliptic curves over finite fields. Its security is based on the hardness of the elliptic curve discrete logarithm problem.
Authentication
With a shared secret derived from Diffie-Hellman, the client and server need to prove to each other they are who they are.
The server proves its identity to the client by signing the shared secret (the key) using a public key algorithm.
The client proves its identity to the server by using:
- Password authentication
- Public Key Authentication (same as the server's)
Symmetric Ciphers
With a shared key in place, data can be encrypted and decrypted between the client and server using symmetric ciphers.
Message Authentication Codes
Message authentication codes provide integrity. An authenticated encryption cipher mode already provides integrity, so extra MACs are not used. MACs are calculated and attached to every message if CTR is used.
There are multiple ways to combine ciphers and MACs:
- Encrypt-then-MAC: encrypt the message, then attach the MAC of the ciphertext.
- MAC-then-encrypt: attach the MAC of the plaintext, then encrypt everything.
- Encrypt-and-MAC: encrypt the message, then attach the MAC of the plaintext.
MAC-then-encrypt and Encrypt-and-MAC are prone to leak information because attackers can control an aspect of the system and perform timing attacks (eg: verifying a message vs. decrypting a message for Encrypt-and-MAC).
Configuration
Server-side Configuration
Here are a few things you should do on the server side configuration to secure OpenSSH.
Disable Weak Key Exchange Protocols
SSH supports a few protocols containing different factors:
- ECDH curve choice
- Bit size of the DH modulus
- The hash function.
To provide good security:
- Avoid NIST curves - they leak data and cannot be trusted.
- Bit sizes smaller than 1024 does not provide sufficient security
- Do not use SHA1
This means, your sshd_config should contain:
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
And your ssh_config should contain:
Host *
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
Prime numbers smaller than 2000 bits should also be removed from the /etc/ssh/moduli file (the 5th column).
Disable Weak Server Authentication
There are 4 public key algorithms for server authentication.
- DSA with SHA1
- ECDSA with SHA256, SHA384 or SHA512 depending on key size
- Ed25519 with SHA512
- RSA with SHA1
For security:
- Do not use SSH v1
- Avoid DSA which requires exactly 1024 bits.
- Avoid NIST (ECDSA uses it)
- DSA and ECDSA requires randomness for its signatures. If random numbers are bad, it is possible to recover the secret key.
Using SHA1 here is not an issue if the value being signed is using something stronger (eg SHA2).
Your sshd_config should contain:
Protocol 2
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
Disable Weak Symmetric Ciphers
For security:
- Do not use DES and RC4 ciphers
- At least 128 bits
- Do not use Blowfish and Cast128 block ciphers since they use 64 bit block sizes.
- Authenticated Encryption ciphers should be used so MAC isn't required. But CTR with Encrypt then MAC is also fine.
Your sshd_config should contain:
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
Your ssh_config should contain:
Host *
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
Disable Weak MACs
For security:
- Do not use MD5 or SHA1 hashing algorithms
- Encrypt-then-MAC only
- Tag and key size must be at least 128 bits
Your sshd_config should contain:
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-ripemd160-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,hmac-ripemd160,umac-128@openssh.com
PermitRootLogin
The PermitRootLogin option specifies whether root can log in. It must be one of:
yesto allow root login with password or public key authentication.noto disallow root login.prohibit-passwordto only allow public key authentication for login. This is the default.without-passwordis a deprecated alias.force-commands-onlyto only allow public key authentication for commands.
Client-side Configuration
You can customize SSH's default behavior using a SSH configuration file located at ~/.ssh/config.
Defining custom hosts in your SSH config file allows you to connect to a host by its name without needing to specify details such as username, port, and keys.
Host dev
HostName dev.example.com
Port 2222
User leo
With the host defined, connecting to dev.example.com only requires executing ssh dev without any other arguments required.
Use Case: GitHub
If you ever have more than one set of SSH private keys, defining a custom host is virtually required in order to make use of services such as GitHub or GitLab where pushing git changes requires key based SSH authentication.
Host github.com
User git
Port 22
IdentityFile ~/.ssh/github_rsa
With that defined, ssh github.com will connect you as git using the private key github_rsa.
Tunnels
You can create a tunnel through your SSH connection to access services that may be behind a firewall or NAT. There are 3 types of tunnels which will be covered below.
In all cases, you may be desirable to not start a shell on the remote server. This can be done with the -N option.
Local tunnel
Local tunnels allow access a specific port on the host you are connected to. Local tunnels allow you to access a remote port or server locally. This is useful when the server is behind a firewall and you want to access a specific service that is not exposed (such as a web portal or status page, or some other service bound to 127.0.0.1 on the server).
You can create a local tunnel with the -L local_port:remote_address:remote_port.
Local tunnels can also access any host via the server. This makes the server act as an intermediate host or proxy and can be useful to access a host that is behind a firewall or within a secure network, for example.
Remote or reverse tunnel
See Also: Reverse SSH Tunnel
Remote tunnels allow access a specific port on the client machine from the server. This remote tunnel allows access from the remote host to a local port. This is useful when the client is behind a NAT or firewall and you want to SSH from the server back to the client or access some service on the client from the server.
You can create a remote tunnel using -R remote_port:local_address:local_port. For example:
client$ ssh -R 22222:localhost:22 user@remote-server
## On another session on remote-server, you may now SSH back to the client:
remote-server$ ssh -p 22222 user@127.0.0.1
Dynamic tunnel
Dynamic tunnels will allow connections to resources via the SSH server as a proxy. This is useful when you want to access resources via the server, such as an iDRAC on a server-only subnet.
You can create a dynamic tunnel using -D local_port. Use 127.0.0.1:local_port as a SOCKS5 proxy.
Tips & Tricks with SSH
Tar Pipe
You can send files via SSH using tar:
# tar -czvf - /dir | ssh root@server "cat > ~/dir.tar.gz"
Host Config
You may customize a host within the ~/.ssh/config file and use a shorthand name to connect to servers. For example:
Host xyz
Hostname full.host.name.com (or 1.2.3.4)
User my-username
IdentitiesOnly yes
IdentityFile ~/.ssh/id_x_ed25519
The shorthand name xyz can then be used with ssh xyz.
SSH Connection Sharing
If you have multiple SSH connections to a server, you may want to enable ControlMaster and ControlPath so that subsequent connections shares the existing TCP connection. This will cause SSH to multiplex multiple sessions through the single connection reducing the need to authenticate again. Keep in mind that the first connection must remain open for the other sessions to work.
Host x
...
ControlMaster auto
ControlPath ~/.ssh/master-%r@%h:%p
This feature is nice for scripts that rely heavily on executing commands on a remote server.
Proxy Jump
You can use a host as an intermediate server transparently.
ssh -J user@intermediate user@finaldestination
ssh -J user@intermediate1,user@intermediate2 user@finaldestination
This is equivalent to setting the host with the ProxyJump option:
Host finaldestination
ProxyJump user@intermediate
ssh user@finaldestination
Using hostname matching in the SSH configuration file, you can make a '-via-x' suffix that will automatically do the proxying. More information at https://jloh.co/posts/dynamic-ssh-jump-hosts/, but the gist of it is to define:
Host *-via-dc1
ProxyCommand ssh you@jump-01.example.com nc $(echo %h | sed 's/-via-dc1$//') %p
Copying Public Key
Use the ssh-copy-id command to automatically append to a remote host's .ssh/authorized_keys file.
$ ssh-copy-id -i ~/.ssh/id_x_ed25519.pub x
Alternatively, use
$ cat ~/.ssh/id_x_ed25519.pub
Custom MOTD for specific users or groups
The /etc/ssh/sshd_config allows conditional options applied to specific users or groups (or many other criteria) using the Match directive.
To show a specific user a custom login banner:
Match User leo
Banner /etc/motd-leo
This applies to groups as well:
Match Group awesome-group
Banner /etc/motd-group
Reload the SSH daemon to apply.
Limit SSH connection to only a specific command
You can specify a specific command be executed when a particular public key is used for authentication. Generate a new public/private keypair and then add it to your authorized_keys file. Specify the command="" as well as any other options/restrictions when using this key.
command="/home/lleung/send_emails_from_arc.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1y... lleung@alpha
Troubleshooting
SSH Breaks during system update
On Fedora 31, when executing a system upgrade, the SSH server stops functioning and fails to start. Journald shows the following messages from OpenSSH:
Jun 06 22:42:23 bnas systemd[1]: Starting OpenSSH server daemon...
Jun 06 22:42:23 bnas sshd[18240]: command-line: line 0: Bad configuration option: CASignatureAlgorithms
Jun 06 22:42:23 bnas systemd[1]: sshd.service: Main process exited, code=exited, status=1/FAILURE
Jun 06 22:42:23 bnas systemd[1]: sshd.service: Failed with result 'exit-code'.
Jun 06 22:42:23 bnas systemd[1]: Failed to start OpenSSH server daemon.
Jun 06 22:43:05 bnas systemd[1]: sshd.service: Service RestartSec=42s expired, scheduling restart.
Jun 06 22:43:05 bnas systemd[1]: sshd.service: Scheduled restart job, restart counter is at 20.
Jun 06 22:43:05 bnas systemd[1]: Stopped OpenSSH server daemon.
This apparently is a known issue by Red Hat (see https://access.redhat.com/solutions/4587501). This can be an issue if the system being updated has many packages and/or is very slow.
Perhaps going forward, update OpenSSH separately to ensure it doesn't break.