Web Services

SSH Passwordless Login: from ssh-keygen to Permission denied

Passwordless SSH boils down to one sentence: put the public key into the remote authorized_keys. What blocks you is usually the three hidden gates — file permissions, the home directory, and SELinux. This guide covers the full setup and a step-by-step debugging path.

By 巧匠 Team·7 min read·Updated 2026-08-30

The whole idea in one sentence

The client private key and the public key in the remote authorized_keys must match — that is all. Debugging therefore revolves around two things: whether the pair matches, and whether the remote sshd is willing to trust that authorized_keys file (bad permissions make sshd silently refuse to read it).

ssh-keygen -t ed25519 -C "you@laptop"   # 生成密钥对(一路回车即可)
ssh-copy-id user@server                    # 把公钥写入远端 authorized_keys
ssh user@server                            # 验证:不再要密码即成功

The standard three steps and how to verify

① Generate a key: prefer ed25519 (shorter, faster, secure), defaulting to ~/.ssh/id_ed25519; ② Distribute: ssh-copy-id is the most reliable (it appends and fixes permissions); on Windows without it, manually append the key to the remote ~/.ssh/authorized_keys; ③ Verify: `ssh -o BatchMode=yes user@server echo ok` — BatchMode forbids interactive passwords, so a clean ok proves passwordless auth truly works, far more trustworthy than "no password prompt appeared".

type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh user@server "cat >> ~/.ssh/authorized_keys"
# Windows 手动分发公钥的写法

Gate 1: file permissions

sshd is extremely picky: if authorized_keys or .ssh is group-writable it silently ignores the whole file (no error, just a fallback to password auth). Correct values: ~/.ssh 700, authorized_keys 600, private key 600. Same on the Windows side — a too-open private key triggers UNPROTECTED PRIVATE KEY FILE and refuses to use it.

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_ed25519

Gate 2: the home directory

The sneakier trap: the home directory itself being group/other-writable (common with shared mounts or legacy chmod 777 after manual useradd) — sshd refuses to trust authorized_keys all the same. Check with `ls -ld /home/user` and tighten to 750 or stricter. The checking order is always home → .ssh → file.

ls -ld /home/user        # 家目录不能组/其他人可写
chmod 750 /home/user      # 需要时收紧

Gate 3: SELinux and the server log

On RHEL/CentOS with SELinux enforcing, a wrong security context on authorized_keys also fails silently (especially after manually mv/cp-ing the file). `restorecon -rv ~/.ssh` restores the standard context. For any stuck scenario there is one ultimate weapon: the server log `tail -f /var/log/secure` (Debian/Ubuntu: /var/log/auth.log) combined with a client-side -v reconnect — with both sides visible, the problem has nowhere to hide.

restorecon -rv ~/.ssh                     # SELinux 上下文修复
ssh -v user@server                          # 客户端详细握手过程
tail -f /var/log/secure                    # 服务端拒绝原因

Level up: config aliases and multiple keys

Put frequent connections into ~/.ssh/config: one Host alias replaces a long command line and can bind a different key per server — much cleaner than typing -i every time, and git remotes and deploy scripts benefit equally.

Host prod
  HostName 203.0.113.10
  User deploy
  IdentityFile ~/.ssh/id_ed25519_prod
  ServerAliveInterval 60
# 之后只需:ssh prod