Compare commits

..

35 Commits

Author SHA1 Message Date
e60b3cf2a7 Overall optimations 2025-03-22 11:42:13 +01:00
94fdcf5758 Implemented wrapper for pkgmgr and more sync for safe image transfer and manjaro gnome 24 2025-03-22 10:28:14 +01:00
b4a0b50e1f Added Funding 2025-03-12 20:52:47 +01:00
d613fbf262 Merge branch 'master' of github.com:kevinveenbirkenbach/linux-image-manager 2025-03-12 11:14:39 +01:00
bf95ba6090 Update README.md 2025-03-12 10:49:37 +01:00
7f82c6fcb9 Update README.md 2025-03-04 19:59:47 +01:00
13454c7e87 Added missing keyy add 2025-02-15 12:46:40 +01:00
72c4d95606 Added overview about LUKS encrypted storage devices 2025-02-15 12:03:51 +01:00
3f272790d6 Update Hints 2025-02-14 15:48:01 +01:00
bac3edc404 Optimized logic 2024-07-22 23:49:25 +02:00
b2881c1319 Added platform 2024-07-22 23:43:02 +02:00
3daed9447b Implemented automatic boot_size and .xz file management 2024-07-22 23:23:40 +02:00
bd05fd4c2d Optimized setup of manjaro for raspberry pi 4 2024-07-22 22:46:02 +02:00
bbb54a4237 Optimized logic for luks and implemented administrator setup 2024-07-22 21:23:03 +02:00
9519b314e9 Added logic for luks memory cost 2024-07-22 17:17:37 +02:00
826aa42565 Removed earlyprint 2024-07-22 02:37:59 +02:00
19f5dc7178 Removed typo 2024-07-21 23:13:36 +02:00
5aa8f65584 Implemented optimations for raspberry pi 4 2024-07-21 22:44:39 +02:00
e37d57d569 Solved bug 2024-07-21 16:03:04 +02:00
c05e804fb5 Added cleanup hints 2024-07-21 15:34:05 +02:00
e40974f56d Refactored code and solved bugs 2024-07-21 15:30:17 +02:00
e8581cb448 Solved replacement bug 2024-07-21 03:32:52 +02:00
38a289083d Implemented search replace validation and solved password bug 2024-07-21 01:11:29 +02:00
796028670f Refactored; https://chatgpt.com/share/12370808-a645-405b-bbd0-e59cf7b648c7 2024-07-20 23:21:47 +02:00
265e5c6f20 Implemented count for blocks to overwrite; https://chatgpt.com/share/ac2b59af-c0f0-486f-89fe-0301d4915837 2024-07-20 22:16:51 +02:00
de0090b60c Optimized logic for arch setup 2024-07-20 21:49:19 +02:00
ab8b8b6e3a Optimized hints 2024-07-20 17:44:59 +02:00
59f38c4089 Added correct value for raspberry pi 4 2024-07-20 17:44:12 +02:00
93beadb519 Added default value for boot size partition: https://chatgpt.com/share/a27feda0-8f84-4315-95b7-686a35986a49 2024-07-20 15:14:55 +02:00
c5b091e3b3 Added signature verification (untested). See AI chat: https://chatgpt.com/share/b521328b-7d7e-4b51-ae1a-9efec2f307c6 2024-07-20 12:54:04 +02:00
ca6951d1fe Added md5 verification 2024-07-20 12:20:51 +02:00
42e9b6bd7f Implemented manjaro for raspberry pi 2024-06-18 10:25:19 +02:00
2d8a7fb3cb Implemented manjaro 22 and sha 512 check 2023-06-20 08:50:59 +02:00
cd7b58d4fe Adapted physical block size and implemented new version of manjaro 2023-06-19 21:31:42 +02:00
4ed8a3468d Added Android-x86 2023-06-19 18:22:58 +02:00
11 changed files with 643 additions and 170 deletions

7
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
github: kevinveenbirkenbach
patreon: kevinveenbirkenbach
buy_me_a_coffee: kevinveenbirkenbach
custom: https://s.veen.world/paypaldonate

142
README.md
View File

@@ -1,40 +1,136 @@
# Linux Image Manager
[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](./LICENSE.txt)
# Linux Image Manager 🖥️🛠️
This repository contains some shell scripts to download and configure linux images and to transfer them to a storage.
[![GitHub Sponsors](https://img.shields.io/badge/Sponsor-GitHub%20Sponsors-blue?logo=github)](https://github.com/sponsors/kevinveenbirkenbach) [![Patreon](https://img.shields.io/badge/Support-Patreon-orange?logo=patreon)](https://www.patreon.com/c/kevinveenbirkenbach) [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20me%20a%20Coffee-Funding-yellow?logo=buymeacoffee)](https://buymeacoffee.com/kevinveenbirkenbach) [![PayPal](https://img.shields.io/badge/Donate-PayPal-blue?logo=paypal)](https://s.veen.world/paypaldonate)
## Virtual Btrfs RAID1 Setup
[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](./LICENSE.txt) [![GitHub stars](https://img.shields.io/github/stars/kevinveenbirkenbach/linux-image-manager.svg?style=social)](https://github.com/kevinveenbirkenbach/linux-image-manager/stargazers)
To setup a virtual btrfs encrypted raid 1 execute:
Linux Image Manager (lim) is a powerful collection of shell scripts for downloading, configuring, and managing Linux images. Whether you're setting up encrypted storage, configuring a virtual Btrfs RAID1, performing backups, or chrooting into an image, this tool makes Linux image administration simple and efficient. 🚀
> **Note:** In this project, `lim` is an alias for the **main.py** wrapper script which orchestrates the execution of the various shell scripts.
## Features ✨
- **Image Download & Setup:** Automatically download and prepare Linux distributions.
- **Encrypted Storage:** Configure LUKS encryption for secure image management.
- **Virtual RAID1:** Easily set up virtual Btrfs RAID1 for data redundancy.
- **Backup & Restore:** Create image backups from devices using dd.
- **Chroot Environment:** Easily enter a chroot shell to maintain or modify Linux images.
- **Automated Procedures:** Simplify partitioning, formatting, mounting, and more.
## Installation 📦
Install Linux Image Manager quickly using [Kevin's Package Manager](https://github.com/kevinveenbirkenbach/package-manager) under the alias `lim`. Just run:
```bash
bash scripts/encryption/storage/raid1/setup.sh
package-manager install lim
```
## Setup
This command makes Linux Image Manager globally available as `lim` in your terminal. The `lim` alias points to the **main.py** wrapper script.
To install a Linux distribution execute:
## Usage ⚙️
```bash
sudo bash ./scripts/image/setup.sh
```
The **main.py** wrapper provides a unified interface to run the different shell scripts included in this project. It supports various script types and allows you to pass additional parameters. The built-in `--help` option displays detailed usage information.
## Chroot
### Available Script Types
To chroot into a Linux distribution on a storage execute:
- **Image Setup (`--type image`):**
Executes the Linux image setup located at `scripts/image/setup.sh`. This setup:
- Creates partitions and formats them.
- Transfers the Linux image file to the device.
- Configures boot and root partitions.
```bash
sudo bash ./scripts/image/chroot.sh
```
- **Single Drive Encryption Setup (`--type single`):**
Executes the single-drive encryption setup from `scripts/encryption/storage/single_drive/setup.sh`. This setup:
- Sets up disk encryption using LUKS on one drive.
- Configures a Btrfs file system for secure storage.
## Backup
- **RAID1 Encryption Setup (`--type raid1`):**
Executes the RAID1 encryption setup found at `scripts/encryption/storage/raid1/setup.sh`. This setup:
- Configures a virtual RAID1 with two drives.
- Uses LUKS encryption and a Btrfs RAID1 file system for redundancy.
To backup a image execute:
- **Backup Image Setup (`--type backup`):**
Executes the backup image setup located at `scripts/image/backup.sh`. This setup:
- Creates an image backup from a memory device to a file.
- Uses `dd` to transfer the image from the specified device to an image file.
```bash
sudo bash ./scripts/image/backup.sh
```
- **Chroot Environment Setup (`--type chroot`):**
Executes the chroot setup from `scripts/image/chroot.sh`. This setup:
- Mounts partitions and configures the chroot environment for a Linux image.
- Provides a shell within the Linux image for system maintenance.
## License
### Command-Line Options
The ["GNU GENERAL PUBLIC LICENSE Version 3"](./LICENSE.txt) applies to this project.
- **`--type`**
**(Required)** Choose the type of script to execute. Options include: `image`, `single`, `raid1`, `backup`, and `chroot`.
- **`--extra`**
**(Optional)** Pass any extra parameters directly to the selected shell script.
- **`--auto-confirm`**
**(Optional)** Automatically bypass the confirmation prompt before executing the selected script.
- **`--help`**
**(Optional)** Displays detailed help information about the command-line options and usage of the wrapper. Simply run:
```bash
lim --help
```
to view the complete help message.
### Example Commands
- **Display Help:**
```bash
lim --help
```
- **Show Information About the Image Setup:**
```bash
lim --type image --info
```
- **Execute the Linux Image Setup (with extra parameters):**
```bash
lim --type image --extra --some-option value
```
- **Run the Single Drive Encryption Setup without a confirmation prompt:**
```bash
lim --type single --auto-confirm
```
- **Execute the RAID1 Encryption Setup:**
```bash
lim --type raid1
```
- **Perform a Backup of an Image:**
```bash
lim --type backup
```
- **Enter a Chroot Environment for a Linux Image:**
```bash
lim --type chroot
```
For additional details on each script and further configuration options, please refer to the `scripts/` and `configuration/` directories.
## Configuration & Customization 🔧
Customize your environment in the `configuration/` folder:
- **General Packages:** Contains common packages for all setup scripts.
- **Server LUKS Packages:** Contains packages needed for setting up LUKS encryption on servers.
## License 📜
This project is licensed under the GNU General Public License Version 3. See the [LICENSE.txt](./LICENSE.txt) file for details.
## Contact & Support 💬
- **Author:** Kevin Veen-Birkenbach
- **Email:** [kevin@veen.world](mailto:kevin@veen.world)
- **Website:** [https://www.veen.world/](https://www.veen.world/)
Feel free to contribute, report issues, or get in touch. Happy Linux managing! 😊
```

118
main.py Executable file
View File

@@ -0,0 +1,118 @@
#!/usr/bin/env python3
import subprocess
import os
import argparse
import sys
def run_script(script_path, extra_args):
if not os.path.exists(script_path):
print(f"[ERROR] Script not found at {script_path}")
exit(1)
command = ["sudo", "bash", script_path] + extra_args
print(f"[INFO] Running command: {' '.join(command)}")
# Pass the parent's stdout and stderr so that progress output shows in real time.
result = subprocess.run(command, stdout=sys.stdout, stderr=sys.stderr)
if result.returncode != 0:
print(f"[ERROR] Script exited with code {result.returncode}")
exit(result.returncode)
print("[SUCCESS] Script executed successfully.")
def main():
# Use os.path.realpath to get the actual path of this file regardless of symlinks.
repo_root = os.path.dirname(os.path.realpath(__file__))
# Define available scripts along with their descriptions.
setup_scripts = {
"image": {
"path": os.path.join(repo_root, "scripts", "image", "setup.sh"),
"description": (
"Linux Image Setup:\n"
" - Creates partitions and formats them.\n"
" - Transfers the Linux image file to the device.\n"
" - Configures boot and root partitions."
)
},
"single": {
"path": os.path.join(repo_root, "scripts", "encryption", "storage", "single_drive", "setup.sh"),
"description": (
"Single Drive Encryption Setup:\n"
" - Sets up disk encryption using LUKS on one drive.\n"
" - Configures a Btrfs file system for secure storage."
)
},
"raid1": {
"path": os.path.join(repo_root, "scripts", "encryption", "storage", "raid1", "setup.sh"),
"description": (
"RAID1 Encryption Setup:\n"
" - Configures a virtual RAID1 with two drives.\n"
" - Uses LUKS encryption and a Btrfs RAID1 file system for redundancy."
)
},
"backup": {
"path": os.path.join(repo_root, "scripts", "image", "backup.sh"),
"description": (
"Backup Image Setup:\n"
" - Creates an image backup from a memory device to a file.\n"
" - Uses dd to transfer the image from the specified device to an image file."
)
},
"chroot": {
"path": os.path.join(repo_root, "scripts", "image", "chroot.sh"),
"description": (
"Chroot Environment Setup:\n"
" - Mounts partitions and configures the chroot environment for a Linux image.\n"
" - Provides a shell within the Linux image for system maintenance."
)
}
}
parser = argparse.ArgumentParser(
description="Wrapper for executing various scripts from Linux Image Manager.",
epilog=(
"Available script types:\n"
" image - Linux Image Setup\n"
" single - Single Drive Encryption Setup\n"
" raid1 - RAID1 Encryption Setup\n"
" backup - Backup Image Setup\n"
" chroot - Chroot Environment Setup\n\n"
"Additional Options:\n"
" --extra Pass extra parameters to the selected script.\n"
" --auto-confirm Bypass the confirmation prompt before execution.\n"
" --help Display this help message and exit."
),
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument("--type", required=True, choices=list(setup_scripts.keys()),
help="Select the script type to execute. Options: " + ", ".join(setup_scripts.keys()))
parser.add_argument("--extra", nargs=argparse.REMAINDER, default=[],
help="Extra parameters to pass to the selected script.")
parser.add_argument("--auto-confirm", action="store_true",
help="Automatically confirm execution without prompting the user.")
args = parser.parse_args()
script_info = setup_scripts[args.type]
print("[INFO] Selected script type:", args.type)
print("[INFO] Description:")
print(script_info["description"])
print("[INFO] Script path:", script_info["path"])
if args.extra:
print("[INFO] Extra parameters provided:", " ".join(args.extra))
else:
print("[INFO] No extra parameters provided.")
if not args.auto_confirm:
try:
input("Press Enter to execute the script or Ctrl+C to cancel...")
except KeyboardInterrupt:
print("\n[ERROR] Execution aborted by user.")
exit(1)
run_script(script_info["path"], args.extra)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n[ERROR] Execution aborted by user.")
exit(1)

2
requirements.yml Normal file
View File

@@ -0,0 +1,2 @@
pacman:
- pv

View File

@@ -81,25 +81,62 @@ set_device_path(){
info "Device path set to: $device_path"
# @see https://www.heise.de/ct/hotline/Optimale-Blockgroesse-fuer-dd-2056768.html
PHYSICAL_BLOCK_SIZE_PATH="/sys/block/$device/queue/physical_block_size"
if [ -f "$device_path" ]
then
OPTIMAL_BLOCKSIZE=$(expr 64 \* "$(sudo cat $PHYSICAL_BLOCK_SIZE_PATH)") || error
else
OPTIMAL_BLOCKSIZE="1KB"
if [ -f "$PHYSICAL_BLOCK_SIZE_PATH" ]; then
PHYSICAL_BLOCK_SIZE=$(sudo cat $PHYSICAL_BLOCK_SIZE_PATH)
if [ $? -eq 0 ]; then
OPTIMAL_BLOCKSIZE=$((64 * PHYSICAL_BLOCK_SIZE)) || error
else
echo "Unable to read $PHYSICAL_BLOCK_SIZE_PATH"
OPTIMAL_BLOCKSIZE="4K"
fi
else
OPTIMAL_BLOCKSIZE="4K"
fi
info "Optimal blocksize set to: $OPTIMAL_BLOCKSIZE" ||
error
info "Optimal blocksize set to: $OPTIMAL_BLOCKSIZE" || error
}
overwritte_device_with_zeros(){
question "Should $device_path be overwritten with zeros before copying?(y/N)" && read -r copy_zeros_to_device
if [ "$copy_zeros_to_device" = "y" ]
then
info "Overwritting..." &&
dd if=/dev/zero of="$device_path" bs="$OPTIMAL_BLOCKSIZE" status=progress || error "Overwritting $device_path failed."
else
info "Skipping Overwritting..."
fi
print_partition_table_info() {
echo "##########################################################################################"
echo "Note on Partition Table Deletion:"
echo "---------------------------------------------"
echo "• MBR (Master Boot Record):"
echo " - Typically occupies the first sector (512 bytes), i.e., 1 block."
echo ""
echo "• GPT (GUID Partition Table):"
echo " - Uses a protective MBR (1 block), a GPT header (1 block),"
echo " and usually a partition entry array that takes up about 32 blocks."
echo " - Total: approximately 34 blocks (assuming a 512-byte block size)."
echo ""
echo "Recommendation: For deleting a GPT partition table, use a block size of 512 bytes"
echo " and overwrite at least 34 blocks to ensure the entire table is cleared."
echo "##########################################################################################"
}
overwrite_device() {
# Call the function to display the information.
print_partition_table_info
question "Should $device_path be overwritten with zeros before copying? (y/N/block count)" && read -r copy_zeros_to_device
case "$copy_zeros_to_device" in
y)
info "Overwriting entire device..." &&
dd if=/dev/zero of="$device_path" bs="$OPTIMAL_BLOCKSIZE" status=progress && sync || error "Overwriting $device_path failed."
;;
N|'')
info "Skipping Overwriting..."
;;
''|*[!0-9]*)
error "Invalid input."
;;
*)
if [[ "$copy_zeros_to_device" =~ ^[0-9]+$ ]]; then
info "Overwriting $copy_zeros_to_device blocks..." &&
dd if=/dev/zero of="$device_path" bs="$OPTIMAL_BLOCKSIZE" count="$copy_zeros_to_device" status=progress && sync || error "Overwriting $device_path failed."
else
error "Invalid input. Block count must be a number."
fi
;;
esac
}
get_packages(){

View File

@@ -29,12 +29,13 @@ create_luks_key_and_update_cryptab(){
info "Generate secret key under: $secret_key_path" || error
if [ -f "$secret_key_path" ]
then
warning "File allready exist. Overwritting!"
warning "File already exists. Overwriting!"
fi
sudo dd if=/dev/urandom of="$secret_key_path" bs=512 count=8 &&
sudo cryptsetup -v luksAddKey "$2" "$secret_key_path" &&
info "Opening and closing device to verify that that everything works fine..." || error
sudo cryptsetup -v luksClose "$1" || info "No need to luksClose $1."
sudo dd if=/dev/urandom of="$secret_key_path" bs=512 count=8 && sync &&
info "Opening and closing device to verify that everything works fine..." &&
sudo cryptsetup -v luksClose "$1" || info "No need to luksClose $1. Device isn't open." &&
sudo cryptsetup luksAddKey $2 $secret_key_path &&
sudo cryptsetup -v luksOpen "$2" "$1" --key-file="$secret_key_path" &&
sudo cryptsetup -v luksClose "$1" &&
info "Reading UUID..." &&
@@ -45,7 +46,7 @@ create_luks_key_and_update_cryptab(){
info "Adding crypttab entry..." || error
if sudo grep -q "$crypttab_entry" "$crypttab_path";
then
warning "File $crypttab_path contains allready the following entry:" &&
warning "File $crypttab_path already contains the following entry:" &&
echo "$crypttab_entry" &&
info "Skipped." ||
error
@@ -59,6 +60,7 @@ create_luks_key_and_update_cryptab(){
error
}
# @var $1 mapper_name
# @var $2 mount_path
#

View File

@@ -3,7 +3,13 @@
# shellcheck disable=SC2154 # Referenced but not assigned
# shellcheck disable=SC2015 #Deactivate bool hint
source "$(dirname "$(readlink -f "${0}")")/base.sh" || (echo "Loading base.sh failed." && exit 1)
info "Automount raid1 encrypted storages..." &&
info "Activate Automount raid1 encrypted storages..." &&
echo ""
for dev in $(lsblk -dno NAME); do
if sudo cryptsetup isLuks /dev/$dev 2>/dev/null; then
info "/dev/$dev is a LUKS encrypted storage device."
fi
done
set_raid1_devices_mount_partition_and_mapper_paths &&
create_luks_key_and_update_cryptab "$mapper_name_1" "$device_path_1" &&
info "Creating mount folder unter \"$mount_path_1\"..." &&

View File

@@ -7,7 +7,7 @@ echo "Setups disk encryption"
set_device_mount_partition_and_mapper_paths
overwritte_device_with_zeros
overwrite_device
info "Creating new GPT partition table..."
( echo "g" # create a new empty GPT partition table

View File

@@ -25,6 +25,6 @@ question "Please confirm by pushing \"Enter\". To cancel use \"Ctrl + Alt + C\""
read -r bestaetigung && echo "$bestaetigung";
info "Imagetransfer starts. This can take a while..." &&
dd if="$device_path" of="$ofi" bs=1M status=progress || error "\"dd\" failed.";
dd if="$device_path" of="$ofi" bs=1M status=progress && sync || error "\"dd\" failed.";
success "Imagetransfer successfull." && exit 0;

View File

@@ -38,7 +38,7 @@ make_mount_folders(){
}
make_working_folder(){
working_folder_path="/tmp/raspberry-pi-tools-$(date +%s)/" &&
working_folder_path="/tmp/linux-image-manager-$(date +%s)/" &&
info "Create temporary working folder in $working_folder_path" &&
mkdir -v "$working_folder_path" ||
error
@@ -48,7 +48,7 @@ decrypt_root(){
if [ "$(blkid "$root_partition_path" -s TYPE -o value)" == "crypto_LUKS" ]
then
root_partition_uuid=$(blkid "$root_partition_path" -s UUID -o value) &&
root_mapper_name="arch-root-$root_partition_uuid" &&
root_mapper_name="linux-image-manager-$root_partition_uuid" &&
root_mapper_path="/dev/mapper/$root_mapper_name" &&
info "Decrypting of $root_partition_path is neccessary..." &&
sudo cryptsetup -v luksOpen "$root_partition_path" "$root_mapper_name" || error

View File

@@ -21,6 +21,32 @@ install(){
esac
}
replace_in_file() {
# Assign the first function argument to the local variable search_string
local search_string=$1
# Assign the second function argument to the local variable replace_string
local replace_string=$2
# Assign the third function argument to the local variable file_path
local file_path=$3
# Create a temporary file and store its path in temp_file
temp_file=$(mktemp)
# Use sed to replace the search_string with replace_string in the file at file_path
# Write the output to the temporary file
sed "s/$search_string/$replace_string/g" "$file_path" > "$temp_file"
# Compare the original file with the temporary file
if cmp -s "$file_path" "$temp_file"; then
# If files are identical, remove the temporary file and signal an error
rm -f "$temp_file"
error "Error: Search string '$search_string' not found in $file_path."
else
# If files are different, move the temporary file to overwrite the original file
mv "$temp_file" "$file_path"
fi
}
info "Setupscript for images started..."
info "Checking if root..."
@@ -58,31 +84,55 @@ case "$operation_system" in
question "Which distribution should be used [arch,moode,retropie,manjaro,torbox...]?" && read -r distribution || error
case "$distribution" in
"android-x86")
base_download_url="https://www.fosshub.com/Android-x86.html?dwl=android-x86_64-9.0-r2.iso";
image_name="android-x86_64-9.0-r2.iso"
image_checksum="f7eb8fc56f29ad5432335dc054183acf086c539f3990f0b6e9ff58bd6df4604e"
boot_size="+500M"
;;
"torbox")
base_download_url="https://www.torbox.ch/data/";
image_name="torbox-20220102-v050.gz"
image_checksum="0E1BA7FFD14AAAE5F0462C8293D95B62C3BF1D9E726E26977BD04772C55680D3"
boot_size="+200M"
;;
"arch")
question "Which Raspberry Pi will be used(e.g.:1,2,3,4,aarch64):" && read -r version
question "Which Raspberry Pi will be used (e.g.: 1, 2, 3b, 3b+, 4...):" && read -r raspberry_pi_version
boot_size="+500M"
base_download_url="http://os.archlinuxarm.org/os/";
if [ "$version" == "1" ]
then
image_name="ArchLinuxARM-rpi-latest.tar.gz"
else
image_name="ArchLinuxARM-rpi-$version-latest.tar.gz"
fi
case "$raspberry_pi_version" in
"1")
image_name="ArchLinuxARM-rpi-armv7-latest.tar.gz"
luks_memory_cost="64000"
;;
"2")
image_name="ArchLinuxARM-rpi-armv7-latest.tar.gz"
luks_memory_cost="128000"
;;
"3b" | "3b+")
image_name="ArchLinuxARM-rpi-aarch64-latest.tar.gz"
luks_memory_cost="128000"
;;
"4" )
image_name="ArchLinuxARM-rpi-aarch64-latest.tar.gz"
luks_memory_cost="256000"
;;
*)
error "Version $raspberry_pi_version isn't supported."
;;
esac
;;
"manjaro")
question "Which version(e.g.:architect,gnome) should be used:" && read -r version
case "$version" in
question "Which version(e.g.:architect,gnome) should be used:" && read -r gnome_version
boot_size="+500M"
case "$gnome_version" in
"architect")
image_checksum="6b1c2fce12f244c1e32212767a9d3af2cf8263b2"
base_download_url="https://osdn.net/frs/redir.php?m=dotsrc&f=%2Fstorage%2Fg%2Fm%2Fma%2Fmanjaro%2Farchitect%2F20.0%2F";
image_name="manjaro-architect-20.0-200426-linux56.iso"
;;
"gnome")
question "Which release(e.g.:20,21) should be used:" && read -r release
question "Which release(e.g.:20,21,raspberrypi) should be used:" && read -r release
case "$release" in
"20")
image_checksum="2df3697908483550d4a473815b08c1377e6b6892"
@@ -93,19 +143,39 @@ case "$operation_system" in
base_download_url="https://download.manjaro.org/gnome/21.3.7/"
image_name="manjaro-gnome-21.3.7-220816-linux515.iso"
;;
"22")
base_download_url="https://download.manjaro.org/gnome/22.1.3/"
image_name="manjaro-gnome-22.1.3-230529-linux61.iso"
;;
"24")
base_download_url="https://download.manjaro.org/gnome/24.2.1/"
image_name="manjaro-gnome-24.2.1-241216-linux612.iso"
;;
"raspberrypi")
# at the moment just optimized for raspberry pi 4
base_download_url="https://github.com/manjaro-arm/rpi4-images/releases/download/23.02/"
image_name="Manjaro-ARM-gnome-rpi4-23.02.img.xz"
luks_memory_cost="256000"
raspberry_pi_version="4"
;;
*)
error "Gnome Version $gnome_version isn't supported."
;;
esac
;;
esac
;;
"moode")
boot_size="+200M"
image_checksum="185cbc9a4994534bb7a4bc2744c78197"
base_download_url="https://github.com/moode-player/moode/releases/download/r651prod/"
image_name="moode-r651-iso.zip";
;;
"retropie")
question "Which version(e.g.:1,2,3,4) should be used:" && read -r version
boot_size="+500M"
question "Which version(e.g.:1,2,3,4) should be used:" && read -r raspberry_pi_version
base_download_url="https://github.com/RetroPie/RetroPie-Setup/releases/download/4.8/";
case "$version" in
case "$raspberry_pi_version" in
"1")
image_checksum="95a6f84453df36318830de7e8507170e"
image_name="retropie-buster-4.8-rpi1_zero.img.gz"
@@ -159,30 +229,81 @@ case "$operation_system" in
;;
esac
if [ -z "$image_checksum" ]
then
sha1_download_url="$download_url.sha1"
info "Image Chechsum is not defined. Try to download image signature from $sha1_download_url."
if wget -q --method=HEAD "$sha1_download_url";
then
image_checksum="$(wget $sha1_download_url -q -O - | cut -d ' ' -f1 )"
info "Defined image_checksum as $image_checksum"
else
warning "No checksum found under $sha1_download_url."
fi
info "Verifying image..."
info "Verifying checksum..."
if [ -z "$image_checksum" ]; then
for ext in sha1 sha512 md5; do
sha_download_url="$download_url.$ext"
info "Image Checksum is not defined. Try to download image signature from $sha_download_url."
if wget -q --method=HEAD "$sha_download_url"; then
image_checksum="$(wget $sha_download_url -q -O - | cut -d ' ' -f1)"
info "Defined image_checksum as $image_checksum"
break
else
warning "No checksum found under $sha_download_url."
fi
done
fi
info "Verifying image..."
if [[ -v image_checksum ]]
then
(info "Checking md5 checksum..." && echo "$image_checksum $image_path"| md5sum -c -) ||
(info "Checking sha1 checksum..." && echo "$image_checksum $image_path"| sha1sum -c -) ||
(info "Checking sha256 checksum..." && echo "$image_checksum $image_path"| sha256sum -c -) ||
error "Verification failed. HINT: Force the download of the image."
else
warning "Verification is not possible. No checksum is defined."
if [[ -v image_checksum ]]; then
info "A checksum is defined for the image."
info "Checksums verify file integrity to ensure that the file was not corrupted during download."
info "The script will try verifying the integrity using MD5, then SHA1, and finally SHA256 if needed."
info "Trying MD5 checksum verification..."
(info "Checking md5 checksum..." && echo "$image_checksum $image_path" | md5sum -c -) ||
(warning "MD5 verification failed. This may indicate data corruption." &&
info "Trying SHA1 checksum verification for a secondary integrity check..." &&
info "Checking sha1 checksum..." && echo "$image_checksum $image_path" | sha1sum -c -) ||
(warning "SHA1 verification failed. Attempting SHA256 verification for thoroughness." &&
info "SHA256 provides a more robust check and is used as a final integrity measure." &&
info "Checking sha256 checksum..." && echo "$image_checksum $image_path" | sha256sum -c -) ||
error "Verification failed. HINT: Force the download of the image."
else
warning "No checksum is defined. Skipping checksum verification."
fi
info "Note: Checksums verify integrity but do not confirm authenticity."
info "Proceeding to signature verification, which ensures the file comes from a trusted source."
signature_download_url="$download_url.sig"
info "Attempting to download the image signature from: $signature_download_url"
info "Try to download image signature from $signature_download_url."
if wget -q --method=HEAD "$signature_download_url"; then
signature_name="${image_name}.sig"
signature_path="${image_folder}${signature_name}"
info "Download the signature file"
if wget -q -O "$signature_path" "$signature_download_url"; then
info "Extract the key ID from the signature file"
key_id=$(gpg --status-fd 1 --verify "$signature_path" "$image_path" 2>&1 | grep 'NO_PUBKEY' | awk '{print $NF}')
if [ -n "$key_id" ]; then
info "Check if the key is already in the keyring"
if gpg --list-keys "$key_id" > /dev/null 2>&1; then
info "Key $key_id already in keyring."
else
info "Import the public key"
gpg --keyserver keyserver.ubuntu.com --recv-keys "$key_id"
fi
info "Verify the signature again after importing the key"
if gpg --verify "$signature_path" "$image_path"; then
info "Signature verification succeeded."
else
warning "Signature verification failed."
fi
else
warning "No public key found in the signature file."
fi
else
warning "Failed to download the signature file."
fi
else
warning "No signature found under $signature_download_url."
fi
make_mount_folders
set_partition_paths
@@ -202,36 +323,51 @@ if [ "$transfer_image" = "y" ]
info "Skipping partition table deletion..."
fi
overwritte_device_with_zeros
overwrite_device
info "Starting image transfer..."
if [ "$distribution" = "arch" ]
then
# Set default size of the boot partition
boot_size=${boot_size:-"+500M"}
# Use the provided size or the default size
info "The boot partition will be set to $boot_size."
# Partitioning with the specified size
info "Creating partitions..." &&
( echo "o" #Type o. This will clear out any partitions on the drive.
echo "p" #Type p to list partitions. There should be no partitions left
echo "n" #Type n,
echo "p" #then p for primary,
echo "1" #1 for the first partition on the drive,
echo "" #Default start sector
echo "+300M" #then type +300M for the last sector.
echo "t" #Type t,
echo "c" #then c to set the first partition to type W95 FAT32 (LBA).
echo "n" #Type n,
echo "p" #then p for primary,
echo "2" #2 for the second partition on the drive,
echo "" #Default start sector
echo "" #Default end sector
echo "w" #Write the partition table and exit by typing w.
)| fdisk "$device_path" || error
(
echo "o" # Type o. This will clear out any partitions on the drive.
echo "p" # Type p to list partitions. There should be no partitions left
echo "n" # Type n,
echo "p" # then p for primary,
echo "1" # 1 for the first partition on the drive,
echo "" # Default start sector
echo "$boot_size" # Size of the boot partition
echo "t" # Type t,
echo "c" # then c to set the first partition to type W95 FAT32 (LBA).
echo "n" # Type n,
echo "p" # then p for primary,
echo "2" # 2 for the second partition on the drive,
echo "" # Default start sector
echo "" # Default end sector
echo "w" # Write the partition table and exit by typing w.
) | fdisk "$device_path" || error
info "Format boot partition..." &&
mkfs.vfat "$boot_partition_path" || error
if [ "$encrypt_system" == "y" ]
then
info "Formating $root_partition_path with LUKS..." &&
sudo cryptsetup -v luksFormat -c aes-xts-plain64 -s 512 -h sha512 --use-random -i 1000 "$root_partition_path" &&
# Check if luks_memory_cost is defined and set the luksAddKey command accordingly
# @see https://chatgpt.com/share/008ea5f1-670c-467c-8320-1ca67f25ac9a
if [ -n "$luks_memory_cost" ]; then
info "Formating $root_partition_path with LUKS with --pbkdf-memory set to $luks_memory_cost" &&
sudo cryptsetup -v luksFormat -c aes-xts-plain64 -s 512 -h sha512 --use-random -i 1000 --pbkdf-memory "$luks_memory_cost" "$root_partition_path" || error
else
info "Formating $root_partition_path with LUKS" &&
sudo cryptsetup -v luksFormat -c aes-xts-plain64 -s 512 -h sha512 --use-random -i 1000 "$root_partition_path" || error
fi
decrypt_root || error
fi
@@ -262,7 +398,13 @@ if [ "$transfer_image" = "y" ]
elif [ "${image_path: -4}" = ".iso" ]
then
info "Transfering .iso file..." &&
sudo dd if="$image_path" of="$device_path" bs="$OPTIMAL_BLOCKSIZE" conv=fsync status=progress &&
pv "$image_path" | sudo dd of="$device_path" bs="$OPTIMAL_BLOCKSIZE" conv=fsync &&
sync ||
error
elif [ "${image_path: -3}" = ".xz" ]
then
info "Transferring .xz file..." &&
unxz -c "$image_path" | sudo dd of="$device_path" bs="$OPTIMAL_BLOCKSIZE" conv=fsync status=progress &&
sync ||
error
else
@@ -272,7 +414,8 @@ if [ "$transfer_image" = "y" ]
info "Skipping image transfer..."
fi
if [ "$distribution" != "manjaro" ]
# Execute Raspberry Pi specific procedures
if [ -n "$raspberry_pi_version" ]
then
info "Start regular mounting procedure..."
if mount | grep -q "$boot_partition_path"
@@ -296,35 +439,53 @@ if [ "$distribution" != "manjaro" ]
info "Content of $fstab_path:$(cat "$fstab_path")" || error
info "Define target paths..." &&
administrator_username="administrator"
target_home_path="$root_mount_path""home/" &&
target_username=$(ls "$target_home_path") &&
default_username=$(ls "$target_home_path") &&
question "Should the $default_username be renamed to $administrator_username? (y/N):" && read -r rename_decision
if [ "$rename_decision" == "y" ];
then
variable_old_username="$default_username" &&
target_username="$administrator_username" &&
info "Rename home directory from $target_home_path$variable_old_username to $target_home_path$target_username..." &&
mv -v "$target_home_path$variable_old_username" "$target_home_path$target_username" || error "Failed to rename home directory"
else
target_username="$default_username"
fi
target_user_home_folder_path="$target_home_path$target_username/" &&
target_user_ssh_folder_path="$target_user_home_folder_path"".ssh/" &&
target_authorized_keys="$target_user_ssh_folder_path""authorized_keys" &&
question "Should the ssh-key be copied to the image?(y/N)" && read -r copy_ssh_key || error
if [ "$copy_ssh_key" == "y" ]
# Activate later. Here was a bug
question "Should the $target_username have sudo rights? (y/N):" && read -r sudo_decision
if [ "$sudo_decision" == "y" ]; then
sudo_config_dir="$root_mount_path""etc/sudoers.d/"
sudo_config_file="$sudo_config_dir$target_username"
mkdir -vp $sudo_config_dir
echo "$target_username ALL=(ALL:ALL) ALL" > "$sudo_config_file" || error "Failed to create sudoers file for $target_username"
chmod 440 "$sudo_config_file" || error "Failed to set permissions on sudoers file for $target_username"
fi
question "Enter the path to the SSH key to be added to the image (default: none):" && read -r origin_user_rsa_pub || error
if [ -z "$origin_user_rsa_pub" ]
then
correct_ssh_key_path=false;
while [ "$correct_ssh_key_path" != true ]
do
question "Whats the absolut path to the ssh key:" && read -r origin_user_rsa_pub || error
if [ -f "$origin_user_rsa_pub" ]
then
correct_ssh_key_path=true;
else
warning "The ssh key \"$origin_user_rsa_pub\" can't be copied to \"$target_authorized_keys\" because it doesn't exist."
fi
done
info "Copy ssh key to target..."
mkdir -v "$target_user_ssh_folder_path" || warning "Folder \"$target_user_ssh_folder_path\" exists. Can't be created."
cat "$origin_user_rsa_pub" > "$target_authorized_keys" &&
target_authorized_keys_content=$(cat "$target_authorized_keys") &&
info "$target_authorized_keys contains the following: $target_authorized_keys_content" &&
chown -vR 1000 "$target_user_ssh_folder_path" &&
chmod -v 700 "$target_user_ssh_folder_path" &&
chmod -v 600 "$target_authorized_keys" || error
else
info "Skipped SSH-key copying.."
else
if [ -f "$origin_user_rsa_pub" ]
then
info "Copy ssh key to target..."
mkdir -v "$target_user_ssh_folder_path" || warning "Folder \"$target_user_ssh_folder_path\" exists. Can't be created."
cat "$origin_user_rsa_pub" > "$target_authorized_keys" &&
target_authorized_keys_content=$(cat "$target_authorized_keys") &&
info "$target_authorized_keys contains the following: $target_authorized_keys_content" &&
info "Set permissions with chmod..." &&
chmod -v 700 "$target_user_ssh_folder_path" &&
chmod -v 600 "$target_authorized_keys" || error "Failed to set ownership and permissions on ssh folder"
else
error "The ssh key \"$origin_user_rsa_pub\" can't be copied to \"$target_authorized_keys\" because it doesn't exist."
fi
fi
info "Start chroot procedures..."
@@ -335,41 +496,57 @@ if [ "$distribution" != "manjaro" ]
copy_resolve_conf
question "Should the password of the standart user \"$target_username\" be changed?(y/N)" && read -r change_password
if [ "$change_password" == "y" ]
then
info "Changing passwords on target system..."
question "Type in new password: " && read -r password_1
question "Repeat new password\"$target_username\"" && read -r password_2
if [ "$password_1" = "$password_2" ]
then
(
echo "(
echo '$password_1'
echo '$password_1'
) | passwd $target_username"
echo "(
echo '$password_1'
echo '$password_1'
) | passwd"
) | chroot "$root_mount_path" /bin/bash || error
else
error "Passwords didn't match."
fi
else
info "Skipped password change..."
chroot_user_home_path="/home/$target_username/"
chroot_user_ssh_folder_path="$chroot_user_home_path.ssh"
if [ "$rename_decision" == "y" ]; then
info "Delete old user and create new user" &&
(
echo "userdel -r $variable_old_username"
echo "useradd -m -d $chroot_user_home_path -s /bin/bash $target_username"
echo "chown -R $target_username:$target_username $chroot_user_home_path"
) | chroot "$root_mount_path" /bin/bash || error "Failed to delete old user and create new user"
fi
hostname_path="$root_mount_path""etc/hostname"
question "Should the hostname be changed?(y/N)" && read -r change_hostname
if [ "$change_hostname" == "y" ]
if [ -n "$origin_user_rsa_pub" ]
then
question "Type in the hostname:" && read -r target_hostname;
echo "$target_hostname" > "$hostname_path" || error
info "Chroot to set ownership..." &&
( echo "chown -vR $target_username:$target_username $chroot_user_ssh_folder_path" ) | chroot "$root_mount_path" /bin/bash || error
fi
question "Type in new password for user root and $target_username (leave empty to skip): " && read -r password_1
if [ -n "$password_1" ]; then
question "Repeat new password for \"$target_username\": " && read -r password_2
if [ "$password_1" = "$password_2" ]; then
info "Changing passwords on target system..."
(
echo "(
echo '$password_1'
echo '$password_1'
) | passwd $target_username"
echo "(
echo '$password_1'
echo '$password_1'
) | passwd"
) | chroot "$root_mount_path" /bin/bash || error "Failed to change password."
else
target_hostname=$(cat "$hostname_path")
info "Skipped hostname change..."
error "Passwords didn't match."
fi
else
info "No password change requested, skipped password change..."
fi
hostname_path="$root_mount_path/etc/hostname"
question "Type in the hostname (leave empty to skip): " && read -r target_hostname
if [ -n "$target_hostname" ]; then
echo "$target_hostname" > "$hostname_path" || error "Failed to set hostname."
else
target_hostname=$(cat "$hostname_path")
info "No hostname change requested, skipped hostname change..."
fi
info "Used hostname is: $target_hostname"
case "$distribution" in
@@ -412,7 +589,9 @@ if [ "$distribution" != "manjaro" ]
if [ "$encrypt_system" == "y" ]
then
# Adapted this instruction for setting up encrypted systems @see https://gist.github.com/gea0/4fc2be0cb7a74d0e7cc4322aed710d38
# Adapted this instruction for setting up encrypted systems
# @see https://gist.github.com/gea0/4fc2be0cb7a74d0e7cc4322aed710d38
# @see https://gist.github.com/EnigmaCurry/2f9bed46073da8e38057fe78a61e7994
info "Setup encryption..." &&
info "Installing neccessary software..." &&
@@ -422,19 +601,43 @@ if [ "$distribution" != "manjaro" ]
info "Adding $target_authorized_keys to dropbear..." &&
cp -v "$target_authorized_keys" "$dropbear_root_key_path" &&
#Concerning mkinitcpio warning @see https://gist.github.com/imrvelj/c65cd5ca7f5505a65e59204f5a3f7a6d
# Concerning mkinitcpio warning
# @see https://gist.github.com/imrvelj/c65cd5ca7f5505a65e59204f5a3f7a6d
mkinitcpio_path="$root_mount_path""etc/mkinitcpio.conf" &&
info "Configuring $mkinitcpio_path..." &&
mkinitcpio_search_modules="MODULES=()" &&
mkinitcpio_replace_modules="MODULES=(g_cdc usb_f_acm usb_f_ecm smsc95xx g_ether)" &&
mkinitcpio_search_modules="MODULES=()" || error
# Concerning which moduls to load
# @see https://raspberrypi.stackexchange.com/questions/67051/raspberry-pi-3-with-archarm-and-encrypted-disk-will-not-boot-how-can-be-identif
case "$raspberry_pi_version" in
"1" | "2")
mkinitcpio_additional_modules=""
;;
"3b")
mkinitcpio_additional_modules="smsc95xx"
;;
"3b+" | "4")
mkinitcpio_additional_modules="lan78xx"
;;
*)
warning "Version $raspberry_pi_version isn't supported."
;;
esac
mkinitcpio_replace_modules="MODULES=(g_cdc usb_f_acm usb_f_ecm $mkinitcpio_additional_modules g_ether)" || error
mkinitcpio_search_binaries="BINARIES=()" &&
mkinitcpio_replace_binaries=$(echo "BINARIES=(/usr/lib/libgcc_s.so.1)"| sed -e 's/[\/&]/\\&/g') &&
mkinitcpio_encrypt_hooks="sleep netconf dropbear encryptssh" &&
mkinitcpio_search_hooks="HOOKS=(base udev autodetect modconf block filesystems keyboard fsck)" &&
mkinitcpio_replace_hooks="HOOKS=(base udev autodetect modconf block $mkinitcpio_encrypt_hooks filesystems keyboard fsck)" &&
sed -i "s/$mkinitcpio_search_modules/$mkinitcpio_replace_modules/g" "$mkinitcpio_path" &&
sed -i "s/$mkinitcpio_search_binaries/$mkinitcpio_replace_binaries/g" "$mkinitcpio_path" &&
sed -i "s/$mkinitcpio_search_hooks/$mkinitcpio_replace_hooks/g" "$mkinitcpio_path" &&
mkinitcpio_hooks_prefix="base udev autodetect microcode modconf kms keyboard keymap consolefont block"
mkinitcpio_hooks_suffix="filesystems fsck"
mkinitcpio_search_hooks="HOOKS=($mkinitcpio_hooks_prefix $mkinitcpio_hooks_suffix)" &&
mkinitcpio_replace_hooks="HOOKS=($mkinitcpio_hooks_prefix $mkinitcpio_encrypt_hooks $mkinitcpio_hooks_suffix)" &&
replace_in_file "$mkinitcpio_search_modules" "$mkinitcpio_replace_modules" "$mkinitcpio_path" &&
replace_in_file "$mkinitcpio_search_binaries" "$mkinitcpio_replace_binaries" "$mkinitcpio_path" &&
replace_in_file "$mkinitcpio_search_hooks" "$mkinitcpio_replace_hooks" "$mkinitcpio_path" &&
info "Content of $mkinitcpio_path:$(cat "$mkinitcpio_path")" &&
info "Generating mkinitcpio..." &&
echo "mkinitcpio -vP" | chroot "$root_mount_path" /bin/bash &&
@@ -467,9 +670,11 @@ if [ "$distribution" != "manjaro" ]
info "Configuring $boot_txt_path..." &&
boot_txt_delete_line=$(echo "part uuid \${devtype} \${devnum}:2 uuid" | sed -e 's/[]\/$*.^[]/\\&/g') &&
boot_txt_setenv_origin=$(echo "setenv bootargs console=ttyS1,115200 console=tty0 root=PARTUUID=\${uuid} rw rootwait smsc95xx.macaddr=\"\${usbethaddr}\"" | sed -e 's/[]\/$*.^[]/\\&/g') &&
boot_txt_setenv_replace=$(echo "setenv bootargs console=ttyS1,115200 console=tty0 ip=::::$target_hostname:eth0:dhcp $cryptdevice_configuration rw rootwait smsc95xx.macaddr=\"\${usbethaddr}\""| sed -e 's/[\/&]/\\&/g') &&
sed -i "s/$boot_txt_delete_line//g" "$boot_txt_path" &&
sed -i "s/$boot_txt_setenv_origin/$boot_txt_setenv_replace/g" "$boot_txt_path" &&
# Concerning issues with network adapter names;
# @see https://forum.iobroker.net/topic/40542/raspberry-pi4-kein-eth0-mehr/16
boot_txt_setenv_replace=$(echo "setenv bootargs console=ttyS1,115200 console=tty0 ip=::::$target_hostname:eth0:dhcp $cryptdevice_configuration rw rootwait smsc95xx.macaddr=\"\${usbethaddr}\" net.ifnames=0 biosdevname=0"| sed -e 's/[\/&]/\\&/g') &&
replace_in_file "$boot_txt_delete_line" "" "$boot_txt_path" &&
replace_in_file "$boot_txt_setenv_origin" "$boot_txt_setenv_replace" "$boot_txt_path" &&
info "Content of $boot_txt_path:$(cat "$boot_txt_path")" &&
info "Generating..." &&
echo "cd /boot/ && ./mkscr || exit 1" | chroot "$root_mount_path" /bin/bash || error
@@ -478,7 +683,7 @@ if [ "$distribution" != "manjaro" ]
info "Configuring $cmdline_txt_path..." &&
cmdline_search_string=$(echo "root=/dev/mmcblk0p2" | sed -e 's/[\/&]/\\&/g') &&
cmdline_replace_string=$(echo "$cryptdevice_configuration rootfstype=$root_filesystem"| sed -e 's/[\/&]/\\&/g') &&
sed -i "s/$cmdline_search_string/$cmdline_replace_string/g" "$cmdline_txt_path" &&
replace_in_file "$cmdline_search_string" "$cmdline_replace_string" "$cmdline_txt_path" &&
info "Content of $cmdline_txt_path:$(cat "$cmdline_txt_path")" || error
fi
fi
@@ -486,7 +691,7 @@ if [ "$distribution" != "manjaro" ]
info "Running system specific procedures..."
if [ "$distribution" = "retropie" ]
then
if [ "$copy_ssh_key" == "y" ]
if [ -n "$origin_user_rsa_pub" ]
then
ssh_file="$boot_mount_path""ssh" &&
echo "" > "$ssh_file"