Compare commits

..
1 Commits
Author SHA1 Message Date
claude-botandClaude Opus 5.5 41efcd9f91 fix(time-sync): keep the internet VPS hosts on NTP
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
common and plays/configure-vm-time-sync.yml masked systemd-timesyncd on
every KVM guest, assuming a Proxmox host supplies the time. The Proxmox
VMs are gone; the only KVM guests left are the Hetzner/netcup VPS hosts,
which have no such host. netbird.renner.uno ran with no NTP client since
provisioning, drifted behind authentik, and NetBird management rejected
SSO tokens with "token used before issued".

Drop both masking paths and add a timesyncd role to the netbird server
and proxy plays; it unmasks, enables and starts systemd-timesyncd and
waits for NTPSynchronized=yes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 12:43:00 +01:00
8 changed files with 31 additions and 98 deletions
-70
View File
@@ -1,70 +0,0 @@
---
# Configure VMs to use host time sync via kvm-clock
# Disables systemd-timesyncd to prevent NTP conflicts
#
# VMs get time from Proxmox host via kvm-clock paravirtualized clock
# This is more accurate and reliable than network NTP for VMs
- name: Configure VM time synchronization from host
hosts: all
become: yes
gather_facts: yes
tasks:
- name: Check if running as VM (not LXC)
stat:
path: /sys/devices/system/clocksource/clocksource0/current_clocksource
register: clocksource_file
- name: Get current clock source
slurp:
src: /sys/devices/system/clocksource/clocksource0/current_clocksource
register: clocksource
when: clocksource_file.stat.exists
- name: Display clock source
debug:
msg: "Clock source: {{ (clocksource.content | b64decode | trim) if clocksource_file.stat.exists else 'N/A (LXC)' }}"
- name: Check if systemd-timesyncd is installed
command: systemctl list-unit-files systemd-timesyncd.service
register: timesyncd_installed
check_mode: false
failed_when: false
changed_when: false
- name: Stop and disable systemd-timesyncd on VMs using kvm-clock
systemd:
name: systemd-timesyncd
state: stopped
enabled: no
when:
- clocksource_file.stat.exists
- clocksource.content is defined
- "'kvm-clock' in (clocksource.content | b64decode)"
- "'systemd-timesyncd.service' in timesyncd_installed.stdout"
- name: Mask systemd-timesyncd to prevent re-enabling
systemd:
name: systemd-timesyncd
masked: yes
when:
- clocksource_file.stat.exists
- clocksource.content is defined
- "'kvm-clock' in (clocksource.content | b64decode)"
- "'systemd-timesyncd.service' in timesyncd_installed.stdout"
- name: Verify time sync status
command: timedatectl show --property=NTPSynchronized
register: timesync_status
check_mode: false
changed_when: false
failed_when: false
- name: Display final status
debug:
msg: |
Host: {{ inventory_hostname }}
Type: {{ 'VM (kvm-clock)' if (clocksource_file.stat.exists and 'kvm-clock' in (clocksource.content | b64decode | default(''))) else 'LXC/Other' }}
Time sync: {{ timesync_status.stdout | default('unknown') }}
Note: VMs sync time from Proxmox host, LXCs inherit host time automatically
+1
View File
@@ -22,6 +22,7 @@
become: true
roles:
- common
- timesyncd
- docker-install
- netbird-proxy
- netbird-proxy-watchdog
+1
View File
@@ -16,5 +16,6 @@
become: true
roles:
- common
- timesyncd
- docker-install
- netbird-server
+2 -11
View File
@@ -54,18 +54,9 @@
# via plays/configure-ssh-ca.yml.
# -------------------------------------------------------
# 5. Disable NTP on VMs/LXC (use host time)
# 5. Time sync
# -------------------------------------------------------
- name: Stop and disable systemd-timesyncd on VMs/LXC
systemd:
name: systemd-timesyncd
state: stopped
enabled: no
masked: yes
when:
- ansible_facts['virtualization_role'] == 'guest'
- ansible_facts['virtualization_type'] in ['kvm', 'lxc']
ignore_errors: yes
# Moved to separate roles: timesyncd (internet VPS), chrony-ntp-server (edge).
# -------------------------------------------------------
# 6. Certificates
+26
View File
@@ -0,0 +1,26 @@
---
# Keep the system clock on NTP with systemd-timesyncd, on hosts that run no
# chrony. A KVM guest's clocksource does not steer wall-clock time; without an
# NTP client the clock drifts until JWT iat/exp checks fail.
- name: Install systemd-timesyncd
ansible.builtin.apt:
name: systemd-timesyncd
state: present
when: ansible_facts['os_family'] == 'Debian'
- name: Run systemd-timesyncd
ansible.builtin.systemd_service:
name: systemd-timesyncd
masked: false
enabled: true
state: started
- name: Wait for the clock to synchronize
ansible.builtin.command:
argv: [timedatectl, show, --property=NTPSynchronized, --value]
register: timesyncd_synchronized
changed_when: false
until: timesyncd_synchronized.stdout == "yes"
retries: 12
delay: 5
-2
View File
@@ -12,8 +12,6 @@
ansible.builtin.import_playbook: plays/configure-synology-python.yml
- name: Base system
ansible.builtin.import_playbook: plays/common.yml
- name: VM time sync
ansible.builtin.import_playbook: plays/configure-vm-time-sync.yml
- name: SSH user CA
ansible.builtin.import_playbook: plays/configure-ssh-ca.yml
@@ -66,13 +66,6 @@
auth: password
deliver: {esp32: "https://edge-2.iot.renner.uno:8443"}
acl: ["readwrite open3e/#"]
# vitocal-can: open3e-esp32 CAN gateway at the Vitocal 250-A (S28-28).
# The firmware registers its compiled-in DHCP name `open3e`, not its
# configured hostname.
- name: vitocal-can
auth: password
deliver: {esp32: "http://open3e.iot.renner.uno"}
acl: ["readwrite open3e/#"]
# Shellies (REN-1017): one password account per device, pushed by the
# controller through shelly-manager's per-device API. Name shelly-<id> =
# shelly-manager device id. ACL as the former shared user `shellies`
@@ -492,7 +492,7 @@ spec:
- port: "6638"
protocol: TCP
---
# mqtt-account-controller: credential delivery to deliver.esp32 targets in mqtt-accounts.yaml.
# mqtt-account-controller: credential delivery to edge-1 (deliver.esp32 in mqtt-accounts.yaml).
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
@@ -524,13 +524,6 @@ spec:
- ports:
- port: "8443"
protocol: TCP
# vitocal-can (open3e-esp32), plain HTTP API.
- toFQDNs:
- matchName: "open3e.iot.renner.uno"
toPorts:
- ports:
- port: "80"
protocol: TCP
---
# whisper and speech-to-phrase: model downloads over HTTPS.
apiVersion: cilium.io/v2