Skip to content

Fix and improve GELI disk encryption support#25

Merged
ericbsd merged 2 commits into
masterfrom
68-feature-full-system-encryption-using-geli
May 2, 2026
Merged

Fix and improve GELI disk encryption support#25
ericbsd merged 2 commits into
masterfrom
68-feature-full-system-encryption-using-geli

Conversation

@ericbsd
Copy link
Copy Markdown

@ericbsd ericbsd commented May 1, 2026

  • Fix GELI initialization parameters: Use explicit AES-XTS cipher, 256-bit key length, and 4096-byte sector size (matching FreeBSD/bsdinstall defaults) instead of relying on implicit defaults
  • Fix encrypted swap handling: Write .eli suffix in fstab for encrypted swap partitions so the kernel creates a one-time GELI provider with a random key at boot, matching standard FreeBSD behavior
  • Fix ZFS ashift sysctl: Correct vfs.zfs.min_auto_ashift to vfs.zfs.vdev.min_auto_ashift
  • Fix GELI cleanup: Clear stale GELI metadata before geli init to prevent failures on reinstall, and use rc_nohalt for detach/clear to avoid aborting on already-detached providers
  • Fix loader.conf entries: Only write geom_eli_load and aesni_load when encryption is actually in use; remove redundant crypto_load entry
  • Load aesni module during GELI setup for hardware-accelerated encryption
  • Increase key file size from 64 bytes to 4096 bytes for stronger key-file-only encryption
  • Fix livezfs extraction: Wrap cpdup call with rc_halt_echo for proper error handling
  • Remove -g (geliboot) flag from key-file mode where it is unnecessary — only passphrase mode needs geliboot for the loader prompt
  • Update examples and documentation to reflect ZFS+GELI layout with encrypted swap

Summary by Sourcery

Improve and harden GELI disk encryption handling across installation, cleanup, and boot, aligning behavior with standard FreeBSD defaults.

Bug Fixes:

  • Correct fstab entries for encrypted swap to use .eli devices so the kernel creates one-time encrypted swap at boot.
  • Fix ZFS ashift tuning by using the correct vfs.zfs.vdev.min_auto_ashift sysctl.
  • Prevent reinstall failures by clearing stale GELI metadata during cleanup and avoiding hard failures when detaching already-detached providers.
  • Ensure live ZFS installations properly report errors by wrapping cpdup in rc_halt_echo.
  • Only configure GELI-related loader.conf entries when encryption is enabled, avoiding unnecessary or redundant module loads.

Enhancements:

  • Standardize GELI initialization to explicit AES-XTS with 256-bit keys and 4096-byte sectors to match FreeBSD installer defaults.
  • Load AES-NI during GELI setup to enable hardware-accelerated encryption.
  • Increase GELI key file size to 4096 bytes for stronger key-file-only encryption.
  • Clarify and tighten key-file vs passphrase handling, including loader.conf configuration and mirrored ZFS GELI setup.

- Fix GELI initialization parameters: Use explicit AES-XTS cipher, 256-bit key length, and 4096-byte sector size (matching FreeBSD/bsdinstall
  defaults) instead of relying on implicit defaults
- Fix encrypted swap handling: Write .eli suffix in fstab for encrypted swap partitions so the kernel creates a one-time GELI provider with a
  random key at boot, matching standard FreeBSD behavior
- Fix ZFS ashift sysctl: Correct vfs.zfs.min_auto_ashift to vfs.zfs.vdev.min_auto_ashift
- Fix GELI cleanup: Clear stale GELI metadata before geli init to prevent failures on reinstall, and use rc_nohalt for detach/clear to avoid
  aborting on already-detached providers
- Fix loader.conf entries: Only write geom_eli_load and aesni_load when encryption is actually in use; remove redundant crypto_load entry
- Load aesni module during GELI setup for hardware-accelerated encryption
- Increase key file size from 64 bytes to 4096 bytes for stronger key-file-only encryption
- Fix livezfs extraction: Wrap cpdup call with rc_halt_echo for proper error handling
- Remove -g (geliboot) flag from key-file mode where it is unnecessary — only passphrase mode needs geliboot for the loader prompt
- Update examples and documentation to reflect ZFS+GELI layout with encrypted swap
@ericbsd ericbsd requested review from a team as code owners May 1, 2026 01:03
@ericbsd ericbsd linked an issue May 1, 2026 that may be closed by this pull request
@ghostbsd-bot ghostbsd-bot moved this to In Review in Development Tracker May 1, 2026
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 1, 2026

Reviewer's Guide

Refines GELI-based disk encryption handling across installation scripts by aligning GELI init parameters with FreeBSD defaults, correctly configuring encrypted swap and loader.conf behavior, fixing ZFS ashift sysctl usage, making GELI cleanup more robust, and tightening live ZFS extraction error handling and documentation.

Sequence diagram for GELI-encrypted ZFS passphrase boot flow

sequenceDiagram
    actor User
    participant Loader as FreeBSD_loader
    participant Kernel as FreeBSD_kernel
    participant GEOMELI as geom_eli
    participant ZFS as ZFS_vdev

    User->>Loader: Power on system
    Loader->>Loader: Read boot_loader_conf
    Loader->>Kernel: Load module geom_eli (geom_eli_load=YES)
    Loader->>Kernel: Load module aesni (aesni_load=YES)
    Loader->>User: Prompt for GELI passphrase (-bg on geli init)
    User->>Loader: Enter passphrase

    Loader->>GEOMELI: Attach GELI provider for data partition
    GEOMELI-->>Loader: Provider created partition_eli

    Loader->>ZFS: Import ZFS pool on partition_eli
    ZFS-->>Loader: Pool imported

    Loader->>Kernel: Hand off boot to kernel
    Kernel->>GEOMELI: Use attached providers for I O
    Kernel->>ZFS: Mount root filesystem
    ZFS-->>Kernel: Root mounted
    Kernel-->>User: Encrypted system booted
Loading

Flow diagram for GELI setup in setup_filesystems

flowchart TD
    A[Start setup_filesystems for partition] --> B{PARTENC = ON?}
    B -- No --> C[Set EXT empty string]
    C --> Z[Continue with filesystem setup]

    B -- Yes --> D{PARTFS = SWAP?}

    D -- Yes --> E[Do not run geli init]
    E --> F[Set EXT empty string so glabel uses raw device]
    F --> Z

    D -- No --> G[Load geom_eli module if needed]
    G --> H[Load aesni module]
    H --> I[Clear stale GELI metadata geli clear PARTDEV]
    I --> J{Passphrase file exists?}

    J -- Yes --> K[geli init -bg -e AES-XTS -l 256 -s 4096 -J encpass PARTDEV]
    K --> L[geli attach -j encpass PARTDEV]
    L --> M[Set EXT .eli]
    M --> N{Mirrored ZFS disks configured?}
    N -- Yes --> O[Loop over GELI_CLONE_ZFS_DISKS]
    O --> P[geli init -b -e AES-XTS -l 256 -s 4096 -J encpass mirror_disk]
    P --> Q[geli attach -j encpass mirror_disk]
    Q --> Z
    N -- No --> Z

    J -- No --> R[dd if=/dev/random of=GELIKEYDIR PART key bs=4096 count=1]
    R --> S[geli init -b -e AES-XTS -l 256 -s 4096 -P -K key PARTDEV]
    S --> T[geli attach -p -k key PARTDEV]
    T --> M
Loading

File-Level Changes

Change Details Files
Align GELI initialization and attachment behavior with FreeBSD defaults, including AES-XTS parameters, keyfile handling, and mirrored ZFS disks.
  • Load geom_eli and aesni modules before initializing encrypted partitions.
  • Use AES-XTS cipher with 256-bit keys and 4096-byte sector size for all GELI init calls.
  • Generate larger 4096-byte random key files for keyfile-only mode and attach using keyfile-only flags.
  • Remove unnecessary -g (geliboot) flag from keyfile mode while preserving it for passphrase boot unlock.
  • Apply consistent GELI parameters when initializing cloned/mirrored ZFS disks.
backend/functions-newfs.sh
Improve GELI lifecycle and encrypted swap handling, including fstab entries and cleanup logic.
  • Write .eli suffix for encrypted swap in fstab so the kernel creates one-time random-key swap providers at boot.
  • Skip GELI init for swap partitions that will be encrypted via kernel-managed .eli providers.
  • Change GELI detach to rc_nohalt and add a pass that clears GELI metadata on all partitions to avoid reinstall issues.
  • Clarify that setup_geli_loading only applies to keyfile mode and copies keys into the installed system.
backend/functions-cleanup.sh
backend/functions-newfs.sh
backend/functions-disk.sh
Tighten boot loader configuration for GELI and AES-NI modules so they are only enabled when encryption is used.
  • Only emit aesni_load and geom_eli_load entries in loader.conf when partition metadata indicates encryption is enabled.
  • Remove redundant crypto_load entry and avoid configuring geom_eli_passphrase_prompt to prevent duplicate prompts.
  • Add explanatory comments on passphrase vs keyfile modes and loader behavior.
backend/functions-cleanup.sh
Fix supporting system behavior and error handling for ZFS and live ZFS installs.
  • Correct ZFS ashift sysctl from vfs.zfs.min_auto_ashift to vfs.zfs.vdev.min_auto_ashift.
  • Wrap livezfs cpdup call in rc_halt_echo for consistent error handling and logging.
backend/functions-newfs.sh
backend/functions-extractimage.sh
Update examples and documentation to match the new ZFS+GELI and encrypted swap behavior.
  • Adjust example installer configuration and README to describe the ZFS+GELI layout and encrypted swap usage.
examples/README
examples/pcinstall.cfg.geli

Assessment against linked issues

Issue Objective Addressed Explanation
ghostbsd/issues#68 Enable reliable full-system disk encryption using GELI in the GhostBSD installer (including correct initialization, attachment, boot-time unlocking, and swap encryption behavior).
ghostbsd/issues#68 Update installer-related configuration examples and documentation to describe and support a ZFS+GELI full-system encrypted layout (including swap).

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • When setting up mirrored ZFS GELI members in setup_filesystems(), you now initialise only the primary device with geli clear and aesni loaded, but the mirror members are initialised directly; for consistency and to avoid leftover metadata, consider applying the same geli clear and module-loading logic to each mirror disk before geli init.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- When setting up mirrored ZFS GELI members in `setup_filesystems()`, you now initialise only the primary device with `geli clear` and aesni loaded, but the mirror members are initialised directly; for consistency and to avoid leftover metadata, consider applying the same `geli clear` and module-loading logic to each mirror disk before `geli init`.

## Individual Comments

### Comment 1
<location path="backend/functions-disk.sh" line_range="419-423" />
<code_context>
+  done
+
+  # Clear GELI metadata from all partitions on this disk
+  for i in `ls ${_geld}*`
+  do
+    echo $i | grep -q '.eli' 2>/dev/null
+    if [ $? -ne 0 ] ; then
+      rc_nohalt "geli clear ${i}"
     fi
   done
</code_context>
<issue_to_address>
**issue:** Using command substitution with `ls ${_geld}*` can break when the glob does not match and may feed error text into the loop.

If `${_geld}*` matches nothing, `ls` prints `No such file or directory` to stdout and exits non‑zero, and that error text is then treated as a path in the loop. A safer approach is to iterate over the glob directly and skip non‑existent entries:

```sh
for i in ${_geld}*; do
  [ -e "$i" ] || continue
  echo "$i" | grep -q '.eli' 2>/dev/null || rc_nohalt "geli clear ${i}"
done
```

This removes the `ls` command substitution and correctly handles the no‑match case.
</issue_to_address>

### Comment 2
<location path="backend/functions-newfs.sh" line_range="165-174" />
<code_context>
+        rc_halt "geli init -bg -e AES-XTS -l 256 -s 4096 -J ${PARTDIR}-enc/${PART}-encpass ${PARTDEV}"
</code_context>
<issue_to_address>
**question (bug_risk):** Mirror members are initialised without `-g`, which may diverge from the boot behaviour of the primary device.

Mirrored devices created by this code path are now initialised without `-g`, whereas they previously were and the main device still is. If these mirrors are ever used in a boot pool or need geliboot metadata for early‑boot attachment, this mismatch could cause them to behave differently or fail to boot. If mirrors are intended to remain bootable, please also include `-g` when initialising the cloned devices.
</issue_to_address>

### Comment 3
<location path="backend/functions-newfs.sh" line_range="177-182" />
<code_context>
       fi

       EXT=".eli"
+    elif [ "${PARTENC}" = "ON" -a "${PARTFS}" = "SWAP" ]
+    then
+      # Swap encryption uses one-time random keys managed by the kernel
+      # at boot via the .eli suffix in fstab. No geli init is needed;
+      # just skip the EXT so glabel operates on the raw device.
+      EXT=""
     else
       # No Encryption
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Using `-a` in `[ ... ]` can be brittle; `&&` in a `test`/`[` context is generally safer.

`[ "${PARTENC}" = "ON" -a "${PARTFS}" = "SWAP" ]` uses `-a`, which has precedence quirks and can misbehave with empty or space-containing values. Prefer chaining tests with `&&`:

```sh
elif [ "${PARTENC}" = "ON" ] && [ "${PARTFS}" = "SWAP" ]; then
    ...
fi
```

This is more robust and easier to read in `/bin/sh`.

Suggested implementation:

```
      EXT=".eli"
    elif [ "${PARTENC}" = "ON" ] && [ "${PARTFS}" = "SWAP" ]
    then
      # Swap encryption uses one-time random keys managed by the kernel
      # at boot via the .eli suffix in fstab. No geli init is needed;
      # just skip the EXT so glabel operates on the raw device.
      EXT=""
    else

```

```
    # If we are doing mirrored ZFS disks, initialise GELI on each mirror member
    # using the same passphrase so all members can be unlocked at boot.
    if [ -n "$GELI_CLONE_ZFS_DISKS" ] && [ "$GELI_CLONE_ZFS_DEV" = "$PARTDEV" ] ; then
       for gC in $GELI_CLONE_ZFS_DISKS
       do
         echo_log "Setting up GELI on mirrored disks: ${gC}"

```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread backend/functions-disk.sh Outdated
Comment thread backend/functions-newfs.sh
Comment thread backend/functions-newfs.sh Outdated
- Replace `-a` with `&&` in conditional statements for better readability and consistency.
- Add `-g` (geliboot) flag during GELI initialization in mirrored ZFS disk setup.
- Simplify GELI metadata clearing loop by streamlining checks to handle missing files gracefully.
@ericbsd ericbsd merged commit 5bdd05e into master May 2, 2026
1 check passed
@ericbsd ericbsd deleted the 68-feature-full-system-encryption-using-geli branch May 2, 2026 02:23
@github-project-automation github-project-automation Bot moved this from In Review to Done in Development Tracker May 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Feature] Full system encryption using GELI

2 participants