Clone a Linux System

Target Preparation

Partitioning

Creating and exporting a variable for the drive that I would like to prepare as the target device:

export TGTDEV=sda

Looking at the source device, I have this layout:

(parted) p                                                                
Model: ATA Samsung SSD 860 (scsi)
Disk /dev/sdb: 1000215216s
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags: 

Number  Start      End         Size        File system  Name  Flags
 1      2048s      1050623s    1048576s    fat32        EFI   boot, esp
 2      1050624s   35651583s   34600960s   ext2         SWAP  swap
 3      35651584s  943718399s  908066816s               ROOT

So I will replicate the same thing:

# parted /dev/$TGTDEV --script mklabel gpt
# parted /dev/$TGTDEV --script mkpart EFI fat32 2048s 1050623s
# parted /dev/$TGTDEV --script set 1 esp on                
# parted /dev/$TGTDEV --script mkpart SWAP ext2 1050624s 35651583s
# parted /dev/$TGTDEV --script set 2 swap on                
# parted /dev/$TGTDEV --script mkpart ROOT ext4 35651584s 943718399s

I also want to add another system, so I create anoter SWAP and ROOT partition for it. I intentionally create a new swap partition, as there will be situations when I run both systems, one physically and the other one virtualized.

# parted /dev/$TGTDEV --script mkpart MWSWAP ext2 943718400s 978319359s
# parted /dev/$TGTDEV --script set 4 swap on
# parted /dev/$TGTDEV --script mkpart MWROOT ext4 978319360s 100%

EFI setup

Given that I have created a fresh EFI partition, this needs to be formatted and the EFI needs to be installed:

# mkfs.fat -F32 /dev/${TGTDEV}1

Then I need to mount it

# mkdir -p /efiroot
# mount /dev/${TGTDEV}1 /efiroot

And install EFI there:

# bootctl --path=/efiroot install

I got a couple of warnings here:

Created "/efiroot/EFI".
Created "/efiroot/EFI/systemd".
Created "/efiroot/EFI/BOOT".
Created "/efiroot/loader".
Created "/efiroot/loader/entries".
Created "/efiroot/EFI/Linux".
Copied "/usr/lib/systemd/boot/efi/systemd-bootx64.efi" to "/efiroot/EFI/systemd/systemd-bootx64.efi".
Copied "/usr/lib/systemd/boot/efi/systemd-bootx64.efi" to "/efiroot/EFI/BOOT/BOOTX64.EFI".
⚠️ Mount point '/efiroot' which backs the random seed file is world accessible, which is a security hole! ⚠️
⚠️ Random seed file '/efiroot/loader/.#bootctlrandom-XXX' is world accessible, which is a security hole! ⚠️
Random seed file /efiroot/loader/random-seed successfully written (32 bytes).
Created EFI boot entry "Linux Boot Manager".

After that make sure that you have a nice menu that will be displayed for some time:

# tee "/efiroot/loader/loader.conf" << EOF
timeout 10
console-mode keep
EOF

I decided to ignore those for now. Will go ahead and unmount the filesystem for now:

# umount /efiroot

Swap setup

Now that partition layout has been created, I am creating an encrypted swap partition based on https://wiki.archlinux.org/title/dm-crypt/Swap_encryption#UUID_and_LABEL

Creating a false filesystem on the device, so that it has a label

# mkfs.ext2 -L cryptswap /dev/${TGTDEV}2 1M

Now I can verify what is the label and the uuid for the partition:

# blkid /dev/${TGTDEV}2

Also for the other system I again need another swap partition to be created

# mkfs.ext2 -L mwcryptswap /dev/${TGTDEV}4 1M

Preparations

I realised that I want different labels on the target computer, so I started with renaming the partitions:

# parted /dev/$TGTDEV --script name 3 SCROOT
# parted /dev/$TGTDEV --script name 2 SCSWAP

I also want the swap to have it's own label:

# mkfs.ext2 -L sccryptswap /dev/${TGTDEV}2 1M

Now I want to create the encrypted root filesystem:

# cryptsetup -y -v luksFormat /dev/disk/by-partlabel/SCROOT

Also another root filesystem for the other root:

# cryptsetup -y -v luksFormat /dev/disk/by-partlabel/MWROOT

Cloning

Open the encrypted device:

# cryptsetup open /dev/disk/by-partlabel/SCROOT scroot

Format the drive with ext4. This might be optional, if you are doing it the second time, you do not need to do it.

# mkfs.ext4 /dev/mapper/scroot

Mount the root

# mkdir -p /mnt/newroot
# mount /dev/mapper/scroot /mnt/newroot

Now I will use rsync to copy through the files to the target system, but before that, let's check the connection.

Given that I am running the copy as root, but only my user has the agent running, I am hijacking the agent with root. As a user I do:

$ echo "export SSH_AUTH_SOCK=$SSH_AUTH_SOCK" > grab-ssh-agent

And then as root I do:

# . grab-ssh-agent

On the source system let's check connection:

# export TGTSYS=root@192.168.0.129
# ssh $TGTSYS ls /mnt/newroot

If everything went fine, then it is time to start the sync:

# rsync \
   -aAXHS \
   --delete \
   --numeric-ids \
   --exclude={"/efiroot/*","/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found"} \
   / \
   "${TGTSYS}:/mnt/newroot/"

Back to the target system:

Mount the EFI partition under the new root. If you have the EFI partition mounted, please unmount it first.

# mkdir -p /mnt/newroot/efiroot
# mount /dev/disk/by-partlabel/EFI /mnt/newroot/efiroot

Create boot loader configuration:

# mkdir -p /mnt/newroot/efiroot/EFI/sc
# ROOT_UUID=$(blkid -o value -s UUID /dev/disk/by-partlabel/SCROOT)
# tee "/mnt/newroot/efiroot/loader/entries/sc.conf" << EOF
title   SC
linux   /EFI/sc/vmlinuz-linux-lts
initrd  /EFI/sc/intel-ucode.img
initrd  /EFI/sc/initramfs-linux-lts-sc.img
options root=/dev/mapper/cryptroot cryptdevice=UUID=${ROOT_UUID}:cryptroot rw
EOF

Prepare fstab

# genfstab -U /mnt/newroot > /mnt/newroot/etc/fstab

Then you need to remove the last line about swap if there is one, and add this line:

# echo "/dev/mapper/swap none swap defaults 0 0" >> /mnt/newroot/etc/fstab

Fix mkinitcpio:

# tee "/mnt/newroot/etc/mkinitcpio.d/linux-lts.preset" << EOF
ALL_config="/etc/mkinitcpio.conf"
ALL_kver="/efiroot/EFI/sc/vmlinuz-linux-lts"

[[ -e /boot/vmlinuz-linux-lts ]] && cp -af /boot/vmlinuz-linux-lts "/efiroot/EFI/sc/"
[[ -e /boot/intel-ucode.img ]] && cp -af /boot/intel-ucode.img "/efiroot/EFI/sc/"

PRESETS=('sc')

sc_config="/etc/mkinitcpio-sc.conf"
sc_image="/efiroot/EFI/sc/initramfs-linux-lts-sc.img"
EOF

And:

# tee "/mnt/newroot/etc/mkinitcpio-sc.conf" << EOF
MODULES=()
BINARIES=()
FILES=()
HOOKS=(base udev autodetect keyboard keymap consolefont modconf block encrypt filesystems fsck)
EOF

Re-generate initramfs

# arch-chroot /mnt/newroot pacman -S --noconfirm linux-lts

Adjust swap

# echo "swap UUID=$(blkid -o value -s UUID /dev/disk/by-partlabel/SCSWAP) /dev/urandom swap,offset=2048,cipher=aes-xts-plain64,size=512" > /mnt/newroot/etc/crypttab

Adjust X settings:

# vim /mnt/newroot/home/matelakat/.config/xfce4/xinitrc
# for fname in $(find /mnt/newroot/home/matelakat/.config -name pycharm64.vmoptions); do grep uiScale $fname && vim $fname; done

And finish:

# umount /mnt/newroot/efiroot
# umount /mnt/newroot
# cryptsetup close scroot