How to Lock Down File Permissions for SSH and Config Files
Lock down file permissions for SSH keys, .env files and config: correct modes for common files, audit with find and stat, umask, and fix a whole tree safely.

Permissions are the oldest access control on Linux and the one most often left too loose. A private key readable by other users is refused by SSH, and a .env file readable by everyone is an invitation on a shared server. The right modes are few and worth knowing by heart. This post lists them, shows how to audit a machine and how to fix a tree without breaking it. It builds on debugging permissions with namei and umask in scripts.
The modes to know
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519 ~/.ssh/authorized_keys ~/.ssh/config
chmod 644 ~/.ssh/id_ed25519.pub ~/.ssh/known_hosts
chmod 600 ~/.netrc ~/.pgpass .env
chmod 640 /etc/myapp/secrets.conf && chgrp myapp /etc/myapp/secrets.confThe numbers are owner, group and others, with read as 4, write as 2 and execute as 1. A private key at 600 means only its owner can read or write it. OpenSSH refuses a key with looser permissions, and with StrictModes yes (the default) the server also ignores an authorized_keys file whose directory or file is writable by others. For a service secret, 640 with the service's group lets the daemon read it while nobody else can. Programs such as psql ignore a .pgpass that is not 0600.
Audit
find ~/.ssh -type f ! -name '*.pub' ! -perm 600 -ls
find / -xdev -type f -perm -0002 -not -path '/proc/*' 2>/dev/null
find / -xdev -type d -perm -0002 ! -perm -1000 2>/dev/null
find / -xdev -perm -4000 -type f 2>/dev/null
stat -c '%a %U:%G %n' /etc/shadow /etc/ssh/sshd_configThe first finds private files that are not 600. The second lists world-writable files, which are rarely right. The third lists world-writable directories without the sticky bit; /tmp has it (mode 1777), so users cannot delete each other's files. The fourth lists setuid programs, a short list you should recognize: sudo, passwd, mount and a few others. -xdev stays on one filesystem so the search skips /proc and mounted volumes. stat -c prints the numeric mode, which is quicker to read than the symbolic form. See find with exec for acting on results safely.
Fix a tree
chmod -R u=rwX,go= ~/private
chmod -R go-w /srv/app
find /srv/app -type d -exec chmod 755 {} + -o -type f -exec chmod 644 {} +The capital X sets execute only on directories, and on files that already have an execute bit, so a recursive change does not turn every data file into a program. go= removes all permissions for group and others. The third line sets conventional modes for a web tree. Run these first without -R on a single file to see the effect, and be careful with chmod -R on /, /etc or a home directory: it can break logins and services in ways that are hard to reverse.
Defaults: umask
umask
umask 027The umask subtracts bits from the default modes of new files (666) and directories (777). With 027 new files are 640 and directories 750: owner full, group read, others nothing. Set it in /etc/login.defs or the shell profile for users, and use UMask=0027 in a systemd unit. A service that writes logs or uploads under a loose umask creates world-readable files, which is the usual source of a leak.
Services under systemd
[Service]
UMask=0027
ProtectHome=true
ProtectSystem=strict
ReadWritePaths=/var/lib/myapp
PrivateTmp=trueThese directives restrict what the process can see and write, regardless of file modes, a second layer. Verify with systemd-analyze security myapp.service.
Where this does not apply
Modes control access for ordinary users, not for root, and not against someone with the disk. Finer sharing between several users needs ACLs, as in sharing files with ACLs and groups. Encrypted storage protects data at rest. Network filesystems may map permissions differently.
How permission checks work along a path
Access to a file depends on every directory on the way to it, not only on the file's own mode. To open /home/alice/.ssh/id_ed25519, a process needs execute permission, the search bit, on /, /home, /home/alice and /home/alice/.ssh, and read permission on the file. A private key that is mode 600 sits behind a directory that is mode 700, which is why both settings matter, and why a wrong mode on a parent folder can produce a permission error for a file that looks fine. The namei -l command prints the owner, group and mode of every component of a path, and it is the fastest way to find which element blocks access. For directories, read lets you list the names, write lets you create or delete entries regardless of the files' own modes, and execute lets you traverse. That last point means a world-writable directory lets anyone delete another user's file in it unless the sticky bit is set, as it is on /tmp.
Ownership counts as much as mode. A key with the right mode and the wrong owner is unreadable to the right user, and one owned by root but readable by everyone is a leak. Check both with ls -ln, and use stat for the numeric form.
Why SSH is strict and what it checks
The SSH client refuses to use a private key that other users can read, with the message that the key file is unprotected, and the server, with StrictModes on, which is the default, ignores an authorized_keys file or a home directory that is writable by others. The reasoning is sound: a group-writable home directory lets another user add their own key to the file and log in as you. The usual working modes are 700 for ~/.ssh, 600 for private keys, 600 or 644 for authorized_keys, 644 for public keys and 600 for the config file, which may also hold paths and options you prefer to keep private. The home directory must not be writable by group or others. When key login fails with no obvious reason, the server's log says "Authentication refused: bad ownership or modes", and the line names the path. Tools that sync dotfiles between machines or restore from a backup often reset modes, so recheck them after any restore.
Other files deserve the same care: database password files such as .pgpass, which PostgreSQL clients ignore if they are group or world accessible, cloud credential files, token caches and environment files that hold secrets.
Fixing a tree without making it worse
Recursive changes are where damage happens. Setting a single mode on everything with chmod -R 600 makes directories unusable, since they lose the execute bit, and chmod -R 755 makes every file executable. Use the symbolic form with a capital X, as in chmod -R u=rwX,go= path, which gives the owner read and write, adds execute only to directories and files that already have it, and removes all access for group and others. Or run find twice, once for directories with -type d and once for files with -type f, each with its own chmod. Preview with find -perm before changing anything, and never run a recursive change as root from a variable that might be empty or from a path like / or ~ expanded in the wrong place. The default permissions of new files come from the umask, 022 on most systems, which yields 644 files and 755 directories, and 077 for private work. Set the umask in the service's unit file with UMask= for daemons, since a service inherits nothing from your shell.
See chmod(1) and StrictModes in sshd_config(5).


