Introduction

Server automation now plays an essential role in systems administration, due to the disposable nature of modern application environments. Configuration management tools such as Ansible are typically used to streamline the process of automating server setup by establishing standard procedures for new servers while also reducing human error associated with manual setups.

Ansible offers a simple architecture that doesn’t require special software to be installed on nodes. It also provides a robust set of features and built-in modules which facilitate writing automation scripts.

This guide explains how to use Ansible to automate the steps contained in our Initial Server Setup Guide for Ubuntu 18.04 servers.

Prerequisites

In order to execute the automated setup provided by the playbook we’re discussing in this guide, you’ll need:

  • One Ansible control node: an Ubuntu 18.04 machine with Ansible installed and configured to connect to your Ansible hosts using SSH keys. Make sure the control node has a regular user with sudo permissions and a firewall enabled, as explained in our Initial Server Setup guide. To set up Ansible, please follow our guide on How to Install and Configure Ansible on Ubuntu 18.04.
  • One or more Ansible Hosts: one or more remote Ubuntu 18.04 servers.

Before proceeding, you first need to make sure your Ansible control node is able to connect and execute commands on your Ansible host(s). For a connection test, please check step 3 of How to Install and Configure Ansible on Ubuntu 18.04.

What Does this Playbook Do?

This Ansible playbook provides an alternative to manually running through the procedure outlined in the Ubuntu 18.04 initial server setup guide and the guide on setting up SSH keys on Ubuntu 18.04.

Running this playbook will perform the following actions on your Ansible hosts:

  1. Install aptitude, which is preferred by Ansible as an alternative to the apt package manager.
  2. Create the administrative group wheels and configure it for passwordless sudo.
  3. Create a new sudo user.
  4. Copy a local SSH public key and include it in the authorized_keys file for the new administrative user on the remote host.
  5. Disable password-based authentication for the root user.
  6. Install system packages.
  7. Configure the UFW firewall to only allow SSH connections and deny any other requests.

Once the playbook has finished running, you’ll have a new user which you can use to log in to the server.

How to Use this Playbook

The first thing you’ll need to do is obtain the initial server setup playbook and its dependencies from the do-community/ansible-playbooks repository. We’ll clone this repository to a local folder inside the Ansible control node.

If this is your first time using the do-community/ansible-playbooks repository, you should start by cloning the repository to your controller node with:

  • cd ~
  • git clone https://github.com/do-community/ansible-playbooks.git
  • cd ansible-playbooks

In case you have cloned this repository before while following a different guide, access your existing ansible-playbooks copy and run a git pull command to make sure you have updated contents:

  • cd ~/ansible-playbooks
  • git pull

The files we’re interested in are located inside the setup_ubuntu1804 folder, which has the following structure:

setup_ubuntu1804
├── playbook.yml
└── vars
    └── default.yml

Here is what each of these files are:

  • vars/default.yml: Variable file for customizing playbook settings.
  • playbook.yml: The playbook file, containing the tasks to be executed on the remote server(s).

We’ll edit the playbook’s variable file to customize its values. Access the setup_ubuntu1804 directory and open the vars/default.yml file using your command line editor of choice:

  • cd setup_ubuntu1804
  • nano vars/default.yml

This file contains a few variables that require your attention:

vars/default.yml
---
create_user: sammy
copy_local_key: "{{ lookup('file', lookup('env','HOME') + '/.ssh/id_rsa.pub') }}"
sys_packages: [ 'curl', 'vim', 'git', 'ufw']

The following list contains a brief explanation of each of these variables and how you might want to change them:

  • create_user: The name of the sudo user that will be created. In this example, we will be using sammy.
  • copy_local_key: The path to a local SSH public key file that should be copied to the remote server and added as authorized_key for the new sudo user. The default value uses the lookup plugin to obtain the full path to the default public key for the current system user at the Ansible control node.
  • sys_packages: An array containing the list of packages you wish to install on your hosts as part of your initial server setup. In this example, we are going to make sure the packages curl, vim, git and ufw are present.

Once you’re done updating the variables inside vars/default.yml, save and close this file. If you used nano, do so by pressing CTRL + X, Y, then ENTER.

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. We can use the -l flag to make sure that only a subset of servers, or a single server, is affected by the playbook. We can also use the -u flag to specify which user on the remote server we’re using to connect and execute the playbook commands on the remote hosts.

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

  • ansible-playbook playbook.yml -l server1 -u root

You will get output similar to this:

Output
PLAY [all] ***************************************************************************************************************************** TASK [Gathering Facts] ***************************************************************************************************************** ok: [server1] TASK [Install Prerequisites] *********************************************************************************************************** changed: [server1] TASK [Make sure we have a 'wheel' group] *********************************************************************************************** changed: [server1] TASK [Allow 'wheel' group to have passwordless sudo] *********************************************************************************** changed: [server1] TASK [Create a new regular user with sudo privileges] ********************************************************************************** changed: [server1] TASK [Set authorized key for remote user] ********************************************************************************************** changed: [server1] TASK [Disable password authentication for root] **************************************************************************************** changed: [server1] TASK [Update apt] ********************************************************************************************************************** changed: [server1] TASK [Install required system packages] ************************************************************************************************ ok: [server1] TASK [UFW - Allow SSH connections] ***************************************************************************************************** changed: [server1] TASK [UFW - Deny all other incoming traffic by default] ******************************************************************************** changed: [server1] PLAY RECAP ***************************************************************************************************************************** server1 : ok=11 changed=9 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Note: For more information on how to run Ansible playbooks, check our Ansible Cheat Sheet Guide.

Once the playbook execution is finished, you’ll be able to log in to the server with:

  • ssh sammy@server_host_or_IP

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

In case you have changed the copy_local_key variable to point to a custom SSH key (not your current system user’s one), you’ll need to provide an extra parameter specifying the location of its private key counterpart when connecting via SSH as the new user:

  • ssh sammy@server_host_or_IP -i ~/.ssh/ansible_controller_key

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

  • 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.

The Playbook Contents

You can find the initial server setup playbook featured in this tutorial in the ansible-playbooks repository, within the DigitalOcean Community Playbooks. To copy or download the script contents directly, click the Raw button towards the top of each script.

The full contents of the playbook as well as its associated files are also included here for your convenience.

vars/default.yml

The default.yml variable file contains values that will be used within the playbook tasks, such as the name of the user that will be created and the packages that should be installed as part of the initial server setup.

vars/default.yml
---
create_user: sammy
copy_local_key: "{{ lookup('file', lookup('env','HOME') + '/.ssh/id_rsa.pub') }}"
sys_packages: [ 'curl', 'vim', 'git', 'ufw']

playbook.yml

The playbook.yml file is where all tasks from this setup are defined. It starts by defining the group of servers that should be the target of this setup (all), after which it uses become: true to define that tasks should be executed with privilege escalation (sudo) by default. Then, it includes the vars/default.yml variable file to load configuration options.

playbook.yml
---
- hosts: all
  become: true
  vars_files:
    - vars/default.yml

  tasks:
    - name: Install Prerequisites
      apt: name=aptitude update_cache=yes state=latest force_apt_get=yes

  # Sudo Group Setup
    - name: Make sure we have a 'wheel' group
      group:
        name: wheel
        state: present

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

  # User + Key Setup
    - name: Create a new regular user with sudo privileges
      user:
        name: "{{ create_user }}"
        state: present
        groups: wheel
        append: true
        create_home: true
        shell: /bin/bash

    - name: Set authorized key for remote user
      authorized_key:
        user: "{{ create_user }}"
        state: present
        key: "{{ copy_local_key }}"

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

  # Install Packages
    - name: Update apt
      apt: update_cache=yes

    - name: Install required system packages
      apt: name={{ sys_packages }} state=latest

 # UFW Setup
    - name: UFW - Allow SSH connections
      ufw:
        rule: allow
        name: OpenSSH

    - name: UFW - Deny all other incoming traffic by default
      ufw:
        state: enabled
        policy: deny
        direction: incoming


Feel free to modify this playbook or include new tasks to best suit your individual needs within your own workflow.

Conclusion

Automating the initial server setup can save you time, while also making sure your servers will follow a standard configuration that can be improved and customized to your needs. With the distributed nature of modern applications and the need for more consistency between different staging environments, automation like this becomes a necessity.

In this guide, we demonstrated how to use Ansible for automating the initial tasks that should be executed on a fresh server, such as creating a non-root user with sudo access, enabling UFW and disabling remote password-based root login.

If you’d like to include new tasks in this playbook to further customize your initial server setup, please refer to our introductory Ansible guide Configuration Management 101: Writing Ansible Playbooks.

0 Comments

Creative Commons License