Flow of Security Compliance Using Ansible

 

Step 1 — Preparing Ansible control node

On your Ansible control node server, add your Ansible host remote server’s IP to your Ansible inventory file. Using your preferred text editor:

  1. sudo nano /etc/ansible/hosts

This will open your Ansible inventory file. Add your Ansible host remote server’s IP to the [servers] block:

/etc/ansible/hosts
[servers]
server1 ansible_host=your_remote_server_ip

. . .

Now you’ll test and authenticate your SSH connection between this Ansible control node and your Ansible host remote server:

  1. ssh root@your_remote_server_ip

Accept the authentication request, and enter your password if prompted. Once you’ve verified the SSH connection, hit CTRL+D to close the connection and return to your control node.

Step 2 — Preparing Playbook

The playbook.yml file is where all your tasks are defined. A task is the smallest unit of action you can automate using an Ansible playbook. But first, create your playbook file using your preferred text editor:

  1. nano playbook.yml

This will open an empty YAML file. Before diving into adding tasks to your playbook, start by adding the following:

playbook.yml
---
- hosts: all
  become: true
  vars:
    created_username: sammy

Feel free to replace the username with one of your choosing.

Almost every playbook you come across will begin with declarations similar to this. hosts declares which servers the Ansible control node will target with this playbook. become states whether all commands will be done with escalated root privileges.

vars allows you to store data in variables. If you decide to change this username in the future, you will only have to edit this single line in your file.

Note: If you want to see the playbook file in its final finished state, jump to Step 8. YAML files can be particular with their indentation structure, so you may want to double-check your playbook once you’ve added all your tasks.

Step 3 — Adding an Aptitude Installation Task to Playbook

By default, tasks are executed synchronously by Ansible in order from top to bottom in your playbook. This means task ordering is important, and you can safely assume one task will finish executing before the next task begins.

All tasks in this playbook can stand alone and be re-used in your other playbooks.

Add your first task of installing aptitude, a tool for interfacing with the Linux package manager:

playbook.yml
  tasks:
    - name: Install aptitude
      apt:
        name: aptitude
        state: latest
        update_cache: true

Here, you’re using the apt Ansible built-in module to direct Ansible to install aptitude. Modules in Ansible are shortcuts to execute operations that you would otherwise have to run as raw bash commands. Ansible safely falls back onto apt for installing packages if aptitude is not available. So while this step is technically optional, Ansible has historically preferred aptitude.

Step 4 — Adding Sudo User Setup Tasks to Playbook

It is good practice to avoid extensive use of the root user. You can automate the creation of a user that is granted sudo privileges by adding:

playbook.yml
    - name: Setup passwordless sudo
      lineinfile:
        path: /etc/sudoers
        state: present
        regexp: '^%sudo'
        line: '%sudo ALL=(ALL) NOPASSWD: ALL'
        validate: '/usr/sbin/visudo -cf %s' 

    - name: Create a new regular user with sudo privileges
      user:
        name: "{{ created_username }}"
        state: present
        groups: sudo
        append: true
        create_home: true

You’re using the lineinfile Ansible module to target and replace a specific line in a file. In this case, you’re using regex to target a specific line in the sudoers file then modifying it to allow passwordless use of sudo. You also use visudo to validate your changes to prevent anything from breaking.

In order to take advantage of this, you’re adding a new user with the user module. Ansible will ensure this user is created if not already in existence, that the user belongs to the sudo group while not being removed from other groups, and a home directory is created.

Note: Make sure you include the quotes around the curly braces that indicate a variable. Omission of these quotes is a very common Ansible syntax error.

Step 5 — Adding SSH Key Setup and Disabling Root Password Tasks to Playbook

Ansible operates under the assumption that you’re using SSH keys. It is strongly recommended and generally good practice to pair SSH key usage with disabling root password authentication. To automate this, add:

playbook.yml
    - name: Set authorized key for remote user
      ansible.posix.authorized_key:
        user: "{{ created_username }}"
        state: present
        key: "{{ lookup('file', lookup('env','HOME') + '/.ssh/id_rsa.pub') }}"

    - name: Disable password authentication for root
      lineinfile:
        path: /etc/ssh/sshd_config
        state: present
        regexp: '^#?PermitRootLogin'
        line: 'PermitRootLogin prohibit-password'

The authorized_key module can be used if you supply the username and the location of the key. Here, the path towards your key is built using Ansible’s lookup function.

The lineinfile module is used to search and replace a line in sshd_config in order to disable password authentication for root, limiting access to its privileges for heightened security.

Step 6 — Adding a Package Installation Task to Playbook

Ansible can ensure certain packages are always installed on your server. Rather than call apt install on each individual package, or break it into multiple tasks, you can list out all your desired packages:

playbook.yml
    - name: Update apt and install required system packages
      apt:
        pkg:
          - curl
          - vim
          - git
          - ufw
        state: latest
        update_cache: true

You can add or remove packages to your liking. This will ensure all packages are not only present, but on the latest version, and done after an update with apt is called.

Step 7 — Adding a Firewall Setup Task to your Playbook

A firewall is indispensable to any server facing the internet. You can have Ansible ensure UFW (Uncomplicated Firewall) is properly configured by adding:

playbook.yml
    - name: UFW - Allow SSH connections
      community.general.ufw:
        rule: allow
        name: OpenSSH

    - name: UFW - Enable and deny by default
      community.general.ufw:
        state: enabled
        default: deny

The ufw module first ensures SSH access is allowed through. Then, this module enables your firewall while defaulting to denying all other traffic to your server.

Step 8 — Reviewing Complete Playbook

Your playbook should look roughly like the following, with minor differences depending on your customizations:

playbook.yml
---
- hosts: all
  become: true
  vars:
    created_username: sammy

  tasks:
    - name: Install aptitude
      apt:
        name: aptitude
        state: latest
        update_cache: true

    - name: Setup passwordless sudo
      lineinfile:
        path: /etc/sudoers
        state: present
        regexp: '^%sudo'
        line: '%sudo ALL=(ALL) NOPASSWD: ALL'
        validate: '/usr/sbin/visudo -cf %s'

    - name: Create a new regular user with sudo privileges
      user:
        name: "{{ created_username }}"
        state: present
        groups: sudo
        append: true
        create_home: true

    - name: Set authorized key for remote user
      ansible.posix.authorized_key:
        user: "{{ created_username }}"
        state: present
        key: "{{ lookup('file', lookup('env','HOME') + '/.ssh/id_rsa.pub') }}"

    - name: Disable password authentication for root
      lineinfile:
        path: /etc/ssh/sshd_config
        state: present
        regexp: '^#?PermitRootLogin'
        line: 'PermitRootLogin prohibit-password'

    - name: Update apt and install required system packages
      apt:
        pkg:
          - curl
          - vim
          - git
          - ufw
        state: latest
        update_cache: true

    - name: UFW - Allow SSH connections
      community.general.ufw:
        rule: allow
        name: OpenSSH

    - name: UFW - Enable and deny by default
      community.general.ufw:
        state: enabled
        default: deny

Note: This is a gentle reminder to be mindful of your indentations. If you run into an error, this is very likely the culprit. YAML suggests using 2 spaces as an indent, as was done in this example.

Once you’re satisfied with your playbook, you can exit your text editor and save.

Step 9 — Running Playbook for the First Time

You’re now ready to run this playbook on one or more servers. Most playbooks are configured to be executed on every server in your inventory by default, but you’ll specify your server this time.

To execute the playbook only on server1, connecting as root, you can use the following command:

  1. ansible-playbook playbook.yml -l server1 -u root -k

The -l flag specifies your server and the -u flag specifies which user to log into on the remote server. Since you’ve yet to setup your remote server, root is your only option. The -k flag is very important on your first playbook run, since it allows you to enter your SSH password.

You will get output similar to this:

Output
. . . TASK [UFW - Allow SSH connections] *************************************************************************************************************************************************************************************************************************** changed: [server1] TASK [UFW - Enable and deny by default] ********************************************************************************************************************************************************************************************************************** changed: [server1] PLAY RECAP *************************************************************************************************************************************************************************************************************************************************** server1 : ok=9 changed=8 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

This indicates your server setup is complete! Your output doesn’t have to be exactly the same, but it is important that you have zero failures.

Now that you’ve done the first setup for your playbook, all subsequent ansible calls can be done with user sammy and without the -k flag:

  1. ansible-playbook playbook.yml -l server1 -u sammy

You’ll also be able to log in to the server with:

  1. ssh sammy@your_remote_server_ip

Remember to replace sammy with the user defined by the created_username variable, and server_host_or_IP with your server’s hostname or IP address.

After logging in to the server, you can check the UFW firewall’s active rules to confirm that it’s properly configured:

  1. sudo ufw status

You should get output similar to this:

Output
Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6)

This means that the UFW firewall has successfully been enabled. Since this was the last task in the playbook, it confirms that the playbook was fully executed on this server.




Comments

Popular posts from this blog

ERPNext

what is Ansible and why use ansible of Security Compliance project?

Final Work On Internship Time period (Security Compliance Using Ansible & Scaling ERPNext)