Connections and trust

Understand the SSH connection

Trace client, server, transport encryption, host authentication, user authentication, and the remote session.

An SSH connection answers two separate questions. Is this the server I meant to reach? And can I prove to that server who I am?

Most people blur the two into “the key worked”. That is the mistake I want you to avoid in this course. The server proves itself first, with its host key. Only then do you prove yourself, with your user key or a password.

Throughout the course we use one disposable Ubuntu server at 203.0.113.10, with an operator account called deploy. Create one on any provider you like and destroy it when you are done.

The order of events

When you type ssh [email protected], this is what happens:

  1. Your client opens a TCP connection to port 22.
  2. Both sides agree on encryption algorithms and create temporary session keys.
  3. The server signs part of that exchange with its host private key. Your client checks the signature against ~/.ssh/known_hosts.
  4. Your client proves the user identity, usually by signing a challenge with your private key.
  5. The server opens one or more encrypted channels: a shell, a command, a file transfer, a forwarded port.

Steps 2 and 3 protect you from the network. Step 4 protects the server from you. Keep them apart in your head and debugging gets much easier.

Watch it happen

Run the connection in verbose mode:

ssh -v [email protected]

You get many debug1: lines. A few are worth annotating. This one is the transport negotiation:

debug1: kex: algorithm: curve25519-sha256
debug1: kex: host key algorithm: ssh-ed25519

This one is host authentication. It is the server proving itself:

debug1: Server host key: ssh-ed25519 SHA256:wQ2vT9xk8mR0l...
debug1: Host '203.0.113.10' is known and matches the ED25519 host key.

And these are user authentication, where you prove yourself:

debug1: Offering public key: /Users/flavio/.ssh/id_ed25519 ED25519 SHA256:...
debug1: Server accepts key: /Users/flavio/.ssh/id_ed25519
Authenticated to 203.0.113.10 ([203.0.113.10]:22) using "publickey".

Every failure you will ever debug lives in one of those three places.

Break one thing on purpose

Now point the same command at a port nothing listens on:

ssh -v -p 2222 [email protected]

You get Connection refused and the log stops before any kex line. Nothing about keys was involved, because the connection never reached step 2.

Try a wrong username instead:

ssh -v [email protected]

This time the host key line still says “known and matches”. The failure comes later: Permission denied (publickey). The server was the right one. The user was not accepted.

Keep those two outputs. Compare them side by side and notice how far each one got. Predicting where a connection will fail, before you run it, is the skill this whole module builds.

Lesson completed