summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2023-05-27 07:32:26 +0000
committerLars Wirzenius <liw@liw.fi>2023-05-27 07:32:26 +0000
commit016060db2b1968d25a23d54e42b5566d14d3e16d (patch)
tree6f319b361050dce3221159961cccf186a70dc7e7
parentad845a7d26bd43f2b0bff9cb9113c486fd4a19c7 (diff)
parent7230abdc1ece4b08b46da0483a5d75d6f5c5d5de (diff)
downloadv-i-016060db2b1968d25a23d54e42b5566d14d3e16d.tar.gz
Merge branch 'liw/tweaks' into 'main'
small improvements to make for a nicer experience See merge request larswirzenius/v-i!47
-rw-r--r--config.yaml4
-rwxr-xr-xconfigure-installer112
-rw-r--r--installer.vmdb1
-rwxr-xr-xv-i41
4 files changed, 141 insertions, 17 deletions
diff --git a/config.yaml b/config.yaml
new file mode 100644
index 0000000..e6dd414
--- /dev/null
+++ b/config.yaml
@@ -0,0 +1,4 @@
+user_ca_pub_cmd: sshca ca public-key liw.fi/ca/user/v5
+host_key_cmd: sshca host private-key $HOST
+host_cert_cmd: sshca host certify liw.fi/ca/host/v5 $HOST
+cmd_as_user: liw
diff --git a/configure-installer b/configure-installer
index 7851acd..fce3695 100755
--- a/configure-installer
+++ b/configure-installer
@@ -4,13 +4,19 @@ import argparse
import glob
import os
import subprocess
+import sys
import tempfile
import yaml
+verbose = False
+
+
def log(msg):
- if True:
- print(msg)
+ if verbose:
+ sys.stderr.write(msg)
+ sys.stderr.write("\n")
+ sys.stderr.flush()
class Config:
@@ -18,7 +24,11 @@ class Config:
"authorized_keys_file": None,
"host_cert_file": None,
"host_key_file": None,
+ "host_cert_cmd": None,
+ "host_key_cmd": None,
"user_ca_pub_file": None,
+ "user_ca_pub_cmd": None,
+ "cmd_as_user": None,
}
exandable = [
@@ -42,7 +52,55 @@ class Config:
for key in self.exandable:
if self.config[key] is not None:
self.config[key] = os.path.expanduser(self.config[key])
- log(f"config: {self.config}")
+ log(f"config:")
+ for key in self.config:
+ log(f" {key}={self.config[key]!r}")
+
+ def user_ca_pub(self):
+ return self._get_from_file_or_cmd("user_ca_pub", "user CA public key", None)
+
+ def host_key(self, hostname):
+ return self._get_from_file_or_cmd("host_key", "host private key", hostname)
+
+ def host_cert(self, hostname):
+ return self._get_from_file_or_cmd("host_cert", "host certificate", hostname)
+
+ def _get_from_file_or_cmd(self, prefix, msg, hostname):
+ log("_get: A")
+ filename = self.config.get(f"{prefix}_file")
+ log("_get: B")
+ if filename is not None:
+ log("_get: C")
+ log(f"reading {msg} from {filename}")
+ log("_get: D")
+ return cat(filename)
+
+ log("_get: E")
+ cmd = self.config.get(f"{prefix}_cmd")
+ log("_get: F")
+ if hostname is not None:
+ log("_get: G")
+ cmd = hostname.join(cmd.split("$HOST"))
+ log("_get: H")
+ if cmd is not None:
+ log("_get: I")
+ user = self.config.get("cmd_as_user")
+ log("_get: J")
+ if user is not None:
+ log("_get: K")
+ log(f"reading {msg} from command (as {user}): {cmd}")
+ log("_get: L")
+ return run(cmd, user=user)
+ else:
+ log("_get: M")
+ log(f"reading {msg} from command: {cmd}")
+ log("_get: N")
+ return run(cmd)
+
+ log("_get: O")
+ log(f"can't read {msg}")
+ log("_get: Z")
+ return None
def mount(drive, mp):
@@ -55,6 +113,30 @@ def unmount(path):
subprocess.run(["umount", path])
+def run(cmd, user=None):
+ log(f"run: A - user={user!r}")
+ if user is not None:
+ log("run: B")
+ argv = ["sudo", "-u", user, "--", "/bin/bash", "-c", cmd]
+ log("run: C")
+ log(f"argv: {argv}")
+ log("run: D")
+ p = subprocess.run(argv, capture_output=True)
+ log("run: E")
+ else:
+ log("run: F")
+ log(f"cmd={cmd!r}")
+ p = subprocess.run(cmd, shell=True, capture_output=True)
+ log("run: G")
+ if p.returncode != 0:
+ log("run: H")
+ sys.stderr.write(p.stderr.decode())
+ log("run: J")
+ sys.exit(1)
+ log("run: Z")
+ return p.stdout.decode()
+
+
def cat(path):
log(f"reading {path}")
with open(path) as f:
@@ -75,13 +157,11 @@ def dir_exists(mp, path):
raise Exception(f"{full} does not exist")
-def host_id(config, mp):
- key_path = config["host_key_file"]
- cert_path = config["host_cert_file"]
- if key_path is None or cert_path is None:
- return
- key = cat(key_path)
- cert = cat(cert_path)
+def host_id(config, mp, installer_hostname):
+ key = config.host_key(installer_hostname)
+ cert = config.host_cert(installer_hostname)
+ if key is None:
+ sys.exit("could not find host key for installer")
config_d = "/etc/ssh/sshd_config.d"
host_key = "/etc/ssh/ssh_host_key"
@@ -117,10 +197,7 @@ def authorized_keys(config, mp):
def user_ca(config, mp):
- ca_path = config["user_ca_pub_file"]
- if ca_path is None:
- return
- ca_key = cat(ca_path)
+ ca_key = config.user_ca_pub()
include = f"{mp}/etc/ssh/sshd_config.d/user_ca.conf"
write(include, "TrustedUserCAKeys /etc/ssh/user_ca_pubs\n", 0, 0, 0o644)
@@ -133,6 +210,8 @@ def main():
log("configure-image starting")
p = argparse.ArgumentParser()
+ p.add_argument("--verbose", action="store_true", help="be verbose")
+ p.add_argument("--hostname", default="v-i", help="host name of installer image")
p.add_argument("config", metavar="FILE", help="configuration file")
p.add_argument(
"drive",
@@ -141,6 +220,9 @@ def main():
)
args = p.parse_args()
+ global verbose
+ verbose = args.verbose
+
config = Config()
config.read(args.config)
@@ -151,7 +233,7 @@ def main():
mount(drive2, mp)
try:
dir_exists(mp, "etc/ssh")
- host_id(config, mp)
+ host_id(config, mp, args.hostname)
authorized_keys(config, mp)
user_ca(config, mp)
finally:
diff --git a/installer.vmdb b/installer.vmdb
index deb3151..eb88a9f 100644
--- a/installer.vmdb
+++ b/installer.vmdb
@@ -88,3 +88,4 @@ steps:
- grub: uefi
tag: root
efi: efi
+ quiet: true
diff --git a/v-i b/v-i
index 18141ff..c140089 100755
--- a/v-i
+++ b/v-i
@@ -8,6 +8,7 @@ import shutil
import subprocess
import sys
import tempfile
+import time
import yaml
@@ -379,6 +380,31 @@ def vmdb_spec(system, ansible_vars):
return {"steps": steps}
+class Timings:
+ def __init__(self):
+ self.started = time.time()
+ self.times = {}
+ self.whats = []
+
+ def reached(self, what):
+ assert what not in self.whats
+ assert what not in self.times
+ self.times[what] = time.time()
+ self.whats.append(what)
+
+ def report(self):
+ prev = self.started
+ for what in self.whats:
+ secs = "%.1f" % (self.times[what] - prev)
+ prev = self.times[what]
+ log(f"Duration: {what}: {secs} s")
+ print(f"Duration: {what}: {secs} s")
+
+
+def cache_name(spec):
+ return f"{spec.debian_release}.cache.tar.gz"
+
+
class SystemSpec:
def __init__(self, filename):
REQUIRED = "required"
@@ -441,11 +467,13 @@ class SystemSpec:
def main():
+ timings = Timings()
+
p = argparse.ArgumentParser()
p.add_argument("--verbose", action="store_true")
p.add_argument("--very-verbose", action="store_true")
p.add_argument("--log", default="install.log")
- p.add_argument("--cache", default="cache.tar.gz")
+ p.add_argument("--cache", default=None)
p.add_argument("spec")
args = p.parse_args()
@@ -463,6 +491,9 @@ def main():
system = SystemSpec(args.spec)
log(f"spec: {system!r}")
+ cache = args.cache or cache_name(system)
+ log(f"cache: {cache}")
+
ansible_vars = dict(system.ansible_vars)
ansible_vars["hostname"] = system.hostname
for filename in system.ansible_vars_files:
@@ -471,8 +502,10 @@ def main():
vars_dict = yaml.safe_load(f)
ansible_vars.update(vars_dict)
log(f"ansible_vars: {ansible_vars!r}")
+ timings.reached("read configuration")
clean_up_disks([system.drive] + system.extra_drives)
+ timings.reached("clean up storage")
vmdb = vmdb_spec(system, ansible_vars)
tmp = tempfile.mkdtemp()
@@ -490,7 +523,7 @@ def main():
argv = [
"vmdb2",
- f"--rootfs-tarball={args.cache}",
+ f"--rootfs-tarball={cache}",
f"--log={args.log}",
f"--image={system.drive}",
specfile,
@@ -498,12 +531,16 @@ def main():
if verbose:
argv.append("--verbose")
run(argv, check=True, capture_output=True)
+ timings.reached("vmdb2")
log("cleanup")
shutil.rmtree(tmp)
+ timings.reached("clean up")
log("OK, done")
print("OK, done")
+ timings.report()
+
main()