Automação de Infraestrutura DNS com Ansible e Pipeline GitLab CI/CD
Este é um tutorial complementar à nossa série sobre implementação e configuração do DNS BIND no Oracle Linux 9. Nos artigos anteriores, abordamos desde os conceitos fundamentais do DNS até a delegação e gerenciamento de subdomínios. Agora, vamos explorar como automatizar completamente a infraestrutura DNS utilizando Ansible e GitLab CI/CD.
Introdução
A automação de infraestrutura é essencial para ambientes modernos, permitindo maior agilidade, consistência e confiabilidade. Quando aplicada à infraestrutura DNS, a automação reduz erros manuais, facilita a implementação de mudanças e permite a adoção de práticas de Infraestrutura como Código (IaC).
Neste tutorial, vamos explorar como combinar o Ansible, uma poderosa ferramenta de automação, com o GitLab CI/CD, uma plataforma completa de integração e entrega contínua, para criar um fluxo automatizado de gerenciamento de infraestrutura DNS. Abordaremos desde a estruturação do código Ansible até a configuração de pipelines de CI/CD, incluindo validação, testes e implantação controlada.
Ao final deste tutorial, você terá uma solução completa para gerenciar sua infraestrutura DNS como código, com processos automatizados de validação e implantação, seguindo as melhores práticas de DevOps e IaC.
Benefícios da Automação para Infraestrutura DNS
Antes de mergulharmos nos detalhes técnicos, vamos entender os benefícios da automação para infraestrutura DNS:
Consistência e Padronização
- Configurações padronizadas: Todas as instâncias seguem o mesmo padrão
- Eliminação de variações: Reduz problemas causados por configurações inconsistentes
- Aplicação de políticas: Garante que políticas de segurança e boas práticas sejam seguidas
Agilidade e Eficiência
- Implantação rápida: Reduz o tempo para provisionar novos servidores ou zonas
- Resposta a mudanças: Permite implementar alterações rapidamente
- Escalabilidade: Facilita o gerenciamento de infraestruturas grandes e complexas
Confiabilidade e Segurança
- Redução de erros: Minimiza erros humanos em configurações críticas
- Testes automatizados: Valida alterações antes da implantação
- Auditoria e rastreabilidade: Mantém histórico completo de alterações
Colaboração e Governança
- Revisão de código: Permite revisão por pares das alterações
- Aprovações: Implementa fluxos de aprovação para mudanças
- Documentação viva: O código serve como documentação atualizada da infraestrutura
Visão Geral da Solução
Nossa solução combinará:
- Ansible: Para definir a configuração desejada dos servidores DNS
- GitLab: Para armazenar o código e executar pipelines de CI/CD
- Integração: Fluxo completo desde o commit até a produção
Fluxo de Trabalho
O fluxo de trabalho típico será:
- Desenvolvedor faz alterações no código Ansible (configurações, zonas, etc.)
- Commit é enviado para o repositório GitLab
- Pipeline de CI/CD é acionado automaticamente
- Validação sintática e linting são executados
- Testes são realizados em ambiente de desenvolvimento
- Aprovação manual (opcional) para ambientes de produção
- Implantação controlada em produção
- Verificação pós-implantação
+-----------------+ +-----------------+ +-----------------+ +-----------------+
| Desenvolvedor |----->| Repositório Git |----->| Pipeline CI/CD |----->| Servidores DNS |
| (Faz Alterações)| | (GitLab) | | (GitLab Runner) | | (Produção) |
+-------+---------+ +-------+---------+ +-------+---------+ +-------+---------+
| ^ | |
| 1. git commit | 2. git push | 3. Pipeline Triggered | 7. Ansible Aplica |
| git push | | | Configuração |
V | V V
+-------+---------+ +-------+---------+ +-------+---------+ +-------+---------+
| | | | | 4. Validação | | |
| | | | | 5. Testes | | |
| | | | | 6. Implantação | | |
+-----------------+ +-----------------+ +-----------------+ +-----------------+
Figura 1: Fluxo de Trabalho Automatizado com Ansible e GitLab CI/CD
Estrutura do Repositório
Estrutura do Repositório
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
dns-infrastructure/
├── ansible/
│ ├── inventory/
│ │ ├── production/
│ │ │ ├── hosts.yml
│ │ │ └── group_vars/
│ │ └── development/
│ │ ├── hosts.yml
│ │ └── group_vars/
│ ├── roles/
│ │ ├── bind_common/
│ │ ├── bind_primary/
│ │ ├── bind_secondary/
│ │ └── bind_views/
│ ├── playbooks/
│ │ ├── site.yml
│ │ ├── primary.yml
│ │ ├── secondary.yml
│ │ └── views.yml
│ └── templates/
│ ├── named.conf.j2
│ └── zones/
├── zones/
│ ├── example.com.zone
│ └── 200.168.192.in-addr.arpa.zone
├── tests/
│ ├── syntax_check.sh
│ ├── zone_validation.sh
│ └── integration_tests.sh
├── .gitlab-ci.yml
└── README.md
Parte 1: Configuração do Ambiente
Antes de começar, precisamos configurar nosso ambiente de desenvolvimento e as ferramentas necessárias.
Requisitos
- Oracle Linux 9 (ou outra distribuição Linux compatível)
- Ansible 2.9+
- Git
- Acesso a um servidor GitLab (ou gitlab.com)
- Servidores DNS BIND configurados conforme tutoriais anteriores
Instalação do Ansible
1
2
3
4
5
6
# Instalar Ansible no Oracle Linux 9
sudo dnf install oracle-epel-release-el9
sudo dnf install ansible
# Verificar a instalação
ansible --version
Configuração do Git
1
2
3
4
5
6
7
8
9
# Instalar Git
sudo dnf install git
# Configurar Git
git config --global user.name "Seu Nome"
git config --global user.email "seu.email@exemplo.com"
# Gerar chave SSH para GitLab (se necessário)
ssh-keygen -t ed25519 -C "seu.email@exemplo.com"
Inicialização do Repositório
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Criar estrutura de diretórios
mkdir -p dns-infrastructure/{ansible/{inventory/{production,development},roles,playbooks,templates},zones,tests}
# Inicializar repositório Git
cd dns-infrastructure
git init
# Criar arquivo .gitignore
cat > .gitignore << EOF
*.retry
*.swp
*~
.DS_Store
.idea/
.vscode/
__pycache__/
*.pyc
vault_password.txt
EOF
# Commit inicial
git add .gitignore
git commit -m "Inicialização do repositório de infraestrutura DNS"
Conexão com GitLab
1
2
3
4
5
# Adicionar remote do GitLab
git remote add origin git@gitlab.com:seu-usuario/dns-infrastructure.git
# Enviar para GitLab
git push -u origin master
Parte 2: Estruturação do Projeto Ansible
Vamos estruturar nosso projeto Ansible seguindo as melhores práticas.
Inventário
O inventário define os servidores que serão gerenciados pelo Ansible.
Inventário de Produção
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# ansible/inventory/production/hosts.yml
cat > ansible/inventory/production/hosts.yml << EOF
---
all:
children:
dns_servers:
children:
primary:
hosts:
ns1.example.com:
ansible_host: 192.168.200.49
secondary:
hosts:
ns2.example.com:
ansible_host: 192.168.200.50
development_servers:
hosts:
ns1.dev.example.com:
ansible_host: 192.168.200.51
ns2.dev.example.com:
ansible_host: 192.168.200.52
vars:
ansible_user: ansible
ansible_python_interpreter: /usr/bin/python3
EOF
Variáveis de Grupo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# ansible/inventory/production/group_vars/all.yml
mkdir -p ansible/inventory/production/group_vars
cat > ansible/inventory/production/group_vars/all.yml << EOF
---
# Variáveis globais
dns_domain: example.com
dns_admin_email: admin.example.com
dns_ttl: 86400
dns_refresh: 10800
dns_retry: 3600
dns_expire: 604800
dns_minimum: 86400
# Configuração de rede
dns_allow_query:
- localhost
- 192.168.200.0/24
# Configuração de logging
dns_log_dir: /var/log/named
dns_log_versions: 5
dns_log_size: "20m"
# Configuração de segurança
dns_use_tsig: true
dns_tsig_key_name: "xfer-key"
dns_tsig_key_algorithm: "hmac-sha256"
dns_tsig_key_secret: "{{ vault_dns_tsig_key_secret }}" # Armazenado no vault
EOF
# ansible/inventory/production/group_vars/primary.yml
cat > ansible/inventory/production/group_vars/primary.yml << EOF
---
dns_role: primary
dns_zones:
- name: "example.com"
type: "master"
file: "zones/example.com.zone"
- name: "200.168.192.in-addr.arpa"
type: "master"
file: "zones/200.168.192.in-addr.arpa.zone"
EOF
# ansible/inventory/production/group_vars/secondary.yml
cat > ansible/inventory/production/group_vars/secondary.yml << EOF
---
dns_role: secondary
dns_primary_server: 192.168.200.49
dns_zones:
- name: "example.com"
type: "slave"
file: "slaves/example.com.zone"
masters:
- "{{ dns_primary_server }}"
- name: "200.168.192.in-addr.arpa"
type: "slave"
file: "slaves/200.168.192.in-addr.arpa.zone"
masters:
- "{{ dns_primary_server }}"
EOF
Variáveis Sensíveis com Ansible Vault
1
2
# Criar arquivo vault para variáveis sensíveis
ansible-vault create ansible/inventory/production/group_vars/all/vault.yml
Adicione o seguinte conteúdo ao arquivo vault:
1
2
---
vault_dns_tsig_key_secret: "a2xhc2Rqa2xhanNka2xqYWtsc2RqYWtsc2RqbGFrc2o="
Roles Ansible
As roles Ansible organizam o código em unidades reutilizáveis.
Role bind_common
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# Criar estrutura da role
ansible-galaxy role init ansible/roles/bind_common
# Tarefas principais
cat > ansible/roles/bind_common/tasks/main.yml << EOF
---
- name: Instalar pacotes BIND
dnf:
name:
- bind
- bind-utils
state: present
- name: Criar diretórios para zonas
file:
path: "/var/named/{{ item }}"
state: directory
owner: root
group: named
mode: '0750'
loop:
- zones
- data
- name: Criar diretório para logs
file:
path: "{{ dns_log_dir }}"
state: directory
owner: named
group: named
mode: '0770'
- name: Configurar contextos SELinux
sefcontext:
target: "{{ item.target }}"
setype: "{{ item.setype }}"
state: present
loop:
- { target: '/var/named/zones(/.*)?', setype: 'named_zone_t' }
- { target: '/var/named/slaves(/.*)?', setype: 'named_cache_t' }
- { target: '{{ dns_log_dir }}(/.*)?', setype: 'named_log_t' }
- { target: '/var/named/data(/.*)?', setype: 'named_cache_t' }
notify: Restaurar contextos SELinux
- name: Configurar logrotate para BIND
template:
src: named-logrotate.j2
dest: /etc/logrotate.d/named
owner: root
group: root
mode: '0644'
- name: Configurar firewall
firewalld:
service: dns
permanent: yes
state: enabled
notify: Recarregar firewall
EOF
# Handlers
cat > ansible/roles/bind_common/handlers/main.yml << EOF
---
- name: Restaurar contextos SELinux
command: restorecon -Rv /var/named /etc/named.conf {{ dns_log_dir }}
listen: Restaurar contextos SELinux
- name: Reiniciar BIND
systemd:
name: named
state: restarted
listen: Reiniciar BIND
- name: Recarregar BIND
command: rndc reload
listen: Recarregar BIND
- name: Recarregar firewall
command: firewall-cmd --reload
listen: Recarregar firewall
EOF
# Template para logrotate
cat > ansible/roles/bind_common/templates/named-logrotate.j2 << EOF
{{ dns_log_dir }}/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 0640 named named
sharedscripts
postrotate
/usr/bin/systemctl reload named.service > /dev/null 2>&1 || true
endscript
}
EOF
Role bind_primary
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# Criar estrutura da role
ansible-galaxy role init ansible/roles/bind_primary
# Tarefas principais
cat > ansible/roles/bind_primary/tasks/main.yml << EOF
---
- name: Configurar named.conf
template:
src: named.conf.primary.j2
dest: /etc/named.conf
owner: root
group: named
mode: '0640'
notify: Reiniciar BIND
- name: Copiar arquivos de zona
template:
src: "zones/{{ item.name }}.zone.j2"
dest: "/var/named/{{ item.file }}"
owner: root
group: named
mode: '0640'
loop: "{{ dns_zones }}"
when: item.type == 'master'
notify: Recarregar BIND
- name: Habilitar e iniciar serviço BIND
systemd:
name: named
enabled: yes
state: started
EOF
# Template para named.conf
cat > ansible/roles/bind_primary/templates/named.conf.primary.j2 << EOF
// named.conf para servidor primário
options {
directory "/var/named";
listen-on port 53 {
127.0.0.1;
{{ ansible_default_ipv4.address }};
};
listen-on-v6 port 53 { ::1; };
allow-query {
{% for net in dns_allow_query %}
{{ net }};
{% endfor %}
};
recursion yes;
allow-recursion {
{% for net in dns_allow_query %}
{{ net }};
{% endfor %}
};
dnssec-validation auto;
// Logging configuration
logging {
channel general_log {
file "{{ dns_log_dir }}/general.log" versions {{ dns_log_versions }} size {{ dns_log_size }};
severity info;
print-time yes;
print-severity yes;
print-category yes;
};
channel security_log {
file "{{ dns_log_dir }}/security.log" versions {{ dns_log_versions }} size {{ dns_log_size }};
severity info;
print-time yes;
print-severity yes;
print-category yes;
};
channel query_log {
file "{{ dns_log_dir }}/query.log" versions {{ dns_log_versions }} size {{ dns_log_size }};
severity info;
print-time yes;
};
channel error_log {
file "{{ dns_log_dir }}/error.log" versions {{ dns_log_versions }} size {{ dns_log_size }};
severity error;
print-time yes;
print-severity yes;
print-category yes;
};
category default { general_log; };
category general { general_log; };
category security { security_log; };
category queries { query_log; };
category client { general_log; };
category network { general_log; };
category dnssec { security_log; };
category resolver { error_log; };
};
};
// ACLs
acl "dns_servers" {
localhost;
{{ ansible_default_ipv4.address }};
{% for host in groups['secondary'] %}
{{ hostvars[host]['ansible_host'] }};
{% endfor %}
};
{% if dns_use_tsig %}
// TSIG key
key "{{ dns_tsig_key_name }}" {
algorithm {{ dns_tsig_key_algorithm }};
secret "{{ dns_tsig_key_secret }}";
};
{% for host in groups['secondary'] %}
server {{ hostvars[host]['ansible_host'] }} {
keys { {{ dns_tsig_key_name }}; };
};
{% endfor %}
{% endif %}
// Zone definitions
{% for zone in dns_zones %}
zone "{{ zone.name }}" IN {
type {{ zone.type }};
file "{{ zone.file }}";
{% if zone.type == "master" %}
{% if dns_use_tsig %}
allow-transfer { key {{ dns_tsig_key_name }}; };
{% else %}
allow-transfer { dns_servers; };
{% endif %}
also-notify {
{% for host in groups['secondary'] %}
{{ hostvars[host]['ansible_host'] }};
{% endfor %}
};
notify yes;
{% endif %}
};
{% endfor %}
// Include standard zones
include "/etc/named.rfc1912.zones";
// Include DNSSEC root key
include "/etc/named.root.key";
EOF
Role bind_secondary
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# Criar estrutura da role
ansible-galaxy role init ansible/roles/bind_secondary
# Tarefas principais
cat > ansible/roles/bind_secondary/tasks/main.yml << EOF
---
- name: Criar diretório slaves
file:
path: /var/named/slaves
state: directory
owner: named
group: named
mode: '0770'
notify: Restaurar contextos SELinux
- name: Configurar named.conf
template:
src: named.conf.secondary.j2
dest: /etc/named.conf
owner: root
group: named
mode: '0640'
notify: Reiniciar BIND
- name: Habilitar e iniciar serviço BIND
systemd:
name: named
enabled: yes
state: started
EOF
# Template para named.conf
cat > ansible/roles/bind_secondary/templates/named.conf.secondary.j2 << EOF
// named.conf para servidor secundário
options {
directory "/var/named";
listen-on port 53 {
127.0.0.1;
{{ ansible_default_ipv4.address }};
};
listen-on-v6 port 53 { ::1; };
allow-query {
{% for net in dns_allow_query %}
{{ net }};
{% endfor %}
};
recursion yes;
allow-recursion {
{% for net in dns_allow_query %}
{{ net }};
{% endfor %}
};
dnssec-validation auto;
// Logging configuration
logging {
channel general_log {
file "{{ dns_log_dir }}/general.log" versions {{ dns_log_versions }} size {{ dns_log_size }};
severity info;
print-time yes;
print-severity yes;
print-category yes;
};
channel security_log {
file "{{ dns_log_dir }}/security.log" versions {{ dns_log_versions }} size {{ dns_log_size }};
severity info;
print-time yes;
print-severity yes;
print-category yes;
};
channel query_log {
file "{{ dns_log_dir }}/query.log" versions {{ dns_log_versions }} size {{ dns_log_size }};
severity info;
print-time yes;
};
channel error_log {
file "{{ dns_log_dir }}/error.log" versions {{ dns_log_versions }} size {{ dns_log_size }};
severity error;
print-time yes;
print-severity yes;
print-category yes;
};
category default { general_log; };
category general { general_log; };
category security { security_log; };
category queries { query_log; };
category client { general_log; };
category network { general_log; };
category dnssec { security_log; };
category resolver { error_log; };
};
};
// ACLs
acl "dns_servers" {
localhost;
{{ ansible_default_ipv4.address }};
{{ dns_primary_server }};
};
{% if dns_use_tsig %}
// TSIG key
key "{{ dns_tsig_key_name }}" {
algorithm {{ dns_tsig_key_algorithm }};
secret "{{ dns_tsig_key_secret }}";
};
server {{ dns_primary_server }} {
keys { {{ dns_tsig_key_name }}; };
};
{% endif %}
// Zone definitions
{% for zone in dns_zones %}
zone "{{ zone.name }}" IN {
type {{ zone.type }};
{% if zone.type == "slave" %}
masters {
{% for master in zone.masters %}
{{ master }};
{% endfor %}
};
{% endif %}
file "{{ zone.file }}";
};
{% endfor %}
// Include standard zones
include "/etc/named.rfc1912.zones";
// Include DNSSEC root key
include "/etc/named.root.key";
EOF
Playbooks
Os playbooks Ansible definem as tarefas a serem executadas nos servidores.
Playbook Principal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# ansible/playbooks/site.yml
cat > ansible/playbooks/site.yml << EOF
---
- name: Configurar servidores DNS primários
hosts: primary
become: yes
roles:
- bind_common
- bind_primary
- name: Configurar servidores DNS secundários
hosts: secondary
become: yes
roles:
- bind_common
- bind_secondary
EOF
# ansible/playbooks/primary.yml
cat > ansible/playbooks/primary.yml << EOF
---
- name: Configurar servidores DNS primários
hosts: primary
become: yes
roles:
- bind_common
- bind_primary
EOF
# ansible/playbooks/secondary.yml
cat > ansible/playbooks/secondary.yml << EOF
---
- name: Configurar servidores DNS secundários
hosts: secondary
become: yes
roles:
- bind_common
- bind_secondary
EOF
Arquivos de Zona
Os arquivos de zona definem os registros DNS.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# Criar diretório para templates de zona
mkdir -p ansible/roles/bind_primary/templates/zones
# Template para zona example.com
cat > ansible/roles/bind_primary/templates/zones/example.com.zone.j2 << EOF
$TTL {{ dns_ttl }}
@ IN SOA ns1.{{ dns_domain }}. {{ dns_admin_email }}. (
{{ '%Y%m%d01' | strftime }} ; Serial
{{ dns_refresh }} ; Refresh
{{ dns_retry }} ; Retry
{{ dns_expire }} ; Expire
{{ dns_minimum }} ) ; Minimum TTL
IN NS ns1.{{ dns_domain }}.
IN NS ns2.{{ dns_domain }}.
ns1 IN A {{ hostvars[groups['primary'][0]]['ansible_host'] }}
ns2 IN A {{ hostvars[groups['secondary'][0]]['ansible_host'] }}
www IN A {{ hostvars[groups['primary'][0]]['ansible_host'] }}
mail IN A {{ hostvars[groups['primary'][0]]['ansible_host'] }}
@ IN MX 10 mail.{{ dns_domain }}.
; Delegação para dev.example.com
dev IN NS ns1.dev.{{ dns_domain }}.
dev IN NS ns2.dev.{{ dns_domain }}.
ns1.dev IN A {{ hostvars[groups['development_servers'][0]]['ansible_host'] }}
ns2.dev IN A {{ hostvars[groups['development_servers'][1]]['ansible_host'] }}
EOF
# Template para zona reversa
cat > ansible/roles/bind_primary/templates/zones/200.168.192.in-addr.arpa.zone.j2 << EOF
$TTL {{ dns_ttl }}
@ IN SOA ns1.{{ dns_domain }}. {{ dns_admin_email }}. (
{{ '%Y%m%d01' | strftime }} ; Serial
{{ dns_refresh }} ; Refresh
{{ dns_retry }} ; Retry
{{ dns_expire }} ; Expire
{{ dns_minimum }} ) ; Minimum TTL
IN NS ns1.{{ dns_domain }}.
IN NS ns2.{{ dns_domain }}.
; Registros PTR
49 IN PTR ns1.{{ dns_domain }}.
50 IN PTR ns2.{{ dns_domain }}.
EOF
Testes
Vamos criar scripts de teste para validar nossa configuração.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# Script para verificação de sintaxe
cat > tests/syntax_check.sh << EOF
#!/bin/bash
# Verificar sintaxe do Ansible
set -e
echo "Verificando sintaxe do Ansible..."
ansible-playbook ansible/playbooks/site.yml --syntax-check
echo "Verificando lint do Ansible..."
ansible-lint ansible/playbooks/site.yml
echo "Verificação de sintaxe concluída com sucesso."
EOF
chmod +x tests/syntax_check.sh
# Script para validação de zonas
cat > tests/zone_validation.sh << EOF
#!/bin/bash
# Validar arquivos de zona
set -e
echo "Gerando arquivos de zona temporários..."
mkdir -p /tmp/zones
# Gerar arquivos de zona a partir dos templates
for template in ansible/roles/bind_primary/templates/zones/*.zone.j2; do
zone_file="/tmp/zones/\$(basename \$template .j2)"
ansible-playbook -i localhost, -c local ansible/playbooks/generate_zone.yml -e "template=\$template output=\$zone_file" --skip-tags=all
echo "Validando zona \$zone_file..."
named-checkzone \$(basename \$zone_file .zone) \$zone_file
done
echo "Validação de zonas concluída com sucesso."
rm -rf /tmp/zones
EOF
chmod +x tests/zone_validation.sh
# Playbook para gerar zonas
cat > ansible/playbooks/generate_zone.yml << EOF
---
- name: Gerar arquivo de zona para teste
hosts: localhost
connection: local
gather_facts: no
vars:
dns_domain: example.com
dns_admin_email: admin.example.com
dns_ttl: 86400
dns_refresh: 10800
dns_retry: 3600
dns_expire: 604800
dns_minimum: 86400
tasks:
- name: Criar arquivo de zona a partir do template
template:
src: "{{ template }}"
dest: "{{ output }}"
tags: never
EOF
# Script para testes de integração
cat > tests/integration_tests.sh << EOF
#!/bin/bash
# Testes de integração para servidores DNS
set -e
# Definir variáveis
PRIMARY_SERVER="192.168.200.49"
SECONDARY_SERVER="192.168.200.50"
DOMAIN="example.com"
echo "Executando testes de integração..."
# Teste 1: Verificar se o servidor primário responde
echo "Teste 1: Verificar se o servidor primário responde..."
dig @\$PRIMARY_SERVER \$DOMAIN SOA +short
if [ \$? -ne 0 ]; then
echo "Falha: Servidor primário não responde."
exit 1
fi
echo "Sucesso: Servidor primário responde."
# Teste 2: Verificar se o servidor secundário responde
echo "Teste 2: Verificar se o servidor secundário responde..."
dig @\$SECONDARY_SERVER \$DOMAIN SOA +short
if [ \$? -ne 0 ]; then
echo "Falha: Servidor secundário não responde."
exit 1
fi
echo "Sucesso: Servidor secundário responde."
# Teste 3: Verificar se os servidores retornam o mesmo serial
echo "Teste 3: Verificar se os servidores retornam o mesmo serial..."
PRIMARY_SERIAL=\$(dig @\$PRIMARY_SERVER \$DOMAIN SOA +short | awk '{print \$3}')
SECONDARY_SERIAL=\$(dig @\$SECONDARY_SERVER \$DOMAIN SOA +short | awk '{print \$3}')
if [ "\$PRIMARY_SERIAL" != "\$SECONDARY_SERIAL" ]; then
echo "Falha: Seriais diferentes. Primário: \$PRIMARY_SERIAL, Secundário: \$SECONDARY_SERIAL"
exit 1
fi
echo "Sucesso: Seriais iguais (\$PRIMARY_SERIAL)."
# Teste 4: Verificar registros específicos
echo "Teste 4: Verificar registros específicos..."
dig @\$PRIMARY_SERVER www.\$DOMAIN A +short
if [ \$? -ne 0 ]; then
echo "Falha: Não foi possível resolver www.\$DOMAIN."
exit 1
fi
echo "Sucesso: Registro www.\$DOMAIN encontrado."
echo "Todos os testes de integração concluídos com sucesso."
EOF
chmod +x tests/integration_tests.sh
Parte 3: Configuração do GitLab CI/CD
Agora, vamos configurar o pipeline de CI/CD no GitLab.
Arquivo .gitlab-ci.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
cat > .gitlab-ci.yml << EOF
---
stages:
- validate
- test
- deploy_dev
- deploy_prod
variables:
ANSIBLE_CONFIG: ansible/ansible.cfg
# Validação de sintaxe e lint
validate:
stage: validate
image: python:3.9-slim
before_script:
- apt-get update && apt-get install -y bind9utils
- pip install ansible ansible-lint
script:
- ansible-playbook ansible/playbooks/site.yml --syntax-check
- ansible-lint ansible/playbooks/site.yml
tags:
- docker
# Validação de zonas
validate_zones:
stage: validate
image: ubuntu:22.04
before_script:
- apt-get update && apt-get install -y bind9utils python3-pip
- pip3 install ansible jinja2
script:
- mkdir -p /tmp/zones
- for template in ansible/roles/bind_primary/templates/zones/*.zone.j2; do
zone_file="/tmp/zones/\$(basename \$template .j2)";
ansible-playbook -i localhost, -c local ansible/playbooks/generate_zone.yml -e "template=\$template output=\$zone_file" --skip-tags=all;
named-checkzone \$(basename \$zone_file .zone) \$zone_file;
done
tags:
- docker
# Testes em ambiente de desenvolvimento
test_dev:
stage: test
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval \$(ssh-agent -s)
- echo "\$SSH_PRIVATE_KEY" | tr -d '\\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo -e "Host *\\n\\tStrictHostKeyChecking no\\n\\n" > ~/.ssh/config
script:
- ansible-playbook -i ansible/inventory/development/hosts.yml ansible/playbooks/site.yml --check
environment:
name: development
tags:
- shell
only:
- master
- development
# Implantação em ambiente de desenvolvimento
deploy_dev:
stage: deploy_dev
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval \$(ssh-agent -s)
- echo "\$SSH_PRIVATE_KEY" | tr -d '\\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo -e "Host *\\n\\tStrictHostKeyChecking no\\n\\n" > ~/.ssh/config
script:
- ansible-playbook -i ansible/inventory/development/hosts.yml ansible/playbooks/site.yml
environment:
name: development
tags:
- shell
only:
- master
- development
# Implantação em ambiente de produção
deploy_prod:
stage: deploy_prod
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval \$(ssh-agent -s)
- echo "\$SSH_PRIVATE_KEY" | tr -d '\\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo -e "Host *\\n\\tStrictHostKeyChecking no\\n\\n" > ~/.ssh/config
script:
- ansible-playbook -i ansible/inventory/production/hosts.yml ansible/playbooks/site.yml
environment:
name: production
when: manual
tags:
- shell
only:
- master
EOF
Configuração de Variáveis no GitLab
No GitLab, configure as seguintes variáveis:
- Acesse Settings > CI/CD > Variables
- Adicione as seguintes variáveis:
SSH_PRIVATE_KEY
: Chave privada SSH para acessar os servidoresANSIBLE_VAULT_PASSWORD
: Senha para descriptografar o Ansible Vault
Configuração de Runners
Para executar os jobs do pipeline, você precisa configurar GitLab Runners:
- Acesse Settings > CI/CD > Runners
- Siga as instruções para instalar e registrar um runner
- Configure tags apropriadas para os runners (docker, shell)
Parte 4: Fluxo de Trabalho Completo
Agora que temos toda a estrutura configurada, vamos explorar o fluxo de trabalho completo para gerenciar a infraestrutura DNS.
Fluxo para Alterações em Zonas
- Clonar o repositório:
1 2
git clone git@gitlab.com:seu-usuario/dns-infrastructure.git cd dns-infrastructure
- Criar uma branch para a alteração:
1
git checkout -b feature/add-new-record
- Editar o template de zona:
1 2 3 4 5
# Editar o template da zona vi ansible/roles/bind_primary/templates/zones/example.com.zone.j2 # Adicionar um novo registro # app IN A 192.168.200.100
- Testar localmente:
1 2 3 4 5 6 7
# Verificar sintaxe ansible-playbook ansible/playbooks/site.yml --syntax-check # Gerar e verificar zona mkdir -p /tmp/zones ansible-playbook -i localhost, -c local ansible/playbooks/generate_zone.yml -e "template=ansible/roles/bind_primary/templates/zones/example.com.zone.j2 output=/tmp/zones/example.com.zone" --skip-tags=all named-checkzone example.com /tmp/zones/example.com.zone
- Commit e push:
1 2 3
git add ansible/roles/bind_primary/templates/zones/example.com.zone.j2 git commit -m "Adicionar registro app.example.com" git push origin feature/add-new-record
- Criar Merge Request:
- Acesse o GitLab e crie um Merge Request
- Atribua revisores
- Aguarde a execução do pipeline
- Revisão e aprovação:
- Revisores verificam as alterações
- Pipeline de CI/CD valida a sintaxe e as zonas
- Aprovação do Merge Request
- Merge para master:
- Merge da branch para master
- Pipeline de CI/CD é acionado automaticamente
- Implantação automática em desenvolvimento
- Aprovação manual para produção
Fluxo para Alterações em Configuração
- Clonar o repositório:
1 2
git clone git@gitlab.com:seu-usuario/dns-infrastructure.git cd dns-infrastructure
- Criar uma branch para a alteração:
1
git checkout -b feature/update-logging
- Editar a configuração:
1 2 3 4 5 6
# Editar as variáveis de grupo vi ansible/inventory/production/group_vars/all.yml # Atualizar configuração de logging # dns_log_versions: 10 # dns_log_size: "50m"
- Testar localmente:
1 2
# Verificar sintaxe ansible-playbook ansible/playbooks/site.yml --syntax-check
- Commit e push:
1 2 3
git add ansible/inventory/production/group_vars/all.yml git commit -m "Aumentar retenção de logs" git push origin feature/update-logging
- Seguir o mesmo processo de Merge Request e aprovação
Fluxo para Adição de Novo Servidor
- Clonar o repositório:
1 2
git clone git@gitlab.com:seu-usuario/dns-infrastructure.git cd dns-infrastructure
- Criar uma branch para a alteração:
1
git checkout -b feature/add-new-server
- Atualizar o inventário:
1 2 3 4 5 6
# Editar o inventário vi ansible/inventory/production/hosts.yml # Adicionar novo servidor # ns3.example.com: # ansible_host: 192.168.200.53
Atualizar templates e configurações conforme necessário
- Testar localmente:
1 2
# Verificar sintaxe ansible-playbook ansible/playbooks/site.yml --syntax-check
- Commit e push:
1 2 3
git add ansible/inventory/production/hosts.yml git commit -m "Adicionar novo servidor DNS secundário" git push origin feature/add-new-server
- Seguir o mesmo processo de Merge Request e aprovação
Parte 5: Melhores Práticas e Considerações Avançadas
Segurança
Gerenciamento de Segredos
- Ansible Vault: Use para criptografar variáveis sensíveis
1 2 3 4 5 6 7 8
# Criptografar arquivo ansible-vault encrypt ansible/inventory/production/group_vars/all/vault.yml # Editar arquivo criptografado ansible-vault edit ansible/inventory/production/group_vars/all/vault.yml # Executar playbook com arquivo criptografado ansible-playbook -i ansible/inventory/production/hosts.yml ansible/playbooks/site.yml --ask-vault-pass
- GitLab CI/CD Variables: Use para armazenar segredos no GitLab
- Defina variáveis como protegidas e mascaradas
- Limite o escopo a branches específicas
Controle de Acesso
- Permissões do GitLab: Configure permissões adequadas para o repositório
- Desenvolvedores: Podem criar branches e MRs
- Maintainers: Podem aprovar MRs
- Owners: Podem gerenciar configurações do repositório
- Proteção de Branches: Configure proteção para branches importantes
- Exija aprovação para merge em master
- Exija pipeline de CI/CD bem-sucedido
Monitoramento e Alertas
Integração com Sistemas de Monitoramento
- Prometheus e Grafana: Configure para monitorar servidores DNS ```yaml
Exemplo de configuração do Prometheus
- job_name: ‘bind_exporter’ static_configs:
- targets: [‘ns1.example.com:9119’, ‘ns2.example.com:9119’] ```
- job_name: ‘bind_exporter’ static_configs:
- Alertas: Configure alertas para problemas críticos ```yaml
Exemplo de regra de alerta
groups:
- name: dns_alerts rules:
- alert: DNSServerDown expr: up{job=”bind_exporter”} == 0 for: 5m labels: severity: critical annotations: summary: “DNS server down” description: “DNS server {{ $labels.instance }} has been down for more than 5 minutes.” ```
- name: dns_alerts rules:
Integração com Sistemas de Tickets
- GitLab Issues: Use para rastrear problemas e melhorias
- Crie templates para diferentes tipos de issues
- Vincule issues a MRs
- Webhooks: Configure webhooks para integração com sistemas externos
1 2 3 4
# Exemplo de configuração de webhook no GitLab # Settings > Webhooks # URL: https://jira.example.com/webhook # Trigger: Merge requests, Pipeline events
Automação Avançada
Automação de Rotinas
- Renovação de DNSSEC: Automatize a renovação de chaves DNSSEC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
# Criar script para renovação cat > scripts/renew_dnssec.sh << EOF #!/bin/bash # Script para renovação de chaves DNSSEC # Gerar novas chaves dnssec-keygen -a ECDSAP256SHA256 -b 256 -n ZONE example.com dnssec-keygen -a ECDSAP256SHA256 -b 256 -f KSK -n ZONE example.com # Atualizar zona dnssec-signzone -A -3 $(head -c 16 /dev/random | od -v -t x | head -1 | cut -d' ' -f2- | tr -d ' ') -N INCREMENT -o example.com -t /var/named/zones/example.com.zone # Recarregar BIND rndc reload example.com EOF chmod +x scripts/renew_dnssec.sh # Adicionar ao crontab echo "0 0 1 * * /path/to/scripts/renew_dnssec.sh" | sudo tee -a /etc/crontab
- Backup Automatizado: Configure backups automáticos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
# Criar script de backup cat > scripts/backup_dns.sh << EOF #!/bin/bash # Script para backup de configuração DNS BACKUP_DIR="/backup/dns" DATE=$(date +%Y%m%d) # Criar diretório de backup mkdir -p $BACKUP_DIR # Backup de configuração tar -czf $BACKUP_DIR/dns_config_$DATE.tar.gz /etc/named* /var/named/zones # Manter apenas os últimos 30 backups find $BACKUP_DIR -name "dns_config_*.tar.gz" -type f -mtime +30 -delete EOF chmod +x scripts/backup_dns.sh # Adicionar ao crontab echo "0 2 * * * /path/to/scripts/backup_dns.sh" | sudo tee -a /etc/crontab
Integração com Outros Sistemas
- DHCP: Integre com servidores DHCP para atualizações dinâmicas
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# Configuração no named.conf zone "example.com" IN { type master; file "zones/example.com.zone"; allow-update { key dhcp-key; }; }; # Configuração no dhcpd.conf key dhcp-key { algorithm hmac-sha256; secret "a2xhc2Rqa2xhanNka2xqYWtsc2RqYWtsc2RqbGFrc2o="; }; zone example.com. { primary 192.168.200.49; key dhcp-key; }
- IPAM: Integre com sistemas de gerenciamento de endereços IP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
# Exemplo de script para sincronização com IPAM import requests import json # Obter dados do IPAM response = requests.get('https://ipam.example.com/api/subnets/1/addresses', headers={'Authorization': 'Token abc123'}) addresses = response.json() # Gerar arquivo de zona with open('example.com.zone', 'w') as f: f.write('$TTL 86400\n') f.write('@ IN SOA ns1.example.com. admin.example.com. (\n') f.write(' 2025052501 ; Serial\n') f.write(' 10800 ; Refresh\n') f.write(' 3600 ; Retry\n') f.write(' 604800 ; Expire\n') f.write(' 86400 ) ; Minimum TTL\n\n') f.write(' IN NS ns1.example.com.\n') f.write(' IN NS ns2.example.com.\n\n') for address in addresses: if 'hostname' in address and address['hostname']: f.write(f"{address['hostname']} IN A {address['ip']}\n")
Documentação e Conhecimento
Documentação Automatizada
- README.md: Mantenha um README detalhado e atualizado
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
# Exemplo de README.md cat > README.md << EOF # Infraestrutura DNS Este repositório contém a configuração da infraestrutura DNS gerenciada com Ansible e GitLab CI/CD. ## Estrutura - \`ansible/\`: Configuração Ansible - \`inventory/\`: Inventários para diferentes ambientes - \`roles/\`: Roles Ansible - \`playbooks/\`: Playbooks Ansible - \`zones/\`: Arquivos de zona - \`tests/\`: Scripts de teste ## Fluxo de Trabalho 1. Clone o repositório 2. Crie uma branch para sua alteração 3. Faça as alterações necessárias 4. Teste localmente 5. Commit e push 6. Crie um Merge Request 7. Aguarde revisão e aprovação ## Ambientes - **Desenvolvimento**: Implantação automática após merge - **Produção**: Implantação manual após aprovação ## Contato Para dúvidas ou problemas, entre em contato com a equipe de infraestrutura. EOF
- Wiki do GitLab: Use para documentação mais detalhada
- Crie páginas para procedimentos comuns
- Documente arquitetura e decisões de design
- Mantenha um registro de alterações importantes
Treinamento e Onboarding
- Guias de Onboarding: Crie guias para novos membros da equipe
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
# Exemplo de guia de onboarding cat > docs/onboarding.md << EOF # Guia de Onboarding para Infraestrutura DNS Este guia ajudará novos membros da equipe a começar a trabalhar com a infraestrutura DNS. ## Pré-requisitos - Acesso ao GitLab - Conhecimento básico de Git - Conhecimento básico de Ansible - Conhecimento básico de DNS ## Primeiros Passos 1. Clone o repositório 2. Configure seu ambiente local 3. Familiarize-se com a estrutura do projeto 4. Faça uma alteração simples para praticar o fluxo de trabalho ## Recursos - [Documentação do BIND](https://www.isc.org/bind/) - [Documentação do Ansible](https://docs.ansible.com/) - [Documentação do GitLab CI/CD](https://docs.gitlab.com/ee/ci/) ## Contato Para dúvidas ou problemas, entre em contato com a equipe de infraestrutura. EOF
Conclusão
Neste tutorial, exploramos como automatizar completamente a infraestrutura DNS utilizando Ansible para gerenciamento de configuração e GitLab CI/CD para implementar pipelines de entrega contínua. Abordamos desde a estruturação do projeto Ansible até a configuração de pipelines de CI/CD, incluindo validação, testes e implantação controlada.
A combinação de Ansible e GitLab CI/CD oferece uma solução robusta para gerenciar infraestrutura DNS como código, seguindo as melhores práticas de DevOps e IaC. Com esta abordagem, você pode:
- Garantir consistência e padronização em toda a infraestrutura DNS
- Implementar alterações de forma rápida e confiável
- Validar e testar alterações antes da implantação
- Manter um histórico completo de alterações
- Colaborar efetivamente com outros membros da equipe
Ao adotar estas práticas, você estará preparado para gerenciar infraestruturas DNS complexas e de grande escala, com maior eficiência, segurança e confiabilidade.
Referências e Recursos Adicionais
- Documentação oficial do BIND
- Documentação do Ansible
- Documentação do GitLab CI/CD
- Ansible for DevOps - Jeff Geerling
- Infrastructure as Code - Kief Morris
- DNS and BIND, 5th Edition - Cricket Liu & Paul Albitz
- GitLab CI/CD Pipeline Configuration Reference
- Ansible Galaxy - Repositório de roles Ansible
- Ansible Best Practices