Last active
June 7, 2024 10:21
-
-
Save kidpixo/e3d74a9cbb9bb56865aa0cf6ddcb76f2 to your computer and use it in GitHub Desktop.
Script to create and handle rsync backup on crypted external usb as LVM on LUKS
This file contains 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
#!/bin/bash | |
backup_check_disk() { | |
######################################################################## | |
# Function to check the current external disk | |
# | |
# This function iterates over a list of paths of possible external disks | |
# and checks if each path exists. It returns the basename of the first | |
# path that exists. | |
# | |
# Returns: | |
# The basename of the first existing disk path, or an empty string if | |
# no path exists. | |
# | |
# Example usage: | |
# current_disk=$(backup_check_disk) | |
# echo "Current Disk: $current_disk" | |
######################################################################## | |
# Array of possible external disk paths | |
local -a EXTERNAL_DISK_PATHS=( | |
# Black Toshiba 2.5" external disk | |
"/dev/disk/by-id/usb-TOSHIBA_EXTERNAL_USB_20231120008590F-0:0" | |
# 3.5" Toshiba external disk at work (dead?) | |
"/dev/disk/by-id/usb-TOSHIBA_External_USB_3.0_20151012015818-0:0" | |
# Blue Toshiba 2.5" external disk | |
"/dev/disk/by-id/usb-TOSHIBA_External_USB_3.0_20170317010805F-0:0" | |
# Black WD Elements 2.5", Saturn, 28.05.2024 | |
"/dev/disk/by-id/usb-WD_Elements_2620_5758323241373335584E3555-0:0" | |
) | |
local CURRENT_DISK="" | |
# Iterate over each disk path | |
for path in "${EXTERNAL_DISK_PATHS[@]}"; do | |
# Check if the path exists | |
if [[ -e "$path" ]]; then | |
# Set the current disk to the basename of the path | |
local CURRENT_DISK=$(basename "$path") | |
# Break the loop after the first existing disk is found | |
break | |
fi | |
done | |
# Return the current disk or an empty string if no disk is found | |
echo "$CURRENT_DISK" | |
} | |
backup_mount_external_disks() { | |
######################################################################## | |
# Function to mount the current external disk | |
# | |
# This function checks the current external disk and mounts it to the | |
# specified mount point. | |
# | |
# Returns: | |
# None | |
# | |
# Example usage: | |
# backup_mount_external_disks | |
######################################################################### | |
# Check the current external disk | |
local CURRENT_DISK=$(backup_check_disk) | |
# If the disk is found, mount it | |
if [ -n "$CURRENT_DISK" ]; then | |
echo "Current Disk : $CURRENT_DISK" | |
echo "Decrypt $CURRENT_DISK-part2" | |
# add this only if encrypted | |
# sudo cryptsetup isLuks "/dev/disk/by-id/$CURRENT_DISK"-part1 && echo "LUKS Encrypted" || echo "NOT Encrypted" | |
sudo cryptsetup open --type luks "/dev/disk/by-id/$CURRENT_DISK"-part2 cryptlvm_backup | |
echo "Activate LVM volume_backup (root,home,swap)" | |
sudo vgchange -a y volume_backup | |
echo "Mount /dev/volume_backup/root to /mnt/backup" | |
sudo mount /dev/volume_backup/root /mnt/backup | |
echo "Mount /dev/volume_backup/home to /mnt/backup/home" | |
sudo mount /dev/volume_backup/home /mnt/backup/home | |
echo "Mount $CURRENT_DISK-part1 to /mnt/backup/boot" | |
sudo mount "/dev/disk/by-id/$CURRENT_DISK"-part1 /mnt/backup/boot | |
else | |
echo "NO External Disk Present : STOPPING" | |
fi | |
} | |
backup_close_external_disks() { | |
####################################################################### | |
# Function to close the current external disk | |
# | |
# This function checks the current external disk and unmounts it from the | |
# specified mount point. It also deactivates the LVM volume group and | |
# closes the encrypted partition. | |
# | |
# Returns: | |
# None | |
# | |
# Example usage: | |
# backup_close_external_disks | |
####################################################################### | |
# Check the current external disk | |
local CURRENT_DISK=$(backup_check_disk) | |
# If the disk is found, unmount it | |
if [ -n "$CURRENT_DISK" ]; then | |
echo "Current Disk : $CURRENT_DISK" | |
echo "Umount everything under /mnt/backup" | |
# Unmount everything under /mnt/backup | |
sudo umount -R /mnt/backup | |
echo "Deactivate volume_backup" | |
# Deactivate the volume group | |
sudo vgchange -an volume_backup | |
echo "Close encrypted partition" | |
# Close the encrypted partition | |
sudo cryptsetup close cryptlvm_backup | |
else | |
# If no external disk is present, print a message and stop | |
echo "NO External Disk Present : STOPPING" | |
fi | |
} | |
### DEBUG : I am moving this to functions, it is used when you disconnect the disk and have dangling lvm/crypt volumes that won't close | |
# # list device-mapper devices: useful if cryptdevices are stuck (LVM not closed etc) | |
# sudo dmsetup table | |
# cryptlvm: 0 3905945600 ...... | |
# volume-home: 0 3419340800 linear 254:0 486604799 | |
# volume-root: 0 419430400 linear 254:0 67174399 | |
# volume-swap: 0 67108864 linear 254:0 65535 | |
# | |
# # info on cryptlvm_backup | |
# sudo cryptsetup --verbose --debug luksDump cryptlvm_backup | |
# | |
# # remove device-mapper | |
# sudo dmsetup remove volume_backup-{root,home,swap} | |
# # close the cryptlvm_backup device | |
# sudo cryptsetup close cryptlvm_backup | |
backup_rsync(){ | |
####################################################################### | |
# Function to perform an rsync backup of the system to an external disk. | |
# This function checks if the desired mount points are present and if the | |
# external disk is connected. If both conditions are met, it performs the | |
# backup using rsync. Otherwise, it prints a message and stops. | |
# | |
# This function does not take any parameters. | |
# | |
# This function does not return any value | |
####################################################################### | |
# Define color codes for printing messages | |
local green='\033[0;32m' # Green color code | |
local red='\033[0;31m' # Red color code | |
local reset='\033[0m' # Reset color code | |
# Get the current disk | |
local CURRENT_DISK=$(backup_check_disk) | |
echo "Current Disk : $CURRENT_DISK" | |
# Check if the target mount points are present | |
if [[ $(backup_check_mountpoints) -eq 0 ]] && [[ -n "$CURRENT_DISK" ]] ; then | |
echo -e "${green}All desired mount points found!${reset}" | |
echo "Going on with rsync" | |
# Sync the /boot directory in place | |
echo sudo rsync -aAXHl --inplace --delete --info=progress2 --human-readable --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found"} /boot/ /mnt/backup/boot | |
sudo rsync -aAXHl --inplace --delete --info=progress2 --human-readable --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found"} /boot/ /mnt/backup/boot | |
# Copy local backuped systemd-boot entries and fstab to backup disk | |
echo sudo cp $CURRENT_DISK/boot/loader/entries/*conf /mnt/backup/boot/loader/entries/ | |
sudo cp $CURRENT_DISK/boot/loader/entries/*conf /mnt/backup/boot/loader/entries/ | |
echo sudo cp $CURRENT_DISK/etc/fstab /mnt/backup/etc/ | |
sudo cp $CURRENT_DISK/etc/fstab /mnt/backup/etc/ | |
# Sync everything BUT /boot to the current disk | |
echo sudo rsync -aAXHl --delete --info=progress2 --human-readable --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found","/boot"} / /mnt/backup/ | |
sudo rsync -aAXHl --delete --info=progress2 --human-readable --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found","/boot"} / /mnt/backup/ | |
else | |
echo "Some desired mount points are missing!" | |
echo "List what is present: " | |
backup_status | |
fi | |
} | |
backup_check_mountpoints() { | |
####################################################################### | |
# Function to check if the desired mount points are present | |
# This function checks if all desired mount points are present in the /proc/mounts file | |
# and returns 0 if all mounts are found, 1 otherwise | |
# | |
# This function does not take any parameters. | |
# | |
# This function does not return any value | |
####################################################################### | |
# Define the desired mount points as an array | |
# The desired mount points are "/mnt/backup", "/mnt/backup/home" and "/mnt/backup/boot" | |
local desired_mounts=("/mnt/backup" "/mnt/backup/home" "/mnt/backup/boot") | |
# Get all mount points targets from /proc/mounts | |
# The awk command '{print $2}' gets the second column from /proc/mounts file which contains the target mount points | |
local all_mounts=$(awk '{print $2}' /proc/mounts) | |
# Check if each desired mount point is present in the all_mounts list | |
# The '[[ ! $all_mounts =~ $mount_point ]]' checks if the mount point is not present in the all_mounts list | |
# If a mount point is not found, the function sets the 'found' variable to 1 and breaks the loop | |
local found=0 | |
for mount_point in "${desired_mounts[@]}"; do | |
if [[ ! $all_mounts =~ $mount_point ]]; then | |
found=1 | |
break | |
fi | |
done | |
# Return 0 if all mounts found, 1 otherwise | |
echo $found | |
return $found | |
} | |
backup_status(){ | |
####################################################################### | |
# Function to get the status of the backup mount points and the current disk | |
# | |
# This function checks if the desired mount points are present in the /proc/mounts file | |
# and if the current disk is set. It prints the status of each mount point and the current disk value. | |
# | |
# This function does not take any parameters. | |
# | |
# This function does not return any value. | |
####################################################################### | |
# Get the current disk | |
local CURRENT_DISK=$(backup_check_disk) | |
# Define the desired mount points as an array | |
# The desired mount points are "/mnt/backup", "/mnt/backup/home" and "/mnt/backup/boot" | |
local desired_mounts=("/mnt/backup" "/mnt/backup/home" "/mnt/backup/boot") | |
# Define color codes for printing messages | |
local green='\033[0;32m' # Green color code | |
local red='\033[0;31m' # Red color code | |
local reset='\033[0m' # Reset color code | |
lsblk -fA | |
echo | |
# Loop through desired mount points | |
for mount_point in "${desired_mounts[@]}"; do | |
# Check if mount point is present in /proc/mounts | |
if grep -q "$mount_point" /proc/mounts; then | |
# Print message if mount point is mounted | |
echo -e "${green}'$mount_point' is mounted.${reset}" | |
else | |
# Print message if mount point is not mounted | |
echo -e "${red}'$mount_point' is not mounted.${reset}" | |
fi | |
done | |
# Check if the current disk value is empty | |
if [ -e "$CURRENT_DISK" ]; then | |
# Print message if current disk value is not empty | |
echo -e "${green}CURRENT_DISK value is '${CURRENT_DISK}'${reset}" | |
else | |
# Print message if current disk value is empty | |
echo -e "${red}CURRENT_DISK is empty.${reset}" | |
fi | |
} | |
#### SHORT ALIASES | |
alias b_check_disk='backup_check_disk' | |
alias b_mount_external_disks='backup_mount_external_disks' | |
alias b_close_external_disks='backup_close_external_disks' | |
alias b_rsync='backup_rsync' | |
alias b_status='backup_status' | |
_backup_completion() { | |
# Get a list of all functions starting with "backup_" (excluding this function) | |
local functions=( $(compgen -A function "${@:1}" | grep -E '^[backup_|b_]') ) | |
# Add the function names to the completion list (COMPREPLY) | |
COMPREPLY=("${functions[@]}") | |
} | |
# Register the completion function for commands starting with "backup_" | |
complete -F _backup_completion backup_ |
This file contains 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
#!/bin/bash | |
#### LINES TESTED | |
# mostly from [LVM on LUKS Arch installation with systemd-boot](https://gist.github.com/OdinsPlasmaRifle/e16700b83624ff44316f87d9cdbb5c94) | |
# | |
# format new disk as boot + LVM | |
sudo parted --script /dev/sdb \ | |
mklabel gpt \ | |
mkpart primary fat3 21MiB 489MiB \ | |
set 1 esp on \ | |
mkpart primary 489MiB 100% \ | |
set 2 lvm on; | |
# print the new paritition table | |
sudo parted -s /dev/sdb print; | |
# create filesystem on parition | |
sudo mkfs.fat -F32 /dev/sdb1 | |
sudo mkfs.ext4 /dev/sdb2 | |
############## Create linux LVM on second parition | |
sudo pvcreate /dev/sdb2 | |
sudo vgcreate volume_backup /dev/sdb2 | |
###### create CRYPTed parition + on linux LVM | |
sudo cryptsetup luksFormat /dev/sdb2 | |
# Open the disk with the password set above: | |
sudo cryptsetup open --type luks /dev/sdb2 cryptlvm_backup | |
# Check the lvm disk exists: | |
ls /dev/mapper/cryptlvm_backup | |
sudo pvcreate /dev/mapper/cryptlvm_backup | |
# Create a volume group: | |
sudo vgcreate volume_backup /dev/mapper/cryptlvm_backup | |
###### CRYPT + LVM ################################################### | |
# create logical partitions: | |
sudo lvcreate -L32G volume_backup -n swap | |
sudo lvcreate -L200G volume_backup -n root | |
sudo lvcreate -l 99%FREE volume_backup -n home | |
# format file system on logical partitions: | |
sudo mkfs.ext4 /dev/volume_backup/root | |
sudo mkfs.ext4 /dev/volume_backup/home | |
sudo mkswap /dev/volume_backup/swap | |
# show mounted | |
lsblk -f | |
# decrypt new toshiba | |
#sudo cryptsetup open --type luks /dev/disk/by-id/usb-TOSHIBA_EXTERNAL_USB_20231120008590F-0\:0-part2 cryptlvm_backup | |
# activate volume | |
sudo vgchange -a y volume_backup | |
# activate logical volumes | |
sudo lvchange -a y volume_backup/root | |
sudo lvchange -a y volume_backup/home | |
sudo lvchange -a y volume_backup/swap | |
sudo mkdir -p /mnt/swap_backup | |
# # those mountpoints should already exist | |
# # they exists for sure on the original system mounted under: | |
# disk | |
# ├── swap # swap lvm partiton /dev/volume/swap | | |
# └── / # root lvm partiton /dev/volume/root |- cryptsetup luks2 physical partition | |
# ├── /home # home lvm partiton /dev/volume/home | | |
# └── /boot # boot physical partition | |
# | |
# sudo mkdir -p /mnt/boot/backup | |
# sudo mkdir -p /mnt/backup/home | |
# create a /home in the external backup root | |
sudo mount /dev/volume_backup/root /mnt/backup | |
if [ ! -d /mnt/backup/home ]; then | |
sudo mkdir -p /mnt/backup/home; | |
fi | |
# create a /boot in the external backup root | |
sudo mount /dev/volume_backup/home /mnt/backup/home | |
# generic device | |
if [ ! -d /mnt/backup/boot ]; then | |
sudo mkdir -p /mnt/backup/boot; | |
fi | |
# sudo mount /dev/sdb1 /mnt/backup/boot | |
# # small toshiba old | |
# sudo mount /dev/disk/by-id/usb-TOSHIBA_External_USB_3.0_20170317010805F-0\:0-part1 /mnt/backup/boot | |
# sudo mount /dev/disk/by-uuid/3545-FC05 /mnt/backup/boot | |
# # small toshiba new | |
# sudo mount /dev/disk/by-id/usb-TOSHIBA_EXTERNAL_USB_20231120008590F-0:0-part1 /mnt/backup/boot | |
# sudo mount /dev/disk/by-uuid/C379-D5DF /mnt/backup/boot | |
# | |
# # sudo mount /dev/sdb1 /mnt/boot_backup | |
# # sudo swapon /dev/volume_backup/swap | |
################# | |
# close | |
# | |
# umount everything | |
sudo umount -R /mnt/backup | |
# deactivate volume | |
sudo vgchange -an volume_backup | |
# close encrypted partition | |
sudo cryptsetup close cryptlvm_backup | |
####### WARNING : WIP!!!! REFRACTORING | |
# Function to display available disks and prompt the user to select a disk | |
select_disk() { | |
local disks=$(lsblk --output NAME --noheadings) | |
local options=() | |
for disk in $disks; do | |
options+=("$disk") | |
done | |
echo "Available disks:" | |
select disk_name in "${options[@]}"; do | |
if [[ -n $disk_name ]]; then | |
echo "Selected disk: $disk_name" | |
echo "/dev/$disk_name" | |
break | |
else | |
echo "Invalid selection. Please try again." | |
fi | |
done | |
} | |
# Function to partition the new disk | |
partition_new_disk() { | |
local disk=$1 | |
# Partition the new disk as boot + LVM | |
sudo parted --script "$disk" \ | |
mklabel gpt \ | |
mkpart primary 1MiB 512MiB \ | |
set 1 esp on \ | |
mkpart primary 512MiB 100% \ | |
quit | |
# Create physical volumes for root, home, and swap | |
sudo pvcreate "$disk"2 | |
# Create volume group for the new disk | |
sudo vgcreate volume_backup "$disk"2 | |
# Create logical volumes for root, home, and swap | |
sudo lvcreate -L32G volume_backup -n swap | |
sudo lvcreate -L200G volume_backup -n root | |
sudo lvcreate -l 99%FREE volume_backup -n home | |
} | |
# Function to format the new disk | |
format_new_disk() { | |
local disk=$1 | |
# Format the new disk | |
sudo mkfs.ext4 "$disk"volume_backup/root | |
sudo mkfs.ext4 "$disk"volume_backup/home | |
sudo mkswap "$disk"volume_backup/swap | |
} | |
# Function to mount the new disk | |
mount_new_disk() { | |
local disk=$1 | |
# Create mount points for the new disk | |
sudo mkdir -p /mnt/root /mnt/home /mnt/swap | |
# Mount the new disk | |
sudo mount "$disk"volume_backup/root /mnt/root | |
sudo mount "$disk"volume_backup/home /mnt/home | |
sudo mount "$disk"volume_backup/swap /mnt/swap | |
sudo swapon "$disk"volume_backup/swap | |
} | |
# Function to copy files from the old disk to the new disk | |
copy_files() { | |
# Copy files from the old disk to the new disk | |
sudo rsync -aAX /mnt/old_root/ /mnt/root/ | |
sudo rsync -aAX /mnt/old_home/ /mnt/home/ | |
} | |
# Function to unmount the new disk | |
unmount_new_disk() { | |
local disk=$1 | |
# Unmount the new disk | |
sudo umount /mnt/root | |
sudo umount /mnt/home | |
sudo umount /mnt/swap | |
} | |
# Function to update fstab | |
update_fstab() { | |
local disk=$1 | |
# Update fstab with the UUID of the new disk | |
sudo blkid | grep volume_backup | awk '{print $2}' | sed 's/"//g' | sudo tee -a /etc/fstab | |
} | |
# Call the functions in the desired order | |
selected_disk=$(select_disk) | |
partition_new_disk "$selected_disk" | |
format_new_disk "$selected_disk" | |
mount_new_disk "$selected_disk" | |
copy_files | |
unmount_new_disk "$selected_disk" | |
update_fstab "$selected_disk" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment