- changed-files:
- any-glob-to-any-file: 'modules.d/[0-9][0-9]caps/*'
+chrony:
+ - changed-files:
+ - any-glob-to-any-file: 'modules.d/[0-9][0-9]chrony/*'
+
cifs:
- changed-files:
- any-glob-to-any-file: 'modules.d/[0-9][0-9]cifs/*'
test:
- "31"
- "60"
+ - "61"
exclude:
- container: arch:latest
architecture: {runner: 'ubuntu-24.04-arm', tag: 'arm'}
# https://github.com/dracut-ng/dracut/issues/1988
- container: debian:sid
architecture: {runner: 'ubuntu-24.04-arm', tag: 'arm'}
+ # In Debian/Ubuntu both chrony and systemd-timesyncd provide
+ # the virtual package time-daemon, so systemd-timesyncd
+ # would have to be removed, breaking test 41.
+ - container: debian:latest
+ test: "61"
+ - container: debian:sid
+ test: "61"
+ - container: ubuntu:devel
+ test: "61"
+ - container: ubuntu:rolling
+ test: "61"
container:
image: ghcr.io/dracut-ng/${{ matrix.container }}
options: '--device=/dev/kvm --privileged'
test:
- "31"
- "60"
+ - "61"
+ exclude:
+ # In Debian/Ubuntu both chrony and systemd-timesyncd provide
+ # the virtual package time-daemon, so systemd-timesyncd
+ # would have to be removed, breaking test 41.
+ - container: ubuntu:devel
+ test: "61"
container:
image: ghcr.io/dracut-ng/${{ matrix.container }}
options: '--device=/dev/kvm'
|===
| Module | Description
+| chrony
+| Adds support for synchronizing the internal clock via Network Time Protocol (NTP)
+
| cifs
| https://docs.kernel.org/admin-guide/cifs/index.html[CIFS], https://repology.org/project/cifs-utils[cifs-utils]
list of physical (ethernet) interfaces. Bridge without parameters assumes
bridge=br0:eth0
+NTP
+~~~
+Requires the dracut 'chrony' module.
+
+**rd.ntp=**__{server|pool|peer}__:__<hostname-or-ip>__[:__<option>__[,<option>...]]::
+ This parameter can be specified multiple times.
+ IPv6 addresses have to be put in brackets.
+ See man:chrony.conf[5,external] for more information about server, pool and
+ peer options.
++
+[listing]
+.Examples
+--
+ rd.ntp=pool:2.europe.pool.ntp.org:iburst
+ rd.ntp=server:185.177.150.95:iburst,prefer
+ rd.ntp=server:[2600:1f18:631e:db00:363d:d9d7:5c80:d560]:iburst,maxdelay,0.3
+--
+
+**rd.ntp.nodhcp**::
+ Disable using NTP sources from DHCP.
+
NFS
~~~
Requires the dracut 'nfs' module.
--- /dev/null
+#!/bin/sh
+
+command -v getargbool > /dev/null || . /lib/dracut-lib.sh
+
+if getargbool 0 rd.ntp.nodhcp; then
+ info "rd.ntp.nodhcp=1: not adding NTP sources from DHCP."
+ return 0
+fi
+
+_ifname=$1
+[ -n "$_ifname" ] || return 0
+
+_dhcpopts_file="/tmp/dhclient.$_ifname.dhcpopts"
+[ -s "$_dhcpopts_file" ] || return 0
+
+(
+ # shellcheck disable=SC1090
+ . "$_dhcpopts_file"
+ [ -n "$new_ntp_servers" ] || [ -n "$new_dhcp6_ntp_servers" ] || return 0
+
+ info "Adding NTP sources from DHCP ($_ifname)."
+
+ [ -d /run/chrony-dhcp ] || mkdir -p /run/chrony-dhcp
+ for _srv in $new_ntp_servers $new_dhcp6_ntp_servers; do
+ echo "server $_srv iburst" >> "/run/chrony-dhcp/$_ifname.sources"
+ done
+
+ chronyc reload sources > /dev/null 2>&1 \
+ || warn "chronyc failed to reload NTP sources"
+)
+
+unset _ifname _dhcpopts_file
--- /dev/null
+[Unit]
+Description=Wait for chrony to synchronize system clock (initrd)
+AssertPathExists=/etc/initrd-release
+DefaultDependencies=no
+After=chronyd.service
+Requires=chronyd.service
+Before=time-sync.target
+Wants=time-sync.target
+
+[Service]
+Type=oneshot
+# Wait for chronyd to update the clock and the remaining
+# correction to be less than 0.1 seconds
+ExecStart=/usr/bin/chronyc -h 127.0.0.1,::1 waitsync 0 0.1 0.0 1
+# Wait for at most 3 minutes
+TimeoutStartSec=180
+RemainAfterExit=yes
+StandardOutput=null
+
+CapabilityBoundingSet=
+DevicePolicy=closed
+DynamicUser=yes
+IPAddressAllow=localhost
+IPAddressDeny=any
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+PrivateDevices=yes
+PrivateUsers=yes
+ProtectClock=yes
+ProtectControlGroups=yes
+ProtectHome=yes
+ProtectHostname=yes
+ProtectKernelLogs=yes
+ProtectKernelModules=yes
+ProtectKernelTunables=yes
+ProtectProc=invisible
+ProtectSystem=strict
+RestrictAddressFamilies=AF_INET AF_INET6
+RestrictNamespaces=yes
+RestrictRealtime=yes
+SystemCallArchitectures=native
+SystemCallFilter=@system-service
+SystemCallFilter=~@privileged @resources
+UMask=0777
--- /dev/null
+# This file is part of dracut chrony module.
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# Record the rate at which the system clock gains/losses time.
+driftfile /run/chrony/drift
+
+# Allow the system clock to be stepped in the first three updates
+# if its offset is larger than 1 second.
+makestep 1.0 3
+
+# Enable kernel synchronization of the real-time clock (RTC).
+rtcsync
+
+# Save NTS keys and cookies.
+ntsdumpdir /run/chrony
+
+# Specify directory for log files.
+logdir /run/chrony/log
+
+# First, use NTP sources parsed by dracut from the kernel command line.
+sourcedir /run/chrony/dracut.sources.d
+
+# Second, use NTP sources from DHCP.
+sourcedir /run/chrony-dhcp
--- /dev/null
+[Unit]
+Description=NTP client/server (initrd)
+AssertPathExists=/etc/initrd-release
+DefaultDependencies=no
+After=dracut-cmdline.service network.target nss-lookup.target
+Before=time-sync.target
+Conflicts=ntpd.service systemd-timesyncd.service
+Wants=network.target time-sync.target
+ConditionCapability=CAP_SYS_TIME
+
+[Service]
+Type=notify
+PIDFile=/run/chrony/chronyd.pid
+Environment="OPTIONS="
+EnvironmentFile=-/etc/sysconfig/chronyd
+# The default location for chrony.conf can be set at build with the
+# --sysconfdir configuration option, so force /etc/chrony.conf with -f
+ExecStart=/usr/sbin/chronyd -f /etc/chrony.conf -n $OPTIONS
+
+CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE
+CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_KILL CAP_LEASE CAP_LINUX_IMMUTABLE
+CapabilityBoundingSet=~CAP_MAC_ADMIN CAP_MAC_OVERRIDE CAP_MKNOD CAP_SYS_ADMIN
+CapabilityBoundingSet=~CAP_SYS_BOOT CAP_SYS_CHROOT CAP_SYS_MODULE CAP_SYS_PACCT
+CapabilityBoundingSet=~CAP_SYS_PTRACE CAP_SYS_RAWIO CAP_SYS_TTY_CONFIG CAP_WAKE_ALARM
+DeviceAllow=char-pps rw
+DeviceAllow=char-ptp rw
+DeviceAllow=char-rtc rw
+DevicePolicy=closed
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+PrivateTmp=yes
+ProtectControlGroups=yes
+ProtectHome=yes
+ProtectHostname=yes
+ProtectKernelLogs=yes
+ProtectKernelModules=yes
+ProtectKernelTunables=yes
+ProtectProc=invisible
+ProtectSystem=strict
+ReadWritePaths=/run
+RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
+RestrictNamespaces=yes
+RestrictSUIDSGID=yes
+SystemCallArchitectures=native
+SystemCallFilter=~@cpu-emulation @debug @module @mount @obsolete @raw-io @reboot @swap
--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+check() {
+ require_binaries \
+ chronyd \
+ || return 1
+
+ return 255
+}
+
+depends() {
+ echo systemd network
+ return 0
+}
+
+install() {
+ # openSUSE/Fedora: chrony
+ # Ubuntu: _chrony
+ grep -s -E '^(_chrony|chrony):' "${dracutsysrootdir-}"/etc/passwd \
+ | sed 's/\/var\/lib\/chrony/\/run\/chrony/' >> "$initdir/etc/passwd"
+ grep -s -E '^(_chrony|chrony):' "${dracutsysrootdir-}"/etc/group >> "$initdir/etc/group"
+
+ inst_hook cmdline 01 "$moddir/parse-ntp.sh"
+ inst_hook initqueue/online 01 "$moddir/chrony-ntp-source.sh"
+
+ inst_multiple -o \
+ "$systemdntpunits"/50-chronyd.list \
+ "$systemdsystemunitdir"/time-sync.target \
+ chronyd chronyc mkdir chown
+
+ inst_simple "$moddir/chrony.conf" /etc/chrony.conf
+
+ for i in \
+ chronyd.service \
+ chrony-wait.service; do
+ inst_simple "$moddir/$i" "$systemdsystemunitdir/$i"
+ $SYSTEMCTL -q --root "$initdir" add-wants initrd.target "$i"
+ done
+
+ if [[ $hostonly ]]; then
+ local _i _directives _keyfile _source_dirs=()
+
+ # Install the file pointed by the "keyfile" directive, used for NTP
+ # authentication. This directive is intended to be unique, chrony would
+ # end up using the last one processed.
+ readarray -t _directives < <(grep -r -h '^keyfile ' "${dracutsysrootdir-}"/etc/chrony*)
+ if ((${#_directives[@]})); then
+ printf "\n# Specify file containing keys for NTP authentication.\n%s\n" "${_directives[-1]}" >> "$initdir/etc/chrony.conf"
+ _keyfile="${_directives[-1]/#keyfile /}"
+ fi
+
+ # chrony allows to configure directories with .sources files using the
+ # "sourcedir" directive, used to specify NTP sources (server, pool, and
+ # peer directives).
+ readarray -t _directives < <(grep -r -h '^sourcedir /etc' "${dracutsysrootdir-}"/etc/chrony*)
+ if ((${#_directives[@]})); then
+ printf "\n# Use NTP sources configured on the host.\n" >> "$initdir/etc/chrony.conf"
+ for _i in "${_directives[@]}"; do
+ echo "$_i" >> "$initdir/etc/chrony.conf"
+ _source_dirs+=("$(echo "$_i" | sed -e 's/sourcedir //' -e 's/$/\/*.sources/')")
+ done
+ fi
+
+ # We do not want to include /etc/chrony.conf or ".conf" files specified
+ # with "include" or "confdir" directives from the host, because they
+ # can override "driftfile", "ntsdumpdir" or "logdir" directives,
+ # intended to point to /run in the initrd.
+
+ inst_multiple -H -o "$_keyfile" "${_source_dirs[@]}" \
+ /etc/sysconfig/chronyd \
+ "$systemdsystemconfdir"/time-sync.target \
+ "$systemdsystemconfdir/time-sync.target.wants/*.target"
+ fi
+}
--- /dev/null
+#!/bin/sh
+
+command -v getargs > /dev/null || . /lib/dracut-lib.sh
+
+# format: rd.ntp={server|pool|peer}:<hostname-or-ip>[:<option>[,<option>...]]
+parse_ntp_source() {
+ local v="${1}":
+ local i
+ local src addr opts
+
+ set --
+ while [ -n "$v" ]; do
+ if [ "${v#\[*:*:*\]:}" != "$v" ]; then
+ # handle IPv6 address
+ i="${v%%\]:*}"
+ i="${i##\[}"
+ set -- "$@" "$i"
+ v=${v#\["$i"\]:}
+ else
+ set -- "$@" "${v%%:*}"
+ v=${v#*:}
+ fi
+ done
+
+ if [ $# -lt 2 ]; then
+ warn "Failed to parse NTP time source"
+ return 1
+ fi
+
+ case "$1" in
+ server | pool | peer)
+ src=$1
+ ;;
+ *)
+ warn "Invalid time source '$1'. Valid options: server, pool, peer"
+ return 1
+ ;;
+ esac
+
+ [ -n "$2" ] && addr=$2
+ [ -n "$3" ] && opts="$(str_replace "$3" "," " ")"
+
+ echo "${src} ${addr}${opts:+ $opts}"
+ return 0
+}
+
+mkdir -p -m 0750 /run/chrony
+chown chrony: /run/chrony
+mkdir /run/chrony/dracut.sources.d
+
+for _i in $(getargs rd.ntp); do
+ _src=$(parse_ntp_source "$_i")
+ if [ -n "$_src" ]; then
+ echo "$_src" >> /run/chrony/dracut.sources.d/dracut.sources
+ fi
+done
+
+if [ "$(ls -A /run/chrony/dracut.sources.d)" ] && ! getargbool 0 rd.neednet; then
+ echo "rd.neednet=1" > /etc/cmdline.d/01-chrony.conf
+ if ! getarg "ip="; then
+ echo "ip=dhcp" >> /etc/cmdline.d/01-chrony.conf
+ fi
+fi
+
+unset _i _src
--- /dev/null
+-include ../Makefile.testdir
--- /dev/null
+#!/usr/bin/env bash
+set -eu
+
+[ -z "${USE_NETWORK-}" ] && USE_NETWORK="network"
+
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="NTP support with chrony, systemd and $USE_NETWORK"
+
+# Fake time way off in the future, so SSL certificates will appear to have
+# expired
+FAKE_TIME="2100-01-01T00:00:00"
+
+# Name of the SSL certificate
+SSL_CERT="webserver.pem"
+
+test_check() {
+ local binary
+
+ for binary in chronyd openssl; do
+ if ! type -p "$binary" &> /dev/null; then
+ echo "Test needs $binary... Skipping"
+ return 1
+ fi
+ done
+
+ command -v systemctl &> /dev/null
+}
+
+client_run() {
+ local nook="$1"
+ local test_name="$2"
+ local append="$3"
+
+ client_test_start "$test_name"
+
+ # Comments about some qemu options:
+ # - -rtc "base=...,clock=vm" allows to disconnect vm time from host time
+ # - initcall_blacklist=rtc_cmos_init (x86_64) / efi_rtc_init (aarch64)
+ # instructs the kernel to avoid trying to sync the clock
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -rtc "base=$FAKE_TIME,clock=vm" \
+ -device "virtio-net-pci,netdev=lan0" \
+ -netdev "user,id=lan0,net=10.0.2.0/24,dhcpstart=10.0.2.15" \
+ -append "root=LABEL=dracut initcall_blacklist=rtc_cmos_init initcall_blacklist=efi_rtc_init $append $TEST_KERNEL_CMDLINE" \
+ -initrd "$TESTDIR/initramfs.testing"
+
+ # The "nook" variable controls whether a failed test is considered good
+ if [[ $nook != 1 ]]; then
+ check_qemu_log
+ else
+ check_qemu_log || :
+ fi
+
+ client_test_end
+}
+
+test_run() {
+ declare -a disk_args=()
+
+ qemu_add_drive disk_args "$TESTDIR"/root.img root
+
+ start_webserver "HTTPS" "$SSL_CERT"
+
+ client_run 1 \
+ "Fetch file from HTTPS with the system clock out of sync" \
+ "rd.neednet=1 ip=dhcp"
+
+ # 2606:4700:f1::1 should be Cloudflare
+ # "prefer" pool.ntp.org
+ client_run 0 \
+ "Fetch file from HTTPS after sync time via NTP" \
+ "rd.ntp=server:[2606:4700:f1::1]:iburst rd.ntp=pool:pool.ntp.org:iburst,prefer"
+}
+
+test_setup() {
+ # Create plain root filesystem
+ build_client_rootfs "$TESTDIR/rootfs"
+
+ # Create an ext4 image with the rootfs
+ build_ext4_image "$TESTDIR/rootfs" "$TESTDIR/root.img" dracut
+
+ # Create a file to be downloaded from the initrd
+ echo "dracut-chrony-success" > "$TESTDIR/remote-file.txt"
+
+ # We have to trick systemd to avoid taking its build time, see man
+ # systemd(1) for more details
+ touch -d "$FAKE_TIME" "$TESTDIR/clock-epoch"
+
+ # Create a certificate, we need to add it to the initrd, so we can call curl
+ # with the --cacert option, otherwise it fails with the error:
+ # "curl: (60) SSL certificate OpenSSL verify result: self-signed certificate (18)"
+ openssl req \
+ -quiet \
+ -new -x509 \
+ -keyout "$TESTDIR/$SSL_CERT" \
+ -subj "/CN=10.0.2.2/" \
+ -out "$TESTDIR/$SSL_CERT" \
+ -days 365 \
+ -nodes
+
+ # We will add a drop-in for dracut-pre-pivot.service, ordering it after
+ # time-sync.target, so it will wait until the system time is synchronized
+ {
+ echo "[Unit]"
+ echo "After=time-sync.target"
+ } > "$TESTDIR/dracut-wait.conf"
+
+ # Install a pre-pivot hook to fetch "remote-file.txt" using HTTPS, if the
+ # system clock is out of sync, curl will fail with the error:
+ # "curl: (60) SSL certificate OpenSSL verify result: certificate has expired (10)"
+ # and the script will unmount /sysroot, so the test should fail as well
+ # shellcheck disable=SC2016
+ {
+ echo "#!/bin/sh"
+ echo "command -v warn > /dev/null || . /lib/dracut-lib.sh"
+ echo 'warn "$(systemctl status chronyd.service)"'
+ echo "remote_file_content=\$(curl --cacert /etc/$SSL_CERT \"https://10.0.2.2:4443/remote-file.txt\")"
+ echo 'if [ "$remote_file_content" = "dracut-chrony-success" ]; then'
+ echo ' warn "$remote_file_content"'
+ echo "else"
+ echo ' warn "Failed to fetch remote-file.txt using HTTPS"'
+ echo " umount /sysroot"
+ echo "fi"
+ } > "$TESTDIR/fetch-remote-file.sh"
+ chmod +x "$TESTDIR/fetch-remote-file.sh"
+
+ # Synchronizing the clock via NTP usually takes ~30s, if we want to test
+ # that fetching from HTTPS fails when the clock is out of sync, we would
+ # have to wait the default 3-minute timeout for a systemd service, so with
+ # the following drop-in we can reduce the time spent in the test
+ {
+ echo "[Service]"
+ echo "TimeoutSec=1min"
+ } > "$TESTDIR/chrony-less-timeout.conf"
+
+ # Build non-hostonly initrd to avoid adding local chrony configuration,
+ # pointing to a variety of NTP servers depending on the distribution
+ test_dracut \
+ --no-hostonly \
+ -i "$TESTDIR/clock-epoch" "/usr/lib/clock-epoch" \
+ -i "$TESTDIR/$SSL_CERT" "/etc/$SSL_CERT" \
+ -i "$TESTDIR/dracut-wait.conf" "/usr/lib/systemd/system/dracut-pre-pivot.service.d/dracut-wait.conf" \
+ -i "$TESTDIR/fetch-remote-file.sh" "/var/lib/dracut/hooks/pre-pivot/10-fetch-remote-file.sh" \
+ -i "$TESTDIR/chrony-less-timeout.conf" "/usr/lib/systemd/system/chrony-wait.service.d/chrony-less-timeout.conf" \
+ -a "chrony url-lib ${USE_NETWORK}"
+}
+
+test_cleanup() {
+ stop_webserver
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions