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:
- Interactive SSH sessions
- SFTP file transfers (tested with OpenSSH 8.9p1)
- SCP operations (verified on Ubuntu 22.04)
- 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 <