Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions 90bcachefs/80-bcachefs.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
### https://github.com/kode54/dracut-hook-bcachefs/blob/main/80-bcachefs.rules
SUBSYSTEM!="block", GOTO="bcachefs_end"
ACTION!="add|change", GOTO="bcachefs_end"
ENV{ID_FS_TYPE}!="bcachefs", GOTO="bcachefs_end"

RUN+="/sbin/initqueue --finished --unique --name bcachefs_finished /sbin/bcachefs_finished"

LABEL="bcachefs_end"
7 changes: 7 additions & 0 deletions 90bcachefs/bcachefs-unlock-gpg.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh
# /usr/sbin/bcachefs-unlock-gpg.sh:
# Decrypt password using GPG and unlock the volume
GPG_KEY="/etc/keys/bcachefs.gpg"
DEV="/dev/sdX"
echo "Unlocking $DEV..."
gpg --decrypt "$GPG_KEY" | bcachefs unlock "$DEV"
4 changes: 4 additions & 0 deletions 90bcachefs/bcachefs.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
add_drivers+=" bcachefs "
add_dracutmodules+=" bcachefs "
filesystems+=" bcachefs "
install_items+=" /usr/sbin/bcachefs /usr/sbin/bcachefs-unlock /usr/bin/tpm2_unseal /usr/bin/gpg /usr/bin/ykman "
8 changes: 8 additions & 0 deletions 90bcachefs/bcachefs_finished
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/sh
# /sbin/bcachefs_finished
# Called by udev rule when bcachefs devices appear

info "bcachefs device ready: $DEVNAME"
/usr/bin/bcachefs show-super "$DEVNAME" >/dev/null 2>&1 || true


25 changes: 23 additions & 2 deletions 90bcachefs/module-setup.sh
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
#!/bin/sh
# Dracut module setup for bcachefs
# Enhanced for TPM2 / GPG / YubiKey unlock support
# Compatible with upstream breavyn/dracut-bcachefs and kode54/dracut-hook-bcachefs

check() {
require_binaries bcachefs || return 1

return 0
}

depends() {
# No mandatory deps, but dracut may include udev and crypt hooks if available
echo "udev"
return 0
}

install() {
inst_multiple bcachefs keyctl
# Core binaries required for root unlock and mount
inst_multiple bcachefs keyctl modprobe

# Optional unlock toolchain (install only if present)
inst_any tpm2_unseal gpg ykman || true

# Optional key storage
[ -f /etc/keys/bcachefs.gpg ] && inst /etc/keys/bcachefs.gpg /etc/keys/bcachefs.gpg

# Install hook scripts
inst_hook cmdline 90 "$moddir/parse-cmdline.sh"
inst_hook pre-mount 90 "$moddir/unlock.sh"
inst_hook mount 90 "$moddir/mount.sh"

# Optional udev rule for async device handling (systemd-based initramfs)
if [ -f "$moddir/80-bcachefs.rules" ]; then
inst_rules "$moddir/80-bcachefs.rules"
inst_simple "$moddir/bcachefs_finished" "/sbin/bcachefs_finished"
fi

# Logging for verification
inst_simple /usr/bin/echo /usr/bin/echo || true
}
35 changes: 14 additions & 21 deletions 90bcachefs/mount.sh
Original file line number Diff line number Diff line change
@@ -1,31 +1,24 @@
#!/bin/sh
# Mount handler for bcachefs root volumes (dracut)

if [ "$fstype" != bcachefs ]; then
return 0
fi

[ "$fstype" = "bcachefs" ] || return 0
dev="${root#block:}"

case "$dev" in
/dev/disk/by-uuid/*)
dev="UUID=${dev##*/}"
;;
/dev/disk/by-uuid/*) dev="UUID=${dev##*/}" ;;
esac

if [ -n "$root_subvol" ]; then
mkdir /bcachefs_root
bcachefs mount "$dev" /bcachefs_root

real_root="/bcachefs_root$root_subvol"

if [ ! -d "$real_root" ]; then
die "root_subvol=$root_subvol does not exist"
fi

mount -o bind "$real_root" "/sysroot"

mkdir -p "/sysroot/.bcachefs"
mount -o bind "/bcachefs_root" "/sysroot/.bcachefs"
mkdir /bcachefs_root
bcachefs mount "$dev" /bcachefs_root
real_root="/bcachefs_root$root_subvol"
[ -d "$real_root" ] || die "root_subvol=$root_subvol does not exist"

mount -o bind "$real_root" "/sysroot"
mkdir -p "/sysroot/.bcachefs"
mount -o bind "/bcachefs_root" "/sysroot/.bcachefs"
else
bcachefs mount "$dev" "/sysroot"
bcachefs mount "$dev" "/sysroot"
fi


7 changes: 4 additions & 3 deletions 90bcachefs/parse-cmdline.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#!/bin/sh
# Dracut parse for bcachefs root arguments
# Extends upstream to handle root_subvol= and unlock methods

if [ "$fstype" != bcachefs ]; then
return 0
fi
[ "$fstype" = "bcachefs" ] || return 0

root_subvol=$(getarg root_subvol=)
bcachefs_unlock_method=$(getarg bcachefs_unlock_method=)
rootok=1
51 changes: 38 additions & 13 deletions 90bcachefs/unlock.sh
Original file line number Diff line number Diff line change
@@ -1,19 +1,44 @@
#!/bin/sh
# Enhanced unlock for bcachefs with TPM2/GPG/YubiKey integration

if [ "$fstype" != bcachefs ]; then
return 0
fi

[ "$fstype" = "bcachefs" ] || return 0
dev="${root#block:}"

if bcachefs unlock -c "$dev" >/dev/null 2>&1; then
info "Please unlock $dev:"
for _ in 1 2 3; do
bcachefs unlock "$dev" && \
keyctl link @u @s && \
info "$dev successfully unlocked" && \
return 0
done
modprobe bcachefs 2>/dev/null || true

unlock_device() {
echo "$1" | bcachefs unlock "$dev" >/dev/null 2>&1
}

# 1. TPM2 key attempt
if command -v tpm2_unseal >/dev/null 2>&1; then
TPM_HANDLE=${TPM_HANDLE:-0x81010001}
if KEY=$(tpm2_unseal -c "$TPM_HANDLE" 2>/dev/null); then
unlock_device "$KEY" && info "$dev unlocked via TPM2" && return 0
fi
fi

die "maximum number of tries exceeded for $dev"
# 2. GPG fallback
if [ -f /etc/keys/bcachefs.gpg ] && command -v gpg >/dev/null 2>&1; then
if KEY=$(gpg --decrypt /etc/keys/bcachefs.gpg 2>/dev/null); then
unlock_device "$KEY" && info "$dev unlocked via GPG" && return 0
fi
fi

# 3. YubiKey OATH fallback
if command -v ykman >/dev/null 2>&1; then
if KEY=$(ykman oath code "BCACHEFS" 2>/dev/null | awk '{print $NF}'); then
unlock_device "$KEY" && info "$dev unlocked via YubiKey" && return 0
fi
fi

# 4. Manual unlock (upstream-compatible)
info "Please unlock $dev:"
for _ in 1 2 3; do
bcachefs unlock "$dev" && \
keyctl link @u @s && \
info "$dev successfully unlocked" && \
return 0
done

die "maximum number of tries exceeded for $dev"
31 changes: 31 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
PREFIX ?= /usr
DRACUT_MODULE_DIR ?= $(PREFIX)/lib/dracut/modules.d/90bcachefs
SYSTEMD_DIR ?= $(PREFIX)/lib/systemd/system
OPENRC_DIR ?= /etc/init.d
TAB_DIR ?= /etc

all:
@echo "Usage: make install"

install: install-dracut install-systemd install-openrc install-tab

install-dracut:
@echo "Installing dracut module..."
install -d $(DRACUT_MODULE_DIR)
install -m 755 90bcachefs/* $(DRACUT_MODULE_DIR)/

install-systemd:
@echo "Installing systemd unit..."
install -d $(SYSTEMD_DIR)
install -m 644 units/[email protected] $(SYSTEMD_DIR)/[email protected]

install-openrc:
@echo "Installing OpenRC init script..."
install -d $(OPENRC_DIR)
install -m 755 units/bcachefs-unlock.openrc $(OPENRC_DIR)/bcachefs-unlock

install-tab:
@echo "Installing bcachefs-tab template..."
install -d $(TAB_DIR)
install -m 644 bcachefs-tab.new $(TAB_DIR)/bcachefs-tab.new
@echo "NOTE: Copy and edit template: cp $(TAB_DIR)/bcachefs-tab.new $(TAB_DIR)/bcachefs-tab"
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,72 @@ Additional subvolumes can be mounted using fstab.
```
# /etc/fstab
/.bcachefs/<home subvol path> /home none defaults,bind 0 0


```
Unlock Methods
Method Description
TPM2 Use tpm2_unseal with a configured handle.
GPG Use an encrypted key file (default: /etc/keys/bcachefs.gpg).
YubiKey Retrieve OTP from configured OATH slot BCACHEFS.
Manual Fallback prompt if no automated method succeeds.

The module tries each method in order until unlock succeeds.

Systemd Integration

The module now supports systemd units:

[email protected] for unlocking devices during boot.

Example to enable root device unlock:

systemctl enable [email protected]


Note: Systemd support requires udev and initqueue integration. Optional: 80-bcachefs.rules triggers the unlock after device detection.

OpenRC Integration

OpenRC users can enable /etc/init.d/bcachefs-unlock:

rc-update add bcachefs-unlock boot

Configuration Template

A declarative template /etc/bcachefs-tab can be used to specify devices, keys, and unlock methods:

# Format: UUID=<uuid> DEV=<device> KEY=<file|tpm2|yubikey> METHOD=<tpm2|gpg|yubikey|manual>
UUID=abcd-ef12-3456 DEV=/dev/sda2 KEY=/etc/keys/rootfs.gpg METHOD=gpg
UUID=1234-5678-abcd DEV=/dev/nvme0n1p3 KEY=tpm2:auto METHOD=tpm2


Copy /etc/bcachefs-tab.new to /etc/bcachefs-tab and edit accordingly.

Installation
Dracut Module
make install-dracut
dracut --force --add 90bcachefs

Systemd Unit
make install-systemd
systemctl daemon-reload
systemctl enable [email protected]

OpenRC Script
make install-openrc
rc-update add bcachefs-unlock boot

Template
make install-tab
cp /etc/bcachefs-tab.new /etc/bcachefs-tab

Notes

The root of the bcachefs filesystem is always available at /.bcachefs for additional mounts.

Additional subvolumes must be mounted manually in /etc/fstab or via post-boot scripts.

Works with both encrypted root and data volumes.

Tested on both OpenRC and systemd setups with TPM2/GPG/YubiKey unlock support.
3 changes: 3 additions & 0 deletions bcachefs-tab.new
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# /etc/bcachefs-tab
UUID=abcd-ef12-3456 /dev/sda2 key=/etc/keys/rootfs.gpg method=gpg
UUID=1234-5678-abcd /dev/nvme0n1p3 key=tpm2:auto method=tpm2
12 changes: 12 additions & 0 deletions units/bcachefs-unlock.openrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/sbin/openrc-run
description="Unlock encrypted bcachefs volumes"

depend() {
before localmount
}

start() {
ebegin "Unlocking bcachefs volumes"
/usr/local/sbin/bcachefs-unlock.sh
eend $?
}
12 changes: 12 additions & 0 deletions units/[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[Unit]
Description=Unlock encrypted bcachefs volume %i
Before=local-fs-pre.target
After=systemd-udev-settle.service

[Service]
Type=oneshot
ExecStart=/usr/sbin/bcachefs-unlock /dev/%i
RemainAfterExit=yes

[Install]
WantedBy=local-fs-pre.target