#!/usr/bin/env bash # # JbTecWiz Support Centre -- generated fix script # # Fault : Clock drift and time synchronisation failures # Fix : Get chrony synchronised and keep it there # Source: https://jbtecwiz.com/support/lnx-net-chrony # # Run as : Root shell # Expect : 20 minutes # Risk : low # Reversible : yes # # WHEN THIS IS THE RIGHT FIX # chrony is the intended daemon. # # HOW TO UNDO IT # Re-enable the previous daemon if this was the wrong choice for the # environment. # # Walks the fix one step at a time and asks before each. Steps with no # command are yours to do -- it prints those and waits. DRYRUN=1 prints # without executing; UNATTENDED=1 does not ask. # # -------------------------------------------------------------------- # NO WARRANTY - USE AT YOUR OWN RISK # # This script is provided by JbTecWiz as-is and with no warranty of any # kind, express or implied. You run it entirely at your own risk. # # JbTecWiz accepts no liability for any loss or damage arising from its # use, including but not limited to data loss, downtime, or configuration # changes that turn out to be wrong for your system. # # You are responsible for reading this script before running it, for # satisfying yourself that it suits the machine in front of you, and for # having a working backup first. Some steps cannot be undone. # -------------------------------------------------------------------- set -uo pipefail DRYRUN="${DRYRUN:-0}" UNATTENDED="${UNATTENDED:-0}" failed=0 if [ "$(id -u)" -ne 0 ]; then echo " This fix is documented as needing root. Re-run with sudo." >&2 exit 3 fi rule() { printf "\n%s\n" "$(printf '-%.0s' $(seq 1 70))"; if [ $# -gt 0 ]; then echo "$1"; fi; } prose() { echo "$1" | fold -s -w 74 | sed "s/^/ /"; } # Returns 0 when the caller should run the command, 1 when it should not. # A manual step always returns 1 -- there is nothing for the caller to run. step() { # step [command lines...] local n="$1" dotext="$2" why="$3" mode="$4"; shift 4 rule " Step $n of 6" prose "$dotext" if [ -n "$why" ]; then echo; prose "$why"; fi if [ "$mode" = "manual" ]; then echo; echo " -> Do this yourself, then press Enter to carry on." if [ "$UNATTENDED" = "0" ] && [ "$DRYRUN" = "0" ]; then read -r _; fi return 1 fi echo; printf " %s\n" "$@"; echo if [ "$DRYRUN" = "1" ]; then echo " (dry run -- not executed)"; return 1; fi if [ "$UNATTENDED" = "0" ]; then read -r -p " Run this step? [Y]es / [S]kip / [Q]uit " a case "$a" in [Qq]*) echo " Stopped at your request."; exit 0 ;; [Ss]*) echo " Skipped."; return 1 ;; esac fi return 0 } rule echo " Clock drift and time synchronisation failures" echo " Get chrony synchronised and keep it there" echo echo " Risk: low Reversible 20 minutes" echo prose 'No warranty. Use at your own risk - JbTecWiz accepts no liability. Read it before you run it, and have a backup.' rule echo if [ "$UNATTENDED" = "0" ] && [ "$DRYRUN" = "0" ]; then read -r -p " Ready? [y/N] " go case "$go" in [Yy]*) ;; *) echo " Nothing was changed."; exit 0;; esac fi if step 1 'Check the current state and the sources.' 'The Leap status line in tracking says whether the clock is actually disciplined. A machine can be running chronyd with a wrong clock for weeks and nothing else will tell you.' cmd 'chronyc tracking' 'chronyc sources -v'; then chronyc tracking chronyc sources -v if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 1 failed. The rest of the fix may depend on it." >&2 fi fi if step 2 'Make sure no other time service is running.' '' cmd 'systemctl is-active systemd-timesyncd ntpd chronyd 2>/dev/null'; then systemctl is-active systemd-timesyncd ntpd chronyd 2>/dev/null if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 2 failed. The rest of the fix may depend on it." >&2 fi fi if step 3 'Disable the ones you are not using.' '' cmd 'sudo systemctl disable --now systemd-timesyncd' 'sudo systemctl enable --now chronyd'; then sudo systemctl disable --now systemd-timesyncd sudo systemctl enable --now chronyd if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 3 failed. The rest of the fix may depend on it." >&2 fi fi if step 4 'Check UDP 123 is reachable outbound -- a firewall blocking it is the usual cause of sources that never reach a reachable state.' '' cmd 'sudo chronyc sources' 'sudo ss -unp | grep 123'; then sudo chronyc sources sudo ss -unp | grep 123 if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 4 failed. The rest of the fix may depend on it." >&2 fi fi if step 5 'Step the clock immediately if it is far out. Chrony slews small differences and refuses to slew large ones.' 'A machine more than a few minutes out will never converge by slewing, because the correction rate is deliberately gentle. makestep jumps it, which is safe here and is what the machine needs before Kerberos or TLS will work.' cmd 'sudo chronyc makestep'; then sudo chronyc makestep if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 5 failed. The rest of the fix may depend on it." >&2 fi fi step 6 'On a domain-joined machine, point chrony at the domain controllers rather than public pools.' '' manual || true rule " Confirm it worked" prose 'chronyc tracking shows a small offset and a leap status of Normal.' if [ "$DRYRUN" = "0" ]; then chronyc tracking | grep -E 'Leap|System time|Stratum' timedatectl status fi rule if [ "$failed" -gt 0 ]; then echo " Finished with $failed failed step(s)." echo " Read the full write-up at https://jbtecwiz.com/support/lnx-net-chrony" else echo " Finished." fi echo prose 'To undo: Re-enable the previous daemon if this was the wrong choice for the environment.' rule