linux-image-manager 2.3.0__tar.gz → 2.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/PKG-INFO +1 -1
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/packages/server/luks.txt +1 -0
- linux_image_manager-2.4.0/lim/image/hostkeys.py +77 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/initramfs/base.py +4 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/initramfs/initramfs_tools.py +19 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/initramfs/mkinitcpio.py +6 -1
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/unlock.py +21 -12
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/wizard.py +30 -9
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/linux_image_manager.egg-info/PKG-INFO +1 -1
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/linux_image_manager.egg-info/SOURCES.txt +1 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/pyproject.toml +1 -1
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/LICENSE.txt +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/README.md +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/__init__.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/catalog.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/cli.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/config.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/initcpio/tor_hook +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/initcpio/tor_install +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/initcpio/torrc +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/initramfs-tools/tor_bottom +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/initramfs-tools/tor_hook +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/initramfs-tools/tor_premount +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/initramfs-tools/torrc +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/packages/general.txt +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/packages/server/luks-debian.txt +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/packages/server/tor-debian.txt +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/packages/server/tor.txt +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/data/__init__.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/data/crypt.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/data/sync.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/device.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/distributions.yml +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/errors.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/fsutil.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/__init__.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/backup.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/choosers.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/chroot.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/crossarch.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/encryption.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/initramfs/__init__.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/initramfs/keygen.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/loopimg.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/plan.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/raspberry.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/session.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/setup.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/transfer.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/verify.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/luks.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/packages.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/runner.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/storage/__init__.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/storage/common.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/storage/raid1.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/storage/single_drive.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/system.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/ui.py +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/linux_image_manager.egg-info/dependency_links.txt +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/linux_image_manager.egg-info/entry_points.txt +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/linux_image_manager.egg-info/requires.txt +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/linux_image_manager.egg-info/top_level.txt +0 -0
- {linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: linux-image-manager
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: CLI (`lim`) for downloading, verifying and flashing Linux images, LUKS/Btrfs encrypted storage (single drive and RAID1), image backups and chroot maintenance
|
|
5
5
|
Author-email: Kevin Veen-Birkenbach <kevin@veen.world>
|
|
6
6
|
Maintainer-email: Kevin Veen-Birkenbach <kevin@veen.world>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pwd
|
|
3
|
+
import re
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
KNOWN_HOSTS_SUBPATH = ".config/lim/known_hosts"
|
|
7
|
+
SSH_CONFIG_SUBPATH = ".ssh/config"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def host_key_alias(name: str) -> str:
|
|
11
|
+
return f"boot.{name}"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def known_hosts_path(home: Path) -> Path:
|
|
15
|
+
return home / KNOWN_HOSTS_SUBPATH
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def replace_known_hosts(text: str, alias: str, public_keys: list[str]) -> str:
|
|
19
|
+
kept = [line for line in text.splitlines() if line.split(" ", 1)[0] != alias]
|
|
20
|
+
pinned = [f"{alias} {' '.join(key.split()[:2])}" for key in public_keys]
|
|
21
|
+
return "\n".join([*kept, *pinned]) + "\n"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def replace_config_block(text: str, alias: str, block: str) -> str:
|
|
25
|
+
begin, end = f"# lim:{alias}:begin", f"# lim:{alias}:end"
|
|
26
|
+
pattern = rf"^{re.escape(begin)}\n.*?^{re.escape(end)}\n?"
|
|
27
|
+
kept = re.sub(pattern, "", text, flags=re.MULTILINE | re.DOTALL)
|
|
28
|
+
if kept and not kept.endswith("\n"):
|
|
29
|
+
kept += "\n"
|
|
30
|
+
return f"{kept}{begin}\n{block}{end}\n"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def config_block(alias: str, target: str, identity_file: str, known_hosts: Path) -> str:
|
|
34
|
+
lines = [
|
|
35
|
+
f"Host {alias}",
|
|
36
|
+
f" HostName {target}",
|
|
37
|
+
" User root",
|
|
38
|
+
f" HostKeyAlias {alias}",
|
|
39
|
+
f" UserKnownHostsFile {known_hosts}",
|
|
40
|
+
" StrictHostKeyChecking yes",
|
|
41
|
+
]
|
|
42
|
+
if identity_file:
|
|
43
|
+
lines.append(f" IdentityFile {identity_file}")
|
|
44
|
+
return "\n".join(lines) + "\n"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _write_owned(path: Path, text: str, owner: pwd.struct_passwd) -> None:
|
|
48
|
+
path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
|
|
49
|
+
path.write_text(text)
|
|
50
|
+
path.chmod(0o600)
|
|
51
|
+
for entry in (path, path.parent):
|
|
52
|
+
os.chown(entry, owner.pw_uid, owner.pw_gid)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def pin(
|
|
56
|
+
owner: pwd.struct_passwd, name: str, target: str, identity_file: str, public_keys: list[str]
|
|
57
|
+
) -> str:
|
|
58
|
+
"""Write the pinned keys and a ``Host boot.<name>`` ssh config block; return the alias.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
owner: passwd entry of the operator whose home receives the files.
|
|
62
|
+
name: unlock record name; the alias becomes ``boot.<name>``.
|
|
63
|
+
target: onion address or host/IP the initramfs answers on.
|
|
64
|
+
identity_file: private key path for the unlock, empty for ssh defaults.
|
|
65
|
+
public_keys: OpenSSH ``type base64 [comment]`` lines the initramfs serves.
|
|
66
|
+
|
|
67
|
+
"""
|
|
68
|
+
home = Path(owner.pw_dir)
|
|
69
|
+
alias = host_key_alias(name)
|
|
70
|
+
known_hosts = known_hosts_path(home)
|
|
71
|
+
current = known_hosts.read_text() if known_hosts.is_file() else ""
|
|
72
|
+
_write_owned(known_hosts, replace_known_hosts(current, alias, public_keys), owner)
|
|
73
|
+
ssh_config = home / SSH_CONFIG_SUBPATH
|
|
74
|
+
current = ssh_config.read_text() if ssh_config.is_file() else ""
|
|
75
|
+
block = config_block(alias, target, identity_file, known_hosts)
|
|
76
|
+
_write_owned(ssh_config, replace_config_block(current, alias, block), owner)
|
|
77
|
+
return alias
|
|
@@ -40,3 +40,7 @@ class InitramfsBackend(ABC):
|
|
|
40
40
|
@abstractmethod
|
|
41
41
|
def configure_bootloader(self, plan: ImagePlan, session: ImageSession, root: Path) -> None:
|
|
42
42
|
"""Point the bootloader at the encrypted root with early networking."""
|
|
43
|
+
|
|
44
|
+
@abstractmethod
|
|
45
|
+
def host_public_keys(self, root: Path) -> list[str]:
|
|
46
|
+
"""OpenSSH public key lines of the host keys baked into the initramfs."""
|
{linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/image/initramfs/initramfs_tools.py
RENAMED
|
@@ -22,6 +22,7 @@ from lim.image.session import ImageSession, chroot_bash, install_packages
|
|
|
22
22
|
|
|
23
23
|
# Numeric IP, not a hostname: the initramfs has no DNS resolver.
|
|
24
24
|
_DEFAULT_NTP = "162.159.200.123"
|
|
25
|
+
_DROPBEAR_KEY_DIRS = ("etc/dropbear/initramfs", "etc/dropbear-initramfs")
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
class InitramfsToolsBackend(InitramfsBackend):
|
|
@@ -115,3 +116,21 @@ class InitramfsToolsBackend(InitramfsBackend):
|
|
|
115
116
|
text = f"{text} tor_ntp={_DEFAULT_NTP}"
|
|
116
117
|
cmdline_txt.write_text(text + "\n")
|
|
117
118
|
ui.info(f"Content of {cmdline_txt}:{cmdline_txt.read_text()}")
|
|
119
|
+
|
|
120
|
+
def host_public_keys(self, root: Path) -> list[str]:
|
|
121
|
+
key_paths = [
|
|
122
|
+
f"{key_dir}/dropbear_{kind}_host_key"
|
|
123
|
+
for key_dir in _DROPBEAR_KEY_DIRS
|
|
124
|
+
for kind in ("ed25519", "ecdsa", "rsa")
|
|
125
|
+
]
|
|
126
|
+
printed = [
|
|
127
|
+
runner.output(["chroot", str(root), "dropbearkey", "-y", "-f", f"/{path}"], sudo=True)
|
|
128
|
+
for path in key_paths
|
|
129
|
+
if (root / path).is_file()
|
|
130
|
+
]
|
|
131
|
+
return [
|
|
132
|
+
line
|
|
133
|
+
for text in printed
|
|
134
|
+
for line in text.splitlines()
|
|
135
|
+
if line.startswith(("ssh-", "ecdsa-"))
|
|
136
|
+
]
|
|
@@ -16,6 +16,7 @@ MKINITCPIO_HOOKS_PREFIX = (
|
|
|
16
16
|
"base udev autodetect microcode modconf kms keyboard keymap consolefont block"
|
|
17
17
|
)
|
|
18
18
|
MKINITCPIO_HOOKS_SUFFIX = "filesystems fsck"
|
|
19
|
+
DROPBEAR_KEY_TYPES = ("ecdsa", "rsa")
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class MkinitcpioBackend(InitramfsBackend):
|
|
@@ -80,7 +81,11 @@ class MkinitcpioBackend(InitramfsBackend):
|
|
|
80
81
|
)
|
|
81
82
|
ui.info(f"Content of {mkinitcpio_path}:{mkinitcpio_path.read_text()}")
|
|
82
83
|
ui.info("Generating mkinitcpio...")
|
|
83
|
-
chroot_bash(root, "
|
|
84
|
+
chroot_bash(root, "ssh-keygen -A\nmkinitcpio -vP")
|
|
85
|
+
|
|
86
|
+
def host_public_keys(self, root: Path) -> list[str]:
|
|
87
|
+
paths = (root / f"etc/ssh/ssh_host_{kind}_key.pub" for kind in DROPBEAR_KEY_TYPES)
|
|
88
|
+
return [path.read_text().strip() for path in paths if path.is_file()]
|
|
84
89
|
|
|
85
90
|
def configure_bootloader(self, plan: ImagePlan, session: ImageSession, root: Path) -> None:
|
|
86
91
|
cryptdevice = (
|
|
@@ -11,6 +11,7 @@ from pathlib import Path
|
|
|
11
11
|
|
|
12
12
|
from lim import runner, ui
|
|
13
13
|
from lim.errors import LimError
|
|
14
|
+
from lim.image import hostkeys
|
|
14
15
|
|
|
15
16
|
RECORDS_SUBPATH = ".config/lim/unlocks"
|
|
16
17
|
|
|
@@ -19,13 +20,17 @@ def records_dir(home: Path | None = None) -> Path:
|
|
|
19
20
|
return (home or Path.home()) / RECORDS_SUBPATH
|
|
20
21
|
|
|
21
22
|
|
|
22
|
-
def build_unlock_argv(target: str, key: str, unlock_command: str) -> list[str]:
|
|
23
|
-
"""SSH invocation for the unlock; torsocks-wrapped for .onion targets.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
def build_unlock_argv(target: str, key: str, unlock_command: str, alias: str) -> list[str]:
|
|
24
|
+
"""SSH invocation for the unlock; torsocks-wrapped for .onion targets."""
|
|
25
|
+
ssh = [
|
|
26
|
+
"ssh",
|
|
27
|
+
"-o",
|
|
28
|
+
f"HostKeyAlias={alias}",
|
|
29
|
+
"-o",
|
|
30
|
+
f"UserKnownHostsFile={hostkeys.known_hosts_path(Path.home())}",
|
|
31
|
+
"-o",
|
|
32
|
+
"StrictHostKeyChecking=ask",
|
|
33
|
+
]
|
|
29
34
|
if key:
|
|
30
35
|
ssh += ["-i", str(Path(key).expanduser())]
|
|
31
36
|
if unlock_command:
|
|
@@ -66,7 +71,7 @@ def _load_records() -> list[tuple[str, dict]]:
|
|
|
66
71
|
return [(name, record) for name, record in loaded if record is not None]
|
|
67
72
|
|
|
68
73
|
|
|
69
|
-
def _select_record() -> dict | None:
|
|
74
|
+
def _select_record() -> tuple[str, dict] | None:
|
|
70
75
|
records = _load_records()
|
|
71
76
|
if not records:
|
|
72
77
|
return None
|
|
@@ -75,13 +80,14 @@ def _select_record() -> dict | None:
|
|
|
75
80
|
ui.info(f" {index}) {name} -> {record.get('target')}")
|
|
76
81
|
answer = ui.ask("Pick a number, or Enter for manual entry:")
|
|
77
82
|
if answer.isdigit() and 1 <= int(answer) <= len(records):
|
|
78
|
-
return records[int(answer) - 1]
|
|
83
|
+
return records[int(answer) - 1]
|
|
79
84
|
return None
|
|
80
85
|
|
|
81
86
|
|
|
82
87
|
def remote_unlock() -> None:
|
|
83
|
-
|
|
84
|
-
if
|
|
88
|
+
selected = _select_record()
|
|
89
|
+
if selected:
|
|
90
|
+
name, record = selected
|
|
85
91
|
target = record["target"]
|
|
86
92
|
key = record.get("key", "")
|
|
87
93
|
unlock_command = record.get("unlock_command", "")
|
|
@@ -89,6 +95,7 @@ def remote_unlock() -> None:
|
|
|
89
95
|
target = ui.ask("Onion address or host/IP to unlock:")
|
|
90
96
|
if not target:
|
|
91
97
|
raise LimError("No target given.")
|
|
98
|
+
name = target
|
|
92
99
|
key = ui.ask("Path to the SSH private key (empty for ssh defaults):")
|
|
93
100
|
unlock_command = (
|
|
94
101
|
"cryptroot-unlock"
|
|
@@ -97,4 +104,6 @@ def remote_unlock() -> None:
|
|
|
97
104
|
)
|
|
98
105
|
if target.endswith(".onion"):
|
|
99
106
|
ui.info("Routing through Tor (torsocks); a local tor must be running.")
|
|
100
|
-
|
|
107
|
+
alias = hostkeys.host_key_alias(name)
|
|
108
|
+
hostkeys.known_hosts_path(Path.home()).parent.mkdir(mode=0o700, parents=True, exist_ok=True)
|
|
109
|
+
runner.run(build_unlock_argv(target, key, unlock_command, alias), check=False)
|
|
@@ -17,8 +17,9 @@ from pathlib import Path
|
|
|
17
17
|
from lim import device as device_module
|
|
18
18
|
from lim import ui
|
|
19
19
|
from lim.errors import LimError
|
|
20
|
-
from lim.image import choosers, crossarch, transfer, unlock
|
|
20
|
+
from lim.image import choosers, crossarch, hostkeys, transfer, unlock
|
|
21
21
|
from lim.image.encryption import configure_encryption
|
|
22
|
+
from lim.image.initramfs import get_backend
|
|
22
23
|
from lim.image.plan import ImagePlan
|
|
23
24
|
from lim.image.session import ImageSession, chroot_bash
|
|
24
25
|
|
|
@@ -140,12 +141,31 @@ chown -R "$target:$target" "/home/$target/.ssh"
|
|
|
140
141
|
chroot_bash(session.root_mount_path, script)
|
|
141
142
|
|
|
142
143
|
|
|
144
|
+
def _record_name(hostname: str) -> str:
|
|
145
|
+
return hostname or "device"
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _pin_host_keys(
|
|
149
|
+
entry: pwd.struct_passwd, name: str, target: str, private_key: str, host_keys: list[str]
|
|
150
|
+
) -> None:
|
|
151
|
+
if not host_keys:
|
|
152
|
+
ui.warning("No initramfs host key found; the first unlock asks to confirm it.")
|
|
153
|
+
return
|
|
154
|
+
try:
|
|
155
|
+
alias = hostkeys.pin(entry, name, target, private_key, host_keys)
|
|
156
|
+
except OSError as exc:
|
|
157
|
+
ui.warning(f"Could not pin the initramfs host key: {exc}")
|
|
158
|
+
return
|
|
159
|
+
ui.info(f"Pinned the initramfs host key as {alias} in ~/{hostkeys.SSH_CONFIG_SUBPATH}.")
|
|
160
|
+
|
|
161
|
+
|
|
143
162
|
def _save_unlock_record(
|
|
144
|
-
plan: ImagePlan,
|
|
163
|
+
plan: ImagePlan, answers: _Answers, hostname: str, onion: str | None, host_keys: list[str]
|
|
145
164
|
) -> None:
|
|
146
165
|
if not onion:
|
|
147
166
|
return
|
|
148
|
-
|
|
167
|
+
name = _record_name(hostname)
|
|
168
|
+
private_key = str(answers.pubkey).removesuffix(".pub")
|
|
149
169
|
unlock_command = "" if plan.distribution in ("arch", "manjaro") else "cryptroot-unlock"
|
|
150
170
|
record = {
|
|
151
171
|
"target": onion,
|
|
@@ -153,22 +173,22 @@ def _save_unlock_record(
|
|
|
153
173
|
"unlock_command": unlock_command,
|
|
154
174
|
"hostname": hostname,
|
|
155
175
|
}
|
|
156
|
-
entry = pwd.getpwnam(host_user)
|
|
176
|
+
entry = pwd.getpwnam(answers.host_user)
|
|
157
177
|
try:
|
|
158
|
-
unlock.save_record(
|
|
159
|
-
Path(entry.pw_dir), entry.pw_uid, entry.pw_gid, hostname or "device", record
|
|
160
|
-
)
|
|
178
|
+
unlock.save_record(Path(entry.pw_dir), entry.pw_uid, entry.pw_gid, name, record)
|
|
161
179
|
ui.info("Saved unlock target; run later with: lim --type remote-unlock")
|
|
162
180
|
except OSError as exc:
|
|
163
181
|
ui.warning(f"Could not save unlock record: {exc}")
|
|
182
|
+
_pin_host_keys(entry, name, onion, private_key, host_keys)
|
|
164
183
|
|
|
165
184
|
|
|
166
185
|
def _print_summary(answers: _Answers, hostname: str, onion: str | None) -> None:
|
|
167
186
|
ui.success("Encrypted Linux image is ready.")
|
|
168
187
|
ui.info(f"Target device : {answers.device.path} (hostname: {hostname})")
|
|
169
188
|
if onion:
|
|
189
|
+
alias = hostkeys.host_key_alias(_record_name(hostname))
|
|
170
190
|
ui.info(f"Onion address : {onion}")
|
|
171
|
-
ui.info(f"Unlock later : lim --type remote-unlock (or: torsocks ssh
|
|
191
|
+
ui.info(f"Unlock later : lim --type remote-unlock (or: torsocks ssh {alias})")
|
|
172
192
|
ui.info(f"After unlock, log in: ssh {answers.login_user}@<the box>")
|
|
173
193
|
ui.info("Golden image? Clone this device onto the SSD, then grow it:")
|
|
174
194
|
ui.info(" parted <ssd> resizepart 2 100% && cryptsetup resize <mapper> && resize2fs <mapper>")
|
|
@@ -187,8 +207,9 @@ def _execute(plan: ImagePlan, answers: _Answers) -> None:
|
|
|
187
207
|
session.mount_chroot_binds()
|
|
188
208
|
session.copy_resolv_conf()
|
|
189
209
|
onion = configure_encryption(plan, session, authorized_keys)
|
|
210
|
+
host_keys = get_backend(plan.distribution).host_public_keys(session.root_mount_path)
|
|
190
211
|
_configure_login_user(session, answers.login_user, answers.login_password, answers.pubkey)
|
|
191
|
-
_save_unlock_record(plan, answers
|
|
212
|
+
_save_unlock_record(plan, answers, hostname, onion, host_keys)
|
|
192
213
|
_print_summary(answers, hostname, onion)
|
|
193
214
|
finally:
|
|
194
215
|
if authorized_keys is not None:
|
{linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/linux_image_manager.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: linux-image-manager
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: CLI (`lim`) for downloading, verifying and flashing Linux images, LUKS/Btrfs encrypted storage (single drive and RAID1), image backups and chroot maintenance
|
|
5
5
|
Author-email: Kevin Veen-Birkenbach <kevin@veen.world>
|
|
6
6
|
Maintainer-email: Kevin Veen-Birkenbach <kevin@veen.world>
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "linux-image-manager"
|
|
8
|
-
version = "2.
|
|
8
|
+
version = "2.4.0"
|
|
9
9
|
description = "CLI (`lim`) for downloading, verifying and flashing Linux images, LUKS/Btrfs encrypted storage (single drive and RAID1), image backups and chroot maintenance"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.10"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/initcpio/tor_install
RENAMED
|
File without changes
|
|
File without changes
|
{linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/initramfs-tools/tor_bottom
RENAMED
|
File without changes
|
{linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/initramfs-tools/tor_hook
RENAMED
|
File without changes
|
|
File without changes
|
{linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/initramfs-tools/torrc
RENAMED
|
File without changes
|
{linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/packages/general.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/lim/configuration/packages/server/tor.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/linux_image_manager.egg-info/requires.txt
RENAMED
|
File without changes
|
{linux_image_manager-2.3.0 → linux_image_manager-2.4.0}/linux_image_manager.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|