summaryrefslogtreecommitdiff
path: root/src/op_mode
diff options
context:
space:
mode:
Diffstat (limited to 'src/op_mode')
-rwxr-xr-xsrc/op_mode/clear_dhcp_lease.py88
-rwxr-xr-xsrc/op_mode/dhcp.py23
-rwxr-xr-xsrc/op_mode/image_installer.py79
-rwxr-xr-xsrc/op_mode/image_manager.py21
4 files changed, 104 insertions, 107 deletions
diff --git a/src/op_mode/clear_dhcp_lease.py b/src/op_mode/clear_dhcp_lease.py
deleted file mode 100755
index 7d4b47104..000000000
--- a/src/op_mode/clear_dhcp_lease.py
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library. If not, see <http://www.gnu.org/licenses/>.
-
-import argparse
-import re
-
-from vyos.configquery import ConfigTreeQuery
-from vyos.kea import kea_parse_leases
-from vyos.utils.io import ask_yes_no
-from vyos.utils.process import call
-from vyos.utils.commit import commit_in_progress
-
-# TODO: Update to use Kea control socket command "lease4-del"
-
-config = ConfigTreeQuery()
-base = ['service', 'dhcp-server']
-lease_file = '/config/dhcp/dhcp4-leases.csv'
-
-
-def del_lease_ip(address):
- """
- Read lease_file and write data to this file
- without specific section "lease ip"
- Delete section "lease x.x.x.x { x;x;x; }"
- """
- with open(lease_file, encoding='utf-8') as f:
- data = f.read().rstrip()
- pattern = rf"^{address},[^\n]+\n"
- # Delete lease for ip block
- data = re.sub(pattern, '', data)
-
- # Write new data to original lease_file
- with open(lease_file, 'w', encoding='utf-8') as f:
- f.write(data)
-
-def is_ip_in_leases(address):
- """
- Return True if address found in the lease file
- """
- leases = kea_parse_leases(lease_file)
- for lease in leases:
- if address == lease['address']:
- return True
- print(f'Address "{address}" not found in "{lease_file}"')
- return False
-
-if not config.exists(base):
- print('DHCP-server not configured!')
- exit(0)
-
-if config.exists(base + ['failover']):
- print('Lease cannot be reset in failover mode!')
- exit(0)
-
-
-if __name__ == '__main__':
- parser = argparse.ArgumentParser()
- parser.add_argument('--ip', help='IPv4 address', action='store', required=True)
-
- args = parser.parse_args()
- address = args.ip
-
- if not is_ip_in_leases(address):
- exit(1)
-
- if commit_in_progress():
- print('Cannot clear DHCP lease while a commit is in progress')
- exit(1)
-
- if not ask_yes_no(f'This will restart DHCP server.\nContinue?'):
- exit(1)
- else:
- del_lease_ip(address)
- call('systemctl restart kea-dhcp4-server.service')
diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py
index 1d9ad0e76..d27e1baf7 100755
--- a/src/op_mode/dhcp.py
+++ b/src/op_mode/dhcp.py
@@ -31,6 +31,7 @@ from vyos.configquery import ConfigTreeQuery
from vyos.kea import kea_get_active_config
from vyos.kea import kea_get_leases
from vyos.kea import kea_get_pool_from_subnet_id
+from vyos.kea import kea_delete_lease
from vyos.utils.process import is_systemd_service_running
time_string = "%a %b %d %H:%M:%S %Z %Y"
@@ -360,6 +361,28 @@ def show_server_static_mappings(raw: bool, family: ArgFamily, pool: typing.Optio
else:
return _get_formatted_server_static_mappings(static_mappings, family=family)
+def _lease_valid(inet, address):
+ leases = kea_get_leases(inet)
+ for lease in leases:
+ if address == lease['ip-address']:
+ return True
+ return False
+
+@_verify
+def clear_dhcp_server_lease(family: ArgFamily, address: str):
+ v = 'v6' if family == 'inet6' else ''
+ inet = '6' if family == 'inet6' else '4'
+
+ if not _lease_valid(inet, address):
+ print(f'Lease not found on DHCP{v} server')
+ return None
+
+ if not kea_delete_lease(inet, address):
+ print(f'Failed to clear lease for "{address}"')
+ return None
+
+ print(f'Lease "{address}" has been cleared')
+
def _get_raw_client_leases(family='inet', interface=None):
from time import mktime
from datetime import datetime
diff --git a/src/op_mode/image_installer.py b/src/op_mode/image_installer.py
index d677c2cf8..85ebd19ba 100755
--- a/src/op_mode/image_installer.py
+++ b/src/op_mode/image_installer.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This file is part of VyOS.
#
@@ -65,6 +65,8 @@ MSG_INPUT_ROOT_SIZE_SET: str = 'Please specify the size (in GB) of the root part
MSG_INPUT_CONSOLE_TYPE: str = 'What console should be used by default? (K: KVM, S: Serial, U: USB-Serial)?'
MSG_INPUT_COPY_DATA: str = 'Would you like to copy data to the new image?'
MSG_INPUT_CHOOSE_COPY_DATA: str = 'From which image would you like to save config information?'
+MSG_INPUT_COPY_ENC_DATA: str = 'Would you like to copy the encrypted config to the new image?'
+MSG_INPUT_CHOOSE_COPY_ENC_DATA: str = 'From which image would you like to copy the encrypted config?'
MSG_WARN_ISO_SIGN_INVALID: str = 'Signature is not valid. Do you want to continue with installation?'
MSG_WARN_ISO_SIGN_UNAVAL: str = 'Signature is not available. Do you want to continue with installation?'
MSG_WARN_ROOT_SIZE_TOOBIG: str = 'The size is too big. Try again.'
@@ -212,14 +214,17 @@ def search_previous_installation(disks: list[str]) -> None:
disks (list[str]): a list of available disks
"""
mnt_config = '/mnt/config'
+ mnt_encrypted_config = '/mnt/encrypted_config'
mnt_ssh = '/mnt/ssh'
mnt_tmp = '/mnt/tmp'
rmtree(Path(mnt_config), ignore_errors=True)
rmtree(Path(mnt_ssh), ignore_errors=True)
Path(mnt_tmp).mkdir(exist_ok=True)
+ Path(mnt_encrypted_config).unlink(missing_ok=True)
print('Searching for data from previous installations')
image_data = []
+ encrypted_configs = []
for disk_name in disks:
for partition in disk.partition_list(disk_name):
if disk.partition_mount(partition, mnt_tmp):
@@ -227,32 +232,61 @@ def search_previous_installation(disks: list[str]) -> None:
for path in Path(mnt_tmp + '/boot').iterdir():
if path.joinpath('rw/config/.vyatta_config').exists():
image_data.append((path.name, partition))
+ if Path(mnt_tmp + '/luks').exists():
+ for path in Path(mnt_tmp + '/luks').iterdir():
+ encrypted_configs.append((path.name, partition))
disk.partition_umount(partition)
- if len(image_data) == 1:
- image_name, image_drive = image_data[0]
- print('Found data from previous installation:')
- print(f'\t{image_name} on {image_drive}')
- if not ask_yes_no(MSG_INPUT_COPY_DATA, default=True):
- return
-
- elif len(image_data) > 1:
- print('Found data from previous installations')
- if not ask_yes_no(MSG_INPUT_COPY_DATA, default=True):
- return
-
- image_name, image_drive = select_entry(image_data,
- 'Available versions:',
- MSG_INPUT_CHOOSE_COPY_DATA,
- search_format_selection)
+ image_name = None
+ image_drive = None
+ encrypted = False
+
+ if len(image_data) > 0:
+ if len(image_data) == 1:
+ print('Found data from previous installation:')
+ print(f'\t{" on ".join(image_data[0])}')
+ if ask_yes_no(MSG_INPUT_COPY_DATA, default=True):
+ image_name, image_drive = image_data[0]
+
+ elif len(image_data) > 1:
+ print('Found data from previous installations')
+ if ask_yes_no(MSG_INPUT_COPY_DATA, default=True):
+ image_name, image_drive = select_entry(image_data,
+ 'Available versions:',
+ MSG_INPUT_CHOOSE_COPY_DATA,
+ search_format_selection)
+ elif len(encrypted_configs) > 0:
+ if len(encrypted_configs) == 1:
+ print('Found encrypted config from previous installation:')
+ print(f'\t{" on ".join(encrypted_configs[0])}')
+ if ask_yes_no(MSG_INPUT_COPY_ENC_DATA, default=True):
+ image_name, image_drive = encrypted_configs[0]
+ encrypted = True
+
+ elif len(encrypted_configs) > 1:
+ print('Found encrypted configs from previous installations')
+ if ask_yes_no(MSG_INPUT_COPY_ENC_DATA, default=True):
+ image_name, image_drive = select_entry(encrypted_configs,
+ 'Available versions:',
+ MSG_INPUT_CHOOSE_COPY_ENC_DATA,
+ search_format_selection)
+ encrypted = True
+
else:
print('No previous installation found')
return
+ if not image_name:
+ return
+
disk.partition_mount(image_drive, mnt_tmp)
- copytree(f'{mnt_tmp}/boot/{image_name}/rw/config', mnt_config)
+ if not encrypted:
+ copytree(f'{mnt_tmp}/boot/{image_name}/rw/config', mnt_config)
+ else:
+ copy(f'{mnt_tmp}/luks/{image_name}', mnt_encrypted_config)
+
Path(mnt_ssh).mkdir()
host_keys: list[str] = glob(f'{mnt_tmp}/boot/{image_name}/rw/etc/ssh/ssh_host*')
for host_key in host_keys:
@@ -279,6 +313,12 @@ def copy_previous_installation_data(target_dir: str) -> None:
dirs_exist_ok=True)
+def copy_previous_encrypted_config(target_dir: str, image_name: str) -> None:
+ if Path('/mnt/encrypted_config').exists():
+ Path(target_dir).mkdir(exist_ok=True)
+ copy('/mnt/encrypted_config', Path(target_dir).joinpath(image_name))
+
+
def ask_single_disk(disks_available: dict[str, int]) -> str:
"""Ask user to select a disk for installation
@@ -712,6 +752,9 @@ def install_image() -> None:
# owner restored on copy of config data by chmod_2775, above
copy_previous_installation_data(f'{DIR_DST_ROOT}/boot/{image_name}/rw')
+ # copy saved encrypted config volume
+ copy_previous_encrypted_config(f'{DIR_DST_ROOT}/luks', image_name)
+
if is_raid_install(install_target):
write_dir: str = f'{DIR_DST_ROOT}/boot/{image_name}/rw'
raid.update_default(write_dir)
diff --git a/src/op_mode/image_manager.py b/src/op_mode/image_manager.py
index e64a85b95..1510a667c 100755
--- a/src/op_mode/image_manager.py
+++ b/src/op_mode/image_manager.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright 2023 VyOS maintainers and contributors <maintainers@vyos.io>
+# Copyright 2023-2024 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This file is part of VyOS.
#
@@ -95,6 +95,15 @@ def delete_image(image_name: Optional[str] = None,
except Exception as err:
exit(f'Unable to remove the image "{image_name}": {err}')
+ # remove LUKS volume if it exists
+ luks_path: Path = Path(f'{persistence_storage}/luks/{image_name}')
+ if luks_path.is_file():
+ try:
+ luks_path.unlink()
+ print(f'The encrypted config for "{image_name}" was successfully deleted')
+ except Exception as err:
+ exit(f'Unable to remove the encrypted config for "{image_name}": {err}')
+
@compat.grub_cfg_update
def set_image(image_name: Optional[str] = None,
@@ -174,6 +183,16 @@ def rename_image(name_old: str, name_new: str) -> None:
except Exception as err:
exit(f'Unable to rename image "{name_old}" to "{name_new}": {err}')
+ # rename LUKS volume if it exists
+ old_luks_path: Path = Path(f'{persistence_storage}/luks/{name_old}')
+ if old_luks_path.is_file():
+ try:
+ new_luks_path: Path = Path(f'{persistence_storage}/luks/{name_new}')
+ old_luks_path.rename(new_luks_path)
+ print(f'The encrypted config for "{name_old}" was successfully renamed to "{name_new}"')
+ except Exception as err:
+ exit(f'Unable to rename the encrypted config for "{name_old}" to "{name_new}": {err}')
+
def list_images() -> None:
"""Print list of available images for CLI hints"""