Skip to content

Instantly share code, notes, and snippets.

@j0ju
Last active February 17, 2025 00:16
Show Gist options
  • Save j0ju/6c96638ba1d8a42baf04585fa8028272 to your computer and use it in GitHub Desktop.
Save j0ju/6c96638ba1d8a42baf04585fa8028272 to your computer and use it in GitHub Desktop.
A script to install RK1 Ubuntu 24.04 Server Image booted from MMC to root on ZFS(or BTRFS, F2FS) on NVMe( or SATA)
#!/bin/sh
# /lib/rootfs-to.sh
set -eu
set -x
ROOT_DIR=/mnt
SRC_DIR=/media
#FS=btrfs
FS=zfs
TARGET_DEV="/dev/nvme0n1"
PART_SEP=p
ROOT_SIZE=
BOOT_SIZE=384M
# space for bootloaders
BOOT_OFFSET=16M
MOUNT_OPTS_ext4=relatime
MOUNT_OPTS_f2fs=relatime
MOUNT_OPTS_btrfs=relatime,compress=lzo
trap cleanup EXIT 15 INT QUIT STOP CONT USR1 HUP USR2
cleanup() {
local rs=$?
trap '' EXIT
for f in "$SRC_DIR" "$ROOT_DIR"/boot "$ROOT_DIR"; do
while umount "$f"; do :; done 2> /dev/null
done
exit $rs
}
blkdiscard -f "$TARGET_DEV" 2> /dev/null || :
wipefs -af "$TARGET_DEV$PART_SEP"* 2> /dev/null || :
wipefs -af "$TARGET_DEV"
sfdisk "$TARGET_DEV" 1> /dev/null << EOF
label: gpt
${BOOT_OFFSET},${BOOT_SIZE},U
,${ROOT_SIZE},L
EOF
udevadm trigger
BOOT_DEV="$TARGET_DEV$PART_SEP"1
ROOT_DEV="$TARGET_DEV$PART_SEP"2
mkfs.ext4 "$BOOT_DEV"
BOOT_ENTRY="UUID=$(blkid -o value -s UUID $BOOT_DEV)"
# create rootfs
case "$FS" in
ext4 | btrfs | f2fs )
mkfs.$FS "$ROOT_DEV"
ROOT_FSTAB_ENTRY="UUID=$(blkid -o value -s UUID $ROOT_DEV)"
ROOT_CMDLINE="UUID=$(blkid -o value -s UUID $ROOT_DEV)"
eval 'ROOT_OPTS=$MOUNT_OPTS_'"$FS"
mount $ROOT_DEV "$ROOT_DIR" -o $ROOT_OPTS
;;
zfs )
ROOT_FSTAB_ENTRY=
ROOT_CMDLINE="ZFS=z/rootfs"
zpool create z \
-R "$ROOT_DIR" \
-O compression=on \
-O xattr=off \
-O acltype=off \
-O mountpoint=none \
"$ROOT_DEV"
zfs create z/rootfs -o mountpoint=/ -o canmount=noauto -o xattr=sa -o acltype=posix
zfs create z/rootfs/root -o canmount=noauto
zfs create z/rootfs/var -o canmount=noauto
zfs create z/rootfs/var/backups -o canmount=noauto -o exec=off
#zfs create z/rootfs/var/backups/boot -o canmount=noauto -o exec=off
#zfs create z/rootfs/var/backups/boot-firmware -o canmount=noauto -o exec=off
zfs create z/rootfs/var/cache -o canmount=noauto -o com.sun:auto-snapshot=false
zfs create z/rootfs/var/games -o canmount=noauto -o exec=off
zfs create z/rootfs/var/log -o canmount=noauto -o exec=off
zfs create z/rootfs/var/mail -o canmount=noauto -o exec=off
zfs create z/rootfs/var/lib -o canmount=noauto
zfs create z/rootfs/var/lib/nfs -o canmount=noauto -o exec=off -o com.sun:auto-snapshot=false
zfs create z/rootfs/var/spool -o canmount=noauto -o exec=off
# mount rootfs
zfs list -H -r -o name z/rootfs -H | xargs -n 1 zfs mount
# set bootfs property
zpool set bootfs=z/rootfs z
# create datasets for data
zfs create z/home -o mountpoint=/home -o setuid=off
zfs create z/srv -o mountpoint=/srv
;;
esac
mkdir -p "$ROOT_DIR"/boot
mount $BOOT_DEV "$ROOT_DIR"/boot
ln -s . "$ROOT_DIR/boot/boot"
ln -s . "$ROOT_DIR/boot/lib"
ln -s . "$ROOT_DIR/boot/firmware"
mount --bind / "$SRC_DIR"
#rsync -aHAX "$SRC_DIR"/ "$ROOT_DIR"
tar cf - --acls --xattrs -C "$SRC_DIR" . | tar xf - --acls --xattrs -C "$ROOT_DIR"
echo "disabled after rock chip install" > "$ROOT_DIR"/etc/cloud/cloud-init.disabled
if [ -n "$ROOT_FSTAB_ENTRY" ]; then
echo "$ROOT_FSTAB_ENTRY / $FS $ROOT_OPTS 0 1"
fi > "$ROOT_DIR/etc/fstab"
echo "$BOOT_ENTRY /boot ext4 relatime,x-systemd.automount,x-systemd.idle-timeout=31 0 2" >> "$ROOT_DIR/etc/fstab"
echo "tmpfs /tmp tmpfs mode=1777 0 0" >> "$ROOT_DIR/etc/fstab"
echo "vartmpfs /var/tmp tmpfs mode=1777 0 0" >> "$ROOT_DIR/etc/fstab"
# copy current kernel device tree to /boot
echo 'U_BOOT_SYNC_DTBS="true"' >> "$ROOT_DIR/etc/default/u-boot"
KVER="$(uname -r)"
#rsync -aHAX "/lib/firmware/$KVER/" "$ROOT_DIR/boot/dtb-$KVER"
mkdir -p "$ROOT_DIR/boot/dtb-$KVER"
tar cf - --acls --xattrs -C "/lib/firmware/$KVER" . | tar xf - --acls --xattrs -C "$ROOT_DIR/boot/dtb-$KVER"
# UBoot install & config
U_BOOT_ROOT=root="$ROOT_CMDLINE" \
chroot "$ROOT_DIR" u-boot-update
# bootloader rootfs on kernel commandline
sed -i -r -e 's!root=[^ ]+!root='"$ROOT_CMDLINE"'!' "$ROOT_DIR/boot/extlinux/extlinux.conf"
u-boot-install "$ROOT_DEV"
#cloud-config
# /boot/meta-data/user-data
chpasswd:
expire: no
users:
- name: ubuntu
password: utnubu
type: text
ssh_pwauth: true
runcmd:
# remove snapd, lxd/lxc, rsyslog
- |
PS4="runcmd: "; set -e; trap `exit $?` EXIT
for pkg in \
rsyslog open-vm-tools snapd lxc-agent-loader command-not-found
do
if ls /var/lib/dpkg/info/$pkg.* 2> /dev/null >&2 ; then
( set -x
dpkg -P $pkg
)
fi
done
rm -f /var/log/syslog /var/log/auth.log /var/log/kern.log
rm -rf /var/lib/command-not-found
rm -rf /var/*/snapd* /root/snap
rmdir /*-is-merged > /dev/null 2>&1 || :
# disable some gettys
systemctl disable --now [email protected] 2> /dev/null || :
systemctl mask [email protected] 2> /dev/null || :
systemctl mask [email protected] 2> /dev/null || :
# disable services
( set -x
systemctl disable --now \
ModemManager.service cron.service \
udisks2.service \
multipathd.socket multipathd.service iscsid.socket \
sysstat-collect.service sysstat-summary.service sysstat.service \
sysstat-collect.timer sysstat-summary.timer \
wpa_supplicant.service \
update-notifier-motd.timer update-notifier-download.timer \
[email protected] lxd-installer.socket \
apt-news.service esm-cache.service ua-reboot-cmds.service ua-timer.service \
ua-timer.timer ubuntu-advantage.service ufw.service \
#
systemctl mask \
systemd-networkd-wait-online.service \
[email protected] \
update-notifier-motd.timer update-notifier-download.timer \
apt-news.service esm-cache.service ua-reboot-cmds.service ua-timer.service \
ua-timer.timer ubuntu-advantage.service \
[email protected] lxd-installer.socket \
apport.service secureboot-db.service \
#
)
# disable pam_motd - suspect of: homephone
sed -i -r -e '/pam_motd[.]so/ s/^[^#]/#\0/' /etc/pam.d/*
# disable systemd pam_integration triggering logind and user slices
sed -i -r -e '/pam_systemd[.]so/ s/^[^#]/#\0/' /etc/pam.d/*
# re-shuffle UIDs to free 1000
- |
PS4="runcmd: "; set -e; trap `exit $?` EXIT
if gid="$(id -g ubuntu 2> /dev/null)" && [ -n "$gid" ] && [ "$gid" != 963 ] ; then
sed -i- -r -e "/^ubuntu/ s@${gid}:@963:@g" /etc/group
echo "I: GID ubuntu 1000 -> 963"
fi
if uid="$(id -u ubuntu 2> /dev/null)" && [ -n "$uid" ] && [ "$uid" != 963 ] ; then
home="$( getent passwd "ubuntu" | awk -F: '{print $(NF-1)}')"
sed -i- -r -e "/^ubuntu/ s@${uid}:@963:@g" /etc/passwd
sed -i- -r -e "/^ubuntu/ s@963:${gid}:@963:963:@g" /etc/passwd
chown -R 963:963 "$home"
pkill -U $uid || :
echo "I: UID ubuntu 1000 -> 963"
fi
pwck -s
grpck -s
# install packages below /var/cache/apt/archives - eg bird, etckeeper, ..
- |
PS4="runcmd: "; set -e; trap `exit $?` EXIT
if ls /var/cache/apt/archives/*.deb 2> /dev/null 1>&2; then
( export DEBIAN_FRONTEND=noninteractive; set -x
apt-get install -y /var/cache/apt/archives/*.deb
)
rm -f /var/cache/apt/archives/*.deb
fi
# dist-upgrade after we have a default route and DNS
- |
PS4="runcmd: "; set -e; trap `exit $?` EXIT
# wait for default route and DNS, tested via ping
while ! ping -6 -c 1 ubuntu.com > /dev/null 2>&1; do sleep 3; done
# try to fetch the sources 3 times
for i in 1 2 3; do
if apt-get update; then
break
fi
sleep $(( i * 3 ))
done
( export DEBIAN_FRONTEND=noninteractive; set -x
apt-get upgrade -y
apt-get dist-upgrade -y
#- install some handy tools
# TODO: fix missing setuid and capabilities from original image
apt-get install -y \
vim-nox mc tmux \
tcpdump ifstat \
mtr-tiny iputils-ping iputils-arping traceroute mtr-tiny \
strace \
make \
#
# add qemu user emulation to exectute binaries/containers of other archs
apt-get install -y \
qemu-user:arm64 qemu-user-static:arm64 qemu-utils:arm64 \
qemu-system \
#
#- install zfs tools
apt-get install -y \
zfsutils-linux zfs-initramfs
)
systemctl disable --now zfs-zed.service
#- all pks have been installed until here, clean apt cache
apt-get clean
#- reboot if needed, with again a fresh cloud init-run
if [ -f /var/run/reboot-required ]; then
set -x
cloud-init clean # do this now only in case of a needed reboot
reboot
fi
# clean up, disable cloud-init on subsquent boots
- |
PS4="runcmd: "; set -ex; trap `exit $?` EXIT
apt-get clean
find /etc -name "*.dpkg-*" -delete
find /etc -name "*.ucf-*" -delete
# remove all but latest kernel
NEXT_KERNEL=$(readlink -f /boot/vmlinuz)
for i in /boot/vmlinuz-*; do
[ ! "$NEXT_KERNEL" = $i ] || \
continue
pkg="$(dpkg -S $i | cut -f1 -d:)"
version="${pkg#linux-image-}"
apt-get remove --purge -y $(dpkg -l linux-* | awk "/$version/"' {print $2}')
done
apt-get autoremove -y --purge
rm -f \
/root/.bash_history \
/root/viminfo \
/home/*/.bash_history \
/home/*/viminfo \
#
# prevent future cloud init runs
##echo "disabled during/after cloud-init runcmd - $(date)" > /etc/cloud/cloud-init.disabled
#etckeeper commit -m "cloud-init finished and disabled"
# de-couple my PID from units cgroup, to prevent being killed by systemd
echo $$ > /sys/fs/cgroup/cgroup.procs
(
# wait for cloud-init to be finished
while echo "$PS4: unbind PID $$ from systemd service and wait for cloud-final to be done"; do
sleep 11
if systemctl status cloud-final.service | grep exited > /dev/null; then
break
fi
done
set -x
# cloud init will re-run every time booted from mmc
cloud-init clean
# if we have ssh-key for initial user, disable ssh password auth
if [ -s /home/ubuntu/.ssh/authorized_keys ]; then
rm -f \
/etc/ssh/sshd_config.d/50-cloud-init.conf \
/etc/ssh/sshd_config.d/60-cloudimg-settings.conf \
# EO rm -f
sed -i -r \
-e '/^[iI]nclude/ d' \
-e "/^UsePAM/ d" \
-e "/^PubkeyAuthentication/ d" \
-e "/^PermitRootLogin/ d" \
-e "/^PasswordAuthentication/ d" \
-e '/^(#|$)/ d' \
/etc/ssh/sshd_config
# EO sed
echo "UsePAM yes" >> /etc/ssh/sshd_config
echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config
echo "PermitRootLogin no" >> /etc/ssh/sshd_config
echo "PasswordAuthentication no" >> /etc/ssh/sshd_config
fi
# install to ext root if rootfs is on eMMC
if grep " / " /proc/mounts | grep "mmcblk0p" > /dev/null; then
for dev in /dev/nvme0n1; do
if [ -b "$dev" ]; then
sh /lib/rootfs-to.sh "$dev"
reboot
fi
done
fi
) &
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment