Ansible has documentation for most topics. The purpose of this document is to link some places to get started, and explain how NCAE Cyber Games utilizes Ansible. Any questions regarding Ansible should first be searched within the online Ansible Documentation, then Google, and finally brought to NCAE Cyber Games if still unanswered. We can be reached at:

  • Email: bdavis@c2games.org
  • Discord: https://discord.gg/H2jwMxt

From Wikipedia:

Ansible is an open-source software provisioning, configuration management, and application-deployment tool enabling infrastructure as code. It runs on many Unix-like systems, and can configure both Unix-like systems as well as Microsoft Windows.

Each NCAE Cyber Games Challenge Submission is written as an Ansible Role. Ansible is used to make submitting a challenge as simple as possible, while allowing the NCAE Cyber Games team to automate the modification and deployment of challenges.

An Ansible role is a set of tasks, variables, handlers, and other information used to configure a host for a certain purpose. Roles are normally used to configure a host to run a particular service, such as an OpenSSH Server. In short, a role is everything Ansible needs, wrapped up into a standalone directory (although roles may depend on other roles).

Resources to Get Started

The Ansible documentation has several resources for getting started. Recommended places to start include:

Important Terminology

What are Plays and Playbooks?

Playbooks are a list of Plays, which are a list of tasks. The infrastructure-examples repository has a predefined playbook named playbook.yml.

More information: https://docs.ansible.com/ansible/2.4/playbooks_intro.html#playbook-language-example

What are Tasks?

At a basic level, a task is nothing more than a call to an ansible module

Tasks can be thought of as commands, or another single ‘task’ to be performed on the remote system. Examples of tasks include adding/removing a user, ensuring a value is in a configuration file, installing/removing a package, or restarting a service.

Tasks call modules, which do the actual work on the remote system. There are many modules built in to Ansible, and they can usually be found by searching the internet with your favorite search engine. For example, searching for "ansible add user" should bring you to the user module.

Each Ansible task is defined within Yaml, where the indentation of the line changes it’s meaning. At the base level, each task defines the name of the current task, and the module to be used. Take adding a user for example:

- name: "Ensure the user 'bob' exists"
  user:
    name: "bob"
    password: "{{ bobs_password | password_hash('sha256') }}"
    update_password: always

Here, the name attribute is set to the string "Ensure the user ‘bob’ exists", and the user module is being used.

The user module also has several options specified, which are indented two more spaces than the name attribute and user module definition. The options available are defined in the Ansible User Module Documentation.

How do I use variables?

In the example above for adding the user "bob", some curly braces {{ }} can be found in the password field. These curly braces represent a variable in Ansible.

Variables can be defined in several places, but it’s recommended to defined them within your role, either in defaults/main.yml or vars/main.yml.

More information on variables is available here:

https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#simple-variables

In the above example, a pipe character (|) can also be found within the curly braces. This represents passing the data to a Jinja filter. This is an advanced usage of variable, and more information can be found here:

https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#playbooks-filters

What are Handlers?

Handlers are operations to be run once, after all tasks are complete. For example, you may want to restart a service if a task updates the configuration of that service, but not if the configuration is unchanged. You may also have several tasks that could update the service configuration, and the service only needs to be restarted once.

Handlers within roles are defined in handlers/main.yml, and are defined in the same manner as tasks. Handlers are used by adding the notify keyword to any module, which will notify the handler if it needs to run when the Play is finished.

An example can be found in the ssh_authorized_keys role:

# ssh_authorized_keys/handlers/main.yml
- name: Restart SSH
  ansible.builtin.service:
      name: sshd
      state: restarted

# ssh_authorized_keys/tasks/main.yaml
- name: Allow Root Login
  lineinfile:
    path: /etc/ssh/sshd_config
    regexp: '^#?PermitRootLogin.*'
    line: "PermitRootLogin yes"
  notify:
    - Restart SSH

Examples

Several examples are included in the roles/ directory of the the infrastructure-examples repository. The repository can be downloaded using the download button in Gitlab, next to the "Clone" button.

Included Examples:

  • The ssh_authorized_keys role will install an SSH public key onto the system, and allow root to SSH with password authentication (no SSH key required).
  • The manatee_bank_web_app role will install git/apache on a system and clone down a vulnerable web application.

These examples can be executed by first ensuring they are included in the playbook.yml playbook:

  tasks:
    - include_role:
        name: ssh_authorized_keys
    - include_role:
        name: manatee_bank_web_app

Then run the following Ansible command, with the IP(s) of your test machines substituted:

# single host - note the tailing comma!
ansible-playbook -i 10.1.10.17, playbook.yml
# multiple hosts
ansible-playbook -i 10.1.10.17,10.1.10.18 playbook.yml

If a playbook is successful, you should see a footer similar to this at the end of the output. Watch out for ok=xxx to be non-zero, and failed=xxx to be zero.

PLAY RECAP *******************************************************************************************************
10.1.11.28                 : ok=6    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Don’t forget to restore your VM to it’s initial snapshot after running the examples!