Introduction

Linux permission denied and access control errors occur when users or processes lack the required permissions to access files, directories, network ports, or system resources. Linux implements multiple layers of access control: traditional Unix permissions (owner/group/others), Access Control Lists (ACLs) for fine-grained permissions, Security-Enhanced Linux (SELinux) or AppArmor for mandatory access control, and capabilities for privileged operations. Permission errors can prevent application startup, file access, service binding to ports, and system administration tasks. Common causes include file ownership mismatch (wrong user/group), permission bits too restrictive (missing read/write/execute), SELinux context blocking access despite Unix permissions, AppArmor profile restricting application, missing capabilities for privileged operations (binding to port < 1024), immutable flag set on files, shared filesystem permission mismatch (NFS, SMB), ACL denying specific user despite group membership, umask creating files with wrong permissions, and setuid/setgid bits misconfigured. The fix requires understanding Linux permission models, SELinux/AppArmor policies, capability system, and debugging tools. This guide provides production-proven troubleshooting for permission denied errors across RHEL/CentOS, Ubuntu/Debian, SUSE, and containerized environments.

Symptoms

  • Permission denied error when accessing files
  • Operation not permitted for privileged operations
  • Cannot bind to port: Permission denied
  • Application fails to start with access errors
  • sudo: user is not in the sudoers file
  • SELinux denial in audit logs
  • Files visible but not readable/writable
  • Execute permission denied for scripts
  • Directory listing fails with permission errors
  • NFS/SMB mount permission issues

Common Causes

  • File ownership mismatch
  • Permission bits too restrictive
  • SELinux context mismatch
  • AppArmor profile blocking
  • Missing capabilities
  • Immutable flag set
  • ACL denying access
  • Umask creating wrong permissions
  • Setuid/setgid misconfigured
  • Container user mapping issues

Step-by-Step Fix

### 1. Diagnose permission issues

Check file permissions:

```bash # View file permissions ls -la /path/to/file

# Output format: # -rwxr-xr-- 1 owner group 4096 Jan 15 10:00 filename # │││││││││ # ││││││││└── Other: read (r), no write (-), no execute (-) # ││││││└───── Group: read (r), execute (x), no write (-) # │││││└────── Owner: read (r), write (w), execute (x) # ││││└──────── Special bits (setuid/setgid/sticky) # │││└───────── File type (- file, d directory, l link)

# Numeric permissions stat -c "%a %n" /path/to/file

# Common permissions: # 644 = rw-r--r-- (standard file) # 755 = rwxr-xr-x (standard directory/executable) # 600 = rw------- (private file) # 640 = rw-r----- (shared with group) ```

Check ownership:

```bash # View owner and group ls -la /path/to/file

# Check current user whoami id

# Check group memberships id -nG

# Check file ownership details stat /path/to/file

# Output: # Access: (0644/-rw-r--r--) Uid: ( 1000/ user) Gid: ( 1000/ user) ```

Check SELinux context:

```bash # View SELinux context ls -Z /path/to/file

# Output: # unconfined_u:object_r:user_home_t:s0 /home/user/file

# Check SELinux status getenforce # Enforcing, Permissive, or Disabled sestatus

# Check for SELinux denials sudo ausearch -m avc -ts recent sudo grep "denied" /var/log/audit/audit.log | tail -20 ```

### 2. Fix file permissions

Change permission bits:

```bash # Add execute permission chmod +x script.sh

# Add write permission for owner chmod u+w file.txt

# Remove execute from others chmod o-x script.sh

# Set specific permissions (numeric) chmod 644 file.txt # rw-r--r-- chmod 755 directory/ # rwxr-xr-x chmod 600 private.key # rw------- chmod 640 shared.file # rw-r-----

# Recursive change chmod -R 755 /var/www/html

# Use reference file chmod --reference=template.txt target.txt ```

Change ownership:

```bash # Change owner sudo chown newuser file.txt

# Change owner and group sudo chown user:group file.txt

# Change only group sudo chgrp group file.txt

# Recursive change sudo chown -R user:group /var/www/

# Use reference file chown --reference=template.txt target.txt ```

Fix common permission issues:

```bash # SSH key permissions (must be restrictive) chmod 700 ~/.ssh chmod 600 ~/.ssh/id_rsa chmod 644 ~/.ssh/id_rsa.pub chmod 600 ~/.ssh/authorized_keys

# Script execution chmod +x script.sh

# Directory access (need execute to enter) chmod 755 /home/user/public

# Shared directory (group writable) chmod 775 /shared/folder sudo chown :developers /shared/folder

# Sticky bit for shared temp directory chmod 1777 /tmp/shared ```

### 3. Fix ACL permissions

View ACLs:

```bash # Check if ACLs are set getfacl /path/to/file

# Output: # file: path/to/file # owner: user # group: group # user::rw- # user:john:rw- # Named user ACL # group::r-- # group:developers:rw- # Named group ACL # mask::rw- # other::r--

# Check effective permissions getfacl --access /path/to/file ```

Set ACL permissions:

```bash # Grant user read/write setfacl -m u:username:rw- file.txt

# Grant group read/execute setfacl -m g:groupname:rx directory/

# Remove specific ACL setfacl -x u:username file.txt

# Remove all ACLs setfacl -b file.txt

# Set default ACL for new files in directory setfacl -d -m u::rwX,g::rwX,o::r-X directory/

# Recursive ACL setfacl -R -m g:developers:rwX /shared/project/ ```

ACL troubleshooting:

```bash # Check effective permissions (mask limits ACL) getfacl file.txt | grep mask

# If mask is r--, ACL cannot grant more than read # Fix by updating mask setfacl -m m::rw- file.txt

# Check if user is being denied by ACL # User in named ACL but still denied = mask issue

# Test access as specific user sudo -u username cat file.txt ```

### 4. Fix SELinux issues

Check SELinux denials:

```bash # View recent denials sudo ausearch -m avc -ts recent

# Or search audit log sudo grep "denied" /var/log/audit/audit.log | tail -20

# Common denial format: # type=AVC msg=avc: denied { read } for pid=1234 comm="httpd" # name="index.html" dev="sda1" ino=5678 # scontext=system_u:system_r:httpd_t:s0 # tcontext=system_u:object_r:user_home_t:s0 # tclass=file

# scontext = source context (process) # tcontext = target context (file/resource) # tclass = object class (file, dir, tcp_socket, etc.) ```

Fix SELinux context:

```bash # Restore default context restorecon -v /path/to/file

# Restore recursively restorecon -Rv /var/www/html

# Set specific context chcon -t httpd_exec_t /usr/sbin/httpd chcon -t httpd_sys_content_t /var/www/html

# Make permanent (survives relabel) semanage fcontext -a -t httpd_sys_content_t "/var/www/html(/.*)?" restorecon -Rv /var/www/html ```

Configure SELinux booleans:

```bash # List available booleans getsebool -a

# Search for relevant booleans getsebool -a | grep httpd getsebool -a | grep nfs

# Enable boolean sudo setsebool -P httpd_can_network_connect 1 sudo setsebool -P httpd_read_user_content 1 sudo setsebool -P nfs_export_all_rw 1

# -P makes change permanent

# Common booleans: # httpd_can_network_connect - Allow web server network access # httpd_enable_homedirs - Allow access to home directories # selinuxuser_execmod - Allow executable stack/memory # virt_use_nfs - Allow VMs to use NFS ```

Temporarily disable SELinux (debugging only):

```bash # Set to permissive (log but don't enforce) sudo setenforce 0

# Test if issue is SELinux-related

# Re-enable enforcing sudo setenforce 1

# Or edit /etc/selinux/config for permanent change # SELINUX=permissive ```

### 5. Fix AppArmor issues

Check AppArmor status:

```bash # Check status sudo aa-status

# Output shows: # - Profiles loaded # - Profiles in enforce mode # - Profiles in complain mode # - Processes confined

# View denials sudo dmesg | grep DENIED sudo grep DENIED /var/log/kern.log sudo journalctl -k | grep apparmor ```

Configure AppArmor profile:

```bash # Find profile for application aa-status | grep application

# Set to complain mode (log but don't block) sudo aa-complain /usr/bin/application

# Or disable profile sudo aa-disable /usr/bin/application

# Generate profile from scratch sudo aa-genprof /usr/bin/application

# Update existing profile sudo aa-logprof # Review logs and update profiles ```

Edit AppArmor profile manually:

```bash # Profile location /etc/apparmor.d/usr.bin.application

# Common profile entries: # /etc/config r, # Read access # /var/log/app w, # Write access # /var/data rw, # Read/write access # /usr/bin/** ix, # Execute with inherit # network inet tcp, # Allow TCP

# After editing: sudo apparmor_parser -r /etc/apparmor.d/usr.bin.application ```

### 6. Fix capability issues

Check file capabilities:

```bash # View capabilities getcap /path/to/binary

# Common capabilities: # cap_net_bind_service - Bind to ports < 1024 # cap_net_raw - Use raw sockets # cap_sys_admin - Administrative operations # cap_dac_override - Bypass file permission checks

# Example output: # /usr/bin/ping = cap_net_raw+ep ```

Set capabilities:

```bash # Grant capability sudo setcap cap_net_bind_service+ep /usr/sbin/nginx

# Allow binding to port 80 without root sudo setcap cap_net_bind_service=+ep /usr/bin/python3

# Remove capability sudo setcap -r /usr/bin/binary

# Multiple capabilities sudo setcap cap_net_bind_service,cap_net_raw+ep /usr/bin/binary ```

Check process capabilities:

```bash # View current process capabilities cat /proc/$$/status | grep Cap

# Decode capability hex capsh --decode=0000003fffffffff

# Or use getpcaps getpcaps PID ```

Use capabilities in applications:

```bash # Run command with specific capabilities sudo setpriv --reuid=1000 --regid=1000 --init-groups \ --inh-caps=cap_net_bind_service \ /usr/bin/application

# Or with capsh sudo capsh --caps="cap_net_bind_service+eip" \ --keep=1 \ --user=appuser \ --addamb=cap_net_bind_service \ -- -c "/usr/bin/application" ```

### 7. Fix immutable and special flags

Check for immutable flag:

```bash # View file attributes lsattr /path/to/file

# Common attributes: # i = immutable (cannot modify, delete, rename) # a = append only # d = no dump # s = secure deletion # A = no atime updates

# Output: # ----i---------e------- /path/to/immutable/file ```

Remove immutable flag:

```bash # Remove immutable flag sudo chattr -i /path/to/file

# Remove append-only sudo chattr -a /path/to/file

# Set immutable (for protection) sudo chattr +i /path/to/important/file

# Recursive sudo chattr -R -i /directory/ ```

Fix setuid/setgid bits:

```bash # View setuid/setgid ls -la /usr/bin/passwd # -rwsr-xr-x 1 root root ... passwd # ^^^ setuid bit (s instead of x)

# What setuid/setgid do: # setuid: Execute as file owner # setgid: Execute as file group (or inherit group for directories)

# Set setuid chmod u+s /path/to/binary chmod 4755 /path/to/binary

# Set setgid chmod g+s /path/to/directory chmod 2755 /path/to/directory

# Remove setuid/setgid chmod u-s /path/to/binary chmod g-s /path/to/directory

# Security: Remove setuid from unnecessary binaries find / -perm /6000 -type f 2>/dev/null ```

### 8. Fix sudo permission issues

Check sudo configuration:

```bash # Check if user can sudo sudo -l

# View sudoers file sudo cat /etc/sudoers

# NEVER edit directly! Use visudo sudo visudo ```

Configure sudo access:

```bash # Add user to sudo group (Debian/Ubuntu) sudo usermod -aG sudo username

# Or add to wheel group (RHEL/CentOS) sudo usermod -aG wheel username

# Or add specific sudoers rule # /etc/sudoers.d/username username ALL=(ALL) ALL

# Passwordless sudo (use carefully!) username ALL=(ALL) NOPASSWD: ALL

# Specific commands only username ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart myapp

# Check syntax after editing sudo visudo -c ```

Debug sudo issues:

```bash # Check why sudo failed sudo grep sudo /var/log/auth.log | tail -20 # Debian/Ubuntu sudo grep sudo /var/log/secure | tail -20 # RHEL/CentOS

# Test sudo with verbose output sudo -v -v

# Check sudoers include directory ls -la /etc/sudoers.d/ ```

### 9. Fix container permission issues

Docker volume permissions:

```bash # Check volume permissions docker run --rm -v volume_name:/data alpine ls -la /data

# Fix by running as root temporarily docker run --rm -v volume_name:/data alpine chown -R 1000:1000 /data

# Or specify user docker run -u $(id -u):$(id -g) -v volume_name:/data image

# Named volume with specific permissions docker volume create --opt device=tmpfs --opt type=tmpfs --opt o=uid=1000,gid=1000 volume_name ```

Kubernetes security context:

yaml # Pod security context apiVersion: v1 kind: Pod metadata: name: secure-pod spec: securityContext: runAsUser: 1000 runAsGroup: 3000 fsGroup: 2000 runAsNonRoot: true containers: - name: app image: myapp:latest securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL add: - NET_BIND_SERVICE # If needed volumeMounts: - name: data mountPath: /data volumes: - name: data emptyDir: {}

Fix Kubernetes permission issues:

```bash # Check pod security events kubectl describe pod pod-name | grep -A5 Events

# Check PVC permissions kubectl get pvc kubectl describe pvc pvc-name

# Run init container to fix permissions # initContainers: # - name: fix-permissions # image: busybox # command: ["chown", "-R", "1000:1000", "/data"] # volumeMounts: # - name: data # mountPath: /data ```

### 10. Debug permission issues

Use strace for permission debugging:

```bash # Trace system calls for permission errors strace -e open,openat,access,fstat /usr/bin/application 2>&1 | grep -E "EACCES|EPERM"

# Output shows: # open("/etc/config", O_RDONLY) = -1 EACCES (Permission denied)

# Full trace with output strace -f -o /tmp/strace.log /usr/bin/application grep -E "EACCES|EPERM" /tmp/strace.log ```

Use namei to trace path permissions:

```bash # Check permissions at each path component namei -l /path/to/file

# Output: # f: /path/to/file # drwxr-xr-x root root / # drwxr-xr-x user user path # drwxr-xr-x user user to # -rw-r--r-- user user file

# Shows where permission is denied ```

Test access as different user:

```bash # Test file access as specific user sudo -u username cat /path/to/file

# Or use runuser runuser -u username -- command

# Test with specific group sudo -u username -g groupname command

# Check what user can access sudo -u username bash -c 'test -r /path/to/file && echo readable || echo not-readable' ```

Prevention

  • Document permission requirements for each application
  • Use configuration management (Ansible, Puppet) for consistency
  • Implement principle of least privilege
  • Regular permission audits
  • Use ACLs for fine-grained access control
  • Configure SELinux/AppArmor in production
  • Test applications with correct user context before deployment
  • Document capability requirements for privileged operations
  • **Permission denied (13)**: Unix permission or ACL issue
  • **Operation not permitted (1)**: Capability or SELinux issue
  • **Cannot allocate memory**: May indicate resource limits
  • **No such file or directory**: Path or symlink issue
  • **Read-only file system**: Mount option or filesystem error