Index ¦ Archives ¦ Atom

Ad-hoc Ansible for infrastructure navigation

Introduction

The official definition for Ansible is the following:

Ansible is a radically simple IT automation engine that automates
cloud provisioning, configuration management, application deployment,
intra-service orchestration, and many other IT needs.

Or explained simply, ansible is ssh on steroids wrapped in python+yaml bubblewrap to prevent sysadmins from shooting themselves in the foot. It's ideally suited for dealing with infrastructure that's a complete mess, as it requires very little configuration.

One of the hidden benefits of using Ansible to manage servers is the ability to explore servers using ad-hoc commands. This is a killer-feature for any sysadmin trying to understand an existing, undocumented and heterogenous infrastructure. Since ansible is agentless, all you need to get started exploring an infra is:

  1. a working ssh connection to a server, with or without administrator privileges
  2. a python interpreter on the remote server (python2 preferably, see below on how to configure ansible to use python3)
  3. ansible running on the local machine, with a configured /etc/ansible/hosts file

Prerequisites

Make sure you can access all the servers in a group all (if group all is specified, ansible will ping all servers defined in /etc/ansible/hosts):

ansible all -u root -m ping

It's possible you won't be able to login due to numerous reasons, but it's possible they have python3 installed (ansible defaults to python2 on the remote end). Group the outliers into a special ansible group:

ansible all -u root -m ping | grep "UNREACHABLE!" | cut -d' ' -f1

The hosts file should now contain an entry similar to this:

[failed-hosts]
srv1.example.com
srv6.example.com
10.10.12.12

[failed-hosts:vars]
ansible_python_interpreter=/usr/bin/python3

Rerun the ping command using the failed-hosts ansible group that you defined above:

ansible failed-hosts -m ping

Once you can access all servers, it's time to explore the infrastructure.

Useful ad-hoc commands for exploring an unknown infra:

If you're running as a non-root, you might need to elevate privileges using sudo to get the correct command output:

ansible all -u myuser --become --ask-become-pass -a "netstat -tlpn"

Ansible will prompt you for the password. Make sure you have the same password on all servers or the command won't work.

If you want to do shell piping on the remote end, you will need to use the shell module:

ansible all -u myuser --become --ask-become-pass -m shell -a "netstat -tlpn | \
 grep '80'"

Get distribution name and version:

ansible all -u root -m setup | \
 grep -E 'SUCCESS|ansible_distribution"|ansible_distribution_version'

Only display distribution information:

ansible all -u root -m setup -a "filter=ansible_distribution*"

Get the kernel version:

ansible all -u root -m setup | grep -E "SUCCESS|ansible_kernel"

Check if the server clocks are in sync (they should be, or there's something wrong with ntp):

ansible all -u root -m setup -a 'filter=ansible_date_time' | \
 grep -E 'SUCCESS|"time":'

Check the server uptime:

ansible all -u root -m setup | grep -E "SUCCESS|ansible_uptime_seconds"

Check the current disk usage for all mountpoints:

ansible all -u root -a "df -h"

Check the current disk usage for the root partition:

ansible all -u root -m shell -a 'df -h | grep "\% /$"'

Alternative to ansible if you cannot install a python interpeter on a server

It's possible that you won't be able to install a python interpreter on a remote server, either because the OS has a broken package manager, or if you lack sudo privileges.

If that is the case, one can use the commands below as an alternative to ansible for mapping out an unknown infra.

If you don't already have it installed, install GNU parallel, which is an excellent xargs replacement. Then, create a hosts.txt file which looks similar to this:

ssh root@server1.example.com
ssh -p 2222 myuser@server2.example.com
ssh -p 23122 otheruser@server3.example.com
ssh -p 12 root@10.10.10.10

Then, create a script called recon.sh and make it executable:

#!/bin/sh
HOSTS_PATH="./hosts.txt"

#ASCII colors:
RED="\u001b[31m"
UNDERLINE="\u001b[4m"
RESET="\u001b[0m"

if [ "$1" == "" ]
then
    printf "${RED}${UNDERLINE}No command specified.${RESET}\n"
    exit 1
fi
cat $HOSTS_PATH | sed s"/$/ ${1}/" | parallel -k {}
chmod +x recon.sh

Run the recon.sh script:

./recon.sh "netstat -tlpn"

The -k (--keep-order) option forces gnu parallel to display results in the order specified in the hosts file, so the output can be parsed that way. To view the hostname of the server while running the command, use the following commands:

./recon.sh "hostname; netstat -tlpn"

Or, to view the ip address (assuming curl is installed on the remote machine):

./recon.sh "curl icanhazip.com; netstat -tlpn"

or

./recon.sh "curl -s https:\/\/icanhazip.com; netstat -tlpn"

the slashes in the url need to be escaped because of sed escape rules, therefore, http(s):// becomes http(s):\/\/ )

You can also pipe commands on the remote end to filter the output:

./recon.sh "netstat -tlpn | grep '80'"

You can also pipe the output locally, e.g. to create a json formatted output:

./recon.sh "uname -r" | jq -R

(To install jq, follow the instructions available at https://stedolan.github.io/jq/download/)

While it's possible to run commands this way, it's much, much easier to just use ansible.

© Bruno Henc. Built using Pelican. Theme by Giulio Fidente on github.