OFFSET偏移
← All articlesOFFSET / FIELD NOTESNO. 045

SSH authentication: directories, keys, and TOFU

Distinguish user identity, server identity, and the limits of trust on first use.

English translation · Code and commands follow the Chinese original.

IN THESE READING PATHSSSH authentication, agents, and command-line workflows ↗
About 4 min readStart reading

Directory layout: SSH consists of two independent sides#

OpenSSH acts as both client (ssh) and server (sshd). Different programs in the same suite read their own configurations:

Direction Program Directory Ownership
Outbound (you connect to someone else) ssh client ~/.ssh/ + /etc/ssh/ssh_config You (per-user)
Inbound (someone else connects to you) sshd server /etc/ssh/ + per-user authorized_keys System + user
SSH 出站(你是客户端):
  你 ──ssh──▶ GitHub
    ~/.ssh/          ← 你的通行证、偏好、信任名单

SSH 入站(你是服务器):
  Windows ──ssh──▶ 你
    /etc/ssh/        ← 服务器运行配置 + 系统户口本
    ~/.ssh/authorized_keys  ← 你放行的客人名单

Four kinds of authentication material, four separate roles#

In a typical public-key-authenticated connection, four kinds of material can be distinguished. authorized_keys and known_hosts are list files, not individual keys:

Key Location Question answered Who verifies whom
id_ed25519 (your private key) ~/.ssh/ Who you are The peer verifies you
authorized_keys (other people’s public keys) ~/.ssh/ Who is authorized to connect You verify the peer
host key (system private key + .pub) /etc/ssh/ Which machine I am The peer verifies you
known_hosts (trust records such as peer host public keys or CAs) ~/.ssh/ Which servers I trust You verify the peer

The ownership exception: authorized_keys authorized_keys lives in ~/.ssh/, but it is used for inbound connections. It lists whom the server allows in, with a separate list for each user. It is fundamentally configuration read by sshd, placed in each user’s home directory for convenient self-management.

Public keys are not registered automatically The client verifies the server’s host key. With public-key authentication, the server verifies a signature made with the user’s private key. An administrator must first add the user’s public key to the target account’s authorized_keys (or use certificates or another authorization mechanism); connecting does not automatically add the visiting user’s public key to the authorization list. The client records the server’s public key only after accepting it or establishing trust in advance. Users can also authenticate with passwords or other methods, so not every connection uses a user public key.

Mutual authentication: why host keys are needed#

An SSH connection does more than verify who you are; verification works in both directions:

desktop ──────SSH 连接──────▶ 你的机器

方向1(验证对端身份):
  对端出示自己的私钥签名
  你的机器用 ~/.ssh/authorized_keys 里的公钥验证

方向2(验证你的机器 = host key 的活):
  你的机器出示 host key 私钥签名
  对端用 known_hosts 中已信任的主机公钥验证

The host key handles direction 2: it is your machine’s identity document, proving its identity back to the other side. This is what “both parties verify each other’s identity” means in the A/B machine example above.

Why verify the other direction? To prevent a man-in-the-middle attack:

desktop ──▶ [攻击者假装是你] ──▶ 真的你
                ↑
            desktop 以为连的是你,其实连的是骗子

An attacker connects to you, then impersonates you to accept a connection from desktop. Credentials entered by desktop go to the attacker. This is what the host key guards against: desktop records your fingerprint on the first connection and checks that it still matches on every subsequent connection.

The purpose and limits of TOFU#

TOFU (Trust On First Use):

  • First connection: verify and accept the peer’s host key, then record the host public key in known_hosts
  • Every subsequent connection: check that the fingerprint still matches

What it establishes (continuity):

场景A(TOFU 能防):你在 A 网吧连真 GitHub,记下真指纹;
  之后在 B 网吧,攻击者冒充 GitHub → 指纹对不上 → 警觉 ✓

What it cannot establish (authenticity on first use):

场景B(TOFU 防不了):你第一次就连上假 GitHub → 记下假指纹 → 永远被骗 ✗

TOFU’s blind spot TOFU can only establish whether later connections are to the same party as the first one. It cannot establish that the first party was genuine. If you connected to an impostor the first time, checking the fingerprint offers no protection against that impostor.

Strengthening initial trust: out-of-band verification#

  • Check officially published fingerprints: GitHub and cloud providers publish host key fingerprints on their official sites; compare them with ssh-keygen -lf after connecting
  • SSH certificates (SSH CA): large organizations issue certificates to each server and preinstall the CA public key on clients, allowing verification even on the first connection
  • Private networks/direct connections: these can reduce exposure, but being on the same LAN does not by itself prove identity. Ideally, obtain the fingerprint from the server console or another trusted channel and compare it

TOFU suits situations where the risk of initial trust is acceptable. Published fingerprints, a trusted console, or preconfigured trust material can strengthen the first verification. Do not disable host key checking globally just to remove the prompts.

GitHub’s git entry point and account identification#

When connecting to GitHub, User git is a shared entry-point name, not your GitHub account name. GitHub identifies accounts by public key, not by username. Multiple accounts are distinguished by using the same git user with different IdentityFile values:

Host github-work
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_work    # 工作账号的 key

Generic directory example#

~/.ssh/
  ├── id_ed25519          # 用户私钥,应设置 passphrase
  ├── id_ed25519.pub      # 用户公钥,注释可写 user@workstation
  ├── authorized_keys    # 允许登录此账户的用户公钥
  ├── known_hosts        # 已信任的服务器主机公钥
  └── config             # 用户客户端配置

/etc/ssh/
  ├── ssh_config         # 系统级客户端配置
  ├── sshd_config        # 服务器配置
  └── ssh_host_ed25519_key # 服务器主机私钥

Key insight#

Directory ownership fundamentally answers “Who owns this configuration?” /etc/ssh/ belongs to the system (machine identity, managed by root); ~/.ssh/ belongs to the user (your identity, managed by you). authorized_keys sits between them: it implements server authorization but is divided into each user’s guest list, so it lives in ~/.ssh/ while serving inbound connections.

Related#


References & further reading

  1. OpenSSH ssh manual ↗
  2. OpenSSH ssh_config manual ↗
  3. OpenSSH ssh-keygen manual ↗

Adapted from technical notes. Examples use generic names.

Continue along a reading path

SSH authentication, agents, and command-line workflows

  1. 01SSH authentication: directories, keys, and TOFU
  2. 02ssh-agent, passphrases, and keyrings
  3. 03SSH usage guide: command-line operations
CONTINUE READING / NEXT ARTICLEssh-agent, passphrases, and keyrings

Understand the separate roles of private-key file protection, the agent signing interface, and desktop keyrings.