From 80093037a638af16dac399e8441c0974d725d12b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20D=C3=A9siles?= <1536672+cdesiles@users.noreply.github.com> Date: Sat, 13 Dec 2025 23:49:39 +0100 Subject: [PATCH] chore: replace ntpd by chrony --- roles/ntp-chrony/README.md | 92 ++++++++++++++++++++ roles/ntp-chrony/defaults/main.yml | 58 ++++++++++++ roles/ntp-chrony/handlers/main.yml | 5 ++ roles/ntp-chrony/tasks/main.yml | 92 ++++++++++++++++++++ roles/ntp-chrony/templates/chrony.conf.j2 | 54 ++++++++++++ roles/ntp-chrony/templates/logrotate.conf.j2 | 17 ++++ roles/ntp-chrony/vars/archlinux.yml | 6 ++ roles/ntp-chrony/vars/debian.yml | 6 ++ roles/ntpd/defaults/main.yml | 24 ----- roles/ntpd/handlers/main.yml | 6 -- roles/ntpd/tasks/main.yml | 53 ----------- roles/ntpd/templates/ntp.conf.j2 | 21 ----- 12 files changed, 330 insertions(+), 104 deletions(-) create mode 100644 roles/ntp-chrony/README.md create mode 100644 roles/ntp-chrony/defaults/main.yml create mode 100644 roles/ntp-chrony/handlers/main.yml create mode 100644 roles/ntp-chrony/tasks/main.yml create mode 100644 roles/ntp-chrony/templates/chrony.conf.j2 create mode 100644 roles/ntp-chrony/templates/logrotate.conf.j2 create mode 100644 roles/ntp-chrony/vars/archlinux.yml create mode 100644 roles/ntp-chrony/vars/debian.yml delete mode 100644 roles/ntpd/defaults/main.yml delete mode 100644 roles/ntpd/handlers/main.yml delete mode 100644 roles/ntpd/tasks/main.yml delete mode 100644 roles/ntpd/templates/ntp.conf.j2 diff --git a/roles/ntp-chrony/README.md b/roles/ntp-chrony/README.md new file mode 100644 index 0000000..429b89e --- /dev/null +++ b/roles/ntp-chrony/README.md @@ -0,0 +1,92 @@ +# Chrony NTP Client/Server + +Modern NTP implementation for time synchronization. Chrony is designed for systems with intermittent network connectivity and is the default NTP client on modern Linux distributions. + +## Features + +- Fast initial time synchronization +- Client mode: sync time from NTP pools/servers +- Server mode: serve time to local network clients +- Automatic conflict resolution with systemd-timesyncd and ntpd +- Firewall integration for server mode + +## Usage + +### Client-only mode (default) + +Sync time from public NTP pools, don't serve time to others: + +```yaml +# host_vars/example.yml +ntp_timezone: "Europe/Paris" +ntp_pools: + - "0.fr.pool.ntp.org" + - "1.fr.pool.ntp.org" + - "2.fr.pool.ntp.org" + - "3.fr.pool.ntp.org" +``` + +### Server mode + +Serve time to local network: + +```yaml +# host_vars/ntp-server.yml +ntp_timezone: "UTC" +ntp_server_enabled: true +ntp_allowed_networks: + - 192.168.1.0/24 # Configures both chrony and firewall + - 192.168.27.0/27 +``` + +### Client syncing from local server + +```yaml +# host_vars/client.yml +ntp_pools: [] # Don't use public pools +ntp_servers: + - server: ntp.local.lan + options: iburst prefer + - server: 192.168.1.1 + options: iburst +``` + +## Logging + +**Default: journald** - Logs to systemd journal (recommended) + +```bash +# View chrony logs +journalctl -u chronyd -f +``` + +**Optional: File-based logging** with automatic rotation + +```yaml +ntp_log_backend: file +ntp_log_measurements: true +ntp_log_statistics: true +ntp_log_tracking: true +``` + +## Variables + +See [defaults/main.yml](defaults/main.yml) for all configuration options. + +## Verification + +```bash +# Check chrony status +chronyc tracking + +# Show current time sources +chronyc sources + +# Show detailed source stats +chronyc sourcestats +``` + +## Resources + +- [Arch Wiki: Chrony](https://wiki.archlinux.org/title/Chrony) +- [Chrony Documentation](https://chrony.tuxfamily.org/documentation.html) diff --git a/roles/ntp-chrony/defaults/main.yml b/roles/ntp-chrony/defaults/main.yml new file mode 100644 index 0000000..84465b7 --- /dev/null +++ b/roles/ntp-chrony/defaults/main.yml @@ -0,0 +1,58 @@ +--- +# NTP pools/servers to sync from +ntp_pools: + - "0.arch.pool.ntp.org" + - "1.arch.pool.ntp.org" + - "2.arch.pool.ntp.org" + - "3.arch.pool.ntp.org" + +# NTP servers (use pools instead for most cases) +ntp_servers: [] +# Example: +# ntp_servers: +# - server: time.example.com +# options: iburst + +# System timezone +ntp_timezone: "UTC" + +# Enable NTP server functionality (allow others to sync from this server) +ntp_server_enabled: false + +# NTP server port (standard is 123) +ntp_port: 123 + +# Networks allowed to query this NTP server (when server mode is enabled) +# Used for both chrony config and firewall rules +ntp_allowed_networks: [] +# Example: +# ntp_allowed_networks: +# - 192.168.1.0/24 +# - 192.168.27.0/27 + +# Maximum clock step allowed (0 = allow any step) +ntp_makestep_threshold: 1.0 +ntp_makestep_limit: 3 + +# Enable hardware timestamping for better accuracy +ntp_hwtimestamp: false + +# Drift file location +ntp_driftfile: /var/lib/chrony/drift + +# RTC (hardware clock) sync +ntp_rtcsync: true + +# Logging backend: 'journald' (systemd journal) or 'file' (traditional log files) +ntp_log_backend: journald + +# File backend settings (only used when ntp_log_backend: file) +ntp_logdir: /var/log/chrony +ntp_log_measurements: false +ntp_log_statistics: false +ntp_log_tracking: false + +# Logrotate configuration (only used when ntp_log_backend: file) +ntp_logrotate_rotate: 14 # Keep 14 days of logs +ntp_logrotate_frequency: daily # daily|weekly|monthly +ntp_logrotate_compress: true # Compress rotated logs diff --git a/roles/ntp-chrony/handlers/main.yml b/roles/ntp-chrony/handlers/main.yml new file mode 100644 index 0000000..80fe9dd --- /dev/null +++ b/roles/ntp-chrony/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: Restart chrony + ansible.builtin.systemd: + name: "{{ ntp_service }}" + state: restarted diff --git a/roles/ntp-chrony/tasks/main.yml b/roles/ntp-chrony/tasks/main.yml new file mode 100644 index 0000000..813914d --- /dev/null +++ b/roles/ntp-chrony/tasks/main.yml @@ -0,0 +1,92 @@ +--- +- name: Load OS-specific variables + ansible.builtin.include_vars: "{{ item }}" + with_first_found: + - "{{ ansible_facts['os_family'] }}.yml" + - "debian.yml" + +- name: Install chrony + ansible.builtin.package: + name: chrony + state: present + +- name: Set system timezone + community.general.timezone: + name: "{{ ntp_timezone }}" + notify: Restart chrony + +- name: Ensure chrony drift file directory exists + ansible.builtin.file: + path: "{{ ntp_driftfile | dirname }}" + state: directory + owner: "{{ ntp_user }}" + group: "{{ ntp_group }}" + mode: "0755" + +- name: Ensure chrony log directory exists + ansible.builtin.file: + path: "{{ ntp_logdir }}" + state: directory + owner: root + group: root + mode: "0755" + when: ntp_log_backend == 'file' + +- name: Disable conflicting systemd-timesyncd service + ansible.builtin.systemd: + name: systemd-timesyncd + enabled: false + state: stopped + failed_when: false + +- name: Disable conflicting ntpd service + ansible.builtin.systemd: + name: ntpd + enabled: false + state: stopped + failed_when: false + +- name: Deploy chrony configuration + ansible.builtin.template: + src: chrony.conf.j2 + dest: "{{ ntp_config_path }}" + owner: root + group: root + mode: "0644" + notify: Restart chrony + +- name: Deploy logrotate configuration for chrony + ansible.builtin.template: + src: logrotate.conf.j2 + dest: /etc/logrotate.d/chrony + owner: root + group: root + mode: "0644" + when: ntp_log_backend == 'file' + +- name: Remove logrotate configuration when using journald + ansible.builtin.file: + path: /etc/logrotate.d/chrony + state: absent + when: ntp_log_backend == 'journald' + +- name: Enable and start chrony service + ansible.builtin.systemd: + name: "{{ ntp_service }}" + enabled: true + state: started + +- name: Setup firewall rules for NTP server + community.general.ufw: + rule: allow + port: "{{ ntp_port }}" + proto: udp + src: "{{ item }}" + direction: in + comment: "NTP server (chrony)" + loop: "{{ ntp_allowed_networks }}" + when: ntp_server_enabled and ntp_allowed_networks | length > 0 + retries: 5 + delay: 2 + register: ufw_result + until: ufw_result is succeeded diff --git a/roles/ntp-chrony/templates/chrony.conf.j2 b/roles/ntp-chrony/templates/chrony.conf.j2 new file mode 100644 index 0000000..d3b314a --- /dev/null +++ b/roles/ntp-chrony/templates/chrony.conf.j2 @@ -0,0 +1,54 @@ +# {{ ansible_managed }} +# Chrony configuration file + +# NTP pools - use multiple pools for redundancy +{% for pool in ntp_pools %} +pool {{ pool }} iburst +{% endfor %} + +# NTP servers (if configured) +{% for server in ntp_servers %} +server {{ server.server }} {{ server.options | default('iburst') }} +{% endfor %} + +# Record the rate at which the system clock gains/loses time +driftfile {{ ntp_driftfile }} + +# Allow the system clock to be stepped in the first few updates if offset is large +makestep {{ ntp_makestep_threshold }} {{ ntp_makestep_limit }} + +{% if ntp_rtcsync %} +# Enable kernel synchronization of the real-time clock (RTC) +rtcsync +{% endif %} + +{% if ntp_hwtimestamp %} +# Enable hardware timestamping on all interfaces that support it +hwtimestamp * +{% endif %} + +# Serve time to clients (when server mode is enabled) +{% if ntp_server_enabled %} +# Listen on all interfaces for NTP requests +port {{ ntp_port }} + +# Allow NTP client access from configured networks +{% for network in ntp_allowed_networks %} +allow {{ network }} +{% endfor %} +{% else %} +# Client-only mode: don't listen on NTP port +port 0 + +# Deny all client access (client-only mode) +deny all +{% endif %} + +{% if ntp_log_backend == 'file' %} +# File-based logging (managed with logrotate) +logdir {{ ntp_logdir }} +log {{ [ntp_log_measurements and 'measurements', ntp_log_statistics and 'statistics', ntp_log_tracking and 'tracking'] | select | join(' ') }} +{% else %} +# Using journald/syslog for logging (default on systemd systems) +# View logs with: journalctl -u chronyd +{% endif %} diff --git a/roles/ntp-chrony/templates/logrotate.conf.j2 b/roles/ntp-chrony/templates/logrotate.conf.j2 new file mode 100644 index 0000000..53c4d66 --- /dev/null +++ b/roles/ntp-chrony/templates/logrotate.conf.j2 @@ -0,0 +1,17 @@ +# {{ ansible_managed }} +# Logrotate configuration for chrony logs + +{{ ntp_logdir }}/*.log { + {{ ntp_logrotate_frequency }} + rotate {{ ntp_logrotate_rotate }} + missingok + notifempty + {% if ntp_logrotate_compress %} + compress + delaycompress + {% endif %} + sharedscripts + postrotate + /usr/bin/killall -HUP chronyd 2>/dev/null || true + endscript +} diff --git a/roles/ntp-chrony/vars/archlinux.yml b/roles/ntp-chrony/vars/archlinux.yml new file mode 100644 index 0000000..d09dc34 --- /dev/null +++ b/roles/ntp-chrony/vars/archlinux.yml @@ -0,0 +1,6 @@ +--- +# Arch Linux specific variables +ntp_user: chrony +ntp_group: chrony +ntp_service: chronyd +ntp_config_path: /etc/chrony.conf diff --git a/roles/ntp-chrony/vars/debian.yml b/roles/ntp-chrony/vars/debian.yml new file mode 100644 index 0000000..1ef897f --- /dev/null +++ b/roles/ntp-chrony/vars/debian.yml @@ -0,0 +1,6 @@ +--- +# Debian/Ubuntu specific variables +ntp_user: _chrony +ntp_group: _chrony +ntp_service: chrony +ntp_config_path: /etc/chrony/chrony.conf diff --git a/roles/ntpd/defaults/main.yml b/roles/ntpd/defaults/main.yml deleted file mode 100644 index fce7dd9..0000000 --- a/roles/ntpd/defaults/main.yml +++ /dev/null @@ -1,24 +0,0 @@ ---- -# NTP configuration file -ntp_config_file: "/etc/ntp.conf" - -# NTP servers to use. -ntp_pools: -" 0.uk.pool.ntp.org" -" 1.uk.pool.ntp.org" -" 2.uk.pool.ntp.org" -" 3.uk.pool.ntp.org" - -# System timezone -ntp_timezone: "Europe/London" - -# NTP drift file location -# (keeps track of your clock's time deviation) -ntp_drift_file: "/var/lib/ntp/ntp.drift" - -# NTP security restrictions -ntp_restrict: "kod nomodify notrap nopeer noquery limited" - -# Networks allowed to query this ntpd server -ntp_allowed_networks: - - "127.0.0.1" - - "::1" -# - "192.168.1.0 mask 255.255.255.0" - -ntp_port: 123 diff --git a/roles/ntpd/handlers/main.yml b/roles/ntpd/handlers/main.yml deleted file mode 100644 index ab3ae61..0000000 --- a/roles/ntpd/handlers/main.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -- name: "Restart ntpd service" - ansible.builtin.systemd: - name: ntpd - state: restarted - daemon_reload: true diff --git a/roles/ntpd/tasks/main.yml b/roles/ntpd/tasks/main.yml deleted file mode 100644 index 1823ce3..0000000 --- a/roles/ntpd/tasks/main.yml +++ /dev/null @@ -1,53 +0,0 @@ ---- -- name: Install NTP package - ansible.builtin.package: - name: "ntp" - state: present - update_cache: true - -- name: Set system timezone to {{ ntp_timezone }}" - community.general.timezone: - name: "{{ ntp_timezone }}" - notify: "Restart ntpd service" - -- name: Ensure NTP drift file directory exists - ansible.builtin.file: - path: "{{ ntp_drift_file | dirname }}" - state: directory - owner: "ntp" - group: "ntp" - mode: "0750" - -- name: Setup systems timezone - community.general.timezone: - name: "{{ ntp_timezone }}" - notify: Restart chronyd # Redémarrer chrony peut être utile après un changement de TZ pour qu'il la prenne bien en compte dans ses logs/opérations - -- name: "Configure {{ ntp_config_file }}" - ansible.builtin.template: - src: "ntp.conf.j2" - dest: "{{ ntp_config_file }}" - owner: root - group: root - mode: "0644" - notify: "Restart ntpd service" - -- name: "Ensure ntpd service is started and enabled" - ansible.builtin.systemd: - name: "ntpd" - state: started - enabled: true - -- name: "Configure ufw firewall" - community.general.ufw: - rule: allow - port: "{{ ntp_port }}" - proto: udp - src: "{{ item }}" - direction: in - comment: "NTP traffic" - loop: "{{ ntp_firewall_allowed_sources | default([]) }}" - retries: 5 - delay: 2 - register: ufw_result - until: ufw_result is succeeded diff --git a/roles/ntpd/templates/ntp.conf.j2 b/roles/ntpd/templates/ntp.conf.j2 deleted file mode 100644 index 1121779..0000000 --- a/roles/ntpd/templates/ntp.conf.j2 +++ /dev/null @@ -1,21 +0,0 @@ -# {{ ansible_managed }} -# -# NTP configuration file for ntpd - -restrict default {{ ntp_restrict }} - -{% for network in ntp_allowed_networks %} -restrict {{ network }} -{% endfor %} - -# Use servers from the NTP Pool Project. 'iburst' speeds up initial synchronization. -{% for pool_host in ntp_pools %} -pool {{ pool_host }} iburst -{% endfor %} - -# Frequency drift file -driftfile {{ ntp_drift_file }} - -# Disable the monitoring facility (monlist) to prevent ntpq -c monlist DDOS attacks. -# @see CVE-2013-5211 -disable monitor