Ansible は起動済みと言うが、WireGuard は古い設定のまま動いている
state: started は稼働中のユニットに触れないため、書き直した wg0.conf は決して適用されません。変更ハンドラーを追加し、稼働中のデバイスとファイルを比較してください。
Playbook の実行は緑で終わり、WireGuard ユニットはアクティブで、ハンドシェイクも機能しているのに、新しい peer は何にも到達できません。サーバーが昨日の設定で動いているのに playbook が成功を報告するのを見るのは、出力に何も誤りがないだけに、特に歯がゆいものです。Ansible は求められたとおりのことをしました。ユニットが起動していることを確認したのであり、ユニットはすでに起動していました。書き直した設定ファイルを適用するのは別の作業で、何かがサービスに再読み込みを指示したときにだけ起こります。
二度目の実行が実際に何を報告するか見る
変更した設定を置いたまま playbook を二度実行し、サマリーを比較します。二度目はユニットのタスクを changed ではなく ok と表示するはずです。稼働中のサービスは設計上そのままにされるからです。これは正しく機能している冪等性であり、新しいファイルがカーネルに届かない理由でもあります。
diff 出力付きの確認実行を追加して、どのタスクが何かに触れるのかを見ます。
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三つのタイムスタンプが、ログを一行も読む前に事情を語ります。設定ファイルがユニットのアクティブ化より新しいなら、稼働中のインターフェースは古いファイルから構築されており、その後の症状はすべてそこから来ます。
これは Ansible の欠陥ではありません。「動いていることを確かめる」と「これを動かしていることを確かめる」を分ける設定管理ツールならどれでも同じ挙動になります。その区別は、半日を失うまで衒学的に見えます。
起動済みは状態であり、再読み込みではない
state: started はユニットに関する条件を述べるだけです。サービスが入力を読み直すことは約束せず、一度も開いていないファイルが変わったことを知ることもできません。wg-quick の配置では、入力にはインターフェース設定、アドレス、ルート、DNS エントリ、post-up フックが含まれます。
そのため、更新したファイルに新しい peer、別のアドレス、変更したルーティング指示が含まれていても、稼働中のインターフェースは古い集合を提供し続けます。ハンドシェイクが成功するのは、読み込まれた鍵とエンドポイントがまだ有効だからです。それが故障を見えにくくし、人々がタイムスタンプを確認する前に一時間 DNS を追いかける理由です。
モジュールの別の二つの選択肢が解決策と誤解されがちです。daemon_reload は systemd にユニットファイルを再読み込みさせますが、WireGuard の設定ファイルには関係ありません。state: reloaded はユニットに再読み込み動作を実行させますが、それが必要なことをするかはユニット次第です。どちらも 稼働中のプロセスが実際にどの入力を読んだかを知ること の代わりにはなりません。
ファイルを設置し、それからハンドラーに通知する
冪等性を保ちながら変更を適用する型は普通のものです。ファイルを書くタスクがハンドラーに通知し、ハンドラーはファイルが実際に変わったときだけ実行されます。編集のない二度目の実行は何もしません。それが望ましい姿です。
- 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ハンドラーは play の最後、通知したタスクの後に実行されるため、同じ play の後半のタスクはまだ古いインターフェースを見るかもしれません。順序が重要なら、新しい設定に依存するタスクの前に meta: flush_handlers でハンドラーを明示的に流してください。
ユニットの再読み込み動作を確認していない限り、reload ではなく restart を使ってください。上流の wg-quick ユニットは再読み込みに対応し、WireGuard の設定を同期しますが、アドレス、ルート、DNS、フックの設定は再実行しません。したがってルート指示を追加した後の reload は成功したように見えて何も変えません。
稼働中のデバイスとファイルを比較する
この種のバグを閉じる確認は、カーネルが保持している内容とファイルの記述の比較です。二つのコマンドが比較可能な出力を生み、その差がドリフトです。
sudo wg showconf wg0
sudo wg-quick strip wg0
sudo diff <(sudo wg showconf wg0) <(sudo wg-quick strip wg0)wg showconf は秘密鍵を含む稼働中のデバイスを出力するので、その出力は秘密として扱い、チケットに貼らないでください。wg-quick strip は wg-quick 固有の鍵を除いたファイルを出力するので、両者はそもそも比較できるのです。
peer 一覧の違いは、ファイルが一度も適用されなかったことを意味します。変更を想定していた鍵やエンドポイントだけの違いは、何かが部分的に適用したことを意味します。まったく違いがなければ設定は最新で、故障は別の場所にあり、それ自体が有用な結果です。
再起動のコストを意識して払う
インターフェースを再起動するとトンネルが切れ、そのトンネルが管理セッションを運んでいるなら、再起動はそれを要求したセッションを終わらせます。ハンドラーを変更ウィンドウの後ろに置くか、インターフェースの再起動に依存しない管理経路から playbook を実行してください。
そのコストがあるから人々は reload に手を伸ばします。それは合理的な直感で、reload が実際にどの変更を適用するかを知れば正しくなります。上流の wg-quick ユニットでは peer と鍵の変更には十分ですが、アドレス、ルーティング、フックには不十分です。一度整理しておくほうが、変更ウィンドウ中に発見するより安くつきます。
systemd_service モジュールのドキュメント は started、restarted、reloaded の違いを説明しており、緑のサマリーから推測するのではなく一度読む価値があります。この記事のすべてはその一つの区別から来ています。