Tuesday, November 19, 2024

How to Use SSH Authentication and Privilege Escalation on Ansible

Ansible's power lies in its ability to automate infrastructure management across multiple nodes. The foundation of this automation is the communication method between the Ansible controller and the managed nodes. This communication, primarily handled via the Secure Shell (SSH) protocol, requires a robust and secure authentication mechanism. While password-based authentication is a simple starting point, it poses significant security risks. This article will guide you through implementing key-based authentication and managing privilege escalation within Ansible, significantly enhancing the security and efficiency of your automation workflows.

At its core, Ansible relies on SSH to establish connections with managed nodes and execute tasks. Each playbook run or ad-hoc command necessitates authentication, traditionally requiring the SSH password for each node. This approach is not only cumbersome but also presents a major security vulnerability. A compromised password compromises the entire infrastructure.

Key-Based Authentication

The recommended practice, and the focus of this guide, is to leverage SSH key pairs for authentication. This method eliminates the need for passwords, greatly enhancing security. The process involves generating a key pair (a private and a public key) on the Ansible controller. The public key is then distributed to each managed node, granting the controller secure, passwordless access.

Generating and Distributing SSH Key Pairs

The creation and deployment of SSH keys can be streamlined with a script. For this example, let's assume a simplified environment with nodes named 'controller,' 'managed1,' and 'managed2.' The following bash script will generate key pairs and distribute the public keys to each managed node:

#!/usr/bin/env bash

# This script creates SSH key pairs and distributes them to all nodes.

read -p "Enter the name for the key: " KEY_NAME
ssh-keygen -b 2048 -t rsa -f /home/emka/.ssh/${KEY_NAME} -q -N ""

# Looping through and distributing the key

for val in controller managed1 managed2; do
  echo "-------------------- COPYING KEY TO ${val^^} NODE ------------------------------"
  sshpass -p 'emka' ssh-copy-id -f -i /home/emka/.ssh/${KEY_NAME}.pub -o "StrictHostKeyChecking=no" emka@${val}
done
    
Remember to replace 'emka' with your actual username and adjust the node names as needed. After creating this script (e.g., create_keypair.sh), make it executable (chmod +x create_keypair.sh) and run it. This will generate key pairs and copy the public key to the .ssh/authorized_keys file on each managed node.

Handling Non-Default Key Names

When using key pairs with names other than the default (id_rsa), Ansible might initially attempt to use the default key names. If it doesn't find them, it will revert to password-based authentication, negating the purpose of key-based authentication. To circumvent this, explicitly specify the private key file using the -i flag with the ssh command:

      ssh -v -i /home/emka/.ssh/first_key emka@managed1
    

Integrating Key-Based Authentication into Ansible

To utilize key-based authentication within Ansible, you can employ the --key-file or --private-key flags when running ad-hoc commands or playbooks. These flags allow you to specify the path to your private key file:

      ansible managed1 -m ping --key-file /home/emka/.ssh/second_key
    

Configuring Ansible for Key-Based Authentication

For streamlined management, Ansible offers several configuration options:

  • ansible.cfg: This configuration file allows you to specify the default private key file globally. Add a line like this:

      private_key_file = /home/emka/.ssh/first_key
    
  • Inventory File: This file enables per-host key specification. For example:

[ubuntu1]
managed1 ansible_ssh_private_key_file=/home/emka/.ssh/first_key

[ubuntu2]
managed2 ansible_ssh_private_key_file=/home/emka/.ssh/second_key
    
Precedence of Configuration Options

It's crucial to understand the precedence of these configuration options: command-line arguments override inventory file settings, which in turn override the ansible.cfg settings.

Password-Based Authentication

While key-based authentication is the preferred method, situations might arise where password-based authentication is necessary. For example, connecting as a user without an existing key pair. Ansible provides mechanisms to handle this:

  • -k or --ask-pass: This flag prompts for the SSH password during execution.

  • --connection-password-file or --conn-pass-file: This allows specifying a file containing the password. Caution: Storing passwords in plain text is highly discouraged. Explore Ansible Vault for secure password management.

  • Inventory File: You can specify the username and password directly in the inventory file:

[ubuntu1]
managed1 ansible_ssh_private_key_file=/home/emka/.ssh/first_key ansible_user=emka

[ubuntu2]
managed2 ansible_user=anotheruser ansible_ssh_pass=password
    

Again, storing passwords directly in the inventory file is strongly discouraged due to security risks.

Privilege Escalation: Executing Tasks as Root

Many Ansible tasks require elevated privileges (typically root) to execute successfully, such as package management. Ansible offers the -b or --become flag to enable privilege escalation:

      ansible ubuntu1 -m service -a "name=sshd state=restarted" -b
    
By default, Ansible uses sudo for privilege escalation. Other methods, such as su or doas, can be specified using the --become-method flag. Consult the Ansible documentation for a complete list of supported methods.

Handling Sudo Passwords

If your sudo configuration requires a password, the -K or --ask-become-pass flag prompts for the sudo password. Alternatively, you can specify a password file using --become-password-file or --become-pass-file. Remember the security implications of storing passwords in plain text files. Ansible Vault is the recommended solution for secure password storage.

The become Directive in Playbooks

For more structured control, the become directive can be specified within your Ansible playbooks, either at the play level or the task level. This allows for granular control over privilege escalation within your automation workflows. An example of the become directive at the play level:

- hosts: all
  become: true
  tasks:
    - name: Restart SSH service
      service:
        name: sshd
        state: restarted
    

And at the task level:

- hosts: all
  tasks:
    - name: Restart SSH service
      become: true
      service:
        name: sshd
        state: restarted
    
Configuring become Globally

While specifying become within playbooks is the preferred method, it can also be set in ansible.cfg or the inventory file. However, playbook-level configuration ensures greater control and clarity.

Using a Different Become User

To escalate privileges to a user other than root, use the --become-user flag.

Conclusion

Implementing key-based authentication and effectively managing privilege escalation are crucial for secure and efficient Ansible automation. By understanding the methods and best practices outlined in this article, you can significantly enhance the security of your infrastructure while streamlining your automation workflows. Remember that secure password management is paramount; avoid storing passwords in plain text and utilize Ansible Vault to encrypt sensitive information. Prioritize key-based authentication whenever possible to eliminate the risks associated with password-based authentication.

0 comments:

Post a Comment