Created
December 5, 2025 23:15
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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