Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for DevOps Security & Hardening By James Joyner IV · · 8 min read

TLS & Security Error Guide: 'UNPROTECTED PRIVATE KEY FILE!' (SSH Key Permissions)

Fix SSH 'Permissions are too open' / UNPROTECTED PRIVATE KEY FILE: set 0600 on keys, fix directory modes and ownership, and resolve bad permissions on the server.

  • #security
  • #troubleshooting
  • #errors
  • #ssh

Overview

SSH refuses to use a private key whose file permissions are too permissive, because a key readable by other users on the system is effectively compromised. When a private key is world- or group-readable (e.g. mode 0644), the OpenSSH client ignores it and aborts the authentication that depended on it. The same protection applies on the server side to authorized_keys, host keys, and the .ssh directory itself: if those are writable by others, sshd distrusts them and rejects the login.

The client-side error is unmistakable:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/home/deploy/.ssh/id_ed25519' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "/home/deploy/.ssh/id_ed25519": bad permissions
deploy@app-03: Permission denied (publickey).

On the server side it surfaces only in the auth log as Authentication refused: bad ownership or modes. The fix is precise file modes and ownership — not loosening anything, but tightening to the modes OpenSSH requires.

Symptoms

  • ssh prints “UNPROTECTED PRIVATE KEY FILE!” / “Permissions 0644 … are too open” and falls back to “Permission denied (publickey)”.
  • Key auth works for one user but not another who copied the key with loose modes.
  • On the server, login is refused and /var/log/auth.log shows “Authentication refused: bad ownership or modes for file …”.
  • A key copied via a shared mount or extracted from a tarball inherited group/other read bits.
ls -l ~/.ssh/id_ed25519
ssh -v deploy@app-03.internal.example.com 2>&1 | grep -iE 'bad permissions|too open|Permission denied'
-rw-r--r-- 1 deploy deploy 411 Jun 23 13:40 /home/deploy/.ssh/id_ed25519
Load key "/home/deploy/.ssh/id_ed25519": bad permissions
deploy@app-03.internal.example.com: Permission denied (publickey).

Common Root Causes

1. Private key mode too open (group/other readable)

The key is 0644, 0640, or 0664 instead of 0600.

stat -c '%a %U:%G %n' ~/.ssh/id_ed25519
644 deploy:deploy /home/deploy/.ssh/id_ed25519

Any read bit beyond the owner makes OpenSSH ignore the key.

2. Wrong ownership on the key or .ssh directory

The key is owned by a different user (often root after a sudo cp), so the connecting user cannot use it safely.

stat -c '%a %U:%G %n' ~/.ssh ~/.ssh/id_ed25519
700 deploy:deploy /home/deploy/.ssh
600 root:root /home/deploy/.ssh/id_ed25519

A root-owned key in a user’s .ssh is refused.

3. .ssh directory itself too permissive

The directory is 0755/0777, letting others traverse or modify it.

stat -c '%a %n' ~/.ssh
777 /home/deploy/.ssh

A world-writable .ssh is distrusted even if the key mode is correct.

4. Server-side: authorized_keys writable by group/other

sshd refuses keys when ~/.ssh/authorized_keys or ~/.ssh is writable by anyone but the owner.

sudo grep -i 'Authentication refused' /var/log/auth.log | tail -2
stat -c '%a %U:%G %n' ~/.ssh ~/.ssh/authorized_keys
Authentication refused: bad ownership or modes for directory /home/deploy/.ssh
755 deploy:deploy /home/deploy/.ssh
644 deploy:deploy /home/deploy/.ssh/authorized_keys

With StrictModes yes (the default), a group-writable .ssh blocks login.

5. Home directory writable by group/other

Even with perfect .ssh modes, a group-writable $HOME causes sshd to reject keys under StrictModes.

stat -c '%a %n' /home/deploy
775 /home/deploy

sshd walks the path and refuses if $HOME is writable by others.

6. Key copied via shared mount / tarball that stripped owner-only modes

A key pulled from a CI artifact, NFS share, or unzip/tar extraction lands with default 0644.

ls -l ./deploy_key
-rw-r--r-- 1 ci ci 411 Jun 23 13:55 ./deploy_key

The artifact’s permissions did not preserve 0600.

Diagnostic Workflow

Step 1: Read the exact complaint (client) or auth log (server)

ssh -v <user>@<host> 2>&1 | grep -iE 'bad permissions|too open|bad ownership'
# server
sudo grep -iE 'Authentication refused|bad ownership or modes' /var/log/auth.log | tail

Step 2: Inspect modes and ownership along the whole path

stat -c '%a %U:%G %n' "$HOME" ~/.ssh ~/.ssh/id_ed25519 ~/.ssh/authorized_keys 2>/dev/null

Look for anything wider than: $HOME not group/other-writable, .ssh 700, private key 600, authorized_keys 600 (or 644 but never group/other-writable).

Step 3: Confirm StrictModes on the server (if server-side)

sudo sshd -T 2>/dev/null | grep -i strictmodes
strictmodes yes

With strictmodes yes, loose $HOME/.ssh/authorized_keys modes block login.

Step 4: Fix client-side key permissions

chown "$USER":"$USER" ~/.ssh ~/.ssh/id_ed25519
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pub 2>/dev/null
chmod 644 ~/.ssh/id_ed25519.pub

Step 5: Fix server-side permissions and retry

chmod go-w "$HOME"
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R "$USER":"$USER" ~/.ssh
ssh <user>@<host> 'echo connected'
connected

Example Root Cause Analysis

A CI deploy job fails to SSH to production with:

@@@ WARNING: UNPROTECTED PRIVATE KEY FILE! @@@
Permissions 0644 for '/builds/deploy/id_ed25519' are too open.
This private key will be ignored.
Permission denied (publickey).

The key value is correct and the corresponding public key is in the server’s authorized_keys, so this is purely a permissions problem. Checking how the key arrived in the job:

stat -c '%a %U:%G %n' /builds/deploy/id_ed25519
644 root:root /builds/deploy/id_ed25519

The CI step writes the key from a secret into a file, but the runner creates files with the default umask, leaving them 0644. OpenSSH refuses the world-readable key and never even attempts publickey auth, falling straight to “Permission denied.”

Fix: tighten the mode immediately after writing the key (and avoid leaving it on disk longer than needed):

install -m 600 /dev/null /builds/deploy/id_ed25519   # create with 600 up front
# or after writing:
chmod 600 /builds/deploy/id_ed25519
ssh -i /builds/deploy/id_ed25519 deploy@app-03.internal.example.com 'echo connected'
connected

The deploy authenticates. The pipeline is updated to always write the key with 0600.

Prevention Best Practices

  • Standardize the canonical modes: $HOME not group/other-writable, ~/.ssh 700, private keys 600, public keys 644, authorized_keys 600.
  • In automation, write secret key files with install -m 600 (or umask 077 before the write) so they are never momentarily world-readable.
  • Never chmod 644/777 a key or .ssh to “make it work” — that is the exact condition OpenSSH is protecting against; tighten instead.
  • Keep StrictModes yes on servers and fix the offending modes rather than disabling the check.
  • Prefer an SSH agent or short-lived SSH certificates over long-lived key files on disk where possible. See the security hardening guides for an SSH key-handling baseline.
  • When a deploy suddenly loses key auth, the free incident assistant can map “bad permissions”/“bad ownership or modes” messages to the exact file and mode to correct.

Quick Command Reference

# Client-side complaint or server-side auth log
ssh -v <user>@<host> 2>&1 | grep -iE 'bad permissions|too open'
sudo grep -iE 'Authentication refused|bad ownership or modes' /var/log/auth.log | tail

# Inspect modes/ownership along the path
stat -c '%a %U:%G %n' "$HOME" ~/.ssh ~/.ssh/id_ed25519 ~/.ssh/authorized_keys 2>/dev/null

# Is StrictModes on? (server)
sudo sshd -T 2>/dev/null | grep -i strictmodes

# Correct client-side key modes
chmod 700 ~/.ssh; chmod 600 ~/.ssh/id_ed25519; chmod 644 ~/.ssh/id_ed25519.pub
chown -R "$USER":"$USER" ~/.ssh

# Correct server-side modes
chmod go-w "$HOME"; chmod 700 ~/.ssh; chmod 600 ~/.ssh/authorized_keys

# Write a secret key file safely in CI
install -m 600 /dev/null ./deploy_key   # then write contents

Conclusion

“UNPROTECTED PRIVATE KEY FILE!” / “Permissions are too open” / “bad ownership or modes” all mean OpenSSH found a key or directory accessible to users beyond the owner and refused to trust it. Tighten, never loosen:

  1. The private key is group/other-readable — set it to 0600.
  2. The key or .ssh is owned by the wrong user — fix ownership to the connecting user.
  3. The .ssh directory is too permissive — set it to 0700.
  4. Server-side authorized_keys/.ssh is group/other-writable — sshd refuses it under StrictModes.
  5. A group/other-writable $HOME blocks key auth on the server.
  6. A key copied via a shared mount, tarball, or CI artifact inherited 0644.

Walk the modes and ownership from $HOME down to the key, set the canonical permissions, and in automation create secret files with 0600 from the start — the protection only works when you tighten to the modes OpenSSH expects.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.