Esta pagina se ve mejor con JavaScript habilitado

AWX-Ansible

 ·  🎃 kr0m

Awx es el equivalente gratuito de Ansible Tower, se trata de la última versión de Tower(inestable), aunque cabe decir que en las pruebas realizadas ha funcionado sin dar ningún problema. Con AWX dispondremos de varas funcionalidades como poder delegar ciertas tareas de administración, acceso a informes de ejecución de playbooks, programar tareas, crear workflows y automatizar tareas mediante su API.

Las principales características de awx son:

  • Delegar: AWX nos permite crear usuarios, grupos y equipos. Se pueden filtrar los accesos a los inventarios, credenciales y playbooks, esto nos permite que usuarios sin experiencia puedan ejecutar complejas tareas de automatización. Un desarrollador es capaz de montarse él mismo los servidores de desarrollo que necesite tan solo presionando un botón.
  • Informes de ejecución de playbooks: AWX nos permite visualizar las ejecuciones de los playbooks en el dashboard viendo de este modo si algún playbook falló, cuando se ejecutaron y por quién fueron ejecutados. Podremos ver en un click porque falló la ejecución en un host en concreto.
  • Programar tares: Se pueden programar tareas para ejecutar ciertas acciones como ejecución de templates y pulls de repositorios git. Las tareas se pueden incluso programar para una fecha futura en concreto, pudiendo irnos de vacaciones y seguir desplegando servidores en nuestra ausencia sin intervención humana.
  • Workflows: Los templates nos permiten mediante el workflow editor encadenar la ejecución de playbooks, pulls de repos y notificaciones.
  • Automatización de tareas mediante API: Todo el sistema funciona en base a la API REST, la interfaz web en realidad hace llamadas a esta api, disponer de la api nos puede permitir integrar tareas en scripts, webs de terceros y demás.

Lo manera mas sencilla de desplegar AWX es mediante Docker, en este manual se va a explicar tanto para Gentoo como Ubuntu:

  • Gentoo:

    emerge -av app-emulation/docker app-admin/ansible dev-python/pip
    /etc/init.d/docker start
    rc-update add docker default
    pip install docker --user

  • Ubuntu:

    apt install docker.io git ansible python-pip -y
    pip uninstall -y docker docker-py
    pip install docker

Clonamos los repos del proyecto:

Toda la instalación de servicios se basa en docker, los CTs de Docker se instalan mediante un playbook de Ansible:

cd awx/installer
vi inventory

dockerhub_base=ansible
dockerhub_version=latest
awx_official=true
postgres_data_dir=/var/lib/pgdocker
docker_compose_dir=/var/lib/awxcompose

Ejecutamos el playbook:

ansible-playbook -i inventory install.yml -vv

Podemos ver los CTs:

docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
docker ps
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS                                                 NAMES
b2070d0b4bed        ansible/awx_task:latest      "/tini -- /bin/sh -c…"   6 seconds ago       Up 6 seconds        8052/tcp                                              awx_task
0bcc0f360861        ansible/awx_web:latest       "/tini -- /bin/sh -c…"   8 seconds ago       Up 6 seconds        0.0.0.0:80->8052/tcp                                  awx_web
27e3ad73bc76        postgres:9.6                 "docker-entrypoint.s…"   12 seconds ago      Up 9 seconds        5432/tcp                                              awx_postgres
acbdeb1d0cc4        ansible/awx_rabbitmq:3.7.4   "docker-entrypoint.s…"   12 seconds ago      Up 9 seconds        4369/tcp, 5671-5672/tcp, 15671-15672/tcp, 25672/tcp   awx_rabbitmq
723a9595b52d        memcached:alpine             "docker-entrypoint.s…"   12 seconds ago      Up 10 seconds       11211/tcp                                             awx_memcached

Accedemos a la interfaz web con las credenciales por defecto admin/password:
http://ansible-awx.alfaexploit.com

admin
password

NOTA: Lo primero que debemos hacer es cambiar el password de admin

Una vez cambiado el password de admin, creamos una organización, cada organización puede entenderse como una empresa, cliente o sede:

Ahora crearemos un inventario nuevo indicando la organización creada, además añadiremos algunos hosts:

Añadimos los hosts en la pestaña hosts:

En mi caso los playbooks se encuentran en un repositorio de git, para poder tener acceso a ellos debemos crear unas credenciales de acceso a este:

Para acceder vía ssh a los hosts también debemos crear unas credenciales:

Creamos el repositorio indicando que utilice las credenciales anteriormente configuradas:

Ahora ya estamos en condiciones de crear nuestro primer job template que se encargará de ejecutar el playbook baseGentoo.yml

Para ejecutar el template debemos presionar sobre el icono del cohete:

Una funcionalidad muy interesante de AWX es que podemos programar la ejecución de los templates mediante schedules, por ejemplo en este caso programamos la ejecución de la actualización de los sistemas Gentoo:


En la siguiente captura de pantalla podemos ver los schedules que trae por defecto mas el nuestro:

NOTA: Los schedules pueden ser para ejecutar playbooks, hacer un pull del repo de git o incluso ejecutar tareas administrativas

AWX nos permite ser notificados cuando se ejecuta un playbook, ya sea cuando terminó con éxito o cuando falló.

Para probar las notificaciones vamos a crear una pequeña API web que nos avisará vía Telegram:

vi main.py

#!/bin/python

from apiAWX import Server

if __name__ == '__main__':
    Server().start()
vi apiAWX.py
#!/bin/python
from BaseHTTPServer import BaseHTTPRequestHandler
from BaseHTTPServer import HTTPServer
from SocketServer import ThreadingMixIn
import simplejson
import threading
import requests
import sys

def sendTelegram(msg):
    apiKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    url = "https://api.telegram.org/bot{}/sendMessage".format(apiKey)
    #print msg

    userId = "YYYYYYYYYYYYYY"
    data = {"chat_id":userId,"text":msg}
    r = requests.post(url,json=data)
    #print r

def processAWXRequest(requestObject):
    #print type(requestObject)
    contentLength = int(requestObject.headers.getheader('content-length', 0))
    data = requestObject.rfile.read(contentLength)
    data = simplejson.loads(data)
    #print type(data)
    #print data

    if 'body' in data and data['body'] == 'Ansible Tower Test Notification 1 https://towerhost':
        msg = 'AWX test message detected'
    else:
        playbookStart = data['started']
        playbookFinish = data['finished']
        playbookAffectedHosts = str(data['hosts'].keys())
        playbookStatus = data['status']
        playbookName = data['name']

        #print playbookStart
        #print playbookName
        #print playbookAffectedHosts
        #print playbookStatus
        #print playbookFinish
        msg = playbookStart + '\nPlaybook: ' + playbookName + '\nAffected Hosts: ' + playbookAffectedHosts + '\nStatus: ' + playbookStatus + '\n' + playbookFinish 

    sendTelegram(msg)
    requestObject.send_response(200)

def handler_404(self):
    self.send_response(404)

# Routes array
ROUTES = {
    ('GET', "/"): lambda self: self.wfile.write("AWX-API by kr0m"),
    ('POST', "/processAWXRequest"): (processAWXRequest),
}

# Class handling GET/POST requests
class Handler(BaseHTTPRequestHandler):
    # Send request GET to function defined in ROUTES array
    def do_GET(self):
        ROUTES.get(('GET', self.path), handler_404)(self)

    # Send request POST to function defined in ROUTES array
    def do_POST(self):
        ROUTES.get(('POST', self.path), handler_404)(self)

class MultiThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    pass

class Server(threading.Thread):
    def run(self):
        httpd = MultiThreadedHTTPServer(('', 5555), Handler)
        httpd.serve_forever()

Podemos comprobar que las notificaciones funcionan mediante el test notification, campanita

Para que nuestra API arranque en el boot de Gentoo:

vi /etc/local.d/awxWebhook.start

su kr0m -c "nohup python /home/kr0m/awxWebhook/main.py &"
chmod 700 /etc/local.d/awxWebhook.start

NOTA: En Ubuntu sería crear una unidad de systemd

Arrancamos manualmente la API mediante el script de local.d:

/etc/local.d/awxWebhook.start

Otra funcionalidad muy interesante sobretodo si el equipo de programadores va a ejecutar nuestros playbooks son las surveys, estas les permitirán desplegar equipos con parámetros específicos a su proyecto, por ejemplo el nombre del usuario que desplegará el código, el docRoot del proyecto, etc.

Un ejemplo de encuesta podría ser si desea instalar el binario de symfony:

Al ejecutar el playbook nos aparecerá un formulario para rellenar los datos sobre la encuesta:

Otro aspecto a destacar de AWX es la capacidad de obtener listas de hosts en base a querys sobre datos del host, para ello en vez de crear inventarios crearemos smart inventories, se pueden utilizar varios filtros para ello:

exact: Exact match (default lookup if not specified).
iexact: Case-insensitive version of exact.
contains: Field contains value.
icontains: Case-insensitive version of contains.
startswith: Field starts with value.
istartswith: Case-insensitive version of startswith.
endswith: Field ends with value.
iendswith: Case-insensitive version of endswith.
regex: Field matches the given regular expression.
iregex: Case-insensitive version of regex.
gt: Greater than comparison.
gte: Greater than or equal to comparison.
lt: Less than comparison.
lte: Less than or equal to comparison.
isnull: Check whether the given field or related object is null; expects a boolean value.
in: Check whether the given field’s value is present in the list provided; expects a list of items.
Boolean values may be specified as True or 1 for true, False or 0 for false (both case-insensitive).

Mi preferido es mediante expresiones regulares, por ejemplo para sacar todos los hosts cuyo nombre empieza por test sería:

name.regex:^test.*

Podemos ver el nuevo smart inventory en la lista de smart inventories:

NOTA: A mi parecer la mejor estrategia es crear listas de inventarios en base a EMPRESA-TECH-PROYECTO y nombrar los servidores del mismo modo para luego poder crear smart inventories mas fácilmente.

El proceso de subida de playbooks a producción suele ser programación/testeo en local, cuando el código es suficientemente maduro se sube al repositorio y finalmente los desarrolladores ejecutan los playbooks a través de awx.

Para poder testear en local los playbooks sin subirlos al repo podemos asignar valores a las variables que asignaría AWX pero desde la cli:

ansible-playbook deployLXDCT.yml --extra-vars “host=HOSTNAME password=XXXXXXXXXXXX profileName=default ctName=CTNAME ipAddress=IPADDRESS/NETMASK gw=GWIPADDRESS”

Todas las funcionalidades de Ansible funcionan generando unos ficheros en python que serán copiados vía ssh al servidor para posteriormente ser ejecutados en este, acto seguido son eliminados del servidor de Ansible.

Si necesitamos debugear estos ficheros podemos habilitar la opción ANSIBLE_KEEP_REMOTE_FILES para conservarlos.

Luego tan solo debemos entrar en el CT de Docker para ver los ficheros:

docker exec -it awx_task /bin/bash
cat /root/.ansible/tmp/ansible-tmp-1560498029.06-113770163193085/AnsiballZ_git.py
tail -f /var/log/tower/dispatcher.log

La instalación de AWX mediante docker NO instala el servidor web con soporte para SSL, pero mediante socat podemos hacer un frontend SSL y redirigir el tráfico al puerto de AWX:

docker ps

CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS                                                 NAMES
c74cbb26f09e        ansible/awx_web:latest       "/tini -- /bin/sh -c…"   5 days ago          Up 4 minutes        0.0.0.0:80->8052/tcp                                  awx_web

Primero generamos el certificado a presentar:

openssl genrsa -out cert.key 1024
openssl req -new -key cert.key -x509 -days 36530 -out cert.crt

Country Name (2 letter code) [AU]:ES
State or Province Name (full name) [Some-State]:XXXX
Locality Name (eg, city) []:YYYY
Organization Name (eg, company) [Internet Widgits Pty Ltd]:IT
Organizational Unit Name (eg, section) []:IT
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:kr0m@alfaexploit.com
cat cert.key cert.crt > cert.pem

Arrancamos socat escuchando en el puerto 443, sirviendo el certificado y reenviando el tráfico a localhost:80

socat -v openssl-listen:443,cert=cert.pem,verify=0,reuseaddr,fork tcp4:localhost:80

Para que esto sea automático creamos un script de arranque, en Gentoo sería del siguiente modo en Ubuntu mediante una unidad de systemd:

vi /etc/local.d/socatAwx.start

nohup socat -v openssl-listen:443,cert=/home/kr0m/cert.pem,verify=0,reuseaddr,fork tcp4:localhost:80 &

Le asignamos los permisos necesarios:

chmod 700 /etc/local.d/socatAwx.start

Lo arrancamos manualmente:

/etc/local.d/socatAwx.start

AWX nos permite realizar backups de la configuración mediante la herramienta ansible-tower-cli, la instalamos desde pip:

pip install ansible-tower-cli --user

Hacemos que sea accesible desde el $PATH:

ln -s /home/kr0m/.local/bin/tower-cli /usr/local/bin/tower-cli

Ahora procedemos con la configuración de la tool, necesita que le indiquemos el host donde conectar, el user/pass y algunos parámetros extra:

tower-cli config host localhost
tower-cli config verify_ssl false
tower-cli config username admin
tower-cli config password XXXXXX
tower-cli config verbose True

El backup mas básico es este, que dumpea toda la config en un fichero json:

tower-cli receive --all > backup.json

Si solo queremos dumpear las credenciales:

tower-cli receive --credential all > credentials.json

Para exportar toda la configuración a excepción de los objetos de una organización:

tower-cli receive --prevent organization --all > backup.json

Tan importante con dumpear es poder restaurar los datos extraídos.

El restore básico sería:

tower-cli send backup.json

Para restaurar la config a un AWX remoto:

tower-cli send backup.json --tower-host YOUR_INSTANCE_IP

Para restaurarlo todo a excepción de los datos de una organización en concreto:

tower-cli send --prevent organization organizations.json

Si te ha gustado el artículo puedes invitarme a un RedBull aquí