ChmodCalc

chmod 700 — rwx------

Owner only, nothing for anyone else. The calculator starts on a directory, because that is where 700 is usually applied and where the execute bit stops meaning "run" and starts meaning "search".

chmod — directory mode
whoreadwriteexecutedigit
Owner7
Group0
Others0
special bits — the fourth digit

ls -l → drwx------

chmod 700 ~/.ssh
chmod u=rwx,g=,o= ~/.ssh

Absolute symbolic form — identical result, and the only form that can name the setuid, setgid and sticky bits.

what 700 grants on this directory

  • Ownerrwxlist the names inside, create, delete and rename entries and enter it and reach a known name inside
  • Group---nothing
  • Others---nothing

programs that refuse this mode

  • Apache HTTP Server

    access to /index.html denied (filesystem path '~/.ssh/index.html') because search permissions are missing on a component of the path

    fix: chmod 755 ~/.ssh

The owner digit on a directory, all eight values

700 keeps the group and other digits at zero, so the only interesting question is what the first digit does. Here is each of its eight values applied to a directory, with the five things you might want to do with one. Every column here falls out of the bits themselves, which is why two of the rows come out entirely useless:

A directory at mode d00, from the owner's point of view. "cd" means enter it; "open a known name" means reach a file inside without listing first.
Mode Owner bits ls cd open a known name create delete
000 --- no no no no no
100 --x no yes yes no no
200 -w- no no no no no
300 -wx no yes yes yes yes
400 r-- yes no no no no
500 r-x yes yes yes no no
600 rw- yes no no no no
700 rwx yes yes yes yes yes

Look at the 600 row: read and write, no search, and the result is a directory you can list and otherwise cannot touch. The write bit is inert, because creating an entry requires resolving the directory first. This is what happens when a file mode gets applied to a directory — the search bit is the thing that quietly goes missing, and nothing reports it.

300 and 100 are the mirror image: search without read. You can cd into the directory and open a file whose exact name you know, but ls refuses. That is not a broken configuration; it is a legitimate pattern for a directory holding per-user paths that should not be enumerable.

What sshd actually checks, and what ssh recommends

If you have been told that ~/.ssh must be 700, that is a recommendation being reported as a rule. Here is what the server really does. When StrictModes is on — and its default is yes — sshd resolves the authorized-keys file and then walks upward, directory by directory, to the home directory. For each component it rejects on this condition:

(st.st_mode & 022) != 0

That is group-write or other-write. Nothing else. A ~/.ssh at 755 passes; so does 750, and so does 711. 128 of the 512 three-digit modes satisfy it. What fails is 775, 757, 777 — anything that lets somebody else put a file into the directory, because somebody who can write to ~/.ssh can replace authorized_keys.

Separately, ssh(1)'s FILES section recommends "read/write/execute for the user, and not accessible by others" — which is 700. That is a recommendation about confidentiality, and it is good advice. It is not the rule that is failing your login. The practical consequence: if sshd is refusing your key, do not chmod every directory to 700 and hope. Look for the writable one.

Enforcement source: OpenSSH (portable), misc.c — safe_path() (C source read directly from the project’s master branch). Recommendation source: ssh.1 — FILES (mdoc manual source read directly from the project’s master branch). Default for StrictModes: sshd_config.5 — StrictModes (mdoc manual source read directly from the project’s master branch).

The two messages the directory walk produces. They appear only in the server's log; the client just says “Permission denied (publickey)”.
What you see Where it comes from Fires on Fix
Authentication refused: bad ownership or modes for directory /home/deploy/.ssh

OpenSSH server (sshd) · the server’s log — /var/log/auth.log or journalctl -u sshd

shown for mode 775 on ~/.ssh, and every directory above it up to the home directory

verbatim format string: bad ownership or modes for directory %s

OpenSSH (portable) — misc.c · safe_path()

OpenSSH (portable) — auth2-pubkeyfile.c · auth_openfile()

Same two-file assembly as the file variant: safe_path() fills the buffer while walking upward from the key file, auth_openfile() prefixes "Authentication refused: ".

stat(buf, &st) == -1 || (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || (st.st_mode & 022) != 0 — inside the loop that walks each parent in turn

quoted verbatim from the source

3072 of the 4096 modes fire it.

chmod 700 /home/deploy/.ssh

least-privilege mode 700

Authentication refused: bad ownership or modes for file /home/deploy/.ssh/authorized_keys

OpenSSH server (sshd) · the server’s log — /var/log/auth.log or journalctl -u sshd

shown for mode 664 on ~/.ssh/authorized_keys on the server

verbatim format string: bad ownership or modes for file %s

OpenSSH (portable) — misc.c · safe_path()

OpenSSH (portable) — auth2-pubkeyfile.c · auth_openfile()

safe_path() in misc.c writes this into an error buffer; auth_openfile() in auth2-pubkeyfile.c prints the buffer with logit("Authentication refused: %s", line).

(!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || (stp->st_mode & 022) != 0

quoted verbatim from the source

3072 of the 4096 modes fire it.

chmod 600 /home/deploy/.ssh/authorized_keys

least-privilege mode 600

sshd-authkeys-dir — The mask is 022, not 077. sshd does not require ~/.ssh to be 700 — 755 passes this test. 700 is the mode ssh(1) recommends, which is a different statement from the one sshd enforces.

sshd-authkeys-file — The client sees only "Permission denied (publickey)". The sentence above exists only in the server’s log, which is why this failure is so often misdiagnosed as a wrong key.

Where 700 is the wrong answer

  • A crontab file. cron's mask requires the owner-execute bit to be clear, so 700 disqualifies the file and the jobs stop, logged only as BAD FILE MODE. Use 600.
  • A home directory on a machine with a web server serving ~/public_html. The worker needs the search bit on the home directory to reach anything below it; 700 removes it and every page 403s.
  • A shared deployment directory. 700 locks out everyone including your colleague's account and your CI runner. 750 with a deliberate group is the shape you want.
  • Anything a service account must read. Ownership is the lever, not the mode — give the directory to the service user and keep 700. Widening it is the wrong lever.

Applying it

chmod 700 ~/.ssh is the whole command. The relative form chmod go= ~/.ssh does the same thing without asserting anything about the owner's bits, which is the safer spelling when you are fixing a directory whose current mode you have not looked at. To find the actual offender in an SSH path:

namei -l ~/.ssh/authorized_keys

That prints every component with its mode and owner in one column, so a group-writable parent is obvious immediately. It is a faster diagnosis than chmod-ing your way up the tree, and it leaves the directories that were already fine alone.

## Questions people actually ask

What does chmod 700 mean?

rwx------. The owner gets read, write and execute; the group and everyone else get nothing. On a directory that means only the owner can list it, enter it, or create and delete entries inside it. On a file it means only the owner can read, edit or run it.

Does ~/.ssh have to be 700?

No, and the belief that it must is worth taking apart, because it sends people chmod-ing directories that were never failing. What sshd enforces is (st_mode & 022) != 0, applied to every directory from the key file up to the home directory: the check fails only if the directory is writable by the group or by others. 128 of the 512 three-digit modes pass it, 755 among them. 700 is what ssh(1) recommends in its FILES section. Both statements are true; they are different statements, and using the right one saves you from chmod-ing directories that were never the problem.

What is the difference between 700 and 750?

The group gets read and search with 750, so members of the owning group can list the directory and enter it, but not create or delete anything. That is the right shape for a directory a deploy group needs to read from. 700 is the right shape for a home directory or a key store, where the group has no business at all.

Why can I still see the directory name when it is 700?

Because the name lives in the parent directory, not in the directory itself. Listing /home shows you /home/alice regardless of what mode alice has, because you are reading /home. What 700 stops is going any further: you cannot list what is inside, and you cannot reach a path underneath it even if you know the exact name.

Is 700 right for a private script?

Yes, and it is a better default than 755 for anything in a deployment pipeline that only one account should invoke. The owner can read, edit and run it; nobody else can even see the contents. If a service account needs to run it, either give that account ownership or use 750 with a group containing exactly that account.

Does 700 work for a crontab file?

No, and this catches people. The check in cronie — the cron implementation this site read the source of — is (st_mode & 07533) != 0400, and 700 fails it because the owner-execute bit is set. Only 8 of the 512 modes pass that test, and 700 is not one of them — a crontab tightened to 700 stops running with nothing in the log but BAD FILE MODE. Use 600 for the crontab file and 700 for the script it calls.

elsewhere on this site

other tools we run