You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
223 lines
8.4 KiB
223 lines
8.4 KiB
#!/bin/sh |
|
|
|
# This script generates FreeBSD images for OpenNebula, being heavily inspired |
|
# from srht's FreeBSD build image definition. It assumes running on a FreeBSD host. |
|
# ZFS installation as documented by the FreeBSD project |
|
# https://wiki.freebsd.org/RootOnZFS/GPTZFSBoot |
|
|
|
set -e |
|
set -x |
|
|
|
# XXX: Handle command-line arguments? |
|
RELEASE=13.1-RELEASE |
|
ARCH=amd64 |
|
IMAGE_PATH_ZFS="freebsd-zfs-$RELEASE-$(date -I).img.qcow2" |
|
IMAGE_PATH_UFS="freebsd-ufs-$RELEASE-$(date -I).img.qcow2" |
|
IMAGE_SIZE=10G |
|
|
|
# Comment out to simply use latest version |
|
# Hash checking is disabled when specifying this |
|
#CLOUDSETUP_VERSION=1.2 |
|
|
|
DIST_BASE="https://download.freebsd.org/ftp/releases/$ARCH/$RELEASE" |
|
ZPOOL=zroot |
|
ZPOOL_TMP="zinstalling" |
|
|
|
ZFSTARGET="$(mktemp -d /var/tmp/zfsbuild.XXXXX)" |
|
UFSTARGET="$(mktemp -d /var/tmp/ufsbuild.XXXXX)" |
|
|
|
if zpool list -Ho name "$ZPOOL_TMP" 2>/dev/null; then |
|
echo "The pool $ZPOOL_TMP is already imported." >&2 |
|
exit 1 |
|
fi |
|
|
|
cleanup() { |
|
sync ||: |
|
umount "$UFSTARGET/dev" ||: |
|
umount "$UFSTARGET/tmp" ||: |
|
umount "$UFSTARGET/var/tmp" ||: |
|
umount "$UFSTARGET" ||: |
|
zpool export "$ZPOOL_TMP" ||: |
|
mdconfig -du md0 ||: |
|
mdconfig -du md1 ||: |
|
rm -rf "$CLOUDSETUP_WORK" ||: |
|
rmdir "$ZFSTARGET" ||: |
|
rmdir "$UFSTARGET" ||: |
|
} |
|
trap cleanup EXIT |
|
|
|
if [ "$(whoami)" != 'root' ]; then |
|
echo "This script must be run as root." >&2 |
|
exit 1 |
|
fi |
|
|
|
if ! command -v rsync >/dev/null |
|
then |
|
env ASSUME_ALWAYS_YES=YES pkg install -y rsync |
|
fi |
|
if ! command -v qemu-img >/dev/null |
|
then |
|
env ASSUME_ALWAYS_YES=YES pkg install -y qemu-tools |
|
fi |
|
|
|
portsnap fetch |
|
if [ -f /usr/ports/README ] |
|
then |
|
portsnap update || portsnap extract |
|
else |
|
portsnap extract |
|
fi |
|
|
|
if [ -n "$CLOUDSETUP_VERSION" ] |
|
then |
|
sed -i .bak -e '/^PORTVERSION=/ s/[0-9]*\.[0-9]*/'"$CLOUDSETUP_VERSION/" /usr/ports/sysutils/firstboot-cloudsetup/Makefile |
|
make -C /usr/ports/sysutils/firstboot-cloudsetup makesum |
|
fi |
|
make -C /usr/ports/sysutils/firstboot-cloudsetup clean package |
|
CLOUDSETUP_VERSION="$(fgrep VERSION /usr/ports/sysutils/firstboot-cloudsetup/Makefile | cut -f2- | tr -d \\t)" |
|
CLOUDSETUP_PKG="/usr/ports/sysutils/firstboot-cloudsetup/work/pkg/firstboot-cloudsetup-${CLOUDSETUP_VERSION}.pkg" |
|
tar -tzf "$CLOUDSETUP_PKG" >/dev/null # check that it's a valid tar, or we crash due to set -e |
|
# tar -t lists the contents of a tar file, but does not extract |
|
|
|
make -C /usr/ports/sysutils/firstboot-freebsd-update clean package |
|
FBUPDATE_VERSION="$(fgrep VERSION /usr/ports/sysutils/firstboot-freebsd-update/Makefile | cut -f2- | tr -d \\t)" |
|
FBUPDATE_PKG="/usr/ports/sysutils/firstboot-freebsd-update/work/pkg/firstboot-freebsd-update-${FBUPDATE_VERSION}.pkg" |
|
tar -tzf "$FBUPDATE_PKG" >/dev/null # check that it's a valid tar, or we crash due to set -e |
|
|
|
ufsdisk="$(mktemp /var/tmp/ufsdisk.XXXXX)" |
|
truncate -s 6G "$ufsdisk" |
|
mdconfig -a -t vnode -f "$ufsdisk" -u md1 |
|
gpart create -s gpt /dev/md1 |
|
#gpart add -t efi -l efiboot0 -s 260M md1 |
|
gpart add -t freebsd-boot -l gptboot -b 40 -s 512K md1 |
|
gpart bootcode -b /boot/pmbr -p /boot/gptboot -i 1 md1 |
|
gpart add -t freebsd-ufs -l rootfs -b 1M -s 5G md1 |
|
newfs -U /dev/md1p2 |
|
|
|
# Mount allocated image. |
|
mount /dev/md1p2 "$UFSTARGET" |
|
|
|
# Allocate and partition/format disk image. |
|
# We use "legacy boot", aka BIOS boot |
|
# Preferably, we'd use EFI boot here, check the FreeBSD wiki link in the header |
|
# to see how to make that change, but make the EFI partition larger |
|
zfsdisk="$(mktemp /var/tmp/zfsdisk.XXXXX)" |
|
truncate -s 6G "$zfsdisk" |
|
mdconfig -a -t vnode -f "$zfsdisk" -u md0 |
|
gpart create -s gpt /dev/md0 |
|
#gpart add -t efi -l efiboot0 -s 260M md1 |
|
gpart add -t freebsd-boot -l gptboot0 -b 40 -s 512K md0 |
|
gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 md0 |
|
gpart add -t freebsd-zfs -l zfs0 -b 1M -s 5G md0 |
|
zpool create -O compression=on -o ashift=12 -o "altroot=$ZFSTARGET" -m none -t "$ZPOOL_TMP" "$ZPOOL" md0p2 |
|
|
|
zfs create -o mountpoint=none "$ZPOOL_TMP/ROOT" |
|
# We set zstd-19 so our image will become smaller, at the cost of a longer build time. |
|
# At the end of the process, we disable zstd-19 again using zfs inherit compression, |
|
# but all files already written will remain zstd-19 compressed |
|
zfs create -o mountpoint=/ -o canmount=noauto "$ZPOOL_TMP/ROOT/default" |
|
mount -t zfs "$ZPOOL_TMP/ROOT/default" "$ZFSTARGET" |
|
zpool set "bootfs=$ZPOOL_TMP/ROOT/default" "$ZPOOL_TMP" |
|
|
|
zfs create -o mountpoint=/tmp -o exec=on -o setuid=off "$ZPOOL_TMP/tmp" |
|
zfs create -o canmount=off -o mountpoint=/usr "$ZPOOL_TMP/usr" |
|
zfs create "$ZPOOL_TMP/usr/home" |
|
zfs create -o exec=off -o setuid=off "$ZPOOL_TMP/usr/src" |
|
zfs create -o mountpoint=/usr/ports -o setuid=off "$ZPOOL_TMP/usr/ports" |
|
zfs create -o canmount=off -o mountpoint=/var "$ZPOOL_TMP/var" |
|
zfs create -o exec=off -o setuid=off "$ZPOOL_TMP/var/audit" |
|
zfs create -o exec=off -o setuid=off "$ZPOOL_TMP/var/crash" |
|
zfs create -o exec=off -o setuid=off "$ZPOOL_TMP/var/log" |
|
zfs create -o atime=on -o exec=off -o setuid=off "$ZPOOL_TMP/var/mail" |
|
zfs create -o exec=on -o setuid=off "$ZPOOL_TMP/var/tmp" |
|
|
|
ln -s /usr/home "$ZFSTARGET/home" |
|
chmod 1777 "$ZFSTARGET/var/tmp" |
|
chmod 1777 "$ZFSTARGET/tmp" |
|
|
|
# Download and extract base system. |
|
dist_files="kernel.txz base.txz" |
|
dist_dir="/usr/freebsd-dist/$ARCH/$RELEASE" |
|
|
|
mkdir -p "$dist_dir" |
|
for f in $dist_files |
|
do |
|
fetch -m -o "$dist_dir/$f" "$DIST_BASE/$f" |
|
tar -C "$UFSTARGET" -xJf "$dist_dir/$f" |
|
done |
|
|
|
# Mount dev and tmp in chroot |
|
mount -t devfs devfs "$UFSTARGET/dev" |
|
mount_nullfs /tmp "$UFSTARGET/tmp" |
|
mount_nullfs /var/tmp "$UFSTARGET/var/tmp" |
|
|
|
# Install the first-boot script that configures the network and ssh key |
|
# We must use --rootdir and not --chroot, because the file is read from within the chroot |
|
# --automatic means that the package is considered to be installed "automatically", |
|
# aka as a dependency of something, so pkg autoremove will remove it. |
|
# We do not run pkg autoremove ourselves, that's up to the administrator. |
|
pkg --rootdir "$UFSTARGET" add --automatic "$CLOUDSETUP_PKG" "$FBUPDATE_PKG" |
|
|
|
# Configure new system. |
|
touch "$UFSTARGET/firstboot" |
|
sysrc -f "$UFSTARGET/boot/loader.conf" \ |
|
zfs_load="YES" \ |
|
autoboot_delay="-1" \ |
|
|
|
sysrc -f "$UFSTARGET/etc/rc.conf" \ |
|
zfs_enable="YES" \ |
|
ntpd_enable="YES" \ |
|
sshd_enable="YES" \ |
|
growfs_enable="YES" \ |
|
hostname="freebsd" \ |
|
firstboot_cloudsetup_enable="YES" \ |
|
firstboot_freebsd_update_enable="YES" \ |
|
|
|
# The resolv.conf file is written by firstboot_cloudsetup |
|
#cp /etc/resolv.conf "$UFSTARGET/etc/resolv.conf" |
|
|
|
tzsetup -s -C "$UFSTARGET" UTC |
|
|
|
# Add PermitRootLogin without-password, unless PermitRootLogin yes was already set |
|
sed -i .orig -e '/^#PermitRootLogin[[:blank:]]/a\ |
|
PermitRootLogin without-password |
|
' -e '/^PermitRootLogin[[:blank:]]*no/ s/\([[:blank:]]\).*$/\1without-password/' \ |
|
"$UFSTARGET/etc/ssh/sshd_config" |
|
if ! grep -Eq '^PermitRootLogin (without-password|yes)' "$UFSTARGET/etc/ssh/sshd_config" |
|
then |
|
cat >>"$UFSTARGET/etc/ssh/sshd_config" <<EOF |
|
|
|
# Added by Ungleich |
|
PermitRootLogin without-password |
|
EOF |
|
fi |
|
|
|
# Set zstd-19 compression, copy all data to the pool, and then set compression to default again |
|
# This will make the base image smaller, at the cost of taking longer to generate, as zstd-19 is slow to write |
|
# Therefore, afterwards we restore compression to default, so written files stay zstd-19, which is fast to read, |
|
# but files written by the user afterwards will be written with the default compression algorihtm. |
|
zfs set compression=zstd-19 "$ZPOOL_TMP/ROOT/default" |
|
umount "$UFSTARGET/dev" "$UFSTARGET/tmp" "$UFSTARGET/var/tmp" |
|
rsync -aH --fileflags --inplace "$UFSTARGET/." "$ZFSTARGET" |
|
|
|
sysrc -f "$UFSTARGET/boot/loader.conf" -x zfs_load |
|
sysrc -f "$UFSTARGET/etc/rc.conf" -x zfs_enable |
|
printf '# Device\tMountpoint\tFStype\tOptions\t\tDump\tPass#\n' \ |
|
>"$ZFSTARGET/etc/fstab" |
|
printf '# Device\tMountpoint\tFStype\tOptions\t\tDump\tPass#\n%s\t%s\t\t%s\t%s\t%s\t%s\n' \ |
|
/dev/gpt/rootfs / ufs rw,noatime 1 1 \ |
|
>"$UFSTARGET/etc/fstab" |
|
sync ||: |
|
zfs inherit compression "$ZPOOL_TMP/ROOT/default" |
|
|
|
trap : EXIT |
|
cleanup |
|
|
|
mkdir -p "$ARCH" |
|
qemu-img convert -f raw -O qcow2 "$zfsdisk" "$ARCH/$IMAGE_PATH_ZFS" |
|
qemu-img convert -f raw -O qcow2 "$ufsdisk" "$ARCH/$IMAGE_PATH_UFS" |
|
rm "$zfsdisk" "$ufsdisk" |
|
|
|
# Filesystem will be enlarged by growfs(7) on next startup |
|
qemu-img resize "$ARCH/$IMAGE_PATH_ZFS" "$IMAGE_SIZE" |
|
qemu-img resize "$ARCH/$IMAGE_PATH_UFS" "$IMAGE_SIZE"
|
|
|