Linux Shell Script ‘Permission Denied’ Error on CentOS Stream / Rocky Linux (RHEL-based)
Troubleshoot 'Permission Denied' when running bash scripts on CentOS Stream or Rocky Linux. Learn to fix execute permissions, shebang, and SELinux issues.
Troubleshoot 'Permission Denied' when running bash scripts on CentOS Stream or Rocky Linux. Learn to fix execute permissions, shebang, and SELinux issues.
Introduction
As an experienced Systems Administrator, one of the most common and often frustrating issues newcomers and even seasoned professionals encounter is the dreaded "Permission denied" error when attempting to execute a shell script. While seemingly simple, this error can stem from various underlying causes, especially on security-hardened RHEL-based distributions like CentOS Stream and Rocky Linux. This guide provides a comprehensive, technical walkthrough to diagnose and resolve this issue, focusing on crucial aspects like file permissions, shebang configuration, and critically, SELinux contexts.
Symptom & Error Signature
When you attempt to run a shell script from your terminal, you will typically see an error message similar to one of the following:
-bash: ./my_script.sh: Permission denied
or if the script is located in a directory not in your PATH:
-bash: /opt/scripts/my_script.sh: Permission denied
You might also see the Permission denied error even when attempting to execute the script directly via bash:
bash my_script.sh
bash: my_script.sh: Permission denied
This last symptom often points to issues beyond simple execute permissions, such as SELinux restrictions or filesystem mount options.
Root Cause Analysis
The "Permission denied" error indicates that the operating system is preventing the current user from performing a requested operation – in this case, executing a script. The root causes can be multifaceted:
- Lack of Execute Permission (Primary Cause): The most common reason. Linux file permissions are granular, requiring explicit "execute" (x) permission for the user, group, or others to run a file as a program. Without this bit set, the kernel will refuse to execute it.
- Incorrect Shebang Line: While not directly a "permission denied" error, if the shebang (
#!) line is missing, incorrect, or points to a non-existent interpreter, the system might not know how to execute the file, or it might try to execute it in an unexpected way, leading to permission issues or "command not found". If you explicitly runbash script.sh, this is less of an issue, but if you run./script.sh, the shebang is crucial. - SELinux Context Restrictions: Highly prevalent on RHEL-based systems (CentOS Stream, Rocky Linux, AlmaLinux). Even with correct file permissions, SELinux (Security-Enhanced Linux) might prevent a process from executing a script if its security context does not permit it. For example, a script intended for web execution (
httpd_sys_script_exec_t) might fail if it's placed in a user's home directory (user_home_t) and a web server tries to run it. - Filesystem Mount Options: The filesystem where the script resides might be mounted with the
noexecoption, explicitly preventing any file on that filesystem from being executed. This is common for/tmpor/var/tmpfor security reasons. - Parent Directory Permissions: While the script itself might have correct permissions, if the user doesn't have "execute" (traverse) permission on one or more of its parent directories, they won't be able to access the script's path, resulting in a "Permission denied" error.
Step-by-Step Resolution
Follow these steps systematically to diagnose and resolve the "Permission denied" error.
#### 1. Verify Script Path and Existence
First, ensure the script actually exists and you're in the correct directory or providing the correct path.
# Check current directory
pwd
# List files to confirm script existence
ls -F
# Verify full path, if applicable
ls -l /path/to/your/script.sh
#### 2. Grant Execute Permissions (The Primary Fix)
This is the most common fix. Use the chmod command to add execute permissions.
Check current permissions:
ls -l my_script.sh # Example output: -rw-r--r--. 1 user group 1234 Aug 30 10:00 my_script.sh # Notice the absence of 'x' in the permission string.Add execute permission for the owner:
chmod u+x my_script.sh # Or for owner, group, and others (a common choice for scripts, like 755) # chmod 755 my_script.shchmod 755sets permissions torwxr-xr-x, allowing the owner to read, write, and execute, and group/others to read and execute. Avoidchmod 777(rwxrwxrwx) unless absolutely necessary, as it grants full control to everyone and is a significant security risk. For most scripts,755is appropriate.Verify new permissions:
ls -l my_script.sh # Expected output: -rwxr-xr-x. 1 user group 1234 Aug 30 10:00 my_script.sh # The 'x' bits should now be present.Attempt to run the script:
./my_script.shIf you are still getting
Permission denied, proceed to the next steps.
#### 3. Check the Shebang Line
The shebang line (#!) tells the system which interpreter to use for the script.
Examine the first line of your script:
head -1 my_script.sh # Expected: #!/bin/bash # Or: #!/usr/bin/env bashEnsure the interpreter exists: The path specified after
#!must be valid.which bash # Expected: /usr/bin/bash (or /bin/bash, which is often a symlink to /usr/bin/bash)If
which bashreturns a different path, or no path, adjust your shebang line accordingly.#!/usr/bin/env bashis often more portable as it findsbashin the system's PATH.
#### 4. Address SELinux Context
SELinux is a critical security enhancement on RHEL-based systems. It can block script execution even with correct file permissions.
Check the SELinux context of the script:
ls -Z my_script.sh # Example output: -rwxr-xr-x. user group unconfined_u:object_r:user_home_t:s0 my_script.shPay attention to the
typefield (e.g.,user_home_t,httpd_sys_script_exec_t). If the context isuser_home_tbut the script is being executed by a service (like Apache/Nginx via PHP-FPM, or a systemd service), SELinux might be blocking it.Restore default SELinux context: The
restoreconcommand applies the default SELinux context for files based on system policy.sudo restorecon -v my_script.sh # If the script is in a standard system path, this will often fix it.Manually change SELinux context (if
restorecondoesn't apply the correct one): If the script is, for example, a web application helper script, it might needhttpd_sys_script_exec_t.sudo chcon -v --type=httpd_sys_script_exec_t my_script.sh # Replace 'httpd_sys_script_exec_t' with the appropriate context for your use case. # Common contexts: # httpd_sys_script_exec_t : For scripts executed by web servers. # bin_t : For executables in /usr/bin, /usr/local/bin. # usr_t : For general user-level scripts/executables.chconchanges are temporary and may be reset byrestoreconor a filesystem relabel. For persistent changes, you should update the SELinux policy rules or usesemanage fcontext. For one-off scripts,chconis often sufficient.Check SELinux audit logs for denials: If the script is still failing, SELinux might be silently blocking it. Check the audit logs:
sudo ausearch -c bash -m AVC -ts recent # Or for a broader search: sudo journalctl -t audit -f | grep "AVC"Look for messages containing
AVC deniedrelated to your script or the interpreter (bash). This will provide detailed information about what SELinux blocked and why.Temporarily disable SELinux (for testing ONLY):
Disabling SELinux reduces your system's security. Only do this temporarily for testing purposes in a controlled environment. Re-enable it immediately after testing.
sudo setenforce 0 # Set SELinux to Permissive modeTry running your script now. If it works, SELinux was indeed the cause.
sudo setenforce 1 # Re-enable SELinux (Enforcing mode)If SELinux was the culprit, you need to either adjust the script's SELinux context permanently (using
chconorsemanage fcontext) or write a custom SELinux policy rule.
#### 5. Review Filesystem Mount Options
A less common but important cause is the noexec mount option.
Check mount options for the script's filesystem:
findmnt -n -o OPTIONS --target /path/to/your/script.sh # Or more generally: mount | grep $(df /path/to/your/script.sh | awk 'NR==2 {print $1}')Look for
noexecin the output. If it's present, the system explicitly forbids execution from that filesystem.Remount with
exec(temporary):# Identify the mount point (e.g., /tmp) df /path/to/your/script.sh # Then remount: sudo mount -o remount,exec /tmpPermanently change in
/etc/fstab: Ifnoexecis present and you need to execute scripts from that location permanently, you must edit/etc/fstaband changenoexectoexecfor that specific mount point. Then reboot or remount the filesystem.Modifying
/etc/fstabincorrectly can make your system unbootable. Always back up the file before editing:sudo cp /etc/fstab /etc/fstab.bak.
#### 6. Verify Parent Directory Permissions
Ensure that all parent directories leading to your script allow "traverse" (execute) permission for the user trying to run the script.
Check permissions of parent directories:
namei -l /path/to/your/script.sh # This command shows permissions for each component of the path. # Example output: # dr-xr-xr-x root root / # drwxr-xr-x root root usr # drwxr-xr-x root root local # drwxr-xr-x root root bin # -rwxr-xr-x user user my_script.shFor directories, the
xbit means you cancdinto them and access their contents. If any directory in the path lacks thexbit for the relevant user/group, access will be denied.Adjust directory permissions (if necessary):
sudo chmod a+x /path/to/parent_directory # Or more specific: sudo chmod u+x /path/to/parent_directory # For user sudo chmod g+x /path/to/parent_directory # For groupMake sure not to over-permission directories, especially
/.
Our Production Verification Guarantee
Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.