Configuring Consistent SSH Umask (0027) for SFTP, SCP, and Remote Commands


11 views

After extensive testing across Ubuntu Server environments, I've identified the fundamental inconsistency in how OpenSSH handles umask settings:

# Current behavior matrix:
sftp    - Uses 0022 default (configurable via -u flag with quirks)
scp     - Hardcoded 0022 (no native configuration)
ssh shell - Respects /etc/profile
ssh command - Ignores profile scripts

The most reliable cross-protocol approach modifies /etc/pam.d/sshd:

# /etc/pam.d/sshd addition:
session optional pam_umask.so umask=0027

For comprehensive coverage, also modify /etc/pam.d/login:

# /etc/pam.d/login addition:
session optional pam_umask.so umask=0027

Test all connection types with this diagnostic script (umask_test.sh):

#!/bin/bash
echo "Current umask: $(umask)"
touch testfile
ls -l testfile
rm testfile

Execution examples:

# Shell session
ssh user@host

# Remote command
ssh user@host umask_test.sh

# SFTP batch test
sftp -b <(echo "put umask_test.sh") user@host
ssh user@host ./umask_test.sh

# SCP test
scp umask_test.sh user@host:~/ && ssh user@host ./umask_test.sh

For user-specific overrides in ~/.bashrc or similar, add force-setting logic:

# In /etc/bash.bashrc (affects all users)
if [ "$PS1" ]; then
    umask 0027
fi

The 0027 umask provides these permission outcomes:

Files:       -rw-r-----
Directories: drwxr-x---

This ensures owner read/write, group read, and no others access - a balanced approach for shared environments.

For systems without PAM, consider these workarounds:

# Force umask in sshd environment
# /etc/ssh/sshd_config
AcceptEnv LANG LC_* UMASK

# Client-side:
ssh -o SendEnv=UMASK user@host

Requires matching AcceptEnv and SendEnv configuration.


Many sysadmins struggle with enforcing consistent umask settings across all SSH connection types. The core issue manifests differently for each protocol:

SFTP:  Unexpected permission behavior even with -u flag
SCP:   Hardcoded 0022 umask that ignores system settings
SSH:   Profile-based inheritance works only for interactive shells

The divergence occurs because:

  • SFTP subsystem processes umask calculations differently from regular shells
  • SCP protocol implementations often reset umask for transfer operations
  • Non-interactive SSH sessions skip profile scripts entirely

After extensive testing across Ubuntu/Debian systems, the most reliable approach is:

# /etc/pam.d/sshd modification
session optional pam_umask.so umask=0027

# /etc/pam.d/login modification (for completeness)
session optional pam_umask.so umask=0027

This provides consistent behavior for:

  1. Interactive SSH sessions
  2. SFTP file transfers (tested with OpenSSH 8.9p1)
  3. SCP operations (verified on Ubuntu 22.04)
  4. Direct command execution via SSH

Several key observations from production deployments:

Scenario Behavior Solution Coverage
User ~/.bashrc umask Overrides PAM Requires user education
Chrooted SFTP Works correctly Full support
SCP from Windows Honors PAM umask Verified with WinSCP

Other attempted solutions and their limitations:

# sshd_config approach (partial solution)
Subsystem sftp /usr/lib/openssh/sftp-server -u 0027

# /etc/profile approach (incomplete)
if [ "$SSH_CONNECTION" ]; then
    umask 0027
fi

The PAM method remains superior because it:

  • Works at the Linux kernel level
  • Applies to all PAM-aware services
  • Isn't affected by shell initialization quirks

Use this to test your configuration:

#!/bin/bash
# Test all SSH umask scenarios
echo "Testing interactive SSH:"
ssh localhost 'umask'

echo -e "\nTesting SCP transfer:"
touch testfile
scp testfile localhost:/tmp/
ssh localhost 'ls -l /tmp/testfile'

echo -e "\nTesting SFTP transfer:"
sftp localhost <