Paramiko’s SFTP client may raise SFTPError: Permission denied when attempting file operations on remote systems. This comprehensive guide explains Unix file permissions, ownership, umask, ACLs, and Python-based workarounds to diagnose and fix permission issues in SFTP workflows.
1. Understand Unix File Permissions
Unix permissions consist of three bits for user, group, and others (read, write, execute). Check permissions with:
$ ls -l /remote/path/file.txt -rw-r--r-- 1 alice staff 1024 Aug 21 10:00 file.txt
- Owner (alice) can read/write.
- Group (staff) can read.
- Others can read.
2. Verify Ownership and Group
Mismatched ownership can block access. Identify user and group of the SFTP session:
stdin, stdout, _ = ssh.exec_command('id')
print(stdout.read().decode())
# uid=1001(alice) gid=50(staff) groups=50(staff),...
3. Adjust Remote File Permissions with chmod
Use chmod to grant necessary access:
# Grant owner write and group write
ssh.exec_command('chmod 664 /remote/path/file.txt')
# To allow all users write (use carefully)
ssh.exec_command('chmod 666 /remote/path/file.txt')
4. Change Ownership with chown
If you have sudo privileges, change file owner or group:
ssh.exec_command('sudo chown alice:staff /remote/path/file.txt')
sudo handling may require pty:\nssh.exec_command('sudo -S chown ...', get_pty=True).5. Check and Configure umask
umask defines default permissions for new files. A restrictive umask can block group/other write access:
stdin, stdout, _ = ssh.exec_command('umask')
print(stdout.read().decode()) # e.g., 0022
# umask 0022 yields 644 default perms; use 0002 for 664
ssh.exec_command('umask 0002')
6. Handle ACLs (Access Control Lists)
Some systems use ACLs for fine-grained permissions. Check with:
$ getfacl /remote/path/file.txt
Modify ACLs via setfacl:
ssh.exec_command('setfacl -m u:alice:rw /remote/path/file.txt')
7. Use Paramiko SFTP chmod/chown Methods
Paramiko exposes chmod and chown directly on SFTP:
sftp = ssh.open_sftp()
# Change permissions
sftp.chmod('/remote/path/file.txt', 0o664)
# Change ownership (if server permits over SFTP)
sftp.chown('/remote/path/file.txt', uid=1001, gid=50)
8. Retry Logic and Error Handling
Implement retries for transient permission changes:
import time
def safe_put(local, remote, retries=3):
for i in range(retries):
try:
sftp.put(local, remote)
return
except IOError as e:
if 'Permission denied' in str(e) and i < retries-1:
time.sleep(1)
else:
raise
9. Summary Checklist
- Check remote file permissions with
ls -l. - Verify SFTP user’s
idmatches file owner or group. - Adjust permissions with
chmodor SFTPchmod(). - Change ownership via
chownor SFTPchown()if permitted. - Review umask to ensure correct defaults for new files.
- Inspect and modify ACLs with
getfacl/setfacl. - Wrap file operations in retry logic for resilience.
