Ansible
Ansible Ubuntu Web Servers
# Comprehensive guide for mastering Ansible automation, from creating web servers with MySQL databases 
# to automating SSH key distribution and securing the servers.

Table of Contents

  1. Setting Up Ansible (opens in a new tab)
  2. Creating and Managing Droplets on DigitalOcean (opens in a new tab)
  3. Configuring Inventory and Variables (opens in a new tab)
  4. Automatically Distributing SSH Keys (opens in a new tab)
  5. Creating Users with Hashed Passwords (opens in a new tab)
  6. Setting Up Web Servers and MySQL (opens in a new tab)
  7. Securing Ubuntu (opens in a new tab)
  8. Using Ansible Vault for Sensitive Data (opens in a new tab)
  9. Best Practices, Tips, and Tricks (opens in a new tab)
  10. Detailed Documentation (opens in a new tab)

1. Setting Up Ansible (opens in a new tab)

# Install Ansible and verify installation on your control machine
sudo apt update
sudo apt install ansible -y
ansible --version
# Ansible configuration file (ansible.cfg) for efficiency
[defaults]
inventory = ./inventory.yml
remote_user = root
host_key_checking = False

2. Creating and Managing Droplets on DigitalOcean (opens in a new tab)

# Create 3 Ubuntu 24.04 droplets with SSH keys for secure login.
# Manually add your SSH key to droplets
ssh-copy-id root@<droplet-ip>

3. Configuring Inventory and Variables (opens in a new tab)

# Define server groupings in inventory.yml
all:
  hosts:
    web1:
      ansible_host: <web1-ip>
      ansible_user: root
    web2:
      ansible_host: <web2-ip>
      ansible_user: root
    web3:
      ansible_host: <web3-ip>
      ansible_user: root
  vars:
    ansible_python_interpreter: /usr/bin/python3

4. Automatically Distributing SSH Keys (opens in a new tab)

Creating Users (opens in a new tab)

# distribute_ssh_keys.yml: Automate SSH key distribution to all servers
---
- hosts: all
  become: yes
 
  tasks:
    # Task: Create users and add SSH keys for passwordless login
    - name: "Create users and add SSH keys"
      user:
        name: "{{ item.name }}"
        state: present
        shell: /bin/bash
        createhome: yes
      with_items:
        - { name: 'deploy', groups: 'sudo' }
 
    # Task: Install SSH key for 'deploy' user
    - name: "Install authorized SSH key"
      authorized_key:
        user: "deploy"
        key: "{{ lookup('file', '/home/local_user/.ssh/id_rsa.pub') }}"
# Run the playbook to distribute SSH keys
ansible-playbook distribute_ssh_keys.yml

5. Creating Users with Hashed Passwords (opens in a new tab)

# Generate hashed passwords using Python or OpenSSL
python3 -c "import crypt; print(crypt.crypt('YOUR_PASSWORD', crypt.mksalt(crypt.METHOD_SHA512)))"
openssl passwd -6
# user_with_hashed_password.yml: Playbook to create users with hashed passwords
---
- hosts: all
  become: yes
  vars:
    users:
      - name: deploy
        password: "$6$random_salt$hash"
 
  tasks:
    # Task: Create users with hashed passwords for better security
    - name: "Create users with hashed passwords"
      user:
        name: "{{ item.name }}"
        password: "{{ item.password }}"
        state: present
        shell: /bin/bash
      with_items: "{{ users }}"
# Run the playbook to create users with hashed passwords
ansible-playbook user_with_hashed_password.yml

6. Setting Up Web Servers and MySQL (opens in a new tab)

Services (opens in a new tab)

# webserver_setup.yml: Playbook for installing Nginx web server
---
- hosts: webservers
  become: yes
  tasks:
    # Task: Install Nginx web server
    - name: "Install Nginx"
      apt:
        name: nginx
        state: present
        update_cache: yes
 
    # Task: Ensure Nginx service is running and enabled
    - name: "Ensure Nginx is running"
      service:
        name: nginx
        state: started
        enabled: yes

6A. Ansible mysql_db Module (opens in a new tab)

Ansible mysql_user Module (opens in a new tab) Ansible mysql_query Module (opens in a new tab)

Vault File Setup

Create a Vault file to store sensitive variables securely:

ansible-vault create secrets.yml

Add the following content to secrets.yml:

mysql_root_password: "your_secure_password_here"

Playbook: mysql_setup.yml

- hosts: database
  become: yes
  vars_files:
    - /path/to/secrets.yml
 
  tasks:
    - name: Install MySQL server
      apt:
        name: mysql-server
        state: present
 
    - name: Set MySQL root password
      mysql_user:
        name: root
        password: "{{ mysql_root_password }}"
        host: localhost
        state: present
 
    - name: Remove anonymous MySQL users
      mysql_user:
        name: ""
        host: "%"
        state: absent
 
    - name: Remove remote root user
      mysql_user:
        name: root
        host: "{{ item }}"
        state: absent
      with_items:
        - 'localhost'
        - '127.0.0.1'
 
    - name: Remove test database
      mysql_db:
        name: test
        state: absent
 
    - name: Reload MySQL privilege tables
      mysql_query:
        query: "FLUSH PRIVILEGES;"

Task Details

  1. Install MySQL Server

    • Purpose: Install the MySQL server package.
    • Module: apt
    • Parameters:
      • name: mysql-server
      • state: present
  2. Set MySQL Root Password

    • Purpose: Set the root password for MySQL.
    • Module: mysql_user
    • Parameters:
      • name: root
      • password: Retrieved from the Vault file
      • host: localhost
      • state: present
  3. Remove Anonymous MySQL Users

    • Purpose: Remove any anonymous users in MySQL.
    • Module: mysql_user
    • Parameters:
      • name: "" (empty name matches anonymous users)
      • host: % (all hosts)
      • state: absent
  4. Remove Remote Root User

    • Purpose: Remove the root user with remote access.
    • Module: mysql_user
    • Parameters:
      • name: root
      • host: localhost and 127.0.0.1
      • state: absent
  5. Remove Test Database

    • Purpose: Remove the default test database.
    • Module: mysql_db
    • Parameters:
      • name: test
      • state: absent
  6. Reload MySQL Privilege Tables

    • Purpose: Apply changes by reloading MySQL privilege tables.
    • Module: mysql_query
    • Parameters:
      • query: FLUSH PRIVILEGES;

Running the Playbook

Execute the playbook with the Vault password file or interactive prompt:

ansible-playbook mysql_setup.yml --vault-password-file /path/to/vault-password-file

Or use the interactive prompt:

ansible-playbook mysql_setup.yml --ask-vault-pass

Summary

  • Secure MySQL: This playbook automates the installation and configuration of MySQL to ensure a secure setup.
  • Manage Secrets: Use Ansible Vault to securely handle sensitive data such as MySQL root passwords.
  • Automate: Achieve consistent MySQL configurations across different environments using Ansible.

7. Securing Ubuntu with ufw (opens in a new tab)

# ufw_setup.yml: Playbook for setting up UFW (Uncomplicated Firewall)
---
- hosts: all
  become: yes
  tasks:
    # Task: Allow OpenSSH for SSH access
    - name: "Allow OpenSSH"
      ufw:
        rule: allow
        name: OpenSSH
 
    # Task: Allow Nginx HTTP access
    - name: "Allow Nginx HTTP"
      ufw:
        rule: allow
        name: Nginx HTTP
 
    # Task: Enable UFW
    - name: "Enable UFW"
      ufw:
        state: enabled

7B. Securing Ubuntu with ssh module (opens in a new tab)

# ssh_hardening.yml: Playbook to harden SSH security
---
- hosts: all
  become: yes
  tasks:
    # Task: Disable root login for better security
    - name: "Disable root login"
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^PermitRootLogin'
        line: 'PermitRootLogin no'
 
    # Task: Change SSH port from default 22 to a custom port
    - name: "Change SSH port"
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^#Port 22'
        line: 'Port 2222'
 
    # Task: Restart SSH service to apply changes
    - name: "Restart SSH"
      service:
        name: ssh
        state: restarted
# Run the security playbooks
ansible-playbook ufw_setup.yml
ansible-playbook ssh_hardening.yml

Explanation

lineinfile module (opens in a new tab)

- name: "Disable root login"
  lineinfile:
    path: /etc/ssh/sshd_config
    regexp: '^PermitRootLogin'
    line: 'PermitRootLogin no'
 
- name: "Ensure Port 22 is uncommented"
  lineinfile:
    path: /etc/ssh/sshd_config
    regexp: '^#Port 22'
    line: 'Port 22'
  • name: Descriptive name of the task for readability and documentation.
  • lineinfile: An Ansible module used to ensure a specific line is present or absent in a file.
  • path: Specifies the path to the file you want to modify (/etc/ssh/sshd_config in this case).
  • regexp: A regular expression that identifies the line to be replaced. Here, it matches lines that start with PermitRootLogin.
  • line: The line to ensure is present in the file. If a line matching the regular expression is found, it will be replaced with this line.

What It Does:

  • The task searches for any line in /etc/ssh/sshd_config that starts with PermitRootLogin.
  • It replaces that line with PermitRootLogin no, effectively disabling root login via SSH.

Regular Expression Cheat Sheet

Here’s a basic cheat sheet for common regular expression patterns and their meanings:

PatternDescriptionExample Matches
.Any single character except newlinea, b, 1
^Start of a string^abc matches abc but not zabc
$End of a stringabc$ matches abc but not abcd
*0 or more of the preceding elementa* matches ``, a, aa
+1 or more of the preceding elementa+ matches a, aa but not ``
?0 or 1 of the preceding elementa? matches ``, a
{n}Exactly n occurrences of the preceding elementa{2} matches aa
{n,}At least n occurrences of the preceding elementa{2,} matches aa, aaa
{n,m}Between n and m occurrences of the preceding elementa{2,4} matches aa, aaa, aaaa
[]Any one of the characters inside the brackets[abc] matches a, b, c
[^ ]Any one character not inside the brackets[^abc] matches d, e but not a, b, c
``Alternation (or)
()Grouping(abc)+ matches abc, abcabc
\Escape a special character\. matches .

Usage Tips

  • Use . for matching any character except newlines.
  • Use * and + for matching repeated characters or patterns.
  • Use ^ and $ to anchor patterns to the start or end of a string.
  • Use [] to match any one of a set of characters.
  • Use () for grouping and | for alternation.

Regular expressions can be very powerful but also complex. The cheat sheet provides a foundation to get started with basic patterns and operations.

8. Using Ansible Vault for Sensitive Data (opens in a new tab)

# Encrypt sensitive data, like MySQL passwords, using Ansible Vault
ansible-vault encrypt_string 'your-mysql-password' --name 'vault_mysql_password'
# Example of using Ansible Vault in a playbook
vars:
  mysql_root_password: !vault |
    $ANSIBLE_VAULT;1.1;AES256;...
# Run playbooks that use Ansible Vault for sensitive data
ansible-playbook playbook.yml --ask-vault-pass

9. Best Practices, Tips, and Tricks (opens in a new tab)

Folder Structure:

Organize your Ansible files to make them reusable and scalable:

├── ansible.cfg
├── inventory.yml
├── group_vars
   ├── all.yml
├── host_vars
   ├── web1.yml
├── roles
   ├── common
   ├── webserver
   ├── mysql

Best Practices:

  • Idempotency: Ensure tasks can run repeatedly without unintended consequences.
  • YAML Syntax: Always use correct YAML indentation and formatting.
  • Modularity: Use roles to make your playbooks modular and reusable.
  • Testing: Use --check and --diff flags to test playbooks before applying changes.

Useful Tricks:

  • Dry Run: Use --check to simulate playbook execution without making changes.
  • View Changes: Use --diff to see exactly what will be changed.
  • Parallelism: Use -f to run playbooks on multiple hosts in parallel.

10. Detailed Documentation (opens in a new tab)

For more details, refer to Ansible Documentation (opens in a new tab) for official guides and best practices.


This guide aims to provide a thorough understanding of Ansible and its use for automating server setup, configuration, and security tasks.