DEV Community

Filipi Souza
Filipi Souza

Posted on

🧭 Runbook: Como Rodar um Playbook Ansible com Pipeline CI/CD e SSH via Bastion

“Como automatizei execuções seguras do Ansible via bastion e pipeline CI/CD, com validação de hostnames e conexões persistentes.”


📘 Objetivo

Este runbook documenta o processo de execução controlada de playbooks Ansible — tanto localmente via Docker quanto automatizada através de uma pipeline CI/CD.

O caso de uso principal é o playbook de verificação de atualizações de segurança em servidores Linux baseados em Debian/Ubuntu, para casos onde é preciso desativar o unattended-upgrades por qualquer motivo que seja e assim receber apenas uma notificação que há atualizações para executar de forma planejada.

O playbook foi criado de forma idempotente — ou seja, pode ser executado repetidamente sem causar efeitos colaterais — e utiliza conexão segura via bastion (JumpServer), com autenticação baseada em chaves SSH armazenadas de forma segura.


⚙️ Arquitetura e Fluxo Geral

CI/CD Agent / Local Host
      │
      │ (SSH via chave segura)
      ▼
Bastion Host (JumpServer)
      │
      │ (ProxyCommand)
      ▼
Instâncias Linux (AWS / Cloud Provider)
Enter fullscreen mode Exit fullscreen mode
  1. A pipeline ou execução local inicia a verificação.
  2. A chave SSH segura é baixada de um Key Vault ou Secure Files da ferramenta de CI/CD.
  3. Um container Docker (com Ansible e dependências pré-instaladas) é iniciado.
  4. O Ansible conecta-se ao bastion, que faz o proxy das conexões até os hosts alvo.
  5. O playbook instala/atualiza o script remoto /usr/local/bin/check-security-updates.sh, cria um cron diário e envia notificações para um canal de monitoramento (ex.: Discord, Slack, etc.) quando há atualizações de segurança disponíveis.

✅ Pré-Requisitos

🔑 Acesso e Chaves SSH

As chaves SSH utilizadas devem ser gerenciadas de forma segura.

A chave pública correspondente deve estar presente no arquivo:

~/.ssh/authorized_keys
Enter fullscreen mode Exit fullscreen mode

Nos seguintes sistemas:

  • Bastion host (JumpServer)
  • Servidores-alvo (instâncias Linux)

💻 Dependências Locais

Para execução manual/local:

  • Docker instalado e configurado
  • CLI da nuvem (ex.: Azure CLI / AWS CLI) para acesso a segredos e login no registro de contêineres
  • Acesso de rede até o bastion (porta 22 liberada)
  • Uma imagem toolbox disponível localmente, contendo:
    • Ansible
    • OpenSSH Client
    • Curl

☁️ Acesso Cloud

Antes de executar o playbook:

  • Verifique se o bastion está operacional e com rota válida até as instâncias-alvo.
  • Confirme se o inventário Ansible reflete corretamente os grupos e IPs dos servidores.

Teste rápido de acesso manual:

ssh -i cicd-ansible-key.pem -J ubuntu@<bastion-host> ubuntu@<target-host>
Enter fullscreen mode Exit fullscreen mode

📋 Inventário e Configurações

Exemplo de inventário (ansible/inventory/servers.ini):

[development]
10.0.1.10
10.0.2.15
10.0.3.20

[development:vars]
ansible_user=ubuntu
ansible_ssh_private_key_file=/workspace/ssh_key.pem
ansible_ssh_common_args='
  -o StrictHostKeyChecking=no
  -o UserKnownHostsFile=/dev/null
  -o ConnectTimeout=20
  -o ServerAliveInterval=30
  -o ServerAliveCountMax=3
  -o BatchMode=yes
  -o ControlMaster=auto
  -o ControlPersist=60s
  -o ControlPath=~/.ssh/cm-%r@%h:%p
  -o ProxyCommand="ssh -W %h:%p ubuntu@<bastion-host>"
'
Enter fullscreen mode Exit fullscreen mode

Troque pelo IP/DNS que será seu servidor bastion.

💡 Dica: A combinação de ControlMaster, ControlPersist e ControlPath permite o reaproveitamento da conexão SSH, reduzindo a sobrecarga de autenticação e acelerando execuções consecutivas.


🧱 Playbook — check-security-updates.yaml

Exemplo simplificado de playbook:

- name: Verifica por Atualizações de Segurança
  hosts: development
  become: yes
  tasks:
    - name: Garantir que o curl está instalado
      ansible.builtin.package:
        name: curl
        state: present

    - name: Criar script de verificação de updates
      ansible.builtin.copy:
        dest: /usr/local/bin/check-security-updates.sh
        mode: "0755"
        owner: root
        group: root
        content: |
          #!/bin/bash
          apt update -qq
          updates=$(apt list --upgradable 2>/dev/null | grep -i security | wc -l)
          if [ "$updates" -gt 0 ]; then
            curl -s -X POST -H "Content-Type: application/json"               -d "{"content":"⚠️ Existem $updates atualizações de segurança pendentes no servidor $(hostname)."}"               https://example.com/webhook
          fi

    - name: Criar entrada no cron para rodar diariamente
      ansible.builtin.cron:
        name: "Check security updates and notify"
        user: root
        job: "/usr/local/bin/check-security-updates.sh"
        special_time: daily
Enter fullscreen mode Exit fullscreen mode

Idempotente:

  • Não recria o script se já existir e estiver atualizado.
  • Não duplica a entrada do cron.

🧰 Execução Local (Manual)

Exemplo de execução via Docker:

docker run --rm \
  -v ~/infra/ansible:/workspace \
  -v ~/.ssh/known_hosts:/root/.ssh/known_hosts:ro \
  -v ~/.ssh/cicd-ansible-key.pem:/workspace/ssh_key.pem:ro \  
  -e ANSIBLE_HOST_KEY_CHECKING=False \
  myregistry.azurecr.io/toolbox-ubuntu:latest   \
  ansible-playbook \
  -i /workspace/inventory/servers.ini \
  -l development \
  --timeout 30 \
  --forks 1 \
  --private-key /workspace/ssh_key.pem \
  /workspace/playbooks/check-security-updates.yaml
Enter fullscreen mode Exit fullscreen mode

📋 O que este comando faz:

  • Monta o diretório local do Ansible dentro do container;
  • Usa a chave SSH privada para autenticação direta;
  • Executa o playbook contra o host ou grupo definido;
  • Reutiliza conexões SSH (ControlPersist=60s) para melhor performance.

🧩 Execução via Pipeline (CI/CD)

A pipeline CI/CD executa os mesmos passos de forma automatizada:

  1. Faz login no registro de contêineres (ex.: ACR, ECR, GCR).
  2. Baixa a chave SSH segura da biblioteca de arquivos protegidos.
  3. Prepara o inventário e a configuração SSH.
  4. Valida o hostname remoto antes da execução.
  5. Executa o playbook dentro do container Ansible.

Exemplo de Step (YAML)

- task: Bash@3
  displayName: "Run Ansible Playbook"
  inputs:
    targetType: 'inline'
    script: |
      docker run --rm \
        -v "$(Pipeline.Workspace)/ansible:/workspace"\
        -v "$(Agent.TempDirectory)/cicd-ansible-key.pem:/workspace/ssh_key.pem:ro"\
        --network host\
        myregistry.azurecr.io/toolbox-ubuntu:latest\
        ansible-playbook\
        -i /workspace/inventory/servers.ini\
        -l development\
        --timeout 30\
        --forks 1\
        --private-key /workspace/ssh_key.pem\
        /workspace/playbooks/check-security-updates.yaml
Enter fullscreen mode Exit fullscreen mode

⚠️ Boas Práticas e Cuidados

Tipo Descrição
🔍 Seleção de Hosts Sempre valide o parâmetro -l antes de executar. Ex.: -l staging
Timeouts SSH Use ConnectTimeout=10 e ServerAliveInterval=30 para evitar bloqueios.
🔄 Multiplexação ControlPersist=60s acelera múltiplas conexões consecutivas.
🧱 Permissões O script e o cron são criados como root — revise conforme as políticas de segurança.
🧩 Execução Idempotente Pode ser executado várias vezes, sem impacto negativo.
🧰 Fallback Automático Se o SCP falhar via bastion, o Ansible tenta automaticamente via SFTP.

🧾 Resumo Final

Item Descrição
🔑 Chaves SSH Armazenadas de forma segura (Key Vault / Secret Library)
🧰 Execução Local Via Docker + chave privada montada
☁️ Execução CI/CD Pipeline com container Ansible
🧱 Playbook check-security-updates.yaml
📋 Inventário inventory/servers.ini
🔐 Conexão Segura Bastion via ProxyCommand
⚙️ Timeout e Persistência ConnectTimeout=10, ControlPersist=60s
🟢 Idempotente Sim
🔔 Notificação Integrada com webhook (Discord/Slack/etc.)

🧠 Lições Aprendidas

Lições Aprendidas

  • A multiplexação SSH (ControlPersist) reduz drasticamente o tempo de execução em playbooks grandes.
  • Adicionar timeouts explícitos (ConnectTimeout, ServerAliveInterval) previne travamentos na pipeline.
  • Separar as chaves SSH por contexto (pipeline, bastion, manutenção) aumenta a rastreabilidade e segurança.
  • Validar o hostname remoto antes de executar comandos evita incidentes em ambientes críticos.

✍️ Nem todo o código está nesse post, mas o post trás uma ideia geral de como estrutuar a execução do playbook através de uma pipeline.

Top comments (0)