Fix SSH private-key permissions without hiding the real risk
Trace why OpenSSH ignores an exposed key, repair ownership and ACLs across Linux, macOS, Windows, and WSL, verify access, and rotate when exposure is possible.
OpenSSH prints a wall of warning characters, says a private key is unprotected, and refuses to use it. The server has not rejected the key yet—the client has rejected the local file because another account may be able to read it. That distinction saves time: changing security groups, usernames, or server-side authorized_keys cannot fix a key the client never offers.
Read the warning as a local access-control finding
$ ssh -vvv -i ./production.pem operator@203.0.113.10
...
WARNING: UNPROTECTED PRIVATE KEY FILE!
Permissions 0644 for './production.pem' are too open.
This private key will be ignored.
The current OpenSSH client manual says private-key files should be readable by the user but not accessible by others, and that ssh ignores a private key accessible by others. The accepted Stack Overflow answer by Kof recommends chmod 400, matching Amazon EC2 guidance. That is a safe narrow mode on a normal POSIX filesystem, but the number alone does not diagnose ownership, ACLs, Windows permissions, or a filesystem that cannot enforce Unix modes.
Inspect before changing anything
key="$HOME/.ssh/production.pem"
ls -l "$key"
stat "$key"
namei -l "$key" # Linux: inspect every path component
getfacl -- "$key" # Linux, when ACL tools are available
readlink -f -- "$key" # Confirm the real target on Linux
Verify the file is the intended private key without printing its contents:
ssh-keygen -y -f "$key" > /tmp/production-key.pub
ssh-keygen -lf /tmp/production-key.pub
The derived public key and fingerprint are safe to compare with an approved inventory, but treat even inventory data according to organizational policy. Do not paste the private file into a ticket, chat, or online decoder.
Repair the POSIX case safely
install -d -m 700 "$HOME/.ssh"
chown "$USER":"$(id -gn)" "$key"
chmod u=r,go= "$key"
stat -c '%A %a %U:%G %n' "$key"
ssh -o IdentitiesOnly=yes -i "$key" operator@203.0.113.10
chmod u=r,go= is the symbolic equivalent of 0400: owner read only, no group or other permissions. 0600 is also appropriately private and permits the owner to write. OpenSSH’s security property is lack of access for others, not whether the owner has a write bit.
Avoid sudo ssh. It changes the effective user, home directory, configuration, agent, known-hosts database, and trust boundary. If root owns a key that belongs to a service account, repair the ownership deliberately instead of making the connection as root.
ACLs can disagree with the mode bits
A trailing + in ls -l often signals extended ACLs. Remove only unexpected entries after recording them:
getfacl -- "$key"
setfacl --remove-all -- "$key"
chmod u=r,go= "$key"
getfacl -- "$key"
On macOS, inspect with ls -le and remove an unwanted ACL with the platform’s reviewed chmod -N operation. Do not clear ACLs recursively across ~/.ssh; public keys, configuration, sockets, and organization-managed controls may have different requirements.
Why WSL and shared mounts are different
A key stored under /mnt/c, an SMB share, a container bind mount, or some removable filesystems may show Unix mode bits that are synthesized or cannot be enforced. Copy the file into the Linux user’s own filesystem and set the mode there:
install -d -m 700 "$HOME/.ssh"
cp /mnt/c/Users/me/Downloads/production.pem "$HOME/.ssh/production.pem"
chmod u=r,go= "$HOME/.ssh/production.pem"
Amazon’s current WSL instructions likewise direct users to copy the PEM from the Windows drive into WSL before connecting. Deleting the Windows copy later may be prudent, but first follow retention and incident procedures; moving a file does not prove no other copy exists.
On Windows, fix the ACL rather than imitate a mode
$key = "$env:USERPROFILE\.ssh\production.pem"
icacls $key
icacls $key /inheritance:r
icacls $key /grant:r "$env:USERNAME:(R)"
Review the resulting ACL and retain required entries such as the account and platform administrators according to policy. Domain policy and localized identity names can alter the exact command. The goal remains the same: untrusted local users must not read the private key.
Separate client refusal from server rejection
| Evidence | Likely layer |
|---|---|
UNPROTECTED PRIVATE KEY FILE, key ignored | Local ownership, mode, ACL, or filesystem |
Permission denied (publickey) after offering a key | Wrong login user, wrong key, absent authorized key, or server policy |
| Timeout or no route | Address, routing, firewall, security group, or service availability |
| Host-key warning | Server identity changed or destination is wrong; investigate before accepting |
Use ssh -vvv to see which identity is loaded and offered, but redact usernames, hostnames, paths, and fingerprints as required before sharing logs.
Verification checklist
- The key has the expected owner and resolves to the intended regular file, not a surprising symlink.
- Group, other users, and unintended ACL principals cannot read it.
- The parent directory does not allow another user to replace the file.
ssh-keygen -ycan derive a public key and its fingerprint matches inventory.ssh -o IdentitiesOnly=yes -i …offers only the intended identity.- The server account, network path, and host key are verified independently.
- Automated tests run as the real service account on the real mount type.
When permission repair is not enough
chmod prevents future local reads; it cannot retract copies. If the key was in a public repository, shared download folder, broadly readable backup, build log, or ticket attachment, assume possible disclosure. Remove or revoke the corresponding public key from every server, create a replacement through the approved process, update dependent automation, test access, and then retire the old secret.
For EC2, recovery may require adding a replacement public key through an existing trusted session, an instance-management channel, or an offline root-volume procedure. Plan and test this path before an incident. Never terminate or rebuild an instance merely because the local file mode was wrong unless recovery policy and data protection demand it.
Production trade-offs and rollback
A passphrase limits damage from a stolen file but introduces unlock and agent-management requirements. Agents reduce repeated prompts but create another credential-bearing process. Hardware-backed keys can prevent extraction but complicate unattended workloads. For automation, prefer short-lived identity, managed instance access, or a secret store over distributing long-lived PEM files where the platform supports it.
If tightening ACLs breaks a job, do not restore broad readability. Roll back by granting the exact service identity access, relocating the key to an appropriate private store, or changing the service account. Keep an auditable fingerprint and dependency map so rotation can be reversed only by re-authorizing a known key—not by weakening file permissions.
The warning is doing its job: it turns a locally exposed credential into a visible failure before the client sends it anywhere.
Sources: the Stack Overflow question by Matt Roberts and accepted answer by Kof (CC BY-SA), the current OpenBSD ssh client manual, and Amazon EC2’s current SSH client connection guidance.
Primary source: Review the official reference ↗