Como automatizar a inserção de security headers no Nginx usando Ansible — backup, remoção de duplicatas, incremento via AWK e restart em múltiplos hosts de uma vez.
Num projeto recente eu precisava adicionar security headers no Nginx de vários hosts ao mesmo tempo. Fazer isso na mão, host por host, estava fora de questão — qualquer mudança futura viraria uma dor de cabeça. Resolvi automatizar com Ansible, e foi mais simples do que esperava.
O repositório tá disponível no GitHub caso queira já ir direto ao código.
add-headers-nginx
Português
Playbooks Ansible para adição e remoção de security headers no Nginx de forma automatizada e idempotente.
Por que security headers?
Security headers protegem aplicações web contra ataques como XSS, clickjacking e MIME sniffing. São exigidos em auditorias de segurança e compliance (OWASP, PCI-DSS).
Estrutura
add-headers-nginx/
├── nginx.conf # Exemplo de configuração Nginx
└── playbooks/
├── headers_nginx.txt # Headers a serem inseridos
├── add_headers_nginx.yml # Playbook para adicionar headers
└── remove_headers_nginx.yml # Playbook para remover headers
Pré-requisitos
- Ansible instalado
- Acesso sudo ao host alvo
- Nginx instalado
Como usar
1. Configure os paths no playbook conforme seu ambiente:
vars:
PATH_ADD_HEADERS_TXT: /seu/path/playbooks/headers_nginx.txt
PATH_NGINX_CONF: /etc/nginx/nginx.conf
2. Adicionar headers:
ansible-playbook playbooks/add_headers_nginx.yml
3. Remover headers:
ansible-playbook playbooks/remove_headers_nginx.yml
O que o playbook faz
- Faz backup do
nginx.conf(duas cópias de segurança) - Remove headers existentes para evitar duplicação
- Injeta os novos headers após a diretiva
gzip_vary on - Mantém…
O problema
A aplicação web precisava de uma lista de headers específicos para não quebrar em certas páginas:
Strict-Transport-SecurityContent-Security-PolicyX-XSS-ProtectionX-Frame-OptionsX-Content-Type-OptionsReferrer-PolicyPermissions-Policy
Com muitos hosts e a possibilidade de ajustes frequentes nos valores, qualquer processo manual ia escalar mal. Ansible resolve isso.
Etapa 1 — arquivo de headers
Crio um arquivo headers_nginx.txt com todas as diretivas add_header que quero inserir:
add_header Strict-Transport-Security 'max-age=63072000; includeSubDomains; preload';
add_header Content-Security-Policy "connect-src 'self' www.google-analytics.com ...";
add_header X-XSS-Protection "1; mode=block";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy "strict-origin";
add_header Permissions-Policy "geolocation=(self),midi=(self),...";
Esse arquivo é a fonte da verdade. Quando precisar mudar um header, edito aqui e rodo a playbook de novo — sem tocar no nginx.conf manualmente.
Etapa 2 — a Playbook
A playbook tem seis jobs em sequência:
-
Backup do
nginx.conf— duas formas, pelofetche pelocp -
Remoção das linhas
add_headerexistentes — evita duplicidade -
Remoção das linhas
add_headercomentadas com# - Limpeza de linhas em branco que ficam após a remoção
- Inserção dos novos headers via AWK
- Restart do Nginx
- name: Adicionar Headers no Nginx
hosts: localhost
become: yes
become_method: sudo
vars:
PATH_ADD_HEADERS_TXT: /Users/leonanviana/Repos/add_headers_nginx/playbooks/headers_nginx.txt
PATH_NGINX_CONF: /opt/homebrew/etc/nginx/nginx.conf
tasks:
- name: Fazer backup do nginx.conf via fetch
fetch:
src: /opt/homebrew/etc/nginx/nginx.conf
dest: /var/tmp/
flat: yes
become: true
- name: Fazer backup do nginx.conf via cp
shell: cp "{{ PATH_NGINX_CONF }}" /var/tmp/default_nginx_bkp
- name: Remover linhas com add_header
lineinfile:
path: "{{ PATH_NGINX_CONF }}"
state: absent
regexp: '^(\s*add_header\s.*;)$'
- name: Remover linhas add_header comentadas com #
lineinfile:
path: "{{ PATH_NGINX_CONF }}"
state: absent
regexp: '^(\s*#add_header\s.*;)$'
- name: Remover linhas em branco
replace:
path: "{{ PATH_NGINX_CONF }}"
regexp: '^\s*$'
replace: ''
- name: Inserir headers via AWK
shell: >
awk '/gzip_vary on;/ {print; system("cat \"{{ PATH_ADD_HEADERS_TXT }}\""); next} 1'
"{{ PATH_NGINX_CONF }}" > tmpfile && mv tmpfile "{{ PATH_NGINX_CONF }}"
- name: Restart Nginx
service:
name: nginx
state: restarted
Como o AWK funciona aqui
O job de inserção merece atenção. Ele usa awk para localizar a string gzip_vary on; dentro do nginx.conf como ponto de ancoragem. Quando encontra essa linha, imprime ela e logo em seguida injeta todo o conteúdo do headers_nginx.txt. O resultado vai para um arquivo temporário, que substitui o original se tudo correr bem.
Você pode trocar gzip_vary on; por qualquer outra string que já exista no seu nginx.conf — o comportamento é o mesmo.
Fluxo de execução
Com tudo configurado, o fluxo fica assim:
-
nginx.confsem nenhumadd_header - Playbook roda — backup, limpeza, inserção, restart
-
nginx.confcom todos os headers no lugar certo
Se precisar remover os headers depois, criei uma segunda playbook específica para isso — sem precisar editar o arquivo na mão.
Conclusão
Parece simples porque é. O ganho real aparece quando você tem dezenas de hosts: um único ansible-playbook propaga a mudança em todos ao mesmo tempo, com backup automático e sem risco de duplicidade.
O código tá no GitHub — pull requests são bem-vindos.
Top comments (0)