This content originally appeared on DEV Community and was authored by Filipi Souza
“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)
- A pipeline ou execução local inicia a verificação.
- A chave SSH segura é baixada de um Key Vault ou Secure Files da ferramenta de CI/CD.
- Um container Docker (com Ansible e dependências pré-instaladas) é iniciado.
- O Ansible conecta-se ao bastion, que faz o proxy das conexões até os hosts alvo.
- 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
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>
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>"
'
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
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
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:
- Faz login no registro de contêineres (ex.: ACR, ECR, GCR).
- Baixa a chave SSH segura da biblioteca de arquivos protegidos.
- Prepara o inventário e a configuração SSH.
- Valida o hostname remoto antes da execução.
- 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
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.
This content originally appeared on DEV Community and was authored by Filipi Souza
Seleção de Hosts
Timeouts SSH
Multiplexação
Conexão Segura
Idempotente
Notificação
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.