Skip to content

Instantly share code, notes, and snippets.

@rpstreef
Created September 2, 2025 01:10
Show Gist options
  • Select an option

  • Save rpstreef/e20d0048d39f7497bb99e252f0c8faae to your computer and use it in GitHub Desktop.

Select an option

Save rpstreef/e20d0048d39f7497bb99e252f0c8faae to your computer and use it in GitHub Desktop.
Setup FSCrypt folder on Arch Linux
#!/usr/bin/env bash
# fscrypt-arch-setup.sh
# Usage:
# sudo bash fscrypt-arch-setup.sh
# sudo TEST_DIR=1 USERNAME=<your_login> bash fscrypt-arch-setup.sh # optional: create a test dir
set -euo pipefail
# ---- config via env vars ----
TEST_DIR="${TEST_DIR:-0}" # 1 = create ~/fscrypt_test (interactive)
TARGET_USER="${USERNAME:-${SUDO_USER:-${USER}}}" # user to own the optional test dir
require_root() {
if [[ $EUID -ne 0 ]]; then
echo "This script must run as root (use sudo)." >&2
exit 1
fi
}
ts() { date +%Y%m%d-%H%M%S; }
pkg_install() {
echo "[*] Installing fscrypt (and essentials)..."
pacman -Sy --needed --noconfirm fscrypt e2fsprogs >/dev/null
}
backup_file() {
local f="$1"
[[ -f "$f" ]] || return 0
cp -a -- "$f" "${f}.bak.$(ts)"
}
ensure_pam_line() {
# ensure a line exists in a PAM file, append if missing
local file="$1" line="$2"
grep -Fq -- "$line" "$file" || echo "$line" >> "$file"
}
configure_pam() {
echo "[*] Configuring PAM (Arch style)..."
local LOGIN="/etc/pam.d/system-login"
local PASSWD="/etc/pam.d/passwd"
[[ -f "$LOGIN" ]] || { echo "ERROR: $LOGIN not found."; exit 1; }
[[ -f "$PASSWD" ]] || { echo "ERROR: $PASSWD not found."; exit 1; }
backup_file "$LOGIN"
backup_file "$PASSWD"
# Ensure pam_fscrypt is referenced exactly once in auth & session of system-login.
# We append minimal, safe 'optional' hooks; order is after includes so pam_unix has run.
ensure_pam_line "$LOGIN" "auth optional pam_fscrypt.so"
ensure_pam_line "$LOGIN" "session optional pam_fscrypt.so"
# Keep the login protector in sync on password changes.
ensure_pam_line "$PASSWD" "password optional pam_fscrypt.so"
echo "[*] PAM updated:"
echo " - $LOGIN has: 'auth optional pam_fscrypt.so' and 'session optional pam_fscrypt.so'"
echo " - $PASSWD has: 'password optional pam_fscrypt.so'"
}
is_fs_supported() {
local mp="$1"
local fstype
fstype=$(findmnt -nr -o FSTYPE --target "$mp" || true)
[[ "$fstype" == "ext4" || "$fstype" == "f2fs" ]]
}
device_for_mount() {
local mp="$1"
findmnt -nr -o SOURCE --target "$mp"
}
enable_ext4_encrypt_flag_if_needed() {
local dev="$1"
# Check if encrypt feature is present
if dumpe2fs -h "$dev" 2>/dev/null | grep -qi "Filesystem features:.*encrypt"; then
return 0
fi
echo "[*] Enabling ext4 'encrypt' feature on $dev ..."
# This usually requires the FS to be unmounted; on a mounted root it will fail safely.
if tune2fs -O encrypt "$dev" 2>/tmp/tune2fs.log; then
echo " - 'encrypt' feature set. Running fsck..."
e2fsck -f "$dev"
else
echo " WARNING: Could not set 'encrypt' online on $dev (likely mounted)."
echo " If this partition is root, consider enabling offline from a Live ISO:"
echo " tune2fs -O encrypt $dev && e2fsck -f $dev"
fi
}
setup_fscrypt_on_mount() {
local mp="$1"
echo "[*] Initializing fscrypt on $mp ..."
# Create /.fscrypt metadata if missing; allow all users to create login protectors
fscrypt setup "$mp" >/dev/null || true
fscrypt setup "$mp" --all-users >/dev/null || true
}
init_filesystems() {
# Handle "/" and "/home" if it is a separate mount.
for MP in / /home; do
if mountpoint -q "$MP"; then
if ! is_fs_supported "$MP"; then
echo "[-] Skipping $MP: not ext4/F2FS"
continue
fi
local DEV
DEV="$(device_for_mount "$MP")"
if [[ -z "$DEV" ]]; then
echo "[-] Unable to resolve block device for $MP, skipping."
continue
fi
# If ext4, try to ensure encrypt feature is set
local FSTYPE
FSTYPE=$(findmnt -nr -o FSTYPE --target "$MP")
if [[ "$FSTYPE" == "ext4" ]]; then
enable_ext4_encrypt_flag_if_needed "$DEV"
fi
setup_fscrypt_on_mount "$MP"
echo "[*] fscrypt status for $MP:"
fscrypt status "$MP" || true
fi
done
}
create_test_dir_if_requested() {
[[ "$TEST_DIR" == "1" ]] || return 0
local HOME_DIR
HOME_DIR=$(eval echo "~$TARGET_USER")
local TEST="$HOME_DIR/fscrypt_test"
echo "[*] Creating test directory for $TARGET_USER at: $TEST"
install -d -m 700 -o "$TARGET_USER" -g "$(id -gn "$TARGET_USER")" "$TEST"
echo
echo "[i] About to encrypt $TEST with the LOGIN passphrase (interactive)."
echo " Select: '1 - Your login passphrase' when prompted."
echo
# Run fscrypt encrypt as the target user (interactive)
sudo -u "$TARGET_USER" fscrypt encrypt "$TEST" || {
echo "[-] Test directory encryption was not completed."
return 0
}
echo "[*] Test directory status:"
sudo -u "$TARGET_USER" fscrypt status "$TEST" || true
echo "[*] You can now 'fscrypt lock \"$TEST\"', log out/in, and verify auto-unlock."
}
main() {
require_root
pkg_install
configure_pam
init_filesystems
create_test_dir_if_requested
echo
echo "[✓] fscrypt base setup complete."
echo " Re-login to test auto-unlock. For a quick check now:"
echo " - If you created the test dir: fscrypt lock ~${TARGET_USER}/fscrypt_test ; log out/in ; fscrypt status ~${TARGET_USER}/fscrypt_test"
echo " - Otherwise, encrypt your real dir with: fscrypt encrypt <your_dir> and choose 'Your login passphrase'."
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment