Keys, agent, and config

Write maintainable SSH config

Use host aliases and narrowly scoped options to make the intended connection repeatable.

The explicit command from module one was long on purpose. Now that it works, move it into ~/.ssh/config so you never type it again, and so scp, rsync, and Git can reuse the same settings.

Create the file if it does not exist and add one block:

Host notes-server
  HostName 203.0.113.10
  User deploy
  Port 22
  IdentityFile ~/.ssh/notes_server_ed25519
  IdentitiesOnly yes

Host is the alias you type. HostName is the real address. Everything else is what you used to pass as flags. Keep the file at mode 600, because SSH may refuse a world-readable config.

Now the connection is:

ssh notes-server

Check what SSH actually resolved

Never trust a config file by reading it. Ask SSH what it will use:

ssh -G notes-server
user deploy
hostname 203.0.113.10
port 22
identitiesonly yes
identityfile ~/.ssh/notes_server_ed25519
forwardagent no

If a line surprises you, some other block is contributing. This is the tool that finds it.

Two aliases, one server, two jobs

An alias is not the server. It is one way of reaching it. You can have several. Add a second block that uses the restricted CI key from the previous lesson:

Host status-vps
  HostName 203.0.113.10
  User deploy
  IdentityFile ~/.ssh/ci_test_ed25519
  IdentitiesOnly yes
  RequestTTY no

ssh notes-server gives you an interactive operator shell. ssh status-vps runs the forced command and exits. Same machine, same account, different identity, different powers. Run ssh -G on both and diff the output. Only the identityfile and requesttty lines should differ.

Keep wildcards boring

The order of blocks matters. SSH reads the file top to bottom and, for most options, the first value wins. So specific hosts go first, and a wildcard goes last:

Host notes-server
  ...

Host *
  ServerAliveInterval 30
  ServerAliveCountMax 3

Keepalives are a fine thing to put under Host *. A User, an IdentityFile, ForwardAgent yes, or a ProxyJump under Host * is not. It silently applies to every host you will ever connect to, including ones you have not created yet. When one of those connections fails months later, nobody remembers the wildcard.

One realistic failure: you put Host * at the top with User flavio, then add notes-server below with User deploy. SSH picks flavio, because it saw that first, and you get Permission denied (publickey) with no obvious reason. ssh -G notes-server shows user flavio and the mystery is solved in one line.

My rule: every option that could log me into the wrong place lives in a named host block, never in a wildcard.

Lesson completed