How to Fix Ansible 403 Forbidden on CentOS 7
Troubleshooting “Ansible 403 Forbidden” on CentOS 7
As a Senior DevOps Engineer, encountering a “403 Forbidden” error when Ansible attempts to connect to a target host is a common roadblock. While the HTTP status code “403 Forbidden” typically signifies that a web server understands the request but refuses to authorize it, in the context of Ansible, it almost universally points to an SSH authentication or authorization failure on the target CentOS 7 machine. Ansible primarily relies on SSH for communication, and a “403 Forbidden” message from Ansible’s perspective means the SSH connection was established, but the remote host denied the subsequent operations, usually due to insufficient permissions or system-level security restrictions.
Let’s break down the common causes and how to resolve them on CentOS 7.
1. The Root Cause: Why this happens on CentOS 7
On CentOS 7, the “403 Forbidden” (or more accurately, an SSH permission denied error that Ansible interprets similarly) typically stems from one or more of the following security mechanisms and configurations:
- SELinux (Security-Enhanced Linux): This is by far the most frequent culprit on CentOS. SELinux might be preventing the
sshddaemon from accessing user home directories,.sshdirectories, orauthorized_keysfiles, even if standard file permissions appear correct. - FirewallD: The default firewall on CentOS 7 (
firewalld) may be blocking SSH connections (port 22) from the Ansible controller. - Incorrect File/Directory Permissions: The SSH daemon is very strict about the permissions of the user’s home directory, the
.sshdirectory, and theauthorized_keysfile on the target host. Any misconfiguration here will lead to a rejection. - SSH Daemon Configuration (
sshd_config): Restrictive settings in/etc/ssh/sshd_configon the target host (e.g.,PermitRootLogin no,PasswordAuthentication no,AllowUsers,DenyUsers, incorrectAuthorizedKeysFilepaths) can prevent successful authentication. - Missing or Incorrect SSH Key: The public key for the Ansible user might not be present in
~/.ssh/authorized_keyson the target host, or the private key on the Ansible controller might be incorrect or missing.
2. Quick Fix (CLI)
These commands provide immediate, though sometimes temporary or less secure, workarounds. Always understand the security implications before applying them in production.
On the Target CentOS 7 Machine:
-
Temporarily Disable SELinux: This is often the quickest way to confirm if SELinux is the issue.
sudo setenforce 0 # Verify status sestatus # Output should show "Current mode: permissive"Note:
setenforce 0changes the mode topermissiveuntil the next reboot or untilsetenforce 1is run. This is NOT a permanent solution. -
Ensure SSH Service is Running and Enabled:
sudo systemctl start sshd sudo systemctl enable sshd sudo systemctl status sshd -
Allow SSH through Firewalld:
sudo firewall-cmd --zone=public --add-service=ssh --permanent sudo firewall-cmd --reload # Verify the service is active sudo firewall-cmd --list-services --zone=publicThis is a relatively safe and persistent fix for the firewall.
-
Correct SSH Directory and File Permissions: Assuming the user is
ansible_userand their home directory is/home/ansible_user:sudo chmod 700 /home/ansible_user/.ssh sudo chmod 600 /home/ansible_user/.ssh/authorized_keys sudo chown -R ansible_user:ansible_user /home/ansible_user/.sshThe
.sshdirectory should only be writable by the owner. Theauthorized_keysfile must only be readable by the owner.
On the Ansible Controller (if key transfer is the issue):
- Copy SSH Public Key to Target:
This assumes you have password authentication enabled temporarily or the target has already been provisioned with a working SSH key for another user.
You’ll be prompted forssh-copy-id ansible_user@your_target_ipansible_user’s password on the target.
3. Configuration Check (Permanent Solutions)
For a robust and secure environment, directly editing configuration files provides permanent solutions.
On the Target CentOS 7 Machine:
-
SELinux Configuration (
/etc/selinux/configand Contexts):- Preferred Method (Auditing & Contexts): If
setenforce 0resolves the issue, SELinux is the cause. Instead of disabling it, identify and resolve the context issue.- Check audit logs:
sudo ausearch -c sshd | audit2allow -M my-ssh-policy(This command will suggest a policy to allow the blocked action. Reviewmy-ssh-policy.teandmy-ssh-policy.shbefore running theshscript to install the policy.) - Restore default SELinux contexts: This is often needed after manually moving/creating files.
sudo restorecon -Rv /home/ansible_user/.ssh - Set correct SELinux context manually (less common for
.ssh):sudo semanage fcontext -a -t ssh_home_t "/home/ansible_user/.ssh(/.*)?" sudo restorecon -Rv /home/ansible_user/.ssh
- Check audit logs:
- Permanent Permissive Mode (Less Secure, for specific cases):
If you must disable SELinux for specific non-production environments, edit:
Changesudo vi /etc/selinux/configSELINUX=enforcingtoSELINUX=permissive. Reboot for this change to take full effect. Warning: Disabling SELinux significantly reduces system security.
- Preferred Method (Auditing & Contexts): If
-
FirewallD Configuration (
/etc/firewalld/zones/public.xml): Thefirewall-cmdcommands in the “Quick Fix” section are the recommended way to configure Firewalld permanently. You can verify the configuration by checking/etc/firewalld/zones/public.xml(or the relevant zone file) for a line like<service name="ssh"/>. -
SSH Daemon Configuration (
/etc/ssh/sshd_config): Ensure these critical settings are configured correctly for key-based authentication:sudo vi /etc/ssh/sshd_configPort 22(or your custom SSH port)PubkeyAuthentication yes(ensure this is enabled)AuthorizedKeysFile .ssh/authorized_keys(ensure this points to the correct location relative to the user’s home directory)PasswordAuthentication no(recommended for production, but means you MUST use keys)PermitRootLogin no(recommended for production; if trying to connect as root, you’ll needPermitRootLogin yesorprohibit-passwordwith keys, but using a non-root user andsudois preferred).- Ensure any
AllowUsersorDenyUsersdirectives do not restrict your Ansible user.
After making changes, restart the SSH service:
sudo systemctl restart sshd
4. Verification
After applying any fixes, it’s crucial to verify connectivity from your Ansible controller.
-
Direct SSH Connection (Verbose for Debugging): This is your primary tool for diagnosing SSH issues outside of Ansible.
ssh -v ansible_user@your_target_ip- Look for output indicating successful authentication.
- If it fails, the verbose output (
-v, you can add-vvvfor even more detail) will often explicitly state why (e.g., “Permission denied (publickey,gssapi-keyex,gssapi-with-mic).”, “bad ownership or modes”, “SELinux policy prevented”).
-
Ansible
pingModule: Oncessh -vworks, try the Ansiblepingmodule.ansible all -m ping -u ansible_user --private-key /path/to/your/private_key.pem -i /path/to/your/inventory.ini(Replace
allwith your target host pattern,-uwith your user, and-iwith your inventory if not using the default.)A successful output will look like:
your_target_ip | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } -
Check Logs on Target Host: If SSH or Ansible still fails, review the authentication logs on the target host:
sudo journalctl -u sshd -n 50 --no-pager # Or for older systems/more detail sometimes sudo tail -f /var/log/secureLook for specific error messages related to authentication failures, permission denied, or SELinux AVC denials. These logs will provide the most precise reason for the
403 Forbidden(SSH denial) error.
By systematically working through these steps, you’ll be able to identify and resolve the root cause of “Ansible 403 Forbidden” errors on your CentOS 7 systems, ensuring smooth and secure automation.