summaryrefslogtreecommitdiff
path: root/src/migration-scripts/interfaces
diff options
context:
space:
mode:
authorOleksandr Kuchmystyi <o.kuchmystyi@vyos.io>2026-04-24 17:30:32 +0300
committerOleksandr Kuchmystyi <o.kuchmystyi@vyos.io>2026-04-28 10:40:59 +0300
commit996a872eca8786ddd4335dd23dd9cb048ec11ffd (patch)
treec6f67cb13f7a1dde25aedbdcd29b565d351815ff /src/migration-scripts/interfaces
parent6bbd589ae563e4737845217710ff80c73a3b8fc4 (diff)
downloadvyos-1x-996a872eca8786ddd4335dd23dd9cb048ec11ffd.tar.gz
vyos-1x-996a872eca8786ddd4335dd23dd9cb048ec11ffd.zip
migration: T8280: Fix auth file read for interfaces on first boot of new image
During first boot after installing a new VyOS image, migrations are executed before the filesystem is fully initialized. At this point `/config` is not yet bind-mounted, causing auth files (certificates, keys, credentials) for OpenVPN, WireGuard and Ethernet EAPoL interfaces under `/config/auth` to be inaccessible even though they exist under the underlying `/opt/vyatta/etc/config/auth` path which is available at all boot stages. Add fallback logic to `read_auth_file()` to retry the file lookup under `/opt/vyatta/etc/config/auth` when the file is not found under `/config/auth`.
Diffstat (limited to 'src/migration-scripts/interfaces')
-rw-r--r--src/migration-scripts/interfaces/25-to-26200
1 files changed, 78 insertions, 122 deletions
diff --git a/src/migration-scripts/interfaces/25-to-26 b/src/migration-scripts/interfaces/25-to-26
index e68a75d08..1f10e3dca 100644
--- a/src/migration-scripts/interfaces/25-to-26
+++ b/src/migration-scripts/interfaces/25-to-26
@@ -29,6 +29,7 @@ from vyos.pki import encode_dh_parameters
from vyos.pki import encode_private_key
from vyos.pki import verify_crl
from vyos.utils.process import run
+from vyos.utils.file import read_file
def wrapped_pem_to_config_value(pem):
out = []
@@ -38,20 +39,28 @@ def wrapped_pem_to_config_value(pem):
out.append(line)
return "".join(out)
-def read_file_for_pki(config_auth_path):
- full_path = os.path.join(AUTH_DIR, config_auth_path)
- output = None
+def read_auth_file(config_auth_path):
+ full_path = os.path.normpath(os.path.join(AUTH_DIR, config_auth_path))
+
+ # If the file is not found under `/config/auth`, it may be because the `/config`
+ # partition has not been bind-mounted yet during early boot migration execution.
+ # Fall back to the equivalent path under `/opt/vyatta/etc/config/auth` which
+ # is accessible at all boot stages.
+ if not os.path.isfile(full_path) and full_path.startswith(f'{AUTH_DIR}/'):
+ full_path = AUTH_DIR_FALLBACK + full_path[len(AUTH_DIR): ]
if os.path.isfile(full_path):
if not os.access(full_path, os.R_OK):
- run(f'sudo chmod 644 {full_path}')
+ run(['sudo', 'chmod', '644', full_path])
+
+ return read_file(full_path)
- with open(full_path, 'r') as f:
- output = f.read()
+ return None
- return output
AUTH_DIR = '/config/auth'
+AUTH_DIR_FALLBACK = '/opt/vyatta/etc/config/auth'
+
pki_base = ['pki']
def migrate(config: ConfigTree) -> None:
@@ -69,7 +78,7 @@ def migrate(config: ConfigTree) -> None:
config.set_tag(pki_base + ['openvpn', 'shared-secret'])
key_file = config.return_value(base + [interface, 'shared-secret-key-file'])
- key = read_file_for_pki(key_file)
+ key = read_auth_file(key_file)
key_pki_name = f'{pki_name}_shared'
if key:
@@ -107,7 +116,7 @@ def migrate(config: ConfigTree) -> None:
config.set_tag(pki_base + ['openvpn', 'shared-secret'])
key_file = config.return_value(base + [interface, 'tls', 'auth-file'])
- key = read_file_for_pki(key_file)
+ key = read_auth_file(key_file)
key_pki_name = f'{pki_name}_auth'
if key:
@@ -142,7 +151,7 @@ def migrate(config: ConfigTree) -> None:
config.set_tag(pki_base + ['openvpn', 'shared-secret'])
key_file = config.return_value(base + [interface, 'tls', 'crypt-file'])
- key = read_file_for_pki(key_file)
+ key = read_auth_file(key_file)
key_pki_name = f'{pki_name}_crypt'
if key:
@@ -179,46 +188,41 @@ def migrate(config: ConfigTree) -> None:
config.set_tag(pki_base + ['ca'])
cert_file = config.return_value(x509_base + ['ca-cert-file'])
- cert_path = os.path.join(AUTH_DIR, cert_file)
-
- if os.path.isfile(cert_path):
- if not os.access(cert_path, os.R_OK):
- run(f'sudo chmod 644 {cert_path}')
-
- with open(cert_path, 'r') as f:
- certs_str = f.read()
- certs_data = certs_str.split(CERT_BEGIN)
- index = 1
- for cert_data in certs_data[1:]:
- cert = load_certificate(CERT_BEGIN + cert_data, wrap_tags=False)
-
- if cert:
- ca_certs[f'{pki_name}_{index}'] = cert
- cert_pem = encode_certificate(cert)
-
- # Check if CA already exists - no need to check node existence as
- # it is always created above when entering this context
- ca_exists = None
- for ca_name in config.list_nodes(pki_base + ['ca']):
- ca_cert_path = pki_base + ['ca', ca_name, 'certificate']
- if not config.exists(ca_cert_path):
- continue
-
- ca_base64 = config.return_value(ca_cert_path)
- # Check for duplicate cert/key - and re-use if possible
- if ca_base64 == wrapped_pem_to_config_value(cert_pem):
- ca_exists = ca_name
- break
-
- if ca_exists:
- config.set(x509_base + ['ca-certificate'], value=ca_exists, replace=False)
- else:
- config.set(pki_base + ['ca', f'{pki_name}_{index}', 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
- config.set(x509_base + ['ca-certificate'], value=f'{pki_name}_{index}', replace=False)
+ certs_str = read_auth_file(cert_file)
+
+ if certs_str:
+ certs_data = certs_str.split(CERT_BEGIN)
+ index = 1
+ for cert_data in certs_data[1:]:
+ cert = load_certificate(CERT_BEGIN + cert_data, wrap_tags=False)
+
+ if cert:
+ ca_certs[f'{pki_name}_{index}'] = cert
+ cert_pem = encode_certificate(cert)
+
+ # Check if CA already exists - no need to check node existence as
+ # it is always created above when entering this context
+ ca_exists = None
+ for ca_name in config.list_nodes(pki_base + ['ca']):
+ ca_cert_path = pki_base + ['ca', ca_name, 'certificate']
+ if not config.exists(ca_cert_path):
+ continue
+
+ ca_base64 = config.return_value(ca_cert_path)
+ # Check for duplicate cert/key - and re-use if possible
+ if ca_base64 == wrapped_pem_to_config_value(cert_pem):
+ ca_exists = ca_name
+ break
+
+ if ca_exists:
+ config.set(x509_base + ['ca-certificate'], value=ca_exists, replace=False)
else:
- print(f'Failed to migrate CA certificate on openvpn interface {interface}')
+ config.set(pki_base + ['ca', f'{pki_name}_{index}', 'certificate'], value=wrapped_pem_to_config_value(cert_pem))
+ config.set(x509_base + ['ca-certificate'], value=f'{pki_name}_{index}', replace=False)
+ else:
+ print(f'Failed to migrate CA certificate on openvpn interface {interface}')
- index += 1
+ index += 1
else:
print(f'Failed to migrate CA certificate on openvpn interface {interface}')
@@ -230,24 +234,18 @@ def migrate(config: ConfigTree) -> None:
config.set_tag(pki_base + ['ca'])
crl_file = config.return_value(x509_base + ['crl-file'])
- crl_path = os.path.join(AUTH_DIR, crl_file)
- crl = None
- crl_ca_name = None
+ crl_data = read_auth_file(crl_file)
- if os.path.isfile(crl_path):
- if not os.access(crl_path, os.R_OK):
- run(f'sudo chmod 644 {crl_path}')
-
- with open(crl_path, 'r') as f:
- crl_data = f.read()
- crl = load_crl(crl_data, wrap_tags=False)
+ crl = load_crl(crl_data, wrap_tags=False) if crl_data else None
+ crl_ca_name = None
- for ca_name, ca_cert in ca_certs.items():
- if verify_crl(crl, ca_cert):
- crl_ca_name = ca_name
- break
+ if crl:
+ for ca_name, ca_cert in ca_certs.items():
+ if verify_crl(crl, ca_cert):
+ crl_ca_name = ca_name
+ break
- if crl and crl_ca_name:
+ if crl_ca_name:
crl_pem = encode_certificate(crl)
# Check if CRL already exists - no need to check node
@@ -277,16 +275,9 @@ def migrate(config: ConfigTree) -> None:
config.set_tag(pki_base + ['certificate'])
cert_file = config.return_value(x509_base + ['cert-file'])
- cert_path = os.path.join(AUTH_DIR, cert_file)
- cert = None
+ cert_data = read_auth_file(cert_file)
- if os.path.isfile(cert_path):
- if not os.access(cert_path, os.R_OK):
- run(f'sudo chmod 644 {cert_path}')
-
- with open(cert_path, 'r') as f:
- cert_data = f.read()
- cert = load_certificate(cert_data, wrap_tags=False)
+ cert = load_certificate(cert_data, wrap_tags=False) if cert_data else None
if cert:
cert_pem = encode_certificate(cert)
@@ -316,16 +307,9 @@ def migrate(config: ConfigTree) -> None:
if config.exists(x509_base + ['key-file']):
key_file = config.return_value(x509_base + ['key-file'])
- key_path = os.path.join(AUTH_DIR, key_file)
- key = None
-
- if os.path.isfile(key_path):
- if not os.access(key_path, os.R_OK):
- run(f'sudo chmod 644 {key_path}')
+ key_data = read_auth_file(key_file)
- with open(key_path, 'r') as f:
- key_data = f.read()
- key = load_private_key(key_data, passphrase=None, wrap_tags=False)
+ key = load_private_key(key_data, passphrase=None, wrap_tags=False) if key_data else None
if key:
key_pem = encode_private_key(key, passphrase=None)
@@ -356,16 +340,9 @@ def migrate(config: ConfigTree) -> None:
config.set_tag(pki_base + ['dh'])
dh_file = config.return_value(x509_base + ['dh-file'])
- dh_path = os.path.join(AUTH_DIR, dh_file)
- dh = None
-
- if os.path.isfile(dh_path):
- if not os.access(dh_path, os.R_OK):
- run(f'sudo chmod 644 {dh_path}')
+ dh_data = read_auth_file(dh_file)
- with open(dh_path, 'r') as f:
- dh_data = f.read()
- dh = load_dh_parameters(dh_data, wrap_tags=False)
+ dh = load_dh_parameters(dh_data, wrap_tags=False) if dh_data else None
if dh:
dh_pem = encode_dh_parameters(dh)
@@ -405,15 +382,15 @@ def migrate(config: ConfigTree) -> None:
if config.exists(private_key_path):
key_file = config.return_value(private_key_path)
- full_key_path = f'/config/auth/wireguard/{key_file}/private.key'
+ full_key_path = f'{AUTH_DIR}/wireguard/{key_file}/private.key'
+ key_data = read_auth_file(full_key_path)
- if not os.path.exists(full_key_path):
+ if not key_data:
print(f'Could not find wireguard private key for migration on interface "{interface}"')
continue
- with open(full_key_path, 'r') as f:
- key_data = f.read().strip()
- config.set(private_key_path, value=key_data)
+ key_data = key_data.strip()
+ config.set(private_key_path, value=key_data)
for peer in config.list_nodes(base + [interface, 'peer']):
config.rename(base + [interface, 'peer', peer, 'pubkey'], 'public-key')
@@ -435,16 +412,9 @@ def migrate(config: ConfigTree) -> None:
config.set_tag(pki_base + ['ca'])
cert_file = config.return_value(x509_base + ['ca-cert-file'])
- cert_path = os.path.join(AUTH_DIR, cert_file)
- cert = None
+ cert_data = read_auth_file(cert_file)
- if os.path.isfile(cert_path):
- if not os.access(cert_path, os.R_OK):
- run(f'sudo chmod 644 {cert_path}')
-
- with open(cert_path, 'r') as f:
- cert_data = f.read()
- cert = load_certificate(cert_data, wrap_tags=False)
+ cert = load_certificate(cert_data, wrap_tags=False) if cert_data else None
if cert:
cert_pem = encode_certificate(cert)
@@ -461,16 +431,9 @@ def migrate(config: ConfigTree) -> None:
config.set_tag(pki_base + ['certificate'])
cert_file = config.return_value(x509_base + ['cert-file'])
- cert_path = os.path.join(AUTH_DIR, cert_file)
- cert = None
-
- if os.path.isfile(cert_path):
- if not os.access(cert_path, os.R_OK):
- run(f'sudo chmod 644 {cert_path}')
+ cert_data = read_auth_file(cert_file)
- with open(cert_path, 'r') as f:
- cert_data = f.read()
- cert = load_certificate(cert_data, wrap_tags=False)
+ cert = load_certificate(cert_data, wrap_tags=False) if cert_data else None
if cert:
cert_pem = encode_certificate(cert)
@@ -483,16 +446,9 @@ def migrate(config: ConfigTree) -> None:
if config.exists(x509_base + ['key-file']):
key_file = config.return_value(x509_base + ['key-file'])
- key_path = os.path.join(AUTH_DIR, key_file)
- key = None
-
- if os.path.isfile(key_path):
- if not os.access(key_path, os.R_OK):
- run(f'sudo chmod 644 {key_path}')
+ key_data = read_auth_file(key_file)
- with open(key_path, 'r') as f:
- key_data = f.read()
- key = load_private_key(key_data, passphrase=None, wrap_tags=False)
+ key = load_private_key(key_data, passphrase=None, wrap_tags=False) if key_data else None
if key:
key_pem = encode_private_key(key, passphrase=None)