bootstrap-bookworm.sh 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. #!/bin/bash -e
  2. #----------
  3. # Interactive installation steps for Debian Bullseye from GRML using debootstrap
  4. # Design decisions
  5. # - Fokus on a simple setup, primarly for VMs
  6. # - One disk, one partion, swap-file in the same partion as safety net
  7. # - Use systemd whereever possible (network, ntp, cron, journald logging)
  8. # - Minimal number of packages & cloud kernel
  9. # - support for grub-pc and grub-efi
  10. # - random root and admin user password generation
  11. # - ssh on port 50101 limited to the admin user
  12. # Usage
  13. # # Boot grml
  14. # passwd root
  15. # grml-network
  16. # Start ssh
  17. # git clone https://git.in-ulm.de/ulpeters/bootstrap.git
  18. # cp config.sh.template config.sh # copy template
  19. # config-get-netconf-eth0.sh # get running grml network config
  20. # vi config.sh # update installation variables
  21. # bootstrap-bullseye.sh install # start installation
  22. # !! Note down the admin passwords and reboot
  23. # sudo /installer/bootstrap-bullseye.sh postinstall # run postinstall in the new system
  24. # Default variables
  25. mnt="/mnt/root" # mountpoint for the new root filesystem
  26. hostname="somehost.example.com"
  27. kernelPkg="linux-image-cloud-amd64" # alternative: linux-image-amd64
  28. partition="mbr-single" # mbr-single or efi-crypt
  29. disk="/dev/vda" # find with: lsblk --list
  30. disk0=$disk"p1" # efi partion, only relevant if partion="efi-crypt"
  31. disk1=$disk"1" # "p1" for nbd or p2 for efi-crypt
  32. netDev="eth0" # find with: ip link
  33. netAddress="203.0.113.66/24" # "" blank for dhcp on e*
  34. netGateway="203.0.113.1"
  35. netBroadcast="203.0.113.255"
  36. netDNS1="192.0.2.10"
  37. netDNS2="198.51.100.10"
  38. netNTP="pool.ntp.org"
  39. pwdAdmin="" # "" blank for auto-generation
  40. pwdRoot="" # "" blank for auto-generation
  41. extraPackages="qemu-guest-agent" # additional packages to install
  42. # Overwrite default variables from config file
  43. [ -f ./config.sh ] && source config.sh
  44. # Select grub version based on partition table
  45. case "$partition" in
  46. mbr-single) grubPkg="grub-pc" ;;
  47. efi-crypt) grubPkg="grub-efi" ;;
  48. esac
  49. # Setup network in grml
  50. grmlnetwork(){
  51. ip link show # list interfaces
  52. ip addr add $netAddress dev $netDev
  53. ip link set $netDev up
  54. ip route add default via $netGateway
  55. echo nameserver $netDNS1 >> /etc/resolv.conf
  56. echo nameserver $netDNS2 >> /etc/resolv.conf
  57. }
  58. install(){
  59. # Wipe existing partition table
  60. dd if=/dev/zero of=$disk bs=512 count=34
  61. # Parition disks -- pkg: parted
  62. # Prepare partition tables and partitions
  63. # -parted --script does not accept blanks in partition names
  64. if [ "$partition" = "mbr-single" ]
  65. then
  66. #----------
  67. # Prepare disks with a single partition
  68. parted $disk --script \
  69. mklabel msdos \
  70. mkpart primary ext4 512M 100% toggle 1 boot
  71. fdisk -l $disk
  72. # Format disks -- pkg: e2fsprogs dosfstools and to file system check
  73. mkfs.ext4 $disk1 && e2fsck $disk1
  74. fi
  75. if [ "$partition" = "efi-crypt" ]
  76. then
  77. #----------
  78. # Prepare disks with following layout
  79. # - 301 MB partition for EFI --> p1
  80. # - 50 GB root partition for the OS (includes /boot) --> p2
  81. # - Remaining disk left to create a luks container --> p3
  82. parted $disk --script \
  83. mklabel gpt \
  84. mkpart EFI_system_partition fat32 1MiB 301MiB \
  85. set 1 esp on \
  86. set 1 boot on \
  87. align-check optimal 1 \
  88. mkpart Linux_system_parition ext4 301MiB 50GiB \
  89. align-check optimal 2 \
  90. mkpart Data_partion 50GiB 100% \
  91. align-check optimal 3 \
  92. unit MiB \
  93. print
  94. # Format disks -- pkg: e2fsprogs dosfstools and to file system check
  95. mkfs.fat -F 32 -n EFIBOOT $disk0 && fsck $disk0
  96. mkfs.ext4 $disk1 && fsck $disk1
  97. # Prepare mount points and mount
  98. mkdir $mnt"/boot/efi"
  99. mount $disk0 $mnt"/boot/efi"
  100. echo "$disk0 /boot/efi/ vfat rw 0 0" >> $mnt/etc/fstab
  101. fi
  102. # Create swapfile
  103. swapfile=$mnt/swapfile
  104. dd if=/dev/zero of=$swapfile bs=1M count=1024 status=progress # create 1GB file
  105. chmod 600 $swapfile #restric permissions
  106. mkswap $swapfile #format file
  107. # Prepare mount points and mount
  108. mkdir -p $mnt
  109. mount $disk1 $mnt
  110. # Configure disk mounts
  111. # Or get UUID from blkid...
  112. echo "/swapfile none swap defaults 0 0" >> $mnt/etc/fstab
  113. echo "$disk1 / ext4 rw 0 0" >> $mnt/etc/fstab
  114. # mount for /boot/efi/ set above if neccessary
  115. #----------
  116. # Bootstrap -- pkg: debootstrap
  117. # Remark: Debootstrap does not install recommands!!
  118. debootstrap --variant=minbase --arch=amd64 bookworm $mnt http://ftp2.de.debian.org/debian/
  119. # Configure sources.list
  120. cat >$mnt/etc/apt/sources.list <<EOL
  121. deb http://deb.debian.org/debian bookworm main non-free-firmware
  122. # deb-src http://deb.debian.org/debian bookworm main non-free-firmware
  123. deb http://deb.debian.org/debian-security/ bookworm-security main non-free-firmware
  124. # deb-src http://deb.debian.org/debian-security/ bookworm-security main non-free-firmware
  125. deb http://deb.debian.org/debian bookworm-updates main non-free-firmware
  126. # deb-src http://deb.debian.org/debian bookworm-updates main non-free-firmware
  127. EOL
  128. # Configure hostname
  129. echo "127.0.0.1 $hostname" >> $mnt/etc/hosts
  130. echo "$hostname" > $mnt/etc/hostname
  131. #----------
  132. # Prepare chroot
  133. mount -o bind /dev $mnt/dev
  134. mount -o bind /dev/pts $mnt/dev/pts
  135. mount -t sysfs /sys $mnt/sys
  136. mount -t proc /proc $mnt/proc
  137. cp /proc/mounts $mnt/etc/mtab
  138. cp /etc/resolv.conf $mnt/etc/resolv.conf
  139. mkdir -p $mnt/installer
  140. cp $(dirname `realpath $0`)/*.sh $mnt/installer
  141. # Run script in chroot
  142. chroot $mnt /bin/bash /installer/bootstrap-bullseye.sh install2
  143. # Install bootloader
  144. $0 bootloader
  145. }
  146. #----------
  147. # Function executed within chroot
  148. install2(){
  149. source /installer/config.sh
  150. # Install basic system
  151. apt-get update
  152. apt-get install --yes \
  153. apt-utils dialog msmtp-mta \
  154. systemd-sysv locales tzdata haveged \
  155. $kernelPkg $grubPkg \
  156. iproute2 netbase \
  157. ssh sudo molly-guard \
  158. less vim-tiny bash-completion pwgen lsof \
  159. dnsutils iputils-ping curl \
  160. $extraPackages
  161. # Upgrade and clean up
  162. apt-get upgrade --yes
  163. apt-get autoremove --yes
  164. apt-get clean --yes
  165. # Setup users and passwords
  166. [ -z $pwdAdmin ] && pwdAdmin=`pwgen --capitalize --numerals --ambiguous 12 1`
  167. useradd admin --create-home --shell /bin/bash
  168. echo "admin:$pwdAdmin" | chpasswd
  169. usermod -a -G sudo admin
  170. echo -e "\e[1;33;4;44mPassword for the user admin: $pwdAdmin\e[0m"
  171. pass=`pwgen --capitalize --numerals --ambiguous 12 1`
  172. [ -z $pwdRoot ] && pwdRoot=`pwgen --capitalize --numerals --ambiguous 12 1`
  173. echo "root:$pwdRoot" | chpasswd
  174. echo -e "\e[1;33;4;44mPassword for the user root: $pwdRoot\e[0m"
  175. # Harden SSHD
  176. sed -i -e 's/#Port 22/Port 50101/g' /etc/ssh/sshd_config
  177. sed -i -e 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/g' /etc/ssh/sshd_config
  178. # https://infosec.mozilla.org/guidelines/openssh.html
  179. # Allow admin to sudo without password
  180. echo AllowUsers admin >> /etc/ssh/sshd_config
  181. echo "admin ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/admin
  182. ## Configure network using systemd
  183. if [ -z $netAddress ]
  184. then
  185. ## Network OPTION 1 - DHCP
  186. cat >/etc/systemd/network/20-wired.network <<EOL
  187. [Match]
  188. Name=e*
  189. [Network]
  190. DHCP=ipv4
  191. IPv6PrivacyExtensions=false
  192. IPv6AcceptRA=false
  193. NTP=$netNTP
  194. EOL
  195. else
  196. ## Network OPTION 2 - static
  197. cat >/etc/systemd/network/20-wired.network <<EOL
  198. [Match]
  199. Name=$netDev
  200. [Network]
  201. Address=$netAddress
  202. Gateway=$netGateway
  203. Broadcast=$netBroadcast
  204. DNS=$netDNS1
  205. DNS=$netDNS2
  206. NTP=$netNTP
  207. EOL
  208. fi
  209. ## Setup systemd resolver
  210. apt-get install --yes systemd-resolved
  211. echo -e "\n# Disable local name resolution" >> /etc/systemd/resolved.conf
  212. echo "LLMNR=no" >> /etc/systemd/resolved.conf
  213. echo "MulticastDNS=no" >> /etc/systemd/resolved.conf
  214. systemctl enable systemd-networkd
  215. systemctl enable systemd-resolved
  216. # Limit journald logging to 1 month, 1 GB in total and split files per week
  217. mkdir -p /etc/systemd/journald.conf.d/
  218. cat >/etc/systemd/journald.conf.d/retention.conf <<EOL
  219. MaxRetentionSec=1month
  220. SystemMaxUse=1G
  221. MaxFileSec=1week
  222. EOL
  223. # Show errors in motd
  224. rm /etc/motd
  225. cat >/etc/update-motd.d/15-boot-errors<<EOL
  226. #!/bin/sh
  227. echo
  228. journalctl --boot --priority=3 --no-pager
  229. EOL
  230. chmod 755 /etc/update-motd.d/15-boot-errors
  231. # Setup keyboard layout
  232. cat >/etc/default/keyboard <<EOL
  233. XKBMODEL="pc105"
  234. XKBLAYOUT="de"
  235. XKBVARIANT="nodeadkeys"
  236. XKBOPTIONS=""
  237. BACKSPACE="guess"
  238. EOL
  239. # Leave chroot
  240. exit
  241. }
  242. bootloader(){
  243. # Install grub in the mbr
  244. if [ $partition="mbr-single" ]
  245. then
  246. chroot $mnt /bin/bash -c "grub-install $disk && update-grub"
  247. fi
  248. # Install grub in the efi partition
  249. if [ $partition="efi-crypt" ]
  250. then
  251. chroot $mnt /bin/bash -c \
  252. "grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=grub && update-grub"
  253. fi
  254. }
  255. unmount(){
  256. # Unmount if mounted
  257. ! mountpoint -q $mnt/proc || umount $mnt/proc
  258. ! mountpoint -q $mnt/sys || umount $mnt/sys
  259. ! mountpoint -q $mnt/dev/pts || umount $mnt/dev/pts
  260. ! mountpoint -q $mnt/dev || umount $mnt/dev
  261. ! mountpoint -q $mnt/root || umount $mnt/root
  262. ! mountpoint -q $mnt || umount $mnt
  263. # Delete mount-point if empty and not mounted
  264. [ -z "$(ls -A /mnt/)" ] && ! mountpoint -q $mnt && rm -R $mnt
  265. }
  266. postinstall(){
  267. ####----REBOOT into the new system, so we'll have dbus running
  268. localectl set-locale LANG=de_DE.UTF-8 # Default for LC_* variables not set.
  269. localectl set-locale LC_MESSAGES=en_US.UTF-8 # System messages.
  270. #localectl set-locale LC_RESPONSE=en_US.UTF-8 # How responses (such as Yes and No) appear
  271. update-locale
  272. timedatectl set-timezone Europe/Berlin
  273. }
  274. # Switch to functions...
  275. case $1 in
  276. grmlnetwork)
  277. echo Setup network in grml
  278. grmlnetwork
  279. ;;
  280. install)
  281. echo "Stage 1: Start installation"
  282. install
  283. ;;
  284. install2)
  285. echo "Stage 2: Start installation in chroot"
  286. install2
  287. ;;
  288. bootloader)
  289. echo "Stage 3: Install bootloader and unmount chroot"
  290. bootloader
  291. unmount
  292. echo "We're done and can reboot now"
  293. ;;
  294. postinstall)
  295. echo "Stage 4: Start post-installation in live system"
  296. postinstall
  297. ;;
  298. unmount)
  299. echo "Unmount chroot, e.g. in case installation fails"
  300. unmount
  301. ;;
  302. *)
  303. echo "Valid functions are: grmlnetwork, install, postinstall and unmount" >&2
  304. ;;
  305. esac