File: //sbin/amazon-chrony-config
#!/usr/bin/bash
source /etc/sysconfig/chronyd
set -u -o pipefail -o nounset
shopt -s nullglob
rundir="/run/chrony.d"
declare -i installed_new_sources=0
reload_sources() {
local code=$?
if [ $installed_new_sources -gt 0 ]; then
# reload sources if chronyd is running. We may be
# started before chronyd, so don't log an error if
# it's not yet running
chronyc reload sources > /dev/null 2>&1 || true
fi
exit $code
}
trap reload_sources EXIT
main () {
[ -d "$rundir" ] || install -Z -o root -g root -m0755 -d "$rundir"
static_sources=(/etc/chrony.d/*.sources "${rundir}"/*.sources)
for f in "${static_sources[@]}"; do
# If there are existing sources, we have no thing to do
[ ! -L "$f" ] && [ -s "$f" ] && exit 0
done
local dhcp_sources_installed
dhcp_sources
dhcp_sources_installed=$?
case $dhcp_sources_installed in
0)
# we've installed new sources
exit 0
;;
1)
# DHCP leases contain no time sources at all
:
;;
2)
# Time sources are unchanged from the previous run
exit 0
;;
esac
if [[ $USE_AMAZON_NTP_POOL == 'no' || $USE_AMAZON_NTP_POOL == 'false' ]]; then
# We'll use the public NTP pools only. These time sources
# present standard leap-second handling, so we disable the
# Amazon-provided smearing sources per
# https://www.rfc-editor.org/rfc/rfc8633.html#section-3.7.1
rm_sources
public_pool
elif [[ $USE_AMAZON_NTP_POOL == 'yes' || $USE_AMAZON_NTP_POOL == 'true' ]]; then
# Ensures that Amazon NTP endpoints are refreshed in the case this instance
# has been launched in another partition or switched between IPv4 & IPv6.
rm_sources
link_local
amazon_pool
fi
}
rm_sources() {
rm -f "${rundir}"/*.sources
}
ln_sources() {
local basename partition cfg p
basename="$1"
partition="${2:-unspecified}"
for p in "$partition" aws; do
cfg="/usr/share/amazon-chrony-config/${basename}_${p}.sources"
if [ -f "$cfg" ]; then
installed_new_sources+=1
ln -s "$cfg" "${rundir}/${basename}.sources"
return
fi
done
echo "WARNING: No ${basename} sources for partition $partition" >&2
}
# Configure time sources received via DHCP leases in $rundir/dhcp.sources
# Return values:
# 0: We have installed a new list of time sources from DHCP
# 1: The DHCP lease does not contain any time sources
# 2: The DHCP lease contains time sources that are unchanged from a prior run
dhcp_sources () {
local servers
local cfg="${rundir}/dhcp.sources"
servers=$(
busctl call -j org.freedesktop.network1 \
/org/freedesktop/network1 \
org.freedesktop.network1.Manager \
Describe \
| jq -r '.data[0]' \
| jq '.Interfaces[].NTP[]?.Address | join(".") as $foo | "server " + $foo + " prefer iburst minpoll 4 maxpoll 4"' -r
)
if [ -n "$servers" ]; then
if [ -f "$cfg" ]; then
local old_md5 new_md5
old_md5=$(md5sum "$cfg" | awk '{print $1}')
new_md5=$(echo "$servers" | md5sum | awk '{print $1}')
if [ "$old_md5" = "$new_md5" ]; then
# server list is unchanged
return 2
fi
fi
# The DHCP-provided sources should be the only ones we use, remove any others
rm_sources
echo "$servers" > "$rundir/.dhcp.sources"
mv "$rundir/.dhcp.sources" "$cfg"
installed_new_sources+=1
return 0
fi
if [ -f "$cfg" ]; then
# clean up any stale sources from older DHCP leases
# and ensure we reload to reflect the removal
rm -f "$cfg"
installed_new_sources+=1
fi
return 1
}
# Install a partition-appropriate Amazon network time endpoint,
# defaulting to the classic "aws" partition
amazon_pool () {
local partition cfg
partition=$(ec2-metadata --quiet --partition)
if [ $? -eq 0 ]; then
ln_sources amazon-pool "$partition"
return 0
fi
echo "ec2-metadata returned nonzero" >&2
}
# Install one, but not both, of the link-local time sources
link_local () {
local v6_endpoint=fd00:ec2::123
if ip -6 ro get $v6_endpoint > /dev/null 2>&1; then
ln_sources link-local-ipv6
return
fi
ln_sources link-local-ipv4
}
public_pool () {
ln -s /usr/share/chrony/ntp-pool.sources "$rundir"
installed_new_sources+=1
}
main "$@"