fix(image): keep the Tor onion reachable on RTC-less boards
An RTC-less Pi boots at 1970; Tor then rejects the consensus and the unlock onion never publishes. Bake a clock floor (build epoch) into the initramfs and jump the clock forward to it before Tor starts, and pass a numeric tor_ntp= in the cmdline so busybox ntpd syncs without DNS. Proven on hardware: the onion unlock succeeded on a Pi 3 after this. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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}" \
|
||||
|
||||
@@ -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()}")
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user