diff --git a/meta-balena-common/recipes-connectivity/networkmanager/balena-files/NetworkManager.conf.systemd b/meta-balena-common/recipes-connectivity/networkmanager/balena-files/NetworkManager.conf.systemd index 6a26660d5e..348535dcd4 100644 --- a/meta-balena-common/recipes-connectivity/networkmanager/balena-files/NetworkManager.conf.systemd +++ b/meta-balena-common/recipes-connectivity/networkmanager/balena-files/NetworkManager.conf.systemd @@ -1,6 +1,6 @@ [Unit] -Wants=balena-net-config.service bind-var-lib-NetworkManager.service chronyd.service -After=balena-net-config.service bind-var-lib-NetworkManager.service chronyd.service +Wants=resin-net-config.service bind-var-lib-NetworkManager.service +After=resin-net-config.service bind-var-lib-NetworkManager.service [Service] ExecStartPre=/bin/systemd-tmpfiles --remove /etc/tmpfiles.d/nm-tmpfiles.conf diff --git a/meta-balena-common/recipes-connectivity/openvpn/files/openvpn.service b/meta-balena-common/recipes-connectivity/openvpn/files/openvpn.service index 36f8f1d712..40b9e89b46 100644 --- a/meta-balena-common/recipes-connectivity/openvpn/files/openvpn.service +++ b/meta-balena-common/recipes-connectivity/openvpn/files/openvpn.service @@ -1,7 +1,7 @@ [Unit] Description=OpenVPN -Requires=prepare-openvpn.service bind-etc-openvpn.service -After=syslog.target network.target prepare-openvpn.service bind-etc-openvpn.service time-sync.target +Requires=prepare-openvpn.service bind-etc-openvpn.service time-sync-https-wait.target +After=syslog.target network.target prepare-openvpn.service bind-etc-openvpn.service time-sync-https-wait.target ConditionFileNotEmpty=/etc/openvpn/openvpn.conf [Service] diff --git a/meta-balena-common/recipes-containers/balena-supervisor/balena-supervisor/balena-supervisor.service b/meta-balena-common/recipes-containers/balena-supervisor/balena-supervisor/balena-supervisor.service index 07ffda56df..5bed28a023 100644 --- a/meta-balena-common/recipes-containers/balena-supervisor/balena-supervisor/balena-supervisor.service +++ b/meta-balena-common/recipes-containers/balena-supervisor/balena-supervisor/balena-supervisor.service @@ -14,7 +14,6 @@ After=\ os-config-devicekey.service \ bind-etc-systemd-system-resin.target.wants.service \ bind-etc-balena-supervisor.service \ - chronyd.service \ migrate-supervisor-state.service Wants=balena.service ConditionPathExists=/etc/balena-supervisor/supervisor.conf diff --git a/meta-balena-common/recipes-core/chrony/files/chronyd.conf.systemd b/meta-balena-common/recipes-core/chrony/files/chronyd.conf.systemd index 63465abe90..7330666200 100644 --- a/meta-balena-common/recipes-core/chrony/files/chronyd.conf.systemd +++ b/meta-balena-common/recipes-core/chrony/files/chronyd.conf.systemd @@ -1,7 +1,6 @@ [Unit] -Before=time-sync.target -Wants=time-sync.target var-volatile-lib.service -After=var-volatile-lib.service +Wants=time-sync-https-wait.target var-volatile-lib.service +After=time-sync-https-wait.target var-volatile-lib.service [Service] Type=simple diff --git a/meta-balena-common/recipes-core/systemd/timeinit.bb b/meta-balena-common/recipes-core/systemd/timeinit.bb index 0c6cb69040..8cdd1b0e5c 100644 --- a/meta-balena-common/recipes-core/systemd/timeinit.bb +++ b/meta-balena-common/recipes-core/systemd/timeinit.bb @@ -1,4 +1,4 @@ -# Copyright 2018-2020 Balena Ltd. +# Copyright 2018-2021 Balena Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -27,6 +27,9 @@ SRC_URI = " \ file://fake-hwclock-update.timer \ file://timeinit-rtc.service \ file://timeinit-rtc.sh \ + file://timesync-https.service \ + file://timesync-https.sh \ + file://time-sync-https-wait.target \ file://time-set.target \ file://time-sync.conf \ " @@ -40,6 +43,8 @@ SYSTEMD_SERVICE_${PN} = " \ fake-hwclock-update.service \ fake-hwclock-update.timer \ timeinit-rtc.service \ + timesync-https.service \ + time-sync-https-wait.target \ time-set.target \ " @@ -51,12 +56,15 @@ do_install() { install -d ${D}${sysconfdir}/systemd/system/time-sync.target.d/ install -m 0775 ${WORKDIR}/timeinit-buildtime.sh ${D}${bindir} install -m 0775 ${WORKDIR}/timeinit-rtc.sh ${D}${bindir} + install -m 0775 ${WORKDIR}/timesync-https.sh ${D}${bindir} install -m 0775 ${WORKDIR}/fake-hwclock ${D}${base_sbindir} install -m 0644 ${WORKDIR}/timeinit-buildtime.service ${D}${systemd_unitdir}/system install -m 0644 ${WORKDIR}/fake-hwclock.service ${D}${systemd_unitdir}/system install -m 0644 ${WORKDIR}/fake-hwclock-update.service ${D}${systemd_unitdir}/system install -m 0644 ${WORKDIR}/fake-hwclock-update.timer ${D}${systemd_unitdir}/system install -m 0644 ${WORKDIR}/timeinit-rtc.service ${D}${systemd_unitdir}/system + install -m 0644 ${WORKDIR}/timesync-https.service ${D}${systemd_unitdir}/system + install -m 0644 ${WORKDIR}/time-sync-https-wait.target ${D}${systemd_unitdir}/system install -m 0644 ${WORKDIR}/time-set.target ${D}${systemd_unitdir}/system install -m 0644 ${WORKDIR}/time-sync.conf ${D}${sysconfdir}/systemd/system/time-sync.target.d/ } diff --git a/meta-balena-common/recipes-core/systemd/timeinit/fake-hwclock-update.timer b/meta-balena-common/recipes-core/systemd/timeinit/fake-hwclock-update.timer index 36cfd5a2eb..edd191b39e 100644 --- a/meta-balena-common/recipes-core/systemd/timeinit/fake-hwclock-update.timer +++ b/meta-balena-common/recipes-core/systemd/timeinit/fake-hwclock-update.timer @@ -1,4 +1,4 @@ -# Copyright 2020 Balena Ltd. +# Copyright 2020-2021 Balena Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ [Unit] Description=Periodically update the saved fake-hwclock. -After=time-sync.target +After=time-sync-https-wait.target [Timer] OnCalendar=hourly diff --git a/meta-balena-common/recipes-core/systemd/timeinit/time-sync-https-wait.target b/meta-balena-common/recipes-core/systemd/timeinit/time-sync-https-wait.target new file mode 100644 index 0000000000..a3ec21e93b --- /dev/null +++ b/meta-balena-common/recipes-core/systemd/timeinit/time-sync-https-wait.target @@ -0,0 +1,19 @@ +# Copyright 2021 Balena Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +[Unit] +Description=Wait for time synchronisation via HTTPS header +RefuseManualStart=yes +After=network.target time-set.target +Wants=time-set.target diff --git a/meta-balena-common/recipes-core/systemd/timeinit/timesync-https.service b/meta-balena-common/recipes-core/systemd/timeinit/timesync-https.service new file mode 100644 index 0000000000..ef30793d00 --- /dev/null +++ b/meta-balena-common/recipes-core/systemd/timeinit/timesync-https.service @@ -0,0 +1,28 @@ +# Copyright 2021 Balena Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +[Unit] +Description=Set system clock from a secure website +DefaultDependencies=no +Wants=network.target time-sync.target +After=network.target time-sync.target +Before=time-sync-https-wait.target chronyd.service + +[Service] +Type=oneshot +ExecStart=/usr/bin/timesync-https.sh +RemainAfterExit=yes + +[Install] +WantedBy=time-sync-https-wait.target diff --git a/meta-balena-common/recipes-core/systemd/timeinit/timesync-https.sh b/meta-balena-common/recipes-core/systemd/timeinit/timesync-https.sh new file mode 100644 index 0000000000..c4d28a1c19 --- /dev/null +++ b/meta-balena-common/recipes-core/systemd/timeinit/timesync-https.sh @@ -0,0 +1,74 @@ +#!/bin/sh +# +# Copyright 2021 Balena Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +. /usr/libexec/os-helpers-logging +. /usr/libexec/os-helpers-time + +. /usr/sbin/balena-config-vars + +# Expected HTTP response code. Used to determine that we are not +# behind a captive portal. +EXPECTED_SERVER_CODE=204 +# Initial delay in seconds between poll attempts. +INITIAL_HTTPS_POLL_DELAY=2 +# Maximum delay in seconds between poll attempts. +MAX_HTTPS_POLL_DELAY=64 +# Timeout for curl command in seconds. +# Note that curl does not apply this timeout to DNS lookups. +CURL_TIMEOUT=5 +# Don't bother updating or reporting errors for small differences. +TIME_DIFF_THRESHOLD=2 + +# Poll HTTPS server for time string. +info "Starting HTTPS time synchronisation." + +HTTPS_POLL_DELAY=$INITIAL_HTTPS_POLL_DELAY + +# In theory the maximum duration of each poll delay is given by: +# (HTTPS_POLL_DELAY + CURL_TIMEOUT) seconds. +# Note that this period can be extended as curl DNS lookup timeouts do +# not obey the -m (--max-time) parameter. + +while [ true ]; do + SYS_TIME=$(get_system_time_as_timestamp) + readarray -t https_header <<<$(curl -m5 -k -I -s $OS_NET_CONN_URI | sed 's/\r$//' | awk '/HTTP/{printf $2"\n"} /[Dd]ate/{print $2, $3, $4, $5, $6, $7"\n"}') + SERVER_CODE=${https_header[0]} + SERVER_TIME_STRING=${https_header[1]} + if [ "$SERVER_CODE" = "$EXPECTED_SERVER_CODE" ]; then + if [ ! -z "$SERVER_TIME_STRING" ]; then + SERVER_TIME=$(get_server_time_as_timestamp "$SERVER_TIME_STRING") + TIME_DIFF=$(get_abs_time_diff_from_timestamps "$SYS_TIME" "$SERVER_TIME") + if [ "$TIME_DIFF" -gt "$TIME_DIFF_THRESHOLD" ]; then + $(set_system_time_from_timestamp "$SERVER_TIME") + if [ "$SYS_TIME" -gt "$SERVER_TIME" ]; then + warn "HTTPS header time is in the past." + warn "Check time sources if this issue persists." + fi + info "Time synchronised via HTTPS." + info "Old time: $(get_display_time_from_timestamp "$SYS_TIME")" + info "New time: $(get_display_time_from_timestamp "$SERVER_TIME")" + else + info "System time is already synchronised." + exit 0 + fi + fi + fi + sleep $HTTPS_POLL_DELAY + + if [ "$HTTPS_POLL_DELAY" -lt "$MAX_HTTPS_POLL_DELAY" ]; then + HTTPS_POLL_DELAY=$(($HTTPS_POLL_DELAY * 2)) + fi +done diff --git a/meta-balena-common/recipes-support/os-helpers/os-helpers/os-helpers-time b/meta-balena-common/recipes-support/os-helpers/os-helpers/os-helpers-time index b51593ba1f..114776a684 100644 --- a/meta-balena-common/recipes-support/os-helpers/os-helpers/os-helpers-time +++ b/meta-balena-common/recipes-support/os-helpers/os-helpers/os-helpers-time @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright 2020 Balena Ltd. +# Copyright 2020-2021 Balena Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -31,6 +31,14 @@ get_hwclock_time_as_timestamp() { echo "${timestamp}" } +# Get an HTTP server header time string as a timestamp. +# Arguments: +# 1 - server time string (Day, DD Mon YYYY hh:mm:ss TZ) +get_server_time_as_timestamp() { + local server_time_str=$1 + echo "$(date -u "+%4Y%2m%2d%2H%2M%2S" -d "${server_time_str}")" +} + # Get a 'date' compatible string from a timestamp for display. # YYYYMMDDhhmmss -> YYYYMMDD hh:mm:ss # Arguments: