From 0b9903a85ccbef75b653a4d4349db5710ee9638d Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Wed, 7 Oct 2026 19:56:47 +0200 Subject: interfaces: T3871: persist interface names on the first boot of a pre-built image The check guarding that write looked for a marker which an activation script creates only after interface naming has already run. On a deployed qcow2 the marker is absent on the first boot, so nothing was recorded and the next boot named the hardware from scratch. Removing a card then moved every name below it onto the neighbouring port, taking that interface's address with it. Only an encrypted configuration has a volume which may still be locked, so ask about that instead - everywhere else the directory is already the persistent one. --- python/vyos/ifconfig/ifname_store.py | 44 +++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) (limited to 'python') diff --git a/python/vyos/ifconfig/ifname_store.py b/python/vyos/ifconfig/ifname_store.py index e6a0876fd..9c00810db 100644 --- a/python/vyos/ifconfig/ifname_store.py +++ b/python/vyos/ifconfig/ifname_store.py @@ -218,15 +218,47 @@ def load_store(path: Path = None) -> dict: 'hardware': hardware} -def config_dir_is_mounted(path: Path = None) -> bool: - """Is the config directory the real one, or a bare rootfs overlay? +def encrypted_config_volume() -> bool: + """Does this image keep its configuration on a LUKS volume? + + Mirrors mount_encrypted_config() in src/init/vyos-router - a container + named after the running image, below the persistence path. Anything that + cannot be determined (no persistence, no version file, not a VyOS system + at all) means there is no volume waiting to be mounted over the config + directory. + """ + try: + from vyos.system.image import get_running_image + + code, out = rc_cmd('/opt/vyatta/sbin/vyos-persistpath') + if code != 0 or not out.strip(): + return False + image = get_running_image() + return bool(image) and (Path(out.strip()) / 'luks' / image).is_file() + except Exception: + return False - A failed TPM/LUKS unlock does not stop the boot. Writing to the overlay - underneath would be masked once the real volume mounts, leaving two - stores which silently diverge. + +def config_dir_is_mounted(path: Path = None) -> bool: + """Will a store written here still be there on the next boot? + + Only an encrypted configuration has a volume which may still be locked: + a failed TPM/LUKS unlock does not stop the boot, and writing to the + directory underneath would be masked once the real volume mounts, + leaving two stores which silently diverge. Everywhere else the directory + is already the persistent one. + + Deliberately not keyed on '.vyatta_config'. That marker is an + install-time hint, written by an activation script which runs *after* + interface naming - so on the first boot of a pre-built image it does not + exist yet, no store is ever written, and the next boot names the + hardware from scratch. Removing a card then shifts every name below it + onto the wrong port (T3871). """ path = path or store_path - return (path.parent / '.vyatta_config').exists() + if not encrypted_config_volume(): + return True + return os.path.ismount(path.parent) def save_store(store: dict, path: Path = None) -> bool: -- cgit v1.2.3