Linux / FreeBSD servers.

How to Set Up and Use SSH

SSH Tools and Cautious Configuration

ssh client
man page
sshd_config server
man page

OpenSSH includes what you need to:

Run remote commands and establish remote interactive sessions using the ssh command,

Copy files to and from remote systems using the scp and sftp commands, and

Synchronize collections of files across multiple systems using the rsync command — that is, ensure that all systems contain the same sets of files with the same contents.

These commands enforce authentication of the involved hosts and the user, and confidentiality of the contents of the files and the remote commands and their output.

The installation defaults should provide decent security, but let's see how to make things more secure, more powerful, and easier to use. The following includes some asides and deeper information in  grey boxes  which you may want to skip over on a first reading.

What About Windows?

The portable OpenSSH suite of utilities has existed since 1999, but Microsoft did not include them until April 2018, in version 1803 of Windows 10.

Also see the dnsmasq server and, until recently, the NTP protocol for further examples of the entire Internet relying on one person's project.

That means that the Windows world largely became locked in to using PuTTY. It has been a very nice suite of tools, but it always seemed sketchy because PuTTY is a project by one guy in the UK.

The good news is that since 2018 you can easily use the same broadly supported and carefully audited OpenSSH package on Windows. For the first few years you had to manually enable it, but now the client commands are available by default. The user and host configuration and keys are stored in Windows-specific locations, but otherwise all the following should apply.

How Do the SSH Tools Work?

Some further details will follow, but the short version about the entire suite is:

  1. The client system establishes a connection to TCP port 22 on the server.
  2. The two systems exchange information about their versions of SSH and the cryptography that they support.
  3. The client verifies the server's identity through a cryptographic challenge-response sequence.
  4. The server verifies the user's identity through its choice of cryptographic challenge-response or password.
  5. With the server and the client user authenticated, and a uniquely encrypted session established, the work happens. This will be one of:
    • Run one command and return its output using ssh.
    • Establish an interactive command-line session using ssh.
    • Transfer files from client to server, or from server to client, using scp or rsync.
    • Establish an interactive file-transfer session, using sftp.

Start by Making Sure Your Software is the Latest

Package
Management

Make sure that you are using the latest available OpenSSH packages on both your clients and servers. You need all the latest patches. Once patched, you also want to have the latest features and default settings. See my package management page for how to do this on various Linux distributions and FreeBSD. The quick version is:

Debian-derived Linux with dpkg / apt
  # apt update
  # apt full-upgrade

Red-Hat-derived Linux with rpm / dnf
  # dnf check-update
  # dnf upgrade

FreeBSD
  # pkg update
  # pkg upgrade
  # freebsd-upgrade fetch
  # freebsd-upgrade install

Make Sure You're Ready

OpenSSH keeps its configuration and keys in the /etc/ssh directory. You will see something like this:

$ ls -l /etc/ssh/
total 640
-rw-r--r--  1 root wheel 587027 Nov 29 16:52 moduli
-rw-r--r--  1 root wheel   1526 Nov 29 16:52 ssh_config
-rw-------  1 root wheel    756 Apr 28 23:42 ssh_host_ecdsa_key
-rw-r--r--  1 root wheel    287 Apr 28 23:42 ssh_host_ecdsa_key.pub
-rw-------  1 root wheel    432 Apr 28 23:37 ssh_host_ed25519_key
-rw-r--r--  1 root wheel    115 Apr 28 23:37 ssh_host_ed25519_key.pub
-rw-------  1 root wheel   3401 Apr 28 23:44 ssh_host_rsa_key
-rw-r--r--  1 root wheel    759 Apr 28 23:44 ssh_host_rsa_key.pub
-rw-r--r--  1 root wheel   3431 Nov 29 16:52 sshd_config

Client and server operation are configured by ssh_config and sshd_config, respectively.

Host key pairs are in ssh_host_*, public keys in *.pub and private keys in the other files. Notice the ownership and permissions.

Leave off the -t and list of types and you will get extra unaccompanied lines starting "#" for other known key pair types that the server doesn't support.

You can remotely check to see the key types and sizes with something like the following. It reports, for each key type, the hostname and TCP port, reported server version, and then the server hostname again with the public key type and value in Base64.

$ ssh-keyscan -t rsa,ecdsa,ed25519 cromwell-intl.com
# cromwell-intl.com:22 SSH-2.0-OpenSSH_9.5 FreeBSD-20231004
cromwell-intl.com ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAFix3psmhGRMvthgFbDPx/DURYzmL2xAGcfWQl0QSTF/dl9HPz5LXwR/0z87nAjNkBxp6iqwUbE55AO/D17eyvpqgH0i9YoCI70PBOFnJTrQs+aENHCzAWWJ/UfsuFAAKKh1sehqT5nc7Zub5L7iFwfaY3YfjB/TIvC+ZbBJVm2IZKp2A==
# cromwell-intl.com:22 SSH-2.0-OpenSSH_9.5 FreeBSD-20231004
cromwell-intl.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDr1R2POvLWQT2i0YsMc53fk5LNuNDW5BuzZhM19qyqshfarWPodp0apvAKrjrOizPeYau42UT1I4IgJ4bLUjEY3uv9eL/FqebYeVwyWTjDE7q0Uj/fGhPY/929WBfSRe0utCobh3non4k9y4Ts9H9cNzRRQlF8mSr6rpxuXo8EovcaU2pFsyxiTANYZ+Ueq3/uM31+ow3CJiZyC8nIeyzLUUm6wpafcV1YIZq8KhNJkl79uqliXMWXK+7oFh+ORGzX9ySbkfNUAtbHslNkUOqdgnKGLiTRmk54uJmLPc16J/3P1XnCmUxgDqPhMDW/FONjnl2nqWme3ff6lSAN0U/gmmw+RoA3ZtJ88qIj8pZBmf3V8ao56ny125jTCSRlaqDE5bs8CBG3TL7d+439Pmd4b1zJZsaplFa1mhSG5+6wIaVV4n7pFJUj7eG6iBPey7iq6Rwe99NXXAJLNA9e/4irasfZdIok2MCtkqiGVvnoT1QOcVTT1GEWPwlA9LmgDVHsqvB0WT4h02ROcIbTQDpKIqFeYPKKauSs2lj/R7vOYU72dRzNGneq9zM3UGXqnSpGFVKAl9FHb2TxpYPIJd877KqcszWjjMq0//pB5tvbyb1C5uD0JS5SEN5aK5mAif1hwFZqecA7bAru08v/JMrQw8iC+nYdK0Zy9Cvc95g6Pw==
# cromwell-intl.com:22 SSH-2.0-OpenSSH_9.5 FreeBSD-20231004
cromwell-intl.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKjERE0SSmbpunoNAk8uLowYpcLqMQqcfi0k3vqwfxXG

The public key string actually starts with some additional metadata including key type, which the ssh-keyscan manual page doesn't describe.

That's a a 521-bit ECDSA key pair using the NIST P-521 elliptic curve, a 4096-bit RSA key pair, and a 256-bit key pair using the Ed25519 curve. The first two are the maximum currently supported lengths, the third is always 256 bits. My pages on on elliptic-curve cryptography and quantum-safe cryptography have more on elliptic curves and their coming replacements if you want to go further down that rabbit hole.

Yes, those ECDSA and RSA keys are longer than the defaults, but computers are very fast at integer arithmetic. I'm trying to set a good example here. I generated new, stronger key pairs with the ssh-keygen command and moved them into place.

Logged in on the server itself, I can ask about each public key in turn. These Base64 strings are the SHA-2-256 hashes of the public keys, not the much longer keys themselves.

freebsd$ ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key.pub
4096 SHA256:+rmSJp7u19nLq8DqU2nfAG3pURyx7MJqUNCfhrgfltM root@www.c.cromwell-intl.internal (RSA)
freebsd$ ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key.pub
521 SHA256:u0XjweABjFwqV2NDhqYmhTkZrT6Rcjs/AGjVywkBnWQ root@www.c.cromwell-intl.internal (ECDSA)
freebsd$ ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub
256 SHA256:qjOEfraNaDaNpMVgl8HuyOdi8B6302TS1oMS+VgECEw root@www.c.cromwell-intl.internal (ED25519)

The First Connection

Your first connection to a new system will be met with a question that you really can't answer. Here's what happens when I connect to my system named oracle, a Raspberry Pi running Oracle Linux:

$ ssh oracle uptime
The authenticity of host 'oracle (2601:249:4300:487:ba27:ebff:fe89:4e84)' can't be established.
ED25519 key fingerprint is SHA256:4OwQHUPvvH3fRPbEZ7R9WPQ2KXrJ6ZjT93E1OMrfOdg.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'oracle' (ED25519) to the list of known hosts.
 09:45:29 up 72 days,  2:26,  0 users,  load average: 0.00, 0.00, 0.00

If I hadn't responded with yes it would have abandoned the attempted connection. No user, me included, is going to know the server's public key fingerprint. Everyone quickly learns to always type yes!

Yes, the above ran without asking me for a password — that's because I have set up single sign-on as shown below.

If I immediately re-run the remote command, it connects and runs without asking me. Why? The host name and public key have been stored in my file ~/.ssh/known_hosts.

client$ $ grep cromwell-intl.com ~/.ssh/known_hosts
cromwell-intl.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKjERE0SSmbpunoNAk8uLowYpcLqMQqcfi0k3vqwfxXG
cromwell-intl.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDr1R2POvLWQT2i0YsMc53fk5LNuNDW5BuzZhM19qyqshfarWPodp0apvAKrjrOizPeYau42UT1I4IgJ4bLUjEY3uv9eL/FqebYeVwyWTjDE7q0Uj/fGhPY/929WBfSRe0utCobh3non4k9y4Ts9H9cNzRRQlF8mSr6rpxuXo8EovcaU2pFsyxiTANYZ+Ueq3/uM31+ow3CJiZyC8nIeyzLUUm6wpafcV1YIZq8KhNJkl79uqliXMWXK+7oFh+ORGzX9ySbkfNUAtbHslNkUOqdgnKGLiTRmk54uJmLPc16J/3P1XnCmUxgDqPhMDW/FONjnl2nqWme3ff6lSAN0U/gmmw+RoA3ZtJ88qIj8pZBmf3V8ao56ny125jTCSRlaqDE5bs8CBG3TL7d+439Pmd4b1zJZsaplFa1mhSG5+6wIaVV4n7pFJUj7eG6iBPey7iq6Rwe99NXXAJLNA9e/4irasfZdIok2MCtkqiGVvnoT1QOcVTT1GEWPwlA9LmgDVHsqvB0WT4h02ROcIbTQDpKIqFeYPKKauSs2lj/R7vOYU72dRzNGneq9zM3UGXqnSpGFVKAl9FHb2TxpYPIJd877KqcszWjjMq0//pB5tvbyb1C5uD0JS5SEN5aK5mAif1hwFZqecA7bAru08v/JMrQw8iC+nYdK0Zy9Cvc95g6Pw==
cromwell-intl.com ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAFix3psmhGRMvthgFbDPx/DURYzmL2xAGcfWQl0QSTF/dl9HPz5LXwR/0z87nAjNkBxp6iqwUbE55AO/D17eyvpqgH0i9YoCI70PBOFnJTrQs+aENHCzAWWJ/UfsuFAAKKh1sehqT5nc7Zub5L7iFwfaY3YfjB/TIvC+ZbBJVm2IZKp2A==

Yes, I have an IPv4+IPv6 DNS server at home, authoritative for the domain kc9rg.org (unregistered, local-only). It runs on a Raspberry Pi.

If I make later connections to the same system using its fully-qualified domain name oracle.kc9rg.org and its IPv4 and IPv6 addresses 192.168.1.33 and 2601:249:4300:487:ba27:ebff:fe89:4e84, I will get similar questions and the known_hosts file will be appended.

The intent is that if I start to make an SSH connection to a host I have previously connected to, but its cryptographic identity has changed, I will get a dire warning and the connection will fail:

$ ssh osmc.kc9rg.org uptime
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
SHA256:+rmSJp7u19nLq8DqU2nfAG3pURyx7MJqUNCfhrgfltM.
Please contact your system administrator.
Add correct host key in /home/cromwell/.ssh/known_hosts to get rid of this message.
Offending RSA key in /home/cromwell/.ssh/known_hosts:15
  remove with:
  ssh-keygen -f "/home/cromwell/.ssh/known_hosts" -R "osmc.kc9rg.org"
Host key for osmc.kc9rg.org has changed and you have requested strict checking.
Host key verification failed.

As the warning says, it is possible that some man-in-the-middle attack is underway, possibly DNS spoofing or, less likely, MAC spoofing on one of the LANs between the endpoints.

Most of the time, as was the case here, the server OS has been reinstalled and the SSH service generated new key pairs when it first started. No attack, just a legitimate change.

At this point your personal SSH configuration and identity will look something like this. Again notice the owner and permissions.

$ ls -l ~/.ssh
-rw------- 1 cromwell cromwell 1130 May  4  2024 authorized_keys
-rw-r--r-- 1 cromwell cromwell  256 May  4  2024 config
-rw------- 1 cromwell cromwell  801 May  4  2024 id_ecdsa
-rw-r--r-- 1 cromwell cromwell  282 May  4  2024 id_ecdsa.pub
-rw------- 1 cromwell cromwell  484 May  4  2024 id_ed25519
-rw-r--r-- 1 cromwell cromwell  112 May  4  2024 id_ed25519.pub
-rw------- 1 cromwell cromwell 3434 May  4  2024 id_rsa
-rw-r--r-- 1 cromwell cromwell  752 May  4  2024 id_rsa.pub
-rw------- 1 cromwell cromwell 4749 Apr 29 21:31 known_hosts

OpenSSH will mysteriously refuse to work if you try to use it with recklessly permissive key file permissions. The private key files must be mode 600, while the public key files may be mode 644. The ssh-keygen command will have created the files with appropriate permissions, don't mess that up.

In an enterprise environment you might use ssh-keyscan to read in a list of all host names, fully-qualified domain names, and IP addresses, and generate a system-wide file /etc/ssh/known_hosts that is copied to every potential SSH client system on the network. That way there's an authoritative source of host identity information. If a real attack is underway, users can't disable defenses by editing or removing their personal ~/.ssh/known_hosts file.

The problem with that approach is that you have to re-generate the file and copy it to every system when you re-install or do a major upgrade to any SSH server.

Single Sign-On With an SSH Agent

Before moving on to some examples of the SSH suite, let's set up single sign-on so you don't have to repeatedly type your password.

If you are using a text-only console, do this before continuing:
$ ssh-agent $SHELL
That starts an SSH agent which starts a new shell for you.

You want to use an SSH agent. Your graphical display manager should start one for you when you log in, and then every process you run is associated with the agent. If some process needs to authenticate you over SSH, probably because you ran one of the ssh or scp or sftp or rsync commands, the agent handles your key pairs and does the cryptographic work.

That is, it does that once it has access to your personal SSH private keys.

Create your personal SSH key pairs with the following. You very likely will choose not to bother with an RSA key pair because elliptic curve cryptography can much more efficiently provide equivalent security. Run at least the first two commands, agreeing to the default storage locations. For the sake of your sanity, use the same passphrase for all three. To make things simpler yet, use your login password as the key passphrase:

$ ssh-keygen -b 521 -t ecdsa
$ ssh-keygen -t ed25519
$ ssh-keygen -b 4096 -t rsa

Now generate a file named authorized_keys, containing all your public keys:

$ cat ~/.ssh/*.pub > ~/.ssh/authorized_keys

Copy that file to every SSH server where you have an account:

$ for host in server1 server2 server3 [...]
> do
>   ssh $host mkdir .ssh
>   scp ~/.ssh/authorized_keys $host:.ssh
> done
authorized_keys                               100% 1130     5.7KB/s   00:00
authorized_keys                               100% 1130     7.1KB/s   00:00
authorized_keys                               100% 1130     6.2KB/s   00:00
[...and so on, one line per remote host...]

You will have to type your password twice for each time through the above loop, once to use ssh to create a new directory under your home directory, and a second time to copy authorized_keys into that new directory.

Now you should be able to loop through the same list and run remote commands without re-authenticating! Of course authentication happens, but now the SSH agent is doing it on your behalf. No need to re-type your password! Let's use ssh to run a sequence of two commands with a single connection:

$ for host in server1 server2 server3 [...]
> do
>   ssh $host 'hostname ; uptime'
> done
server1
23:49:01  up 11:49, 0 users, load average: 0.31, 0.27, 0.25
server2
23:49:01  up 19:09, 0 users, load average: 0.08, 0.05, 0.06
server3
21:49:02  up 23:20, 3 users, load average: 0.70, 0.79, 0.86
[...and so on, two lines per remote host...]

Provide your SSH private keys to the SSH agent when you log in. Depending on which display manager you use, and how you have it configured, this may simply happen with no additional work on your part. Or, you may be asked when it's first needed.

The LightDM display manager may pop up a window asking you to enter the SSH private key passphrase when you first run a command that needs it:

LightDM window asking for the passphrase to an SSH private key.

Similarly, when you first start the Chrome browser, it may ask you to re-enter your password so the browser can authenticate you to your Google account.

LightDM window asking for the passphrase to an SSH private key.

Rely On Cryptography, Not Passwords

If a human can select and remember a password, an automated process can find it.

The search space of passwords or pass phrases that a person can remember and then type without being able to see the characters is much smaller than the search spaces of elliptic-curve or RSA key pairs of reasonable length. I trust cryptography. I have very little confidence in passwords.

What we want to do on an Internet-exposed SSH server is make it so there is no such thing as a functioning password, especially for the root account.

Good news: this is very easy to set up on recent releases of Linux and FreeBSD, because the defaults for most OpenSSH configuration details are set up to do that.

Be very careful continuing with this, because I would assume that you are about to start reconfiguring a remote server. It is very easy to lock yourself out. Be careful! I have only had to use the graphical console provided through the Google Cloud dashboard two times, so far, to fix this sort of problem and restore remote access into my cloud-based server.

On this server, running FreeBSD with OpenSSH v9.x, I have made only six changes to the defaults in the server configuration in sshd_config, and none in the client configuration. Here are the few lines that aren't empty or entirely comments. The vitally important one is KbdInteractiveAuthentication:

$ egrep --color -v '^$|^#' /etc/ssh/*config
/etc/ssh/sshd_config:Subsystem  sftp    /usr/libexec/sftp-server
/etc/ssh/sshd_config:KbdInteractiveAuthentication no
/etc/ssh/sshd_config:X11Forwarding yes
/etc/ssh/sshd_config:AcceptEnv LANG LC_*
/etc/ssh/sshd_config:AllowAgentForwarding no
/etc/ssh/sshd_config:ClientAliveInterval 420

Restart the SSH service and make sure it works:

FreeBSD:
  # /etc/rc.d/sshd restart

Linux:
  # systemctl restart sshd.service

Then reboot and test that everything works together.

I have made the same changes to sshd_config on my desktop and laptop, so I get the same convenience back and forth at home. The sshd_config and ssh_config manual pages warn that X11 forwarding comes with an increased risk. However, carefully consider that warning:

X11 forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host (for the user's X11 authorization database) can access the local X11 display through the forwarded connection. An attacker may then be able to perform activities such as keystroke monitoring if the ForwardX11Trusted option is also enabled.

If someone can bypass file permissions on the remote system, then there are much larger problems that need to be fixed immediately!

On my laptop and my desktop at home, I have changed two lines of the client configuration in /etc/ssh/ssh_config:

$ egrep -v '^$|^#' /etc/ssh/ssh_config /dev/null
/etc/ssh/ssh_config:SendEnv LANG LC_*
/etc/ssh/ssh_config:HashKnownHosts no

With the client sending and the server accepting environment variables LANG and LC_ALL, my locale during remote commands is set up the way I like it at my end. U.S. English but supporting UTF-8 characters, and ASCII ordering in ls output and similar.

On many Debian-derived distributions such as Linux Mint, which I prefer, the installation turns on HashKnownHosts which stores hashes of hostnames and IP addresses rather than original values in the .ssh/known_hosts file. I agree that it reduces a rather specialized risk. "Oh no, if someone can break into my system, and violate file permissions and read my .ssh/known_hosts file, they could figure out where else I have accounts!" That shouldn't be possible, once again I would need to fix the underlying problem!

Running Graphical Applications Through X11/SSH

I do all my email with Thunderbird running on my laptop. But when I'm at home, I have the laptop sitting on another desk and I sit in front of a desktop computer. I remotely start Thunderbird this way, change the hostname as needed:

$ ssh -fX laptop thunderbird

-f means "Fork the ssh process into the background as soon as authentication is done."

-X means "Do this through an X11 tunnel, handling graphics to the client and keyboard and mouse events from the client."

To run commands on the laptop while sitting at the desktop, I start a new terminal emulator. The old xterm application doesn't handle Unicode and locale details gracefully, or at least not easily. Besides, I prefer the konsole terminal emulator where Shift-Ctrl-T opens a new tab with its own shell, and then Shift-RightArrow and Shift-LeftArrow switch between tabs. On the desktop I run this:

$ ssh -fX laptop konsole

That new terminal and the shell inside of it are running on the laptop on the other side of the room, and they don't yet have an SSH agent helping them. In that new terminal I would see:

$ ssh-add -l
Could not open a connection to your authentication agent.

Let's start an SSH agent and have it start a new shell. Then I'll try to figure out how the processes are related. Only the ssh-agent command is important here:

$ ps -f
UID          PID    PPID  C STIME TTY          TIME CMD
cromwell  361446  361423  0 18:29 pts/4    00:00:00 /bin/bash
cromwell  361472  361446  0 18:29 pts/4    00:00:00 ps -f
$ ssh-agent $SHELL
$ ps -f
UID          PID    PPID  C STIME TTY          TIME CMD
cromwell  361446  361423  0 18:29 pts/4    00:00:00 /bin/bash
cromwell  361476  361446  0 18:30 pts/4    00:00:00 /bin/bash
cromwell  361502  361476  0 18:30 pts/4    00:00:00 ps -f
$ ps -f $(pgrep ssh-agent)
UID          PID    PPID  C STIME TTY      STAT   TIME CMD
cromwell  361477  361476  0 18:30 ?        Ss     0:00 ssh-agent /bin/bash

The important point is that the new command shell within the terminal is associated with an SSH agent. Let's see what that agent is doing.

$ ssh-add -l
The agent has no identities.

Now I just need to give private key access to the agent.

$ ssh-add
Enter passphrase for /home/cromwell/.ssh/id_rsa:
Identity added: /home/cromwell/.ssh/id_rsa (/home/cromwell/.ssh/id_rsa)
Identity added: /home/cromwell/.ssh/id_ecdsa (/home/cromwell/.ssh/id_ecdsa)
Identity added: /home/cromwell/.ssh/id_ed25519 (/home/cromwell/.ssh/id_ed25519)
$ ssh-add -l
4096 SHA256:wR9dDUnkkXZBSESGe529xqPBHGU9LUacKANVpdsE5dA /home/cromwell/.ssh/id_rsa (RSA)
521 SHA256:TGjRDHyS+OLaYWDct7G7NZN1k+vSS9R8PJcOBkfCmKE /home/cromwell/.ssh/id_ecdsa (ECDSA)
256 SHA256:+o3rP/Mz5bpp+Vwj3XuOsO6zeT3gnwrcutKiRuHD6jM /home/cromwell/.ssh/id_ed25519 (ED25519)

The above shows that it's working. If I want to see the public keys for some reason, I could:

$ ssh-add -L
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC+l8qva0kVPpl7lA7utT1mSyf43LoSrh1X34BBMKufZ192gsolcloUdKjIz8oDmruPqw3RezIgklEXSUx7bf5hXxLfFcsk1F4lYp2psVGya8lKTUssFt+NFO+nmVJmdjm37vpXxq6tpAqYcnbk68iKRKkLAnrRIxLUZBET56l3TnvE6DZWQD4NnwJACLbe6cIt//t0pMKLh8/WbzmMHlGkc1+iXYOmrJQrTUl11Oo6RDYMoEujo4/2dwbcVY83xCIFqiUhsUw4pCN/ZHJTWb+o6T+SSkSC0niMVWQowdZmRorAFGeAeyzonfQ/dlcmNDc4KKkqNdSVasVze6nefLFcEUKr+KERvszBDV8lALyHCDDttHDI2/+G/+oURk2xiTTOFI/53YJ1pUBWvf8+TlNzJE+bHEW1BEYef+zV8nsfy65eimg3WatVGrKW3+2rAjWXS8zcc2ne0aCRtxThC4xX+TqGUmsUJ+Nt2d1lvt711/Sgcv9aJm/kn274Mq9yopDSJtFxS75uflNRuXL+T8LPcM1DRg7/CfYPsze/7IFqZiwXxSaMvk86BBtM4mSJhGuduHjffvnc0GCthS+3EZfaQ05xFeT/FSuewzT3wAg18eO8K+KNb77Ua+wDcRaz75U3xhMdAjQpWZuaX9YSAb5j58nsGjDL9y8+xyLVv8Eh3Q== /home/cromwell/.ssh/id_rsa
ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBABVp9/RxSdQ3McMleyJoMWPiiWHTe/cynfNPUN1seKm7ZC7yl0eSytYK0OPt/jcFGTnaayDdqJk/jTY3hDqNxVHWAHhRX3nsKnvrwbloM2NLC+k0ICxQof0bVpZZBKbCZvyN1qD/W+81hly+XeBi7lUazurJzZdk+kHO02hwHQOZZlQvQ== /home/cromwell/.ssh/id_ecdsa
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKMk1QmQ41sQU8/onCLUhd4ilrjnjqmHr2YIWTOyZfHS /home/cromwell/.ssh/id_ed25519

Synchronizing With rsync

I edit web pages on my desktop, or on my laptop when I'm away from home, adding new pages, with images, and changing existing content. Then I could push those changes to my other system and the server with the following. Be careful, you need the trailing "/" on both local and remote paths, see the rsync manual page for details:

$ rsync -av --progress --exclude '*.swp' \
	/var/www/htdocs/ laptop:/var/www/htdocs/
$ rsync -av --progress --exclude '*.swp' \
	/var/www/htdocs/ cromwell-intl.com:/var/www/htdocs/

The --exclude '*.swp' means that it won't copy vim swap files. Because yes, of course, vim is my preferred HTML editing tool.

I say "could" in the above, because that's a lot of typing and it's still limited. I have a shell script which looks something like the following. Change desktop and laptop and other details as appropriate:

$ cat ~/bin/update-web
#!/bin/bash

if [ "$(uname -n)" == "desktop" ]; then
	otherhost="laptop"
else
	otherhost="desktop"
fi

if [ $# -ne 0 ] && [ $1 == "test" ]
then
	testing="-n"
	echo "JUST TESTING TO SEE WHAT WOULD BE TRANSFERRED:"
else
	testing=""
fi

# Copy data to the server, except do NOT copy
# any files with names ending with '.swp'.
#   -a         = archive mode, preserve metadata
#   -v         = verbose, list files are they're copied
#   --progress = for large files, show progress as it happens
#   --exclude  = but not file names with this pattern
#   -n         = just report what would happen, $testing will
#                be set to "-n" and I just get a report if
#                I run the script like this:
#                  $ update-web test
echo "Copying data to the server."
rsync -av --progress --exclude '*.swp' $testing \
	/var/www/htdocs/ cromwell-intl.com:/var/www/htdocs/

# If I'm at home, copy data to the other system.
if host $otherhost.kc9rg.org > /dev/null
then
	echo "Also copying data to $otherhost."
	rsync -av --progress --exclude '*.swp' $testing \
		/var/www/htdocs/ ${otherhost}:/var/www/htdocs/
else
	echo "Cannot resolve $otherhost, I must be away from home."
fi

Dealing With GoDaddy Shortcomings

GoDaddy makes simple things easy, and then makes more advanced things quite difficult to impossible. And, they leave customers running on some very outdated platforms. As early 2024 one of my client's Linux-hosted websites was on a virtual server still running OpenSSH_5.3p1 from February, 2013. That meant that I needed to add the following to .ssh/config on my desktop and laptop, change www.client.example.com to your actual GoDaddy-hosted name:

Host www.client.example.com
  HostKeyAlgorithms +ssh-rsa

GoDaddy also has a very touchy IDS system built into their load balancers. Add the --timeout=10 option to your rsync commands to tell it to cautiously back off for 10 seconds after a failed connection attempt. Otherwise, you will get blocked by their IDS, and it takes an hour or more on the phone to reach a GoDaddy support person who understands Linux and SSH and their IDS sensitivity to get your access re-established.

I would not select GoDaddy for my project, but if a client wants to use them because of package bundling and ease of doing simple things, I can play along with that.

Going Even Deeper

The first thing to explore would be to ask for one, two, or three levels of verbosity for running a trivial command on a remote server.

$ ssh -v servername date
[... lots of detail ...]
$ ssh -vv servername date
[... more detail ...]
$ ssh -vvv servername date
[... even more detail ...]

The sshd_config manual page has lots of detail on setting up access control in terms of client hostnames, client IP addresses, user names, UNIX groups, and so on. Just don't enable PermitRootLogin unless you are very certain of what you are doing!

As for monkeying around with the key exchange and cipher specifications, you certainly can but I wouldn't advise doing so.