# SSH 1. [TL;DR](#tldr) 1. [Server installation on Windows](#server-installation-on-windows) 1. [Key Management](#key-management) 1. [Client configuration](#client-configuration) 1. [Append domains to a hostname before attempting to check if they exist](#append-domains-to-a-hostname-before-attempting-to-check-if-they-exist) 1. [Optimize connection handling](#optimize-connection-handling) 1. [Integrate with GnuPG](#integrate-with-gnupg) 1. [Server configuration](#server-configuration) 1. [Change listening port](#change-listening-port) 1. [Conditional blocks](#conditional-blocks) 1. [Hardening](#hardening) 1. [SSHFS](#sshfs) 1. [Installation](#installation) 1. [Troubleshooting](#troubleshooting) 1. [No matching host key type found](#no-matching-host-key-type-found) 1. [Further readings](#further-readings) 1. [Sources](#sources) ## TL;DR ```sh # Create new keys. ssh-keygen -t 'rsa' -b '4096' ssh-keygen -t 'dsa' ssh-keygen -t 'ecdsa' -b '521' ssh-keygen -t 'ed25519' -f "${HOME}/.ssh/keys/id_ed25519" -C 'test@winzoz' ssh-keygen -f "${HOME}/.ssh/id_rsa" -N '' -C 'batch-generated key with no password' # Remove elements from the known hosts list. ssh-keygen -R 'pi4.lan' ssh-keygen -R '192.168.1.237' -f '.ssh/known_hosts' ssh-keygen -R 'pi.lan' -f "${HOME}/.ssh/known_hosts" # Change the password of a key. ssh-keygen -f "${HOME}/.ssh/id_rsa" -p # Show keys' fingerprint. ssh-keygen -l -f "${HOME}/.ssh/id_ed25519" # Show certificates' content. ssh-keygen -L -f 'path/to/ssh.cert' # Load keys from '${HOME}/.ssh' and add them to the agent. eval $(ssh-agent) && ssh-add # List keys added to the agent, by fingerprint. ssh-add -l # List keys added to the agent, by public key. ssh-add -L # Authorize keys for passwordless access. ssh-copy-id 'host.fqdn' ssh-copy-id -i "${HOME}/.ssh/id_rsa.pub" 'user@host.fqdn' # Preload trusted keys. ssh-keyscan 'host.fqdn' >> "${HOME}/.ssh/known_hosts" # Connect to a directly unreachable host by tunnelling sessions. ssh -t 'bastion-host' ssh 'unreachable-host' # Mount remote folders. sshfs 'nas.lan:/mnt/data' 'Data' \ -o 'auto_cache,reconnect,defer_permissions,noappledouble,volname=Data' # Validate keys. ssh-keygen -yef 'path/to/key' openssl rsa -check -in 'path/to/key' -noout ``` ## Server installation on Windows Needs Administrator privileges.
Tested on Window 11 22H2. Via PowerShell: 1. Install the server component: ```ps1 Add-WindowsCapability -Online -Name OpenSSH.Server ``` 1. Start and enable the service: ```ps1 Start-Service sshd Set-Service -Name sshd -StartupType 'Automatic' ``` 1. Verify the firewall rule has been created automatically during the installation: ```ps1 if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue | Select-Object Name, Enabled)) { Write-Output "Firewall Rule 'OpenSSH-Server-In-TCP' does not exist, creating it..." New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 } else { Write-Output "Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists." } ``` Via GUI: 1. Open _Settings_ > _Apps_, then select _Optional features_ 1. Scan the list to see if the OpenSSH server is not already installed 1. At the top of the page, select _View features_ in the _Add an optional feature_ field 1. Find _OpenSSH Server_ and select _Install_ 1. Once the setup completes, return to _Apps_ > _Optional features_ and confirm OpenSSH is now listed 1. Open the _Services_ desktop app: 1. Select Start 1. Type `services.msc` in the search box 1. Select the _Services_ app or just press ENTER 1. In the details panel, double-click _OpenSSH SSH Server_ to enter its properties 1. On the _General_ tab, from the _Startup type_ drop-down menu, select _Automatic_ to enable the service 1. In the same tab, select _Start_ to start the service ## Key Management Create a new key: ```sh ssh-keygen -t 'rsa' -b '4096' ssh-keygen -t 'dsa' ssh-keygen -t 'ecdsa' -b '521' ssh-keygen -t 'ed25519' -f '.ssh/id_ed25519' -C 'test@winzoz' ``` ```txt Generating public/private ed25519 key pair. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in C:\Users\test/.ssh/id_ed25519. Your public key has been saved in C:\Users\test/.ssh/id_ed25519.pub. The key fingerprint is: SHA256:lFrpPyqTy0d30TfnN0QRY678LnyCzmvMDbl1Qj2/U/w test@winzoz The key's randomart image is: +--[ED25519 256]--+ | +o.o++| | ==*O| | . .X*| | o . +=| | S S +..==| | . .+..*E| | + ...o| | .+ .o = | | =+ .o .| +----[SHA256]-----+ ``` Remove a host from the list of known hosts: ```sh ssh-keygen -R 'pi4.lan' ssh-keygen -R '192.168.1.237' -f '.ssh/known_hosts' ssh-keygen -R 'raspberrypi.lan' -f '.ssh/known_hosts' ``` ```txt Host pi4.lan found: line 5 /home/user/.ssh/known_hosts updated. Original contents retained as /home/user/.ssh/known_hosts.old ``` Change password of a key file ```sh ssh-keygen -f "${HOME}/.ssh/id_rsa" -p ``` ## Client configuration When connecting to a host, the SSH client will use settings: 1. from the command line, 1. from the user's `~/.ssh/config` file, 1. from the `/etc/ssh/ssh_config` file Unless noted otherwise, for each parameter, only the **first** obtained value will be used (_first-come-first-served_).
Values should hence appear from the most **specific** to the most **generic**, both by file and by position in those files. The configuration files contain sections separated by `Host` specifications
Those sections are only applied to hosts that match one of the patterns given in each specification. The file contains keyword-argument pairs, one per line.
Lines starting with `#` and empty lines are interpreted as comments.
Arguments may optionally be enclosed in **double** quotes (`"`) in order to represent arguments containing spaces.
Configuration options may be separated by whitespace, or optional whitespace and exactly one `=`. The latter format is useful to avoid the need to quote whitespace when specifying configuration options using the ssh, scp, and sftp `-o` option.
Keywords are case-**in**sensitive and arguments are case-**sensitive**. ```ssh-config Host targaryen HostName targaryen.example.com User john Port 2322 IdentityFile ~/.ssh/targaryen.key LogLevel INFO Compression yes Host *ell user oberyn sendenv BE_SASSY StrictHostKeyChecking no Host * !martell LogLevel INFO StrictHostKeyChecking accept-new UserKnownHostsFile /dev/null Host * User root Compression yes SendEnv -LC_* -LANG* SetEnv MYENV=itsvalue ``` ### Append domains to a hostname before attempting to check if they exist ```ssh-config CanonicalizeHostname yes CanonicalDomains xxx.auckland.ac.nz yyy.auckland.ac.nz Host *.xxx.auckland.ac.nz User user_xxx Host *.yyy.auckland.ac.nz User user_yyy ``` ### Optimize connection handling ```ssh-config # Keep a connection open for 30s and reuse it when possible. # Save the above pipe in a safe directory, and use a hash of different data to # identify it. # source: https://www.cyberciti.biz/faq/linux-unix-reuse-openssh-connection/ ControlMaster auto ControlPath ~/.ssh/control-%C ControlPersist 30s ``` ### Integrate with GnuPG ```sh # In BASH and alike. export SSH_AUTH_SOCK=$(gpgconf --list-dirs 'agent-ssh-socket') # In FISH. set -x 'SSH_AUTH_SOCK' (gpgconf --list-dirs 'agent-ssh-socket') ``` ```ssh-config # In ~/.ssh/config. # Value is from `gpgconf --list-dirs 'agent-ssh-socket'`. IdentityAgent ~/.gnupg/S.gpg-agent.ssh ``` ## Server configuration The daemon's default configuration file is `/etc/ssh/sshd_config`.
Reload the server upon config file change. No need to restart it. The configuration file contains keyword-argument pairs, one per line.
Unless noted otherwise, for each keyword, the **first** obtained value is used (_first-come-first-served_).
Lines starting with `#` and empty lines are interpreted as comments.
Arguments may optionally be enclosed in **double** quotes (`"`) in order to represent arguments containing spaces.
Keywords are case-**in**sensitive and arguments are case-**sensitive**. **Some** Linux distributions (e.g., Debian, OpenSUSE) started including `.conf` files in `/etc/ssh/sshd_config.d/` and `/usr/etc/ssh/sshd_config.d/` as first thing in the base configuration file: ```ssh-config # To modify the system-wide sshd configuration, create a "*.conf" file under # "/etc/ssh/sshd_config.d/" which will be automatically included below. # Don't edit this configuration file itself if possible to avoid update # problems. Include /etc/ssh/sshd_config.d/*.conf # The strategy used for options in the default sshd_config shipped with # OpenSSH is to specify options with their default value where # possible, but leave them commented. Uncommented options override the # default value. Include /usr/etc/ssh/sshd_config.d/*.conf ``` This avoids issues from updates overwriting the default file and allows user configurations to override defaults in a cleaner way. ### Change listening port ```ssh-config Port 2222 ``` ### Conditional blocks > Only a subset of keywords may be used in a _Match_ block.
> Check the [`SSHD_CONFIG(5)`][sshd_config man page] man page for more information. ```ssh-config Match Address 192.168.111.0/24 PasswordAuthentication no PermitRootLogin no Match User ashley AllowUsers ashley@203.0.113.1 ``` ### Hardening Don't bother changing the SSH port from the default `22` value unless you require it for other purposes.
Security through obscurity is utterly ineffective and can create issues with tools expecting standard usage [1][Why I don't change SSH from port 22], [2][Security Through Obscurity (STO): History, Criticism & Risks] . Suggestions: - Disable `root` login: ```ssh-config PermitRootLogin no ``` Or limit it to keys: ```ssh-config PermitRootLogin prohibit-password ``` - Limit the maximum number of authentication attempts for any login session: ```ssh-config MaxAuthTries 3 ``` - Limit the amount of time user have to complete authentication after the initial connection: ```ssh-config LoginGraceTime 20 ``` - Disable authentication with empty passwords: ```sh PermitEmptyPasswords no ``` - Disable password authentication altogether: ```ssh-config PasswordAuthentication no ``` - Restrict authentication options to keys: ```ssh-config ChallengeResponseAuthentication no ``` - Disable other authentication methods if not used: ```ssh-config ChallengeResponseAuthentication no KerberosAuthentication no GSSAPIAuthentication no ``` - Disable other features if not used: ```ssh-config AllowAgentForwarding no AllowTcpForwarding no PermitTunnel no X11Forwarding no ``` - Restrict login to specific IP addresses: ```ssh-config AllowUsers*@203.0.113.1 *@203.0.113.2 *@192.0.2.0/24 *@172.16.*.1 sammy@203.0.113.1 alex@203.0.113.2 Match User ashley AllowUsers ashley@203.0.113.1 ``` - Temporarily block IPs after repeated authentication failures.
See [fail2ban]. - [Set up port knocking]. ## SSHFS Notable options: - `auto_cache` enables caching based on modification times; - `reconnect` reconnects to the server; - `defer_permissions` works around the issue where certain shares may mount properly, but cause _permissions denied_ errors when accessed (caused by how Mac OS X's Finder translates and interprets permissions; - `noappledouble` prevents Mac OS X to write `.DS_Store` files on the remote file system; - `volname` defines the name to use for the volume. Usage: ```sh sshfs \ -o 'auto_cache,reconnect,defer_permissions,noappledouble,volname=Data' 'user@nas.lan:/path/to/remote/dir' \ '/path/to/local/dir' ``` ### Installation ```sh # Mac OS X requires `macports`, since `brew` does not offer 'sshfs' anymore sudo port install 'sshfs' ``` ## Troubleshooting ### No matching host key type found Error message example: > `Unable to negotiate with XXX port 22: no matching host key type found. Their offer: ssh-rsa.` Cause: the server only supports the kind of RSA with SHA-1, which is considered weak and deprecated in newer SSH versions. Workaround: explicitly set your client to use the specified key type adding ```ssh-config HostkeyAlgorithms +ssh-rsa PubkeyAcceptedAlgorithms +ssh-rsa ``` to your `~/.ssh/config` like so: ```diff Host azure-devops IdentityFile ~/.ssh/id_rsa IdentitiesOnly yes + HostkeyAlgorithms +ssh-rsa + PubkeyAcceptedAlgorithms +ssh-rsa ``` Solution: update the SSH server. ## Further readings - [`SSH_CONFIG(5)` man page][ssh_config man page] - [`ssh_config` example][ssh_config example] - [`SSHD_CONFIG(5)` man page][sshd_config man page] - [`sshd_config` defaults][sshd_config defaults] - [`sshd_config` example][sshd_config example] - [ssh-agent] - [Use GPG keys for SSH authentication] - [Security Through Obscurity (STO): History, Criticism & Risks] ### Sources - [Use SSHFS to mount a remote directory as a volume on OSX] - [Using the SSH config file] - [How to list keys added to ssh-agent with ssh-add?] - [Multiple similar entries in ssh config] - [How to enable SSH access using a GPG key for authentication] - [How to perform hostname canonicalization] - [How to reuse SSH connection to speed up remote login process using multiplexing] - [Get started with OpenSSH for Windows] - [Restrict SSH login to a specific IP or host] - [Stick with security: YubiKey, SSH, GnuPG, macOS] - [How to check if an RSA public / private key pair match] - [Why I don't change SSH from port 22] - [How To Harden OpenSSH on Ubuntu 20.04] [use gpg keys for ssh authentication]: gnupg.md#use-gpg-keys-for-ssh-authentication [set up port knocking]: set%20up%20port%20knocking.md [ssh_config example]: ../examples/ssh/ssh_config [sshd_config defaults]: ../examples/ssh/sshd_config.defaults [sshd_config example]: ../examples/ssh/sshd_config [ssh_config man page]: https://man.openbsd.org/ssh_config [ssh-agent]: https://www.ssh.com/academy/ssh/agent [sshd_config man page]: https://man.openbsd.org/sshd_config [fail2ban]: https://github.com/fail2ban/fail2ban [get started with openssh for windows]: https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse?tabs=gui [how to check if an rsa public / private key pair match]: https://serverfault.com/questions/426394/how-to-check-if-an-rsa-public-private-key-pair-match#426429 [how to enable ssh access using a gpg key for authentication]: https://opensource.com/article/19/4/gpg-subkeys-ssh [how to harden openssh on ubuntu 20.04]: https://www.digitalocean.com/community/tutorials/how-to-harden-openssh-on-ubuntu-20-04 [how to list keys added to ssh-agent with ssh-add?]: https://unix.stackexchange.com/questions/58969/how-to-list-keys-added-to-ssh-agent-with-ssh-add [how to perform hostname canonicalization]: https://sleeplessbeastie.eu/2020/08/24/how-to-perform-hostname-canonicalization/ [how to reuse ssh connection to speed up remote login process using multiplexing]: https://www.cyberciti.biz/faq/linux-unix-reuse-openssh-connection/ [multiple similar entries in ssh config]: https://unix.stackexchange.com/questions/61655/multiple-similar-entries-in-ssh-config [restrict ssh login to a specific ip or host]: https://docs.rackspace.com/support/how-to/restrict-ssh-login-to-a-specific-ip-or-host/ [security through obscurity (sto): history, criticism & risks]: https://www.okta.com/uk/identity-101/security-through-obscurity/ [stick with security: yubikey, ssh, gnupg, macos]: https://evilmartians.com/chronicles/stick-with-security-yubikey-ssh-gnupg-macos [use sshfs to mount a remote directory as a volume on osx]: https://benohead.com/mac-os-x-use-sshfs-to-mount-a-remote-directory-as-a-volume/ [using the ssh config file]: https://linuxize.com/post/using-the-ssh-config-file/ [why i don't change ssh from port 22]: https://www.youtube.com/watch?v=Fzt5dqaIMYc