Skip to content

Instantly share code, notes, and snippets.

@Guiorgy
Created December 5, 2025 23:15
Show Gist options
  • Select an option

  • Save Guiorgy/97b40ecafb02fb2fd3e6e5d43df278d2 to your computer and use it in GitHub Desktop.

Select an option

Save Guiorgy/97b40ecafb02fb2fd3e6e5d43df278d2 to your computer and use it in GitHub Desktop.
Generates a modified Finnix ISO file with a default root password and SSH enabled for use on headless systems
#!/usr/bin/env bash
# ============================================================================= #
# Copyright © 2025 Guiorgy #
# #
# This program is free software: you can redistribute it and/or modify it under #
# the terms of the GNU General Public License as published by the Free Software #
# Foundation, either version 3 of the License, or (at your option) any later #
# version. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
# FOR A PARTICULAR PURPOSE. #
# #
# You can see the full GNU General Public License at #
# <https://www.gnu.org/licenses/> for more details. #
# ============================================================================= #
set -euo pipefail
WORKDIR='/tmp/finnix'
ISO_MOUNT="${WORKDIR}-iso"
SQUASHFS_PACK="${WORKDIR}/live/filesystem.squashfs"
SQUASHFS_WORKDIR="${WORKDIR}/squashfs-root"
if [[ "$EUID" -ne 0 ]]; then
echo "ERROR: This script must be run as root (use sudo)" 1>&2
exit 1
fi
check_dependencies() {
local missing_deps=()
local deps=('unsquashfs' 'mksquashfs' 'genisoimage' 'rsync' 'mountpoint' 'dirname' 'basename' 'sed')
for dep in "${deps[@]}"; do
if ! command -v "$dep" &>/dev/null; then
missing_deps+=("$dep")
fi
done
if [[ ${#missing_deps[@]} -ne 0 ]]; then
echo "ERROR: The following required utilities are missing:" 1>&2
for dep in "${missing_deps[@]}"; do
echo " - $dep" 1>&2
done
exit 1
fi
}
check_dependencies
parse_args() {
if [[ "$#" -ne 1 ]]; then
echo "Usage: $(basename $0) <path/to/finnix-xyz.iso>" 1>&2
exit 1
fi
INPUT_ISO="$1"
if [[ "$INPUT_ISO" != *.iso ]]; then
echo "ERROR: '$INPUT_ISO' is not an ISO" 1>&2
exit 1
elif [[ ! -f "$INPUT_ISO" ]]; then
echo "ERROR: File '$INPUT_ISO' was not found" 1>&2
exit 1
elif [[ ! -r "$INPUT_ISO" ]]; then
echo "ERROR: File '$INPUT_ISO' is not readable" 1>&2
exit 1
fi
}
parse_args "$@"
# convert any path to absolute
to_absolute() {
if command -v realpath &>/dev/null; then
echo "$(realpath -s "$1")"
elif command -v readlink &>/dev/null; then
echo "$(readlink -f "$1")"
else
echo "Error: Neither 'realpath' nor 'readlink' found. Cannot resolve path" 1>&2
return 1
fi
}
extract_version() {
local dir_name=$(to_absolute "$(dirname "$INPUT_ISO")")
local base_name=$(basename "$INPUT_ISO")
local version=$(echo "$base_name" | sed 's/^finnix-\([0-9]\+\)\.iso$/\1/')
if [[ -n "$version" ]]; then
echo "Detected version: '$version'"
OUTPUT_ISO="${dir_name}/finnix-${version}-ssh.iso"
ISO_VOLUME="Finnix${version}SSH"
else
echo 'WARNING: Version detection failed'
OUTPUT_ISO="${dir_name}/finnix-ssh.iso"
ISO_VOLUME="FinnixSSH"
fi
}
extract_version "$1"
cleanup() {
if [[ -d "$ISO_MOUNT" ]]; then
echo 'Releasing the ISO mount'
umount --force "$ISO_MOUNT" || true
rmdir "$ISO_MOUNT" || true
fi
if mountpoint -q "${SQUASHFS_WORKDIR}/proc" || mountpoint -q "${SQUASHFS_WORKDIR}/sys" || mountpoint -q "${SQUASHFS_WORKDIR}/dev"; then
echo 'Releasing virtual filesystems'
if mountpoint -q "${SQUASHFS_WORKDIR}/proc"; then
umount --force "${SQUASHFS_WORKDIR}/proc" || true
fi
if mountpoint -q "${SQUASHFS_WORKDIR}/sys"; then
umount --force "${SQUASHFS_WORKDIR}/sys" || true
fi
if mountpoint -q "${SQUASHFS_WORKDIR}/dev"; then
umount --force "${SQUASHFS_WORKDIR}/dev" || true
fi
fi
if [[ -d "$WORKDIR" ]]; then
echo 'Releasing the tmpfs working directory'
umount --force "$WORKDIR" || true
rm -rf "$WORKDIR" || true
fi
}
trap 'cleanup' EXIT
extract_iso() {
echo 'Mounting the ISO as read-only'
rm -rf "$ISO_MOUNT"
mkdir "$ISO_MOUNT"
mount --options loop --read-only "$INPUT_ISO" "$ISO_MOUNT"
echo 'Creating a tmpfs working directory'
rm -rf "$WORKDIR"
mkdir "$WORKDIR"
mount --types tmpfs --options size=3G tmpfs "$WORKDIR"
echo 'Copying the contents of the ISO into the working directory'
rsync --archive "${ISO_MOUNT}/" "$WORKDIR"
echo 'Releasing the ISO mount'
umount --force "$ISO_MOUNT"
rmdir "$ISO_MOUNT"
}
extract_iso
extract_root() {
if [ ! -f "$SQUASHFS_PACK" ]; then
echo "ERROR: Could not find the SquashFS file at '$SQUASHFS_PACK'" 1>&2
exit 1
fi
echo 'Extracting the root filesystem'
unsquashfs -dest "$SQUASHFS_WORKDIR" "$SQUASHFS_PACK"
echo 'Removing the old root filesystem'
rm "$SQUASHFS_PACK"
}
extract_root
mount_virtual() {
echo 'Mounting virtual filesystems'
mount -t proc /proc "${SQUASHFS_WORKDIR}/proc"
mount -t sysfs /sys "${SQUASHFS_WORKDIR}/sys"
mount -o bind /dev "${SQUASHFS_WORKDIR}/dev"
}
unmount_virtual() {
echo 'Releasing virtual filesystems'
umount --force "${SQUASHFS_WORKDIR}/proc"
umount --force "${SQUASHFS_WORKDIR}/sys"
umount --force "${SQUASHFS_WORKDIR}/dev"
}
enable_ssh() {
mount_virtual
echo 'Enabling sshd inside the extracted root'
chroot "$SQUASHFS_WORKDIR" /bin/bash <<EOF
# change the root password
echo "root:finnixssh" | chpasswd --crypt-method SHA512
# enable the SSH daemon
SSHD_SERVICE=$(systemctl show --property=Id sshd.service | sed 's/^Id=\(.\+\)$/\1/')
systemctl enable "$SSHD_SERVICE"
sed -i 's/^#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config
EOF
echo 'The default root password set to: finnixssh'
unmount_virtual
}
enable_ssh
repack_root() {
echo 'Repacking the modified root filesystem'
#mksquashfs "$SQUASHFS_WORKDIR" "$SQUASHFS_PACK" -b 512K -comp zstd -Xcompression-level 22 -e boot
mksquashfs "$SQUASHFS_WORKDIR" "$SQUASHFS_PACK" -b 512K -comp xz -Xbcj x86 -Xdict-size 100% -e boot
echo 'Removing the root filesystem working directory'
rm -rf "$SQUASHFS_WORKDIR"
}
repack_root
generate_iso() {
echo 'Generating a new ISO'
genisoimage \
-J -r -V "$ISO_VOLUME" \
-publisher 'FINNIX' -p 'FINNIX' \
-b isolinux/isolinux.bin -c isolinux/boot.cat \
-no-emul-boot -boot-load-size 4 -boot-info-table \
-input-charset utf-8 \
-o "$OUTPUT_ISO" "$WORKDIR"
echo "DONE: The new ISO generated at '$OUTPUT_ISO'"
}
generate_iso
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment