“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.
Top comments (0)