A execução do playbook termina a verde, a unidade WireGuard está ativa, o handshake funciona e o novo peer continua sem alcançar nada. Ver um playbook a reportar sucesso enquanto o servidor corre a configuração de ontem é especialmente frustrante, porque nada na saída está errado. O Ansible fez exatamente o que lhe foi pedido: garantiu que a unidade estava iniciada, e já estava. Aplicar um ficheiro de configuração reescrito é outra tarefa, e só acontece se algo disser ao serviço para o reler.

Veja o que uma segunda execução relata na realidade

Execute o playbook duas vezes com a configuração alterada e compare o resumo. A segunda execução deve mostrar a tarefa da unidade como ok, e não como changed, porque um serviço já em execução é deixado em paz por design. É a idempotência a funcionar corretamente, e é também a razão pela qual o novo ficheiro nunca chega ao kernel.

Adicione uma execução de verificação com saída diff para ver que tarefas tocariam em alguma coisa.

ansible-playbook -i inventory site.yml --check --diff
systemctl show wg-quick@wg0 -p ActiveEnterTimestamp -p NRestarts
stat -c '%y %n' /etc/wireguard/wg0.conf

Os três carimbos temporais contam a história antes de ler uma única linha de registo. Se o ficheiro de configuração for mais recente do que o momento em que a unidade ficou ativa, a interface em execução foi construída a partir de um ficheiro mais antigo, e todos os sintomas seguintes derivam disso.

Não é um defeito do Ansible. O mesmo comportamento aparece em qualquer ferramenta de gestão de configuração que separa «garante que está a correr» de «garante que está a correr isto». A distinção parece pedante até lhe custar uma tarde.

Iniciado é um estado, não um recarregamento

state: started afirma uma condição sobre a unidade. Não promete que o serviço releia as suas entradas, e não pode saber que um ficheiro que nunca abriu mudou. Numa implementação wg-quick, as entradas incluem a configuração da interface, os endereços, as rotas, as entradas DNS e quaisquer hooks post-up.

Por isso um ficheiro atualizado pode conter um novo peer, outro endereço ou uma diretiva de encaminhamento alterada enquanto a interface ativa continua a servir o conjunto antigo. O handshake ainda tem sucesso porque as chaves e os pontos finais carregados ainda são válidos. É exatamente isso que torna a falha difícil de ver, e por que as pessoas perseguem o DNS durante uma hora antes de verificar um carimbo temporal.

Outras duas opções do módulo são frequentemente confundidas com uma solução. daemon_reload faz o systemd reler os ficheiros de unidade, o que é irrelevante para um ficheiro de configuração do WireGuard. state: reloaded pede à unidade que execute a sua ação de recarregamento, e se isso faz o que precisa depende inteiramente da unidade. Nenhuma delas substitui saber que entradas o processo em execução leu realmente.

Instale o ficheiro e depois notifique um handler

O padrão que mantém a idempotência e ainda aplica alterações é o habitual: a tarefa que escreve o ficheiro notifica um handler, e o handler só corre quando o ficheiro mudou de facto. Uma segunda execução sem edições não faz nada, e é isso que quer.

- name: write the interface configuration
  ansible.builtin.template:
    src: wg0.conf.j2
    dest: /etc/wireguard/wg0.conf
    owner: root
    mode: "0600"
  notify: apply wg0

- name: ensure the interface unit is enabled
  ansible.builtin.systemd_service:
    name: wg-quick@wg0
    enabled: true
    state: started

- name: apply wg0
  listen: apply wg0
  ansible.builtin.systemd_service:
    name: wg-quick@wg0
    state: restarted

Os handlers correm no fim da peça, depois das tarefas que os notificaram, por isso uma tarefa mais adiante na mesma peça pode ainda ver a interface antiga. Se a ordem importa, esvazie os handlers explicitamente com meta: flush_handlers antes da tarefa que depende da nova configuração.

Use restart em vez de reload, a menos que tenha verificado a ação de recarregamento da unidade. A unidade wg-quick de origem suporta uma e sincroniza a configuração do WireGuard, mas não volta a executar a configuração de endereços, rotas, DNS ou hooks. Por isso um reload depois de acrescentar uma diretiva de rota parece ter sucesso e não muda nada.

Compare o dispositivo ativo com o ficheiro

A verificação que fecha esta classe de erros é uma comparação entre o que o kernel detém e o que o ficheiro diz. Dois comandos produzem saída comparável, e a diferença é o desvio.

sudo wg showconf wg0
sudo wg-quick strip wg0
sudo diff <(sudo wg showconf wg0) <(sudo wg-quick strip wg0)

wg showconf imprime o dispositivo ativo, incluindo a chave privada, por isso trate essa saída como um segredo e não a cole num ticket. wg-quick strip imprime o ficheiro sem as chaves específicas do wg-quick, e é por isso que os dois são comparáveis.

Uma diferença na lista de peers significa que o ficheiro nunca foi aplicado. Uma diferença limitada a chaves ou pontos finais que esperava ver mudar significa que algo o aplicou parcialmente. Nenhuma diferença significa que a configuração está atual e a falha está noutro sítio, o que já é um resultado útil.

Pague o custo do reinício de propósito

Reiniciar a interface derruba o túnel, e se esse túnel transporta a sua sessão administrativa, o reinício termina a sessão que o pediu. Coloque o handler atrás de uma janela de alteração, ou execute o playbook a partir de um caminho de gestão que não dependa do reinício da interface.

O custo é a razão pela qual as pessoas recorrem ao reload. É um instinto razoável, e torna-se correto quando sabe que alterações um reload aplica realmente. Para alterações de peers e chaves na unidade wg-quick de origem chega; para endereçamento, encaminhamento e hooks não. Esclarecer isto uma vez sai mais barato do que descobri-lo durante uma janela de alteração.

A documentação do módulo systemd_service explica a diferença entre started, restarted e reloaded, e vale uma leitura em vez de a deduzir de um resumo verde. Tudo neste percurso decorre dessa única distinção.

Artigos relacionados