diff --git a/lim/configuration/initramfs-tools/tor_hook b/lim/configuration/initramfs-tools/tor_hook index 6650f1e..6cd29c8 100644 --- a/lim/configuration/initramfs-tools/tor_hook +++ b/lim/configuration/initramfs-tools/tor_hook @@ -25,6 +25,8 @@ for bb in /bin/busybox /usr/bin/busybox; do [ -x "$bb" ] && { copy_exec "$bb" /usr/local/bin/busybox; break; } done +date -u +%s > "${DESTDIR}/etc/tor-clock-floor" + # Onion keys + torrc, staged on the system by lim. mkdir -p "${DESTDIR}/etc/tor/onion" for key in hostname hs_ed25519_public_key hs_ed25519_secret_key; do diff --git a/lim/configuration/initramfs-tools/tor_premount b/lim/configuration/initramfs-tools/tor_premount index a5e3397..f067acd 100644 --- a/lim/configuration/initramfs-tools/tor_premount +++ b/lim/configuration/initramfs-tools/tor_premount @@ -34,8 +34,12 @@ if [ ! -s /etc/resolv.conf ]; then done fi -# RTC-less boards boot at 1970; Tor rejects the consensus otherwise. Bounded so -# a failed sync never blocks boot. +floor=$(cat /etc/tor-clock-floor 2>/dev/null || echo 0) +if [ "$(date +%s)" -lt "$floor" ]; then + /usr/local/bin/busybox date -s "@$floor" >/dev/null 2>&1 \ + || date -s "@$floor" >/dev/null 2>&1 || true +fi + if [ -x /usr/local/bin/busybox ]; then /usr/local/bin/busybox timeout 30 \ /usr/local/bin/busybox ntpd -n -q -p "${tor_ntp:-pool.ntp.org}" \ diff --git a/lim/image/initramfs/initramfs_tools.py b/lim/image/initramfs/initramfs_tools.py index e07265f..cbb1e37 100644 --- a/lim/image/initramfs/initramfs_tools.py +++ b/lim/image/initramfs/initramfs_tools.py @@ -20,6 +20,9 @@ from lim.image.initramfs.base import InitramfsBackend from lim.image.plan import ImagePlan from lim.image.session import ImageSession, chroot_bash, install_packages +# Numeric IP, not a hostname: the initramfs has no DNS resolver. +_DEFAULT_NTP = "162.159.200.123" + class InitramfsToolsBackend(InitramfsBackend): def luks_package_collection(self) -> str: @@ -91,12 +94,7 @@ class InitramfsToolsBackend(InitramfsBackend): "update-initramfs -c -k all 2>/dev/null || update-initramfs -u -k all", ) - def configure_bootloader( - self, - plan: ImagePlan, # noqa: ARG002 - session: ImageSession, - root: Path, - ) -> None: + def configure_bootloader(self, plan: ImagePlan, session: ImageSession, root: Path) -> None: hostname = (root / "etc/hostname").read_text().strip() boot = session.boot_mount_path @@ -113,5 +111,7 @@ class InitramfsToolsBackend(InitramfsBackend): text = f"{text} ip=::::{hostname}:eth0:dhcp" if "net.ifnames=0" not in text: text = f"{text} net.ifnames=0" + if plan.tor_unlock and "tor_ntp=" not in text: + text = f"{text} tor_ntp={_DEFAULT_NTP}" cmdline_txt.write_text(text + "\n") ui.info(f"Content of {cmdline_txt}:{cmdline_txt.read_text()}") diff --git a/tests/unit/test_tor.py b/tests/unit/test_tor.py index b8061b1..49e4c08 100644 --- a/tests/unit/test_tor.py +++ b/tests/unit/test_tor.py @@ -174,6 +174,7 @@ class TestInitramfsToolsBackend: assert "root=/dev/mapper/cryptroot" in cmdline assert "root=PARTUUID=abcd-02" not in cmdline assert "ip=::::pi:eth0:dhcp" in cmdline + assert "tor_ntp=" in cmdline assert cmdline.count("\n") == 1 # cmdline must stay a single line assert "auto_initramfs=1" in (session.boot_mount_path / "config.txt").read_text() @@ -209,3 +210,11 @@ class TestInitramfsToolsHookHardening: premount = self._read("tor_premount") assert '. "$conf"' not in premount assert "sed -n 's/^IPV4DNS" in premount + + def test_hook_stamps_a_clock_floor(self): + assert "/etc/tor-clock-floor" in self._read("tor_hook") + + def test_premount_raises_clock_to_floor_before_ntp(self): + premount = self._read("tor_premount") + assert "/etc/tor-clock-floor" in premount + assert premount.index("tor-clock-floor") < premount.index("ntpd")