Debian GNU/Linux · Accounts & authentication

Keep user home directories and dot files private

When it fails: User home directories or startup files are readable or writable by others. Other users can read private files, or change a user's shell startup files to run commands as that user.

CB-DEB-AUTH-029mediumactiveautomatedautomated fixprofile core

As released in v0.1.1 beta · pre-release. Current state.

Description

For every interactive account (UID 1000 or above with a login shell):

  • the home directory exists, belongs to the user and has mode 750 or stricter;
  • files and directories whose name starts with a dot directly in the home (.bashrc, .profile, .ssh...) belong to the user and are not writable by group or others;
  • .netrc and .rhosts do not exist, and .forward is not used.

Debian 13 creates new homes with mode 700 (HOME_MODE in /etc/login.defs); Debian 12 creates them with 755.

Rationale

A world-readable home exposes documents, history and keys. A shell startup file writable by someone else lets that person run commands as the user at their next login. .rhosts allows password-less login with legacy tools, and .netrc stores plain-text passwords.

Impact

Other users can no longer browse home directories. Shared files belong in a group directory.

Audit

For each interactive account, check stat -c '%U %a' ~user (owner is the user, mode 750 or stricter), ls -la ~user/.??* (no group or world write, owned by the user) and that .netrc, .rhosts and .forward are absent.

Automated: every check must pass. Scanners report each check by its id.

homes

Ownership and mode of interactive homes and their dot files.

script: |
  rc=0
  awk -F: '$3 >= 1000 && $3 < 65534 && $7 !~ /nologin$|false$/ {print $1 ":" $6}' /etc/passwd > /tmp/.cb_users.$$
  while IFS=: read -r u h; do
    [ -d "$h" ] || { echo "$u: home $h missing"; rc=1; continue; }
    [ "$(stat -c %U "$h")" = "$u" ] || { echo "$u: home not owned by user"; rc=1; }
    m=$(stat -c %a "$h"); [ $(( 0$m & 027 )) -eq 0 ] || { echo "$u: home mode $m"; rc=1; }
    for f in "$h"/.[!.]* "$h"/..?*; do
      [ -e "$f" ] || continue
      [ -L "$f" ] && continue
      case ${f##*/} in .netrc | .rhosts | .forward) echo "$u: ${f##*/} present"; rc=1 ;; esac
      [ "$(stat -c %U "$f")" = "$u" ] || { echo "$u: ${f##*/} not owned by user"; rc=1; }
      m=$(stat -c %a "$f"); [ $(( 0$m & 022 )) -eq 0 ] || { echo "$u: ${f##*/} mode $m"; rc=1; }
    done
  done < /tmp/.cb_users.$$
  rm -f /tmp/.cb_users.$$
  [ "$rc" -eq 0 ] && echo "all interactive homes are private"
  exit $rc

Remediation

For each reported account:

  1. chown <user> ~user and chmod 750 ~user.
  2. chown <user> ~user/.<file> and chmod go-w ~user/.<file>.
  3. Remove .rhosts and .netrc after checking with the user; review and remove .forward.
Restartnone
Disruptionlow

Rollback

Permissions are not backed up: note the previous modes from the audit output before applying.

permissions

awk -F: '$3 >= 1000 && $3 < 65534 && $7 !~ /nologin$|false$/ {print $1 ":" $6}' /etc/passwd |
  while IFS=: read -r u h; do
    [ -d "$h" ] || continue
    chown "$u" "$h"
    chmod g-w,o-rwx "$h"
    for f in "$h"/.[!.]* "$h"/..?*; do
      [ -e "$f" ] && [ ! -L "$f" ] || continue
      chown "$u" "$f"
      chmod go-w "$f"
    done
  done

Handwritten code reads parameters from CB_<NAME> environment variables. Each release turns these fixes into remediate.sh and an Ansible role per profile: dry run by default, a fix runs only when its check fails.

References

Mappings

How this control relates to the other framework's: equivalent, stricter, looser, partial or related.

Threats mitigated

People and lifecycle

Authors
Verified2026-10-18 by Sn0wAlice on 12, Proxmox VM, Debian 12 genericcloud image, kernel 6.1.0-54-cloud-amd64. Generated check, fix and check again for profiles core and server-hardened; state kept after reboot.
Verified2026-10-18 by Sn0wAlice on 13, Proxmox VM, Debian 13 genericcloud image, kernel 6.12.113+deb13-cloud-amd64. Generated check, fix and check again for profiles core and server-hardened; state kept after reboot.
Introduced inv0.1.0
Created, updated2026-10-18, 2026-10-18
Translationsfr