Skip to content

[sec-check] Hardcoded LUKS recovery passphrase in the vendored repart initramfs module #272

Description

@hanthor-hive-agent

Security Finding

Severity: low
Type: unsafe-pattern (hardcoded credential, latent)

files/kde-linux-system/generate-initramfs/modules/50-repart/gen-recovery-key.sh emits a fixed string as the disk-encryption recovery passphrase:

#!/bin/bash
set -eu
umask 0077
if [[ "$#" -ge 1 ]]; then
    exec >"${1}"
fi
echo -n "ihavenotsetarecoverykey"

It is wired to the encryption path:

  • 50-repart/enable-encryption.service (initramfs, ConditionSecurity=tpm2) runs /usr/bin/gen-recovery-key /run/recovery-password
  • 50-repart/gnomeos.conf overrides systemd-repart.service with --key-file=/run/recovery-password
  • files/secure-repart-config/main/50-root.conf declares Encrypt=key-file+tpm2 for the root partition

So on any system provisioned through this path, systemd-repart creates the root LUKS2 volume with a keyslot holding a constant, publicly readable passphrase, plus a TPM2 enrollment. The TPM2 binding does not help: the passphrase keyslot unlocks the volume on its own, from any machine.

This is not reachable in the images this repository currently publishes. The module's install() is gated:

install() {
    if [ "${INITRD_MODE-sysupdate}" != sysupdate ]; then
        exit 0
    fi

and the only element that runs generate-initramfselements/oci/initramfs.bst — sets INITRD_MODE: oci, so 50-repart (and with it gen-recovery-key, enable-encryption.service and the repart drop-in) is never installed into the shipped initramfs. Filed as low for that reason, not because the constant is harmless.

Impact

The failure mode is the fallback direction of that gate: ${INITRD_MODE-sysupdate} defaults to the vulnerable mode. Any future initramfs build that forgets to set INITRD_MODE, or that deliberately selects sysupdate, silently ships full-disk encryption whose recovery keyslot is a string published in this repository. Every machine installed that way shares it, and anyone holding the disk — a stolen laptop, a resold drive, a disk image — unlocks it. Nothing at runtime signals the difference from real encryption.

The string is upstream GNOME OS's placeholder, so the bug is inherited rather than introduced here, but this repository vendors the module and would ship the result.

Recommendation

  1. Make the helper fail closed instead of emitting a constant — generate a real key, e.g.:
dd if=/dev/urandom bs=32 count=1 status=none | basenc --base32 | head -c 55

(dd and basenc are already installed by module.sh for exactly this kind of use), and surface the generated passphrase to the user through first-boot setup so it can be recorded. If no such flow exists yet, exit 1 is a better outcome than encrypting with a known key.

  1. Invert the gate so the encrypting mode is opt-in: if [ "${INITRD_MODE:-oci}" != sysupdate ]; then exit 0; fi, and set INITRD_MODE explicitly at every call site.

  2. If the sysupdate/repart install path is not planned for Tromsø, drop the 50-repart module and files/secure-repart-config/ rather than carrying a disabled encryption path that looks functional.


Filed by sec-check agent (ACMM L6 — full mode)

— hive: agent=sec-check backend=claude model=claude-opus-5 claude=2.1.226

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/securityApproved by a Hive merger/owner for auto-merge on green CIhive/hive-keen-minkApproved by a Hive merger/owner for auto-merge on green CIsecurity

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions