Last active
February 17, 2025 00:16
-
-
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)
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
#!/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" |
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
#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