Compare commits

...

10 Commits

Author SHA1 Message Date
obel1x 0ad82ac4e9 Merge pull request 'install.sh bug fixes, autostart cgroup detachment, vault key security' (#20) from unbrot/fedora-OEMDRV:main into main
Reviewed-on: #20
2026-05-01 17:59:24 +02:00
Daniel unbrot Pätzold 1a8260afff setup system: add compression to mount 2026-05-01 17:53:08 +02:00
Brot der Bot 0c50f7825d 0110_nextcloud_talk_app: fix Electron GPU crash on service exit
--scope ... & had two problems:
1. systemd-run stayed alive in the autostart service cgroup;
   KillMode=control-group sent it SIGTERM when logon_script.sh exited,
   tearing down the scope and killing Talk mid-initialization.
2. The scope lacked Delegate=yes, preventing Electron's zygote from
   creating sub-cgroups for the GPU/renderer processes.

The previous commit added Delegate=yes but kept --scope, so problem 1
remained: the scope was still torn down on service exit, causing the
GPU/network service crash visible in talk.log.

Switch to a transient service unit identical to the Nextcloud Desktop
Client fix: --no-block returns immediately so systemd-run is gone from
the cgroup before the service ends; --property=Delegate=yes is retained
for Electron's zygote. Tested: service active, zygote and network
service running, no GPU crash.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 17:23:49 +02:00
Brot der Bot c454110793 0050_nextcloud_desktopclient: fix Nextcloud client killed on service exit
systemd-run --scope ... & left the systemd-run binary running as a
background process inside the autostart service's cgroup. When
logon_script.sh exited, systemd's KillMode=control-group sent SIGTERM
to all remaining cgroup processes, including systemd-run. systemd-run,
on receiving SIGTERM while monitoring a scope, stopped the scope and
killed the Nextcloud client -- at exactly the same moment the autostart
service ended.

--no-block with --scope is not supported. Switch to a transient service
unit (drop --scope, add --no-block). systemd-run registers the unit and
returns immediately, leaving the cgroup before logon_script.sh ends.
The Nextcloud process then runs as an independent systemd user service,
unaffected by the autostart service lifecycle. Tested: Nextcloud keeps
running after systemd-run exits.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 17:13:41 +02:00
Brot der Bot 93418748d7 0110_nextcloud_talk_app: add Delegate=yes to fix Electron GPU crash
Nextcloud Talk is an Electron app. Electron uses a zygote process to
fork sandboxed child processes (GPU, renderer, network service) into
their own sub-cgroups. systemd-run --scope without Delegate=yes locks
down the cgroup — sub-cgroups cannot be created — so the zygote fails,
causing the GPU process to crash immediately on startup.

Adding --property=Delegate=yes hands cgroup management to the scope,
allowing flatpak/bubblewrap and Electron's zygote to create the
sub-cgroups they need. Tested: no GPU crash with this flag set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 16:57:03 +02:00
Brot der Bot d1ff9e348a mount_ecrypt_home.sh: store vault key in XDG_RUNTIME_DIR instead of /var/tmp
/var/tmp is persistent on-disk storage. The encryption key must never
be written to disk, even temporarily. Replaced all occurrences of
/var/tmp/IPAVAULTKEY.txt with ${XDG_RUNTIME_DIR}/IPAVAULTKEY, which
is a per-user tmpfs directory (/run/user/<UID>) created by
systemd-logind: guaranteed memory-only, mode 0700, wiped on logout.

Also removed the TODO comment that tracked this exact issue.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 16:38:24 +02:00
Brot der Bot e246c1f875 0110_nextcloud_talk_app: detach Talk from autostart service cgroup
setsid -f forks the process into a new session but leaves it in the
calling service's cgroup. systemd-run --user --scope moves it into its
own transient scope cgroup so the autostart service can finish normally.

Added & to background the launch, replacing the fork that setsid -f
was providing. Tested: scope is created and Talk starts correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 16:33:17 +02:00
Brot der Bot 3964f8b081 0010_kwallet: detach kwalletd6 from autostart service cgroup
Same root cause as the gocryptfs and Nextcloud fixes: kwalletd6 is a
long-running daemon that stays alive for the entire KDE session.
Launching it with setsid keeps it in the autostart service cgroup,
preventing app-logon_script.sh@autostart from reaching finished state.

Replace setsid with systemd-run --user --scope so kwalletd6 runs in
its own transient scope cgroup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 15:05:20 +02:00
Brot der Bot 1abf7879cd detach long-running background processes from autostart service cgroup
KDE Plasma runs each autostart .desktop entry as a systemd user unit.
systemd tracks service liveness by cgroup membership, not just the
main PID. Any process forked inside the service — even via setsid or &
— stays in the service's cgroup and keeps app-logon_script.sh@autostart
in active (running) state indefinitely after logon_script.sh exits.

mount_ecrypt_home.sh: wrap the gocryptfs mount call with
  systemd-run --user --scope --unit=gocryptfs-home
The FUSE daemon that gocryptfs forks now lives in its own transient
scope cgroup. Exit-code propagation is unchanged because systemd-run
--scope returns the main process's exit code.

0050_nextcloud_desktopclient/user_run.sh: replace
  /usr/bin/setsid ... &
with
  systemd-run --user --scope --unit=nextcloud-client ... &
setsid creates a new session but does not move the process out of the
cgroup; systemd-run --scope does.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 15:02:39 +02:00
Brot der Bot 92b5e9c4a6 install.sh: fix four bugs found during live testing
Free-space start alignment
  parted reports free space starting at 0,02 MiB (before the GPT
  alignment boundary). The collect_free_space awk now rounds the start
  up to the next whole MiB (ceiling) and enforces a minimum of 1 MiB,
  then recomputes the usable size from the adjusted start. This prevents
  parted from being asked to create a partition at 0 MiB, which it
  cannot do.

Locale-independent partition creation
  The previous `printf 'Yes\n' | parted mkpart` relied on parted
  accepting an English answer to its alignment-confirmation prompt.
  On a German-locale system parted asks "Ist dies noch akzeptabel?"
  and ignores "Yes", causing mkpart to fail. Replaced with `parted -s`
  (script/non-interactive mode), consistent with every other parted
  call in the script.

Correct new-partition detection on disks with gaps
  The old heuristic took the highest partition number after partprobe.
  On a disk where existing partitions are numbered 2/3/4, a new
  partition in the gap before them receives number 1 — making the
  old heuristic point at partition 4 (the existing btrfs volume) and
  subsequently run mkfs.btrfs on it. The new awk matches by start
  position (OEMDRV_START ± 1 MiB) instead, which is unambiguous
  regardless of how numbers are assigned.

Infinite loop on EOF stdin
  When the selection while-loop's `read` hits EOF (e.g. stdin exhausted
  after sudo consumed a piped password), it returns exit code 1 with an
  empty INPUT, which falls through to "Invalid input." and spins
  forever. Added `|| { echo; echo "Aborted."; exit 0; }` to all three
  read calls in the loop.

install.md: drop stale install_from_repo.sh reference from title;
clarify that REPO_URL/REPO_BRANCH overrides are optional.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 13:56:34 +02:00
7 changed files with 30 additions and 24 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ if [[ -z $(wmctrl -m | grep "KWin") ]]; then
fi
#Restart the service
/usr/bin/setsid kwalletd6 >${TEMPDIR}/kwalletd6.log 2>&1 &
systemd-run --user --scope --unit=kwalletd6-logon kwalletd6 >${TEMPDIR}/kwalletd6.log 2>&1 &
sleep 1
#Check if kwalletd is enabled now
@@ -151,7 +151,7 @@ done
#fi
# Now start Nextcloud
echo "Starting Nextcloud Client in Background"
/usr/bin/setsid ${BASECMD} >${TEMPDIR}/nc_desktop_client.log 2>&1 &
systemd-run --user --no-block --unit=nextcloud-client.service ${BASECMD} >>${TEMPDIR}/nc_desktop_client.log 2>&1
sleep 2
echo "Done Setup of Nextcloud."
exit 0
@@ -8,6 +8,7 @@ fi
# Start Nextcloud Talk in Background
#Current Version of Talk is dumping Core
echo "Starting Nextcloud Talk in Background."
/usr/bin/setsid -f /usr/bin/flatpak run --branch=stable --arch=x86_64 --command=electron-wrapper --file-forwarding com.nextcloud.talk --background >${TEMPDIR}/talk.log 2>&1
systemd-run --user --no-block --unit=nextcloud-talk.service --property=Delegate=yes \
/usr/bin/flatpak run --branch=stable --arch=x86_64 --command=electron-wrapper --file-forwarding com.nextcloud.talk --background >>${TEMPDIR}/talk.log 2>&1
exit 0
+2 -2
View File
@@ -1,4 +1,4 @@
# OEMDRV Bootstrap — install.sh + install_from_repo.sh
# OEMDRV Bootstrap — install.sh
the script `./system_setup/install.sh` prepares a target machine for automated Fedora deployment. It shrinks an existing partition to carve out a dedicated **OEMDRV** partition, which Anaconda/Kickstart will detect automatically during installation.
@@ -37,7 +37,7 @@ curl -fsSL ${REPO_URL%.git}/raw/branch/${REPO_BRANCH:-main}/system_setup/install
sudo -E bash /tmp/install.sh
```
That way, install.sh should know what to pull.
Both are optional. That way, install.sh should know what to pull.
## After the script completes
+13 -9
View File
@@ -196,9 +196,12 @@ collect_free_space() {
$1+0 > 0 {
for (i = 1; i <= NF; i++) {
if ($i == "free") {
start=$2; end=$3; size=$4;
gsub(/MiB/,"",start); gsub(/MiB/,"",end); gsub(/MiB/,"",size);
s=int(start+0); e=int(end+0); sz=int(size+0);
gsub(/MiB/,"",$2); gsub(/MiB/,"",$3);
e=int($3+0);
raw_s=$2+0;
s=int(raw_s)+(raw_s>int(raw_s)?1:0);
if (s < 1) s = 1;
sz=e-s;
if (sz >= min) print s " " e " " sz;
break
}
@@ -332,12 +335,12 @@ SEL=-1
while true; do
echo
if [[ $FS_IDX -gt 0 && $shrink_count -gt 0 ]]; then
read -r -p "Enter f<n> to use free space, s<n> to shrink a partition, or q to quit: " INPUT
read -r -p "Enter f<n> to use free space, s<n> to shrink a partition, or q to quit: " INPUT || { echo; echo "Aborted."; exit 0; }
elif [[ $FS_IDX -gt 0 ]]; then
read -r -p "Enter number of free space region to use, or q to quit: " INPUT
read -r -p "Enter number of free space region to use, or q to quit: " INPUT || { echo; echo "Aborted."; exit 0; }
[[ "$INPUT" =~ ^[0-9]+$ ]] && INPUT="f${INPUT}"
else
read -r -p "Enter number of partition to shrink, or q to quit: " INPUT
read -r -p "Enter number of partition to shrink, or q to quit: " INPUT || { echo; echo "Aborted."; exit 0; }
[[ "$INPUT" =~ ^[0-9]+$ ]] && INPUT="s${INPUT}"
fi
@@ -458,15 +461,16 @@ fi
# ── Create OEMDRV partition ───────────────────────────────────────────────────
info "Creating new OEMDRV partition (${OEMDRV_START}${OEMDRV_END} MiB) on $WORK_DISK..."
printf 'Yes\n' | parted "$WORK_DISK" mkpart anacondainstall btrfs "${OEMDRV_START}MiB" "${OEMDRV_END}MiB" \
parted -s "$WORK_DISK" mkpart anacondainstall btrfs "${OEMDRV_START}MiB" "${OEMDRV_END}MiB" \
|| die "parted mkpart failed. Check that the target area is free space on $WORK_DISK."
partprobe "$WORK_DISK"
sleep 1
# Determine new partition number (highest on the disk after partprobe)
# Find the partition whose start matches OEMDRV_START (±1 MiB for alignment)
NEW_PNUM=$(parted -s "$WORK_DISK" -m unit MiB print 2>/dev/null \
| awk -F: '/^[0-9]/{n=$1} END{print n}')
| awk -F: -v s="$OEMDRV_START" '
/^[0-9]/ { gsub(/MiB/,"",$2); if (int($2+0) >= s-1 && int($2+0) <= s+1) { print $1; exit } }')
[[ -n "$NEW_PNUM" ]] || die "Could not determine new partition number on $WORK_DISK."
OEMDRV_DEV=$(new_part_device "$WORK_DISK" "$NEW_PNUM")
+10 -9
View File
@@ -42,7 +42,7 @@ if [ $? -ne 0 ]; then
if [ -d "${ENCRYPTEDDATADIR}" ]; then
echo "The encrypted Directory ${ENCRYPTEDDATADIR} exists."
read -p "To mount it with your Key, that you noticed when installing that PC, enter the Key now or press CTRL+C to abort: " ENCKEY
echo ${ENCKEY} > /var/tmp/IPAVAULTKEY.txt
echo ${ENCKEY} > ${XDG_RUNTIME_DIR}/IPAVAULTKEY
else
echo "The Server ${SERVERFQDN_IPA} is offline and no Directory ${ENCRYPTEDDATADIR} exists. Cannot continue."
echo "Please check your Connection/Server and retry."
@@ -52,12 +52,12 @@ else
# Server is online
#Get the Token from IPA
echo Getting the Vault ${IPAVAULTNAME}
ipa vault-retrieve ${IPAVAULTNAME} --out /var/tmp/IPAVAULTKEY.txt >/dev/null #TODO: Instead of /var/tmp use tmpfs for more security
ipa vault-retrieve ${IPAVAULTNAME} --out ${XDG_RUNTIME_DIR}/IPAVAULTKEY >/dev/null
if [ $? -ne 0 ]; then
echo "No Key found. Will try to Setup a new one."
ENCKEY=$( openssl rand -base64 24 )
echo ${ENCKEY} > /var/tmp/IPAVAULTKEY.txt
ipa vault-add "${IPAVAULTNAME}" --desc "Key for Fileencrytption of ${HOSTNM}" --type=standard && ipa vault-archive "${IPAVAULTNAME}" --in /var/tmp/IPAVAULTKEY.txt
echo ${ENCKEY} > ${XDG_RUNTIME_DIR}/IPAVAULTKEY
ipa vault-add "${IPAVAULTNAME}" --desc "Key for Fileencrytption of ${HOSTNM}" --type=standard && ipa vault-archive "${IPAVAULTNAME}" --in ${XDG_RUNTIME_DIR}/IPAVAULTKEY
if [ $? -eq 0 ]; then
echo
echo "Your Key has been sucessfully stored to the Vault ${IPAVAULTNAME}"
@@ -75,13 +75,13 @@ else
ENCKEY=""
fi
else
ENCKEY=$( cat /var/tmp/IPAVAULTKEY.txt )
ENCKEY=$( cat ${XDG_RUNTIME_DIR}/IPAVAULTKEY )
# echo "The Key is: ${ENCKEY}"
fi
fi
if [ "${ENCKEY}." == "." ]; then
echo "Some Error while fetching your IPA Vault Key. This should not happen. Quit."
rm /var/tmp/IPAVAULTKEY.txt
rm ${XDG_RUNTIME_DIR}/IPAVAULTKEY
exit 2
fi
echo "Sucessfuly obtained IPA vault fileencryption key."
@@ -91,11 +91,12 @@ if [ ! -d "${DECRYPTEDDATADIR}" ] || [ ! -f "${HOME}/.config/gocryptfs/gocryptfs
#Key has been obtained, but no Directory was created till know
echo "First Setup of encryption: Creating new Directories now"
mkdir -p ${ENCRYPTEDDATADIR} ${DECRYPTEDDATADIR} ${HOME}/.config/gocryptfs
gocryptfs -init -allow_other -passfile /var/tmp/IPAVAULTKEY.txt -config ${HOME}/.config/gocryptfs/gocryptfs.conf ${ENCRYPTEDDATADIR} >/dev/null
gocryptfs -init -allow_other -passfile ${XDG_RUNTIME_DIR}/IPAVAULTKEY -config ${HOME}/.config/gocryptfs/gocryptfs.conf ${ENCRYPTEDDATADIR} >/dev/null
fi
gocryptfs -noprealloc -allow_other -passfile /var/tmp/IPAVAULTKEY.txt -config ${HOME}/.config/gocryptfs/gocryptfs.conf ${ENCRYPTEDDATADIR} ${DECRYPTEDDATADIR} >/dev/null
systemd-run --user --scope --unit=gocryptfs-home \
gocryptfs -noprealloc -allow_other -passfile ${XDG_RUNTIME_DIR}/IPAVAULTKEY -config ${HOME}/.config/gocryptfs/gocryptfs.conf ${ENCRYPTEDDATADIR} ${DECRYPTEDDATADIR} >/dev/null
RETVAL=$?
rm /var/tmp/IPAVAULTKEY.txt
rm ${XDG_RUNTIME_DIR}/IPAVAULTKEY
cd ${EXECDIR}
if [ ${RETVAL} -eq 0 ]; then
echo "Sucessfully mounted encrypted private Directory ${DECRYPTEDDATADIR}"
+1 -1
View File
@@ -127,7 +127,7 @@ install_sw()
( sed 's/^UMASK.*022$/UMASK\t077/' /etc/login.defs | sudo tee /etc/login.defs ) >/dev/null
#Append OEMDRV mount to SYSCONFIGPATH in fstab
echo "LABEL=OEMDRV ${SYSCONFIGPATH} btrfs noatime,nodiratime,nofail 0 0" >> /etc/fstab
echo "LABEL=OEMDRV ${SYSCONFIGPATH} btrfs noatime,nodiratime,nofail,compress=zstd:6 0 0" >> /etc/fstab
#Make KDE single click
echo -e "[KDE]\nSingleClick=true" | tee -a /etc/xdg/kdeglobals