: Path to the X.509 file containing the certificate for the signed
UEFI kernel image, if `--secure-boot` is used.
+`--secure-boot-common-name=`
+
+: Common name to be used when generating SecureBoot keys via mkosi's `genkey`
+ command. Defaults to `mkosi of %u`, where `%u` expands to the username of the
+ user invoking mkosi.
+
+`--secure-boot-valid-days=`
+
+: Number of days that the keys should remain valid when generating SecureBoot
+ keys via mkosi's `genkey` command. Defaults to two years (730 days).
+
`--read-only`
: Make root file system read-only. Only applies to `gpt_ext4`,
| `--secure-boot` | `[Output]` | `SecureBoot=` |
| `--secure-boot-key=` | `[Output]` | `SecureBootKey=` |
| `--secure-boot-certificate=` | `[Output]` | `SecureBootCertificate=` |
+| `--secure-boot-valid-days=` | `[Output]` | `SecureBootValidDays=` |
+| `--secure-boot-common-name=` | `[Output]` | `SecureBootCommonName=` |
| `--read-only` | `[Output]` | `ReadOnly=` |
| `--encrypt=` | `[Output]` | `Encrypt=` |
| `--verity=` | `[Output]` | `Verity=` |
import crypt
import ctypes
import ctypes.util
+import datetime
import enum
import errno
import fcntl
MKOSI_COMMANDS_CMDLINE = ("build", "shell", "boot", "qemu", "ssh")
MKOSI_COMMANDS_NEED_BUILD = ("shell", "boot", "qemu")
MKOSI_COMMANDS_SUDO = ("build", "clean", "shell", "boot", "qemu")
-MKOSI_COMMANDS = ("build", "clean", "help", "summary") + MKOSI_COMMANDS_CMDLINE
+MKOSI_COMMANDS = ("build", "clean", "help", "summary", "genkey") + MKOSI_COMMANDS_CMDLINE
DRACUT_SYSTEMD_EXTRAS = [
"/usr/lib/systemd/systemd-veritysetup",
continue
# replace arguments referencing files with the file content
try:
+ # This used to use configparser.ConfigParser before, but
+ # ConfigParser's interpolation clashes with systemd style
+ # specifier, e.g. %u for user, since both use % as a sigil.
config = configparser.RawConfigParser(delimiters="=")
config.optionxform = str # type: ignore
with open(arg_string[1:]) as args_file:
)
group.add_argument("--secure-boot-key", help="UEFI SecureBoot private key in PEM format", metavar="PATH")
group.add_argument("--secure-boot-certificate", help="UEFI SecureBoot certificate in X509 format", metavar="PATH")
+ group.add_argument(
+ "--secure-boot-valid-days",
+ help="Number of days UEFI SecureBoot keys should be valid when generating keys",
+ metavar="DAYS",
+ default="730",
+ )
+ group.add_argument(
+ "--secure-boot-common-name",
+ help="Template for the UEFI SecureBoot CN when generating keys",
+ metavar="CN",
+ default="mkosi of %u",
+ )
group.add_argument(
"--read-only",
action=BooleanAction,
)
+def generate_secure_boot_key(args: CommandLineArguments) -> NoReturn:
+ """Generate secure boot keys using openssl"""
+ args.secure_boot_key = args.secure_boot_key or "./mkosi.secure-boot.key"
+ args.secure_boot_certificate = args.secure_boot_certificate or "./mkosi.secure-boot.crt"
+
+ keylength = 2048
+ expiration_date = datetime.date.today() + datetime.timedelta(int(args.secure_boot_valid_days))
+ cn = expand_specifier(args.secure_boot_common_name)
+
+ for f in (args.secure_boot_key, args.secure_boot_certificate):
+ if os.path.exists(f) and not args.force:
+ die(
+ dedent(
+ f"""\
+ {f} already exists.
+ If you are sure you want to generate new secure boot keys
+ remove {args.secure_boot_key} and {args.secure_boot_certificate} first.
+ """
+ )
+ )
+
+ MkosiPrinter.print_step(f"Generating secure boot keys rsa:{keylength} for CN `{cn}`.")
+ MkosiPrinter.info(
+ dedent(
+ f"""
+ The keys will expire in {args.secure_boot_valid_days} days ({expiration_date:%A %d. %B %Y}).
+ Remember to roll them over to new ones before then.
+ """
+ )
+ )
+
+ cmd = [
+ "openssl",
+ "req",
+ "-new",
+ "-x509",
+ "-newkey",
+ f"rsa:{keylength}",
+ "-keyout",
+ args.secure_boot_key,
+ "-out",
+ args.secure_boot_certificate,
+ "-days",
+ str(args.secure_boot_valid_days),
+ "-subj",
+ f"/CN={cn}/",
+ ]
+
+ os.execvp(cmd[0], cmd)
+
+
def expand_paths(paths: List[str]) -> List[str]:
if not paths:
return []
os.environ["PATH"] = new_path + ":" + original_path
+def expand_specifier(s: str) -> str:
+ user = os.getenv("SUDO_USER") or os.getenv("USER")
+ assert user is not None
+ return s.replace("%u", user)
+
+
def needs_build(args: CommandLineArguments) -> bool:
return args.verb == "build" or (not os.path.exists(args.output) and args.verb in MKOSI_COMMANDS_NEED_BUILD)
prepend_to_environ_path(args.extra_search_paths)
+ if args.verb == "genkey":
+ generate_secure_boot_key(args)
+
if args.verb in MKOSI_COMMANDS_SUDO:
check_root()
unlink_output(args)
"secure_boot": False,
"secure_boot_certificate": None,
"secure_boot_key": None,
+ "secure_boot_common_name": "mkosi of %u",
+ "secure_boot_valid_days": "730",
"sign": False,
"skeleton_trees": [],
"source_file_transfer": None,
self.reference_config[job_name]["secure_boot_key"] = mk_config_output["SecureBootKey"]
if "SecureBootCertificate" in mk_config_output:
self.reference_config[job_name]["secure_boot_certificate"] = mk_config_output["SecureBootCertificate"]
+ if "SecureBootCommonName" in mk_config_output:
+ self.reference_config[job_name]["secure_boot_common_name"] = mk_config_output["SecureBootCommonName"]
+ if "SecureBootValidDays" in mk_config_output:
+ self.reference_config[job_name]["secure_boot_valid_days"] = mk_config_output["SecureBootValidDays"]
if "ReadOnly" in mk_config_output:
self.reference_config[job_name]["read_only"] = mk_config_output["ReadOnly"]
if "Encrypt" in mk_config_output:
"SecureBoot": False,
"SecureBootKey": "/foo.pem",
"SecureBootCertificate": "bar.crt",
+ "SecureBootCommonName": "mkosi for %u",
+ "SecureBootValidDays": "730",
"ReadOnly": False,
"Encrypt": "all",
"Verity": False,