Skip to content

Commit 8ecac32

Browse files
committed
T5455: copy known SSH host fingerprints on image update
1 parent 42f33f0 commit 8ecac32

File tree

1 file changed

+91
-7
lines changed

1 file changed

+91
-7
lines changed

src/op_mode/image_installer.py

+91-7
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from os import readlink
2727
from os import getpid, getppid
2828
from typing import Union
29-
from urllib.parse import urlparse
29+
from urllib.parse import urlparse, uses_relative
3030
from passlib.hosts import linux_context
3131
from errno import ENOSPC
3232

@@ -37,7 +37,7 @@
3737
from vyos.system import disk, grub, image, compat, raid, SYSTEM_CFG_VER
3838
from vyos.template import render
3939
from vyos.utils.io import ask_input, ask_yes_no, select_entry
40-
from vyos.utils.file import chmod_2775
40+
from vyos.utils.file import chmod, chmod_2775
4141
from vyos.utils.process import cmd, run, rc_cmd
4242
from vyos.version import get_version_data
4343

@@ -318,6 +318,30 @@ def copy_preserve_owner(src: str, dst: str, *, follow_symlinks=True):
318318
chown(dst, user=st.st_uid)
319319

320320

321+
def copy_all_matching(src: str, dst: str, *, follow_symlinks=True) -> None:
322+
"""Copies all that match the given pattern files from to the target
323+
324+
:param src: Path to the file or files to copy; can be a pattern
325+
:param dst: destination path;
326+
:param follow_symlinks: If False, symlinks won't be followed
327+
"""
328+
og_files: list[str] = glob(src)
329+
for og in og_files:
330+
if not Path(og).is_file():
331+
return
332+
# Create directory if needed and ensure proper ownership
333+
if Path(dst).is_dir():
334+
st = Path(src).parent.stat()
335+
Path(dst).mkdir(parents=True, exist_ok=True)
336+
chmod(Path(dst), st.st_mode)
337+
chown(Path(dst), user=st.st_uid, group=st.st_gid)
338+
dst = Path(dst).joinpath(Path(og).name)
339+
340+
st = Path(og).stat()
341+
copy(og, dst, follow_symlinks=follow_symlinks)
342+
chown(dst, user=st.st_uid, group=st.st_gid)
343+
344+
321345
def copy_previous_installation_data(target_dir: str) -> None:
322346
if Path('/mnt/config').exists():
323347
copytree('/mnt/config', f'{target_dir}/opt/vyatta/etc/config',
@@ -634,6 +658,19 @@ def copy_ssh_host_keys() -> bool:
634658
return False
635659

636660

661+
def copy_ssh_hosts_fingerprints() -> bool:
662+
"""Ask user to copy known SSH hosts (fingerprints)
663+
664+
Returns:
665+
bool: user's decision
666+
"""
667+
question = 'Would you like to save the SSH known hosts (fingerprints) '\
668+
'from your current configuration?'
669+
if ask_yes_no(question, default=True):
670+
return True
671+
return False
672+
673+
637674
def console_hint() -> str:
638675
pid = getppid() if 'SUDO_USER' in environ else getpid()
639676
try:
@@ -995,13 +1032,60 @@ def add_image(image_path: str, vrf: str = None, username: str = '',
9951032
chmod_2775(target_config_dir)
9961033
Path(f'{target_config_dir}/.vyatta_config').touch()
9971034

998-
target_ssh_dir: str = f'{root_dir}/boot/{image_name}/rw/etc/ssh/'
1035+
src_ssh_etc: str = '/etc/ssh/known_hosts'
1036+
src_ssh_root: str = '/root/.ssh'
1037+
target_ssh_etc: str = f'{root_dir}/boot/{image_name}/rw/etc/ssh/'
1038+
target_ssh_root: str = f'{root_dir}/boot/{image_name}/rw/root/.ssh/'
1039+
9991040
if no_prompt or copy_ssh_host_keys():
10001041
print('Copying SSH host keys')
1001-
Path(target_ssh_dir).mkdir(parents=True)
1002-
host_keys: list[str] = glob('/etc/ssh/ssh_host*')
1003-
for host_key in host_keys:
1004-
copy(host_key, target_ssh_dir)
1042+
copy_all_matching('/etc/ssh/ssh_host*', target_ssh_etc)
1043+
# Path(target_ssh_etc).mkdir(parents=True)
1044+
# host_keys: list[str] = glob('/etc/ssh/ssh_host*')
1045+
# for host_key in host_keys:
1046+
# copy(host_key, target_ssh_etc)
1047+
1048+
if no_prompt or copy_ssh_hosts_fingerprints():
1049+
if not any([
1050+
Path(src_ssh_etc).exists(),
1051+
Path(src_ssh_root).exists(),
1052+
]):
1053+
print('No SSH host fingerprints are found')
1054+
else:
1055+
print('Copying known SSH hosts (fingerprints)')
1056+
if Path(src_ssh_etc).exists():
1057+
copy_all_matching(
1058+
src=src_ssh_etc,
1059+
dst=target_ssh_etc
1060+
)
1061+
1062+
if Path(src_ssh_root).exists():
1063+
copy_all_matching(
1064+
src=src_ssh_root,
1065+
dst=target_ssh_root
1066+
)
1067+
# print('Copying known SSH hosts (fingerprints)')
1068+
# # Copy global fingerprints
1069+
# copy_all_matching(
1070+
# src='/etc/ssh/known_hosts*',
1071+
# dst=target_ssh_etc,
1072+
# follow_symlinks=False
1073+
# )
1074+
# # Copy saved fingerprints of each user
1075+
# homedirs: list[str] = glob('/home/*')
1076+
# for user_dir in homedirs:
1077+
# # target = f'{root_dir}/boot/{image_name}/rw{user_dir}/.ssh/'
1078+
# # copy_all_matching(
1079+
# # src=f'{user_dir}/.ssh/known_hosts*',
1080+
# # dst=target,
1081+
# # follow_symlinks=False
1082+
# # )
1083+
# # Copy root fingerprints
1084+
# copy_all_matching(
1085+
# src='/root/.ssh/known_hosts*',
1086+
# dst=target_ssh_root,
1087+
# follow_symlinks=False
1088+
# )
10051089

10061090
# copy system image and kernel files
10071091
print('Copying system image files')

0 commit comments

Comments
 (0)