armbian_rootenc_setup.sh 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209
  1. #!/bin/bash
  2. PATH="$PATH:/usr/sbin:/sbin"
  3. RED="\e[31;1m" GREEN="\e[32;1m" YELLOW="\e[33;1m" BLUE="\e[34;1m" PURPLE="\e[35;1m" RESET="\e[0m"
  4. PROGNAME=$(basename $0)
  5. TITLE='Armbian Encrypted Root Filesystem Setup'
  6. CONFIG_VARS='
  7. ARMBIAN_IMAGE
  8. BOOTPART_LABEL
  9. ROOTFS_NAME
  10. DISK_PASSWD
  11. UNLOCKING_USERHOST
  12. IP_ADDRESS
  13. ADD_ALL_MODS
  14. USE_LOCAL_AUTHORIZED_KEYS
  15. '
  16. STATES='
  17. card_partitioned
  18. bootpart_copied
  19. bootpart_label_created
  20. rootpart_copied
  21. target_configured
  22. '
  23. USER_OPTS_INFO="
  24. NO_CLEANUP no cleanup of mounts after program run
  25. FORCE_REBUILD force full rebuild
  26. FORCE_RECONFIGURE force reconfiguration
  27. FORCE_REFORMAT_ROOT force reformat of encrypted root partition
  28. ADD_ALL_MODS add all currently loaded modules to initramfs
  29. USE_LOCAL_AUTHORIZED_KEYS use local 'authorized_keys' file
  30. PARTITION_ONLY partition and create filesystems only
  31. ERASE zero boot sector, boot partition and beginning of root partition
  32. ROOTENC_REUSE_FS reuse existing filesystems (for development only)
  33. ROOTENC_TESTING developer tweaks
  34. ROOTENC_PAUSE pause along the way
  35. ROOTENC_IGNORE_APT_ERRORS continue even if apt update fails
  36. "
  37. RSYNC_VERBOSITY='--info=progress2'
  38. print_help() {
  39. echo " ${PROGNAME^^}: Create an Armbian image with encrypted root filesystem
  40. USAGE: $PROGNAME [options] <SD card device name>
  41. OPTIONS: '-h' Print this help message
  42. '-C' Don't perform unmounts or clean up build directory at exit
  43. '-d' Produce tons of debugging output
  44. '-f' Force reconfiguration of target system
  45. '-F' Force a complete rebuild of target system
  46. '-m' Add all currently loaded modules to the initramfs (may help
  47. fix blank screen on bootup issues)
  48. '-p' Partition and create filesystems only. Do not copy data
  49. '-R' Force reformat of encrypted root partition
  50. '-s' Use 'authorized_keys' file from working directory, if available
  51. (see below)
  52. '-v' Be more verbose
  53. '-u' Perform an 'apt upgrade' after each 'apt update'
  54. '-z' Erase boot sector and first partition of SD card before partitioning
  55. (an extra paranoia step, but it can’t hurt)
  56. For non-interactive operation, set the following variables in your environment
  57. or on the command line:
  58. ROOTFS_NAME - device mapper name of target root filesystem
  59. IP_ADDRESS - IP address of target (set to 'dhcp' for dynamic IP
  60. or 'none' to disable remote SSH unlocking support)
  61. BOOTPART_LABEL - Boot partition label of target
  62. DISK_PASSWD - Disk password of target root filesystem
  63. UNLOCKING_USERHOST - USER@HOST of remote unlocking host
  64. SERIAL_CONSOLE - Set this to 'y' to enable disk unlocking from the
  65. serial console
  66. INSTRUCTIONS FOR USE
  67. This script must be invoked as superuser on a running Armbian system.
  68. Packages will be installed using APT, so the system must be Internet-
  69. connected and its clock correctly set.
  70. If remote unlocking via SSH is desired, the unlocking host must be reachable.
  71. Alternatively, SSH public keys for the unlocking host or hosts may be listed
  72. in the file 'authorized_keys' in the current directory. This file has the
  73. same format as a standard SSH 'authorized_keys' file.
  74. Architecture of host and target (e.g. 64-bit or 32-bit ARM) must be the same.
  75. For best results, the host and target hardware should also be identical or
  76. similar. Building on a host with more memory than the target, for example,
  77. may lead to disk unlocking failure on the target. For most users, who’ll be
  78. building for the currently-running board, this point is a non-issue.
  79. 1. Place an Armbian boot image file for the target system in the current
  80. directory. For best results, the image file should match the Debian
  81. or Ubuntu release of the host system.
  82. 2. Insert a USB card reader with a blank micro-SD card for the target
  83. system into the host’s USB port.
  84. 3. Determine the SD card’s device name using 'dmesg' or 'lsblk'.
  85. 4. Invoke the script with the device name as argument. If any options
  86. are desired, they must precede the device name.
  87. If the board has an eMMC, it may be used as the target device instead of
  88. an SD card." | less
  89. }
  90. pause() {
  91. echo -ne $GREEN'(Press any key to continue)'$RESET >&$stderr_dup
  92. read
  93. no_fmsg=1
  94. }
  95. _debug_pause() { [ "$ROOTENC_PAUSE" ] && pause; true; }
  96. imsg() { echo -e "$1" >&$stdout_dup; no_fmsg=1; }
  97. imsg_nonl() { echo -ne "$1" >&$stdout_dup; no_fmsg=1; }
  98. tmsg() {
  99. no_fmsg=1
  100. [ "$ROOTENC_TESTING" ] || return 0
  101. echo -e "$1" >&$stdout_dup
  102. }
  103. warn() { echo -e "$YELLOW$1$RESET" >&$stdout_dup; no_fmsg=1; }
  104. warn_nonl() { echo -ne "$YELLOW$1$RESET" >&$stdout_dup; no_fmsg=1; }
  105. rmsg() { echo -e "$RED$1$RESET" >&$stdout_dup; no_fmsg=1; }
  106. gmsg() { echo -e "$GREEN$1$RESET" >&$stdout_dup; no_fmsg=1; }
  107. pu_msg() { echo -e "$PURPLE$1$RESET" >&$stdout_dup; no_fmsg=1; }
  108. do_partprobe() {
  109. if [ "$VERBOSE" ]; then partprobe; else partprobe 2>/dev/null; fi
  110. no_fmsg=1
  111. }
  112. _show_output() { [ "$VERBOSE" ] || exec 1>&$stdout_dup 2>&$stderr_dup; }
  113. _hide_output() { [ "$VERBOSE" ] || exec &>'/dev/null'; }
  114. bail() { exit; }
  115. die() {
  116. echo -e "$RED$1$RESET" >&$stdout_dup
  117. no_fmsg=1
  118. exit 1
  119. }
  120. _return_handler() {
  121. local funcname=${FUNCNAME[1]} exitval=$? res
  122. if [ "${funcname:0:1}" == '_' -o "$no_fmsg" ]; then
  123. no_fmsg=
  124. return 0
  125. fi
  126. if [ "$exitval" -eq 0 ]; then res='OK'; else res="False ($exitval)"; fi
  127. printf "$BLUE%-32s $res$RESET\n" "$funcname" >&$stdout_dup
  128. }
  129. _sigint_handler() {
  130. warn "\nExiting at user request"
  131. exit 1
  132. }
  133. _error_handler() {
  134. local exitval=$?
  135. warn "$(basename ${BASH_SOURCE[1]}):${BASH_LINENO[0]}: ${FUNCNAME[1]}() failed at command '$BASH_COMMAND'"
  136. rmsg "$SCRIPT_DESC exiting with error ($exitval)"
  137. }
  138. _do_header() {
  139. echo
  140. local reply
  141. if banner=$(toilet --filter border --filter gay --width 51 -s -f smbraille "$TITLE" 2>/dev/null); then
  142. while read reply; do
  143. echo -e " $reply"
  144. done <<-EOF
  145. $banner
  146. EOF
  147. else
  148. echo -n ' '
  149. echo $TITLE
  150. echo
  151. fi
  152. echo " For detailed usage information,"
  153. echo " invoke with the '-h' switch"
  154. echo
  155. }
  156. _warn_user_opts() {
  157. local out opt text
  158. while read opt text; do
  159. [ "$opt" ] || continue
  160. if [ "${!opt}" ]; then
  161. out+=" + $text\n"
  162. fi
  163. done <<<$USER_OPTS_INFO
  164. if [ "$out" ]; then
  165. warn "\n The following user options are in effect:"
  166. warn_nonl "${out}"
  167. fi
  168. }
  169. _set_host_vars() {
  170. BUILD_DIR='armbian_rootenc_build'
  171. SRC_ROOT="$BUILD_DIR/src"
  172. BOOT_ROOT="$BUILD_DIR/boot"
  173. TARGET_ROOT="$BUILD_DIR/target"
  174. CONFIG_VARS_FILE="$BOOT_ROOT/.rootenc_config_vars"
  175. host_distro=$(lsb_release --short --codename)
  176. host_kernel=$(ls '/boot' | egrep '^vmlinu[xz]') # allow 'vmlinux' or 'vmlinuz'
  177. }
  178. check_sdcard_name_and_params() {
  179. local dev chk
  180. dev=$1
  181. [ "$dev" ] || die "You must supply a device name"
  182. [ "${dev:0:5}" == '/dev/' ] || dev="/dev/$dev"
  183. [ -e "$dev" ] || die "$dev does not exist"
  184. chk="$(lsblk --noheadings --nodeps --list --output=TYPE $dev 2>/dev/null)"
  185. [ "$chk" != 'disk' ] && {
  186. [ "$chk" == 'part' ] && die "$dev is a partition, not a block device!"
  187. die "$dev is not a block device!"
  188. }
  189. local pttype size nodos oversize removable non_removable part_sep
  190. pttype=$(blkid --output=udev $dev | grep TYPE | cut -d '=' -f2)
  191. size="$(lsblk --noheadings --nodeps --list --output=SIZE --bytes $dev 2>/dev/null)"
  192. removable="$(lsblk --noheadings --nodeps --list --output=RM $dev 2>/dev/null)"
  193. nodos=$([ "$pttype" -a "$pttype" != 'dos' ] && echo "Partition type is ${pttype^^}") || true
  194. oversize=$([ $size -gt 137438953472 ] && echo 'Size is > 128GiB') || true
  195. non_removable=$([ $removable -ne 0 ] || echo 'Device is non-removable')
  196. SD_INFO="$(lsblk --noheadings --nodeps --list --output=VENDOR,MODEL,SIZE $dev 2>/dev/null)"
  197. SD_INFO=${SD_INFO// / }
  198. if [ "$nodos" -o "$oversize" -o "$non_removable" ]; then
  199. warn " $dev ($SD_INFO) doesn’t appear to be an SD card"
  200. warn " for the following reasons:"
  201. if [ "$non_removable" ]; then warn " $non_removable"; fi
  202. if [ "$nodos" ]; then warn " $nodos"; fi
  203. if [ "$oversize" ]; then warn " $oversize"; fi
  204. _user_confirm ' Are you sure this is the correct device of your blank SD card?' 'no'
  205. fi
  206. SDCARD_DEVNAME=${dev:5}
  207. [ "${SDCARD_DEVNAME%[0-9]}" == $SDCARD_DEVNAME ] || part_sep='p'
  208. BOOT_DEVNAME=$SDCARD_DEVNAME${part_sep}1
  209. ROOT_DEVNAME=$SDCARD_DEVNAME${part_sep}2
  210. [ "$SDCARD_DEVNAME" ] || die 'You must supply a device name for the SD card!'
  211. pu_msg "Will write to target $dev ($SD_INFO)"
  212. }
  213. _get_user_var() {
  214. local var desc dfl prompt pat pat_errmsg vtest cprompt seen_prompt reply redo
  215. var=$1 desc=$2 dfl=$3 prompt=$4 pat=$5 pat_errmsg=$6 vtest=$7
  216. while true; do
  217. if [ -z "${!var}" -o "$seen_prompt" -o "$redo" ]; then
  218. if [ "$seen_prompt" ]; then
  219. echo -n " Enter $desc: "
  220. else
  221. cprompt=
  222. while read reply; do
  223. cprompt+=" ${reply## }\n"
  224. done <<-EOF
  225. $prompt
  226. EOF
  227. echo
  228. if [ "$dfl" ]; then
  229. printf "${cprompt:0:-2} " "$dfl"
  230. else
  231. echo -ne "${cprompt:0:-2} "
  232. fi
  233. seen_prompt=1
  234. fi
  235. eval "read $var"
  236. fi
  237. redo=1
  238. if [ -z "${!var}" -a "$dfl" ]; then eval "$var=$dfl"; fi
  239. [ "${!var}" ] || {
  240. rmsg " $desc must not be empty"
  241. continue
  242. }
  243. if [ "$pat" ]; then
  244. local rpat=$pat
  245. if [ "$pat" == 'bool' ]; then
  246. rpat='^[ynYN]*$'
  247. pat_errmsg="You must type 'y' or 'n'"
  248. fi
  249. echo "${!var}" | egrep -qi "$rpat" || {
  250. rmsg " ${!var}: $pat_errmsg"
  251. continue
  252. }
  253. if [ "$pat" == 'bool' ]; then
  254. if [[ ${!var} =~ ^[Yy]$ ]]; then eval "$var=yes"; else eval "$var="; fi
  255. fi
  256. fi
  257. if [ "$vtest" ]; then
  258. $vtest || continue
  259. fi
  260. break
  261. done
  262. }
  263. _get_user_vars() {
  264. local dq='[0-9]{1,3}'
  265. _get_user_var 'IP_ADDRESS' 'IP address' '' \
  266. "Enter the IP address of the target machine.
  267. Enter 'dhcp' for a dynamic IP or 'none' for no remote SSH unlocking support
  268. IP address:" \
  269. "^(dhcp|none|$dq\.$dq\.$dq\.$dq)$" \
  270. 'malformed IP address'
  271. IP_ADDRESS=${IP_ADDRESS,,}
  272. _get_user_var 'BOOTPART_LABEL' 'boot partition label' 'ARMBIAN_BOOT' \
  273. "Enter a boot partition label for the target machine,
  274. or hit ENTER for the default (%s): " \
  275. '^[A-Za-z0-9_]{1,16}$' \
  276. "Label must contain no more than 16 characters in the set 'A-Za-z0-9_'"
  277. _get_user_var 'ROOTFS_NAME' 'root filesystem device name' 'rootfs' \
  278. "Enter a device name for the encrypted root filesystem,
  279. or hit ENTER for the default (%s):" \
  280. '^[a-z0-9_]{1,48}$' \
  281. "Name must contain no more than 48 characters in the set 'a-z0-9_'" \
  282. '_test_rootfs_mounted'
  283. _get_user_var 'DISK_PASSWD' 'disk password' '' \
  284. "Choose a simple disk password for the installation process.
  285. Once your encrypted system is up and running, you can change
  286. the password using the 'cryptsetup' command.
  287. Enter password:" \
  288. '^[A-Za-z0-9_ ]{1,10}$' \
  289. "Temporary disk password must contain no more than 10 characters in the set 'A-Za-z0-9_ '"
  290. if [ "$IP_ADDRESS" == 'none' ]; then
  291. UNLOCKING_USERHOST=
  292. elif [ -e 'authorized_keys' -a "$USE_LOCAL_AUTHORIZED_KEYS" ]; then
  293. UNLOCKING_USERHOST=
  294. else
  295. _get_user_var 'UNLOCKING_USERHOST' 'USER@HOST' '' \
  296. "Enter the user@host of the machine you'll be unlocking from:" \
  297. '\S+@\S+' \
  298. 'malformed USER@HOST' \
  299. '_test_unlocking_host_available'
  300. fi
  301. _get_user_var 'SERIAL_CONSOLE' 'serial console unlocking' '' \
  302. "Unlock the disk from the serial console. WARNING: enabling this will
  303. make it impossible to unlock the disk using the keyboard and monitor,
  304. though unlocking via SSH will still work.
  305. Enable unlocking via serial console? (y/n):" \
  306. 'bool'
  307. true
  308. }
  309. _test_rootfs_mounted() {
  310. [ -e "/dev/mapper/$ROOTFS_NAME" ] && {
  311. local mnt=$(lsblk --list --noheadings --output=MOUNTPOINT /dev/mapper/$ROOTFS_NAME)
  312. [ "$mnt" ] && {
  313. rmsg " Device '$ROOTFS_NAME' is in use and mounted on $mnt"
  314. return 1
  315. }
  316. }
  317. return 0
  318. }
  319. _test_unlocking_host_available() {
  320. local ul_host=${UNLOCKING_USERHOST#*@}
  321. ping -c1 $ul_host &>/dev/null || {
  322. rmsg " Unable to ping host '$ul_host'"
  323. return 1
  324. }
  325. }
  326. _test_sdcard_mounted() {
  327. local chk="$(lsblk --noheadings --list --output=MOUNTPOINT /dev/$SDCARD_DEVNAME)"
  328. [ -z "$chk" ] || {
  329. lsblk --output=NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT /dev/$SDCARD_DEVNAME
  330. die "Device /dev/$SDCARD_DEVNAME has mounted partitions!"
  331. }
  332. }
  333. get_authorized_keys() {
  334. [ -e 'authorized_keys' -a "$USE_LOCAL_AUTHORIZED_KEYS" ] || {
  335. rsync "$UNLOCKING_USERHOST:.ssh/id_*.pub" 'authorized_keys'
  336. }
  337. }
  338. _apt_update() {
  339. [ "$ROOTENC_IGNORE_APT_ERRORS" ] && set +e
  340. apt --yes update
  341. [ "$APT_UPGRADE" ] && apt --yes upgrade
  342. [ "$ROOTENC_IGNORE_APT_ERRORS" ] && set -e
  343. true
  344. }
  345. _print_pkgs_to_install() {
  346. local pkgs pkgs_ssh
  347. case $1 in
  348. 'host')
  349. case "$host_distro" in
  350. focal|bionic|buster) pkgs='cryptsetup-bin ed' ;;
  351. *) pkgs='cryptsetup ed'
  352. warn "Warning: unrecognized host distribution '$host_distro'" ;;
  353. esac ;;
  354. 'target')
  355. case "$target_distro" in
  356. focal|buster) pkgs='cryptsetup-initramfs' pkgs_ssh='dropbear-initramfs' ;;
  357. bionic) pkgs='cryptsetup' pkgs_ssh='dropbear-initramfs' ;;
  358. *) pkgs='cryptsetup' pkgs_ssh='dropbear'
  359. warn "Warning: unrecognized target distribution '$target_distro'" ;;
  360. esac
  361. [ "$IP_ADDRESS" != 'none' ] && pkgs+=" $pkgs_ssh" ;;
  362. esac
  363. for i in $pkgs; do
  364. dpkg -l $i 2>/dev/null | grep -q ^ii || echo $i
  365. done
  366. }
  367. apt_install_host() {
  368. local pkgs=$(_print_pkgs_to_install 'host')
  369. [ "$pkgs" ] && {
  370. _apt_update
  371. apt --yes install $pkgs
  372. }
  373. true
  374. }
  375. create_build_dir() {
  376. mkdir -p $BUILD_DIR
  377. mkdir -p $SRC_ROOT
  378. mkdir -p $BOOT_ROOT
  379. mkdir -p $TARGET_ROOT
  380. }
  381. umount_target() {
  382. for i in $BOOT_ROOT $TARGET_ROOT; do
  383. while mountpoint -q $i; do
  384. umount -Rl $i
  385. done
  386. done
  387. }
  388. remove_build_dir() {
  389. [ -d $TARGET_ROOT ] && rmdir $TARGET_ROOT
  390. [ -d $BOOT_ROOT ] && rmdir $BOOT_ROOT
  391. [ -d $SRC_ROOT ] && rmdir $SRC_ROOT
  392. [ -d $BUILD_DIR ] && rmdir $BUILD_DIR
  393. true
  394. }
  395. _get_device_maps() {
  396. local dm_type=$1 varname="device_maps_$1" dm_name ls mp
  397. eval "$varname="
  398. for dm_name in $(dmsetup ls | awk '{print $1}'); do
  399. [ $(lsblk --noheadings --nodeps -o fstype /dev/mapper/$dm_name) == 'ext4' ] || continue
  400. ls=$(findmnt -n --source /dev/mapper/$dm_name | awk '{print $1}')
  401. if [ "$ls" -a "$dm_type" == 'mounted_on_target' ]; then
  402. for mp in $ls; do
  403. if [ "${mp: -${#TARGET_ROOT}}" == "$TARGET_ROOT" ]; then
  404. eval "$varname+=$dm_name "
  405. fi
  406. done
  407. elif [ -z "$ls" -a "$dm_type" == 'unmounted' ]; then
  408. eval "$varname+=$dm_name "
  409. fi
  410. done
  411. tmsg "$varname=[${!varname}]"
  412. }
  413. _close_device_maps() {
  414. local dm_type=$1
  415. local varname="device_maps_${dm_type}"
  416. for i in ${!varname}; do
  417. tmsg "closing $i"
  418. cryptsetup status $i > '/dev/null' && cryptsetup luksClose $i
  419. done
  420. true
  421. }
  422. _preclean() {
  423. close_loopmount
  424. _get_device_maps 'unmounted'
  425. _close_device_maps 'unmounted'
  426. _get_device_maps 'mounted_on_target'
  427. umount_target
  428. _close_device_maps 'mounted_on_target'
  429. remove_build_dir
  430. }
  431. _clean() {
  432. pu_msg "Cleaning up, please wait..."
  433. _show_output
  434. close_loopmount
  435. _get_device_maps 'mounted_on_target'
  436. umount_target
  437. update_config_vars_file
  438. _close_device_maps 'mounted_on_target'
  439. [ -e 'authorized_keys' -a -z "$USE_LOCAL_AUTHORIZED_KEYS" ] && shred -u 'authorized_keys'
  440. remove_build_dir
  441. }
  442. get_armbian_image() {
  443. ARMBIAN_IMAGE="$(ls *.img)"
  444. [ "$ARMBIAN_IMAGE" ] || die 'You must place an Armbian image in the current directory!'
  445. local count=$(echo "$ARMBIAN_IMAGE" | wc -l)
  446. [ "$count" == 1 ] || die "More than one image file present!:\n$ARMBIAN_IMAGE"
  447. }
  448. _confirm_user_vars() {
  449. echo
  450. echo " Armbian image: $ARMBIAN_IMAGE"
  451. echo " Target device: /dev/$SDCARD_DEVNAME ($SD_INFO)"
  452. echo " Root filesystem device name: /dev/mapper/$ROOTFS_NAME"
  453. echo " Target IP address: $IP_ADDRESS"
  454. echo " Boot partition label: $BOOTPART_LABEL"
  455. echo " Disk password: $DISK_PASSWD"
  456. [ "$UNLOCKING_USERHOST" ] && echo " user@host of unlocking host: $UNLOCKING_USERHOST"
  457. echo " Serial console unlocking: ${SERIAL_CONSOLE:-no}"
  458. echo
  459. _user_confirm ' Are these settings correct?' 'yes'
  460. }
  461. setup_loopmount() {
  462. LOOP_DEV=$(losetup -f)
  463. losetup -P $LOOP_DEV $ARMBIAN_IMAGE
  464. mount ${LOOP_DEV}p1 $SRC_ROOT
  465. START_SECTOR=$(fdisk -l $LOOP_DEV -o Start | tail -n1 | tr -d ' ') # usually 32768
  466. BOOT_SECTORS=409600 # 200MB
  467. }
  468. _umount_with_check() {
  469. mountpoint -q $1 && umount $1
  470. }
  471. update_config_vars_file() {
  472. mount "/dev/$BOOT_DEVNAME" $BOOT_ROOT
  473. _print_config_vars $CONFIG_VARS_FILE
  474. umount $BOOT_ROOT
  475. }
  476. _print_states() {
  477. for i in $STATES; do
  478. echo $i: ${!i}
  479. done
  480. }
  481. _update_state_from_config_vars() {
  482. [ -e $CONFIG_VARS_FILE ] || return 0
  483. local reply
  484. while read reply; do
  485. eval "c$reply"
  486. done <<<$(cat $CONFIG_VARS_FILE)
  487. local saved_states cfgvar_changed
  488. saved_states="$(_print_states)"
  489. cfgvar_changed=
  490. [ $cARMBIAN_IMAGE != $ARMBIAN_IMAGE ] && cfgvar_changed+=' ARMBIAN_IMAGE' card_partitioned='n'
  491. [ $cBOOTPART_LABEL != $BOOTPART_LABEL ] && cfgvar_changed+=' BOOTPART_LABEL' bootpart_label_created='n'
  492. [ $cROOTFS_NAME != $ROOTFS_NAME ] && cfgvar_changed+=' ROOTFS_NAME' target_configured='n'
  493. [ $cDISK_PASSWD != $DISK_PASSWD ] && cfgvar_changed+=' DISK_PASSWD' rootpart_copied='n'
  494. [ "$UNLOCKING_USERHOST" -a "$cUNLOCKING_USERHOST" != "$UNLOCKING_USERHOST" ] && {
  495. cfgvar_changed+=' UNLOCKING_USERHOST' target_configured='n'
  496. }
  497. [ $cIP_ADDRESS != $IP_ADDRESS ] && cfgvar_changed+=' IP_ADDRESS' target_configured='n'
  498. [ "$cADD_ALL_MODS" != "$ADD_ALL_MODS" ] && cfgvar_changed+=' ADD_ALL_MODS' target_configured='n'
  499. [ "$IP_ADDRESS" -a "$cUSE_LOCAL_AUTHORIZED_KEYS" != "$USE_LOCAL_AUTHORIZED_KEYS" ] && {
  500. cfgvar_changed+=' USE_LOCAL_AUTHORIZED_KEYS' target_configured='n'
  501. }
  502. [ $card_partitioned == 'n' ] && {
  503. bootpart_copied='n'
  504. bootpart_label_created='n'
  505. rootpart_copied='n'
  506. target_configured='n'
  507. }
  508. [ $bootpart_copied == 'n' ] && bootpart_label_created='n'
  509. [ $rootpart_copied == 'n' ] && target_configured='n'
  510. [ "$saved_states" != "$(_print_states)" ] && {
  511. warn "Install state altered due to changed config vars:$cfgvar_changed"
  512. for i in $STATES; do
  513. if [ "${!i}" == 'n' ]; then
  514. imsg " $i: ${RED}no$RESET"
  515. else
  516. imsg " $i: ${GREEN}yes$RESET"
  517. fi
  518. done
  519. _delete_state_files
  520. }
  521. true
  522. }
  523. _add_state_file() {
  524. local state=$1 cmd=$2
  525. if [ "$cmd" == 'target' ]; then
  526. touch "$TARGET_ROOT/boot/.rootenc_install_state/$state"
  527. else
  528. [ "$cmd" == 'mount' ] && mount "/dev/$BOOT_DEVNAME" $BOOT_ROOT
  529. mkdir -p "$BOOT_ROOT/.rootenc_install_state"
  530. touch "$BOOT_ROOT/.rootenc_install_state/$state"
  531. [ "$cmd" == 'mount' ] && umount $BOOT_ROOT
  532. fi
  533. eval "$state='y'"
  534. tmsg "added state file '$state'"
  535. }
  536. _delete_state_files() {
  537. for i in $STATES; do
  538. local fn="$BOOT_ROOT/.rootenc_install_state/$i"
  539. [ ${!i} == 'n' -a -e $fn ] && /bin/rm $fn
  540. done
  541. true
  542. }
  543. _get_state_from_state_files() {
  544. for i in $STATES; do
  545. if [ -e "$BOOT_ROOT/.rootenc_install_state/$i" ]; then
  546. eval "$i=y"
  547. else
  548. eval "$i=n"
  549. fi
  550. done
  551. }
  552. _print_state_from_state_files() {
  553. imsg 'Install state:'
  554. for i in $STATES; do
  555. if [ -e "$BOOT_ROOT/.rootenc_install_state/$i" ]; then
  556. imsg " $i: ${GREEN}yes$RESET"
  557. else
  558. imsg " $i: ${RED}no$RESET"
  559. fi
  560. done
  561. }
  562. check_install_state() {
  563. for i in $STATES; do eval "$i=n"; done
  564. if [ "$FORCE_REBUILD" ]; then
  565. return
  566. else
  567. do_partprobe
  568. lsblk --noheadings --list /dev/$SDCARD_DEVNAME -o 'NAME' | grep -q $BOOT_DEVNAME || return 0
  569. lsblk --noheadings --list /dev/$BOOT_DEVNAME -o 'FSTYPE' | grep -q 'ext4' || return 0
  570. mount "/dev/$BOOT_DEVNAME" $BOOT_ROOT
  571. _get_state_from_state_files
  572. if [ "$target_configured" == 'y' -a "$FORCE_RECONFIGURE" ]; then
  573. target_configured='n'
  574. _delete_state_files
  575. fi
  576. _print_state_from_state_files
  577. _update_state_from_config_vars
  578. _umount_with_check $BOOT_ROOT
  579. fi
  580. }
  581. close_loopmount() {
  582. while mountpoint -q $SRC_ROOT; do
  583. umount $SRC_ROOT
  584. done
  585. for i in $(losetup --noheadings --raw --list -j $ARMBIAN_IMAGE | awk '{print $1}'); do
  586. losetup -d $i
  587. done
  588. }
  589. _user_confirm() {
  590. local prompt1 prompt2 dfl_action reply
  591. prompt1=$1 dfl_action=$2
  592. if [ "$dfl_action" == 'yes' ]; then
  593. prompt2='(Y/n)'
  594. else
  595. prompt2='(y/N)'
  596. fi
  597. imsg_nonl "$prompt1 $prompt2 "
  598. read -n1 reply
  599. [ "$reply" ] && imsg ''
  600. [ "$dfl_action" == 'yes' -a -z "$reply" ] && return
  601. [ "$reply" == 'y' -o "$reply" == 'Y' ] && return
  602. warn "Exiting at user request"
  603. exit 1
  604. }
  605. erase_boot_sector_and_first_partition() {
  606. local sectors count
  607. sectors=$((START_SECTOR+BOOT_SECTORS+100))
  608. count=$(((sectors/8192)+1))
  609. pu_msg "Erasing up to beginning of second partition ($sectors sectors, ${count}M):"
  610. _show_output
  611. dd if=/dev/zero \
  612. of=/dev/$SDCARD_DEVNAME \
  613. status=progress \
  614. bs=$((512*8192)) \
  615. count=$count
  616. _hide_output
  617. }
  618. create_partition_label() {
  619. pu_msg "Creating new partition label on /dev/$SDCARD_DEVNAME"
  620. local fdisk_cmds="o\nw\n"
  621. set +e
  622. echo -e "$fdisk_cmds" | fdisk "/dev/$SDCARD_DEVNAME"
  623. set -e
  624. do_partprobe
  625. }
  626. copy_boot_loader() {
  627. local count
  628. count=$((START_SECTOR/2048))
  629. pu_msg "Copying boot loader ($START_SECTOR sectors, ${count}M):"
  630. _show_output
  631. dd if=$ARMBIAN_IMAGE \
  632. of=/dev/$SDCARD_DEVNAME \
  633. status=progress \
  634. bs=$((512*2048)) \
  635. count=$count
  636. _hide_output
  637. do_partprobe
  638. }
  639. _print_config_vars() {
  640. local outfile=$1
  641. local data="$(for i in $CONFIG_VARS; do echo "$i='${!i}'"; done)"
  642. if [ "$outfile" ]; then echo "$data" > $outfile; else echo "$data"; fi
  643. }
  644. partition_sd_card() {
  645. local p1_end p2_start fdisk_cmds bname rname fstype
  646. p1_end=$((START_SECTOR+BOOT_SECTORS-1))
  647. p2_start=$((p1_end+1))
  648. fdisk_cmds="o\nn\np\n1\n$START_SECTOR\n$p1_end\nn\np\n2\n$p2_start\n\nw\n"
  649. set +e
  650. echo -e "$fdisk_cmds" | fdisk "/dev/$SDCARD_DEVNAME"
  651. set -e
  652. do_partprobe
  653. bname="$(lsblk --noheadings --list --output=NAME /dev/$BOOT_DEVNAME)"
  654. [ "$bname" == $BOOT_DEVNAME ] || die 'Partitioning failed!'
  655. rname="$(lsblk --noheadings --list --output=NAME /dev/$ROOT_DEVNAME)"
  656. [ "$rname" == $ROOT_DEVNAME ] || die 'Partitioning failed!'
  657. # filesystem is required by call to _add_state_file(), so we must create it here
  658. fstype=$(lsblk --noheadings --list --output=FSTYPE "/dev/$BOOT_DEVNAME")
  659. [ "$fstype" == 'ext4' -a "$ROOTENC_REUSE_FS" ] || mkfs.ext4 -F "/dev/$BOOT_DEVNAME"
  660. do_partprobe
  661. _add_state_file 'card_partitioned' 'mount'
  662. }
  663. _do_partition() {
  664. imsg "All data on /dev/$SDCARD_DEVNAME ($SD_INFO) will be destroyed!!!"
  665. _user_confirm 'Are you sure you want to continue?' 'no'
  666. if [ "$ERASE" ]; then
  667. erase_boot_sector_and_first_partition
  668. else
  669. create_partition_label
  670. fi
  671. copy_boot_loader
  672. partition_sd_card
  673. }
  674. copy_system_boot() {
  675. [ "$PARTITION_ONLY" ] && {
  676. _add_state_file 'bootpart_copied' 'mount'
  677. return
  678. }
  679. mount "/dev/$BOOT_DEVNAME" $BOOT_ROOT
  680. pu_msg "Copying files to boot partition:"
  681. _show_output
  682. rsync $RSYNC_VERBOSITY --archive $SRC_ROOT/boot/* $BOOT_ROOT
  683. _hide_output
  684. [ -e "$BOOT_ROOT/boot" ] || (cd $BOOT_ROOT && ln -s . 'boot')
  685. _add_state_file 'bootpart_copied'
  686. umount $BOOT_ROOT
  687. }
  688. create_bootpart_label() {
  689. e2label "/dev/$BOOT_DEVNAME" "$BOOTPART_LABEL"
  690. do_partprobe
  691. _add_state_file 'bootpart_label_created' 'mount'
  692. }
  693. copy_system_root() {
  694. if [ "$FORCE_REFORMAT_ROOT" ] || ! cryptsetup isLuks "/dev/$ROOT_DEVNAME"; then
  695. pu_msg "Formatting encrypted root partition:"
  696. echo -n $DISK_PASSWD | cryptsetup luksFormat "/dev/$ROOT_DEVNAME" '-'
  697. fi
  698. echo $DISK_PASSWD | cryptsetup luksOpen "/dev/$ROOT_DEVNAME" $ROOTFS_NAME
  699. local fstype=$(lsblk --noheadings --list --output=FSTYPE "/dev/mapper/$ROOTFS_NAME")
  700. [ "$fstype" == 'ext4' -a "$ROOTENC_REUSE_FS" ] || mkfs.ext4 -F "/dev/mapper/$ROOTFS_NAME"
  701. [ "$PARTITION_ONLY" ] || {
  702. mount "/dev/mapper/$ROOTFS_NAME" $TARGET_ROOT
  703. pu_msg "Copying system to encrypted root partition:"
  704. _show_output
  705. rsync $RSYNC_VERBOSITY --archive --exclude=boot $SRC_ROOT/* $TARGET_ROOT
  706. _hide_output
  707. sync
  708. mkdir -p "$TARGET_ROOT/boot"
  709. touch "$TARGET_ROOT/root/.no_rootfs_resize"
  710. umount $TARGET_ROOT
  711. }
  712. cryptsetup luksClose $ROOTFS_NAME
  713. do_partprobe
  714. _add_state_file 'rootpart_copied' 'mount'
  715. }
  716. mount_target() {
  717. echo $DISK_PASSWD | cryptsetup luksOpen "/dev/$ROOT_DEVNAME" $ROOTFS_NAME
  718. mount "/dev/mapper/$ROOTFS_NAME" $TARGET_ROOT
  719. mount "/dev/$BOOT_DEVNAME" "$TARGET_ROOT/boot"
  720. local src dest args
  721. while read src dest args; do
  722. mount $args $src $TARGET_ROOT/$dest
  723. done <<-EOF
  724. udev dev -t devtmpfs -o rw,relatime,nosuid,mode=0755
  725. devpts dev/pts -t devpts
  726. tmpfs dev/shm -t tmpfs -o rw,nosuid,nodev,relatime
  727. proc proc -t proc
  728. sys sys -t sysfs
  729. EOF
  730. }
  731. _copy_to_target() {
  732. local fn=$1
  733. if [ -e $fn ]; then
  734. echo "Copying '$fn'"
  735. cat $fn > $TARGET_ROOT/$fn
  736. else
  737. imsg "Unable to copy '$fn' to target (file does not exist)"
  738. false
  739. fi
  740. }
  741. create_etc_crypttab() {
  742. local root_uuid="$(lsblk --noheadings --list --nodeps --output=UUID /dev/$ROOT_DEVNAME)"
  743. echo "$ROOTFS_NAME UUID=$root_uuid none initramfs,luks" > "$TARGET_ROOT/etc/crypttab"
  744. _display_file "$TARGET_ROOT/etc/crypttab"
  745. }
  746. copy_etc_files() {
  747. _copy_to_target '/etc/resolv.conf'
  748. _copy_to_target '/etc/hosts'
  749. set +e
  750. _copy_to_target /etc/apt/apt.conf.d/*proxy
  751. set -e
  752. }
  753. _set_target_vars() {
  754. target_distro=$(chroot $TARGET_ROOT 'lsb_release' '--short' '--codename')
  755. target_kernel=$(chroot $TARGET_ROOT 'ls' '/boot' | egrep '^vmlinu[xz]')
  756. imsg "$(printf '%-8s %-28s %s' '' 'Host' 'Target')"
  757. imsg "$(printf '%-8s %-28s %s' '' '----' '------')"
  758. imsg "$(printf '%-8s %-28s %s' 'distro:' $host_distro $target_distro)"
  759. imsg "$(printf '%-8s %-28s %s' 'kernel:' $host_kernel $target_kernel)"
  760. }
  761. _distros_match() {
  762. [ $host_distro == $target_distro ]
  763. }
  764. _kernels_match() {
  765. [ ${host_kernel%.*} == ${target_kernel%.*} ] || return 1
  766. [ ${host_kernel##*-} == ${target_kernel##*-} ]
  767. }
  768. copy_etc_files_distro_specific() {
  769. local files='/etc/apt/sources.list /etc/apt/sources.list.d/armbian.list'
  770. if _distros_match; then
  771. for i in $files; do _copy_to_target $i; done
  772. else
  773. warn 'Warning: host and target distros do not match:'
  774. for i in $files; do imsg " not copying $i"; done
  775. fi
  776. }
  777. _display_file() {
  778. local name text reply
  779. if [ "$2" ]; then
  780. name="$1"
  781. text="$2"
  782. else
  783. name=${1#$TARGET_ROOT}
  784. text="$(cat $1)"
  785. fi
  786. hl='────────────────────────────────────────'
  787. hl="$hl$hl$hl"
  788. hls=${hl:0:${#name}+1}
  789. echo "┌─$hls─┐"
  790. echo "│ $name: │"
  791. echo "├─$hls─┘"
  792. echo "$text" | sed 's/^/│ /'
  793. }
  794. edit_armbianEnv() {
  795. local file text console_arg
  796. file="$TARGET_ROOT/boot/armbianEnv.txt"
  797. ed $file <<-'EOF'
  798. g/^\s*rootdev=/d
  799. g/^\s*console=/d
  800. g/^\s*bootlogo=/d
  801. wq
  802. EOF
  803. case $SERIAL_CONSOLE in
  804. 'yes') console_arg='serial' ;;
  805. *) console_arg='display' ;;
  806. esac
  807. text="rootdev=/dev/mapper/$ROOTFS_NAME
  808. console=$console_arg
  809. bootlogo=false"
  810. echo "$text" >> $file
  811. _display_file $file
  812. }
  813. # Add the following lines to '/etc/initramfs-tools/initramfs.conf'. If
  814. # your board’s IP address will be statically configured, substitute the
  815. # correct static IP address after 'IP='. If it will be configured via
  816. # DHCP, omit the IP line entirely:
  817. edit_initramfs_conf() {
  818. local file="$TARGET_ROOT/etc/initramfs-tools/initramfs.conf"
  819. ed $file <<-'EOF'
  820. g/^\s*IP=/s/^/# /
  821. g/^\s*DEVICE=/d
  822. wq
  823. EOF
  824. [ "$IP_ADDRESS" == 'dhcp' -o "$IP_ADDRESS" == 'none' ] || {
  825. echo "IP=$IP_ADDRESS:::255.255.255.0::eth0:off" >> $file
  826. }
  827. [ "$IP_ADDRESS" == 'none' ] || echo "DEVICE=eth0" >> $file
  828. _display_file $file
  829. }
  830. edit_initramfs_modules() {
  831. local modlist file hdr
  832. [ "$ADD_ALL_MODS" ] && {
  833. if ! _kernels_match; then
  834. warn 'Host and target kernels do not match. Not adding modules to initramfs'
  835. elif ! _distros_match; then
  836. warn 'Host and target distros do not match. Not adding modules to initramfs'
  837. else
  838. modlist=$(lsmod | cut -d ' ' -f1 | tail -n+2)
  839. fi
  840. }
  841. file="$TARGET_ROOT/etc/initramfs-tools/modules"
  842. hdr="# List of modules that you want to include in your initramfs.
  843. # They will be loaded at boot time in the order below.
  844. #
  845. # Syntax: module_name [args ...]
  846. #
  847. # You must run update-initramfs(8) to effect this change.
  848. #
  849. "
  850. echo "$hdr$modlist" > $file
  851. _display_file $file
  852. }
  853. copy_authorized_keys() {
  854. local dest="$TARGET_ROOT/etc/dropbear-initramfs"
  855. mkdir -p $dest
  856. /bin/cp 'authorized_keys' $dest
  857. _display_file "$dest/authorized_keys"
  858. }
  859. create_fstab() {
  860. local boot_uuid file text
  861. boot_uuid="$(lsblk --noheadings --list --output=UUID /dev/$BOOT_DEVNAME)"
  862. file="$TARGET_ROOT/etc/fstab"
  863. text="/dev/mapper/$ROOTFS_NAME / ext4 defaults,noatime,nodiratime,commit=600,errors=remount-ro 0 1
  864. UUID=$boot_uuid /boot ext4 defaults,noatime,nodiratime,commit=600,errors=remount-ro 0 2
  865. tmpfs /tmp tmpfs defaults,nosuid 0 0"
  866. echo "$text" > $file
  867. _display_file $file
  868. }
  869. edit_dropbear_cfg() {
  870. local dest file text
  871. dest="$TARGET_ROOT/etc/dropbear-initramfs"
  872. file="$dest/config"
  873. text='DROPBEAR_OPTIONS="-p 2222"
  874. DROPBEAR=y'
  875. if [ "$IP_ADDRESS" == 'none' ]; then
  876. [ -e $file ] && rm -v $file
  877. true
  878. else
  879. mkdir -p $dest
  880. [ -e $file ] && grep -q '^DROPBEAR_OPTIONS="-p 2222"' $file || echo "$text" >> $file
  881. _display_file $file
  882. fi
  883. }
  884. create_cryptroot_unlock_sh() {
  885. local dest file text
  886. dest="$TARGET_ROOT/etc/initramfs-tools/hooks"
  887. file="$dest/cryptroot-unlock.sh"
  888. text='#!/bin/sh
  889. if [ "$1" = "prereqs" ]; then echo "dropbear-initramfs"; exit 0; fi
  890. . /usr/share/initramfs-tools/hook-functions
  891. source="/tmp/cryptroot-unlock-profile"
  892. root_home=$(echo $DESTDIR/root-*)
  893. root_home=${root_home#$DESTDIR}
  894. echo "if [ \"\$SSH_CLIENT\" ]; then /usr/bin/cryptroot-unlock; fi" > $source
  895. copy_file ssh_login_profile $source $root_home/.profile
  896. exit 0'
  897. mkdir -p $dest
  898. echo "$text" > $file
  899. chmod 755 $file
  900. _display_file $file
  901. }
  902. # begin chroot functions:
  903. apt_install_target() {
  904. local pkgs=$(_print_pkgs_to_install 'target')
  905. [ "$pkgs" ] && {
  906. echo "target packages to install: $pkgs"
  907. local ls1 ls2
  908. _show_output
  909. ls1=$(ls -l /boot/initrd.img-*)
  910. # DEBUG:
  911. # dpkg-reconfigure $pkgs # doesn't work in chroot
  912. # apt --yes purge $pkgs
  913. # apt-get --yes --purge autoremove
  914. dpkg --configure --pending --force-confdef
  915. set +e
  916. apt --yes purge 'bash-completion'
  917. apt --yes purge 'command-not-found'
  918. set -e
  919. _apt_update
  920. echo 'force-confdef' > /root/.dpkg.cfg
  921. apt --yes install $pkgs
  922. rm /root/.dpkg.cfg
  923. apt --yes autoremove
  924. ls2=$(ls -l /boot/initrd.img-*)
  925. [ "$ls1" != "$ls2" ] && initramfs_updated='y'
  926. _hide_output
  927. }
  928. true
  929. }
  930. update_initramfs() {
  931. [ "$ROOTENC_TESTING" ] && return 0
  932. _show_output
  933. local ver=$(echo /boot/vmlinu?-* | sed 's/.boot.vmlinu.-//')
  934. update-initramfs -k $ver -u
  935. _hide_output
  936. }
  937. check_initramfs() {
  938. local text chk count
  939. text="$(lsinitramfs /boot/initrd.img*)"
  940. set +e
  941. chk=$(echo "$text" | grep 'cryptsetup')
  942. count=$(echo "$chk" | wc -l)
  943. [ "$count" -gt 5 ] || { echo "$text"; die 'Cryptsetup scripts missing in initramfs image'; }
  944. _display_file "lsinitramfs /boot/initrd.img* | grep 'cryptsetup'" "$chk"
  945. [ "$IP_ADDRESS" == 'none' ] || {
  946. chk=$(echo "$text" | grep 'dropbear')
  947. count=$(echo "$chk" | wc -l)
  948. [ "$count" -gt 5 ] || { echo "$text"; die 'Dropbear scripts missing in initramfs image'; }
  949. _display_file "lsinitramfs /boot/initrd.img* | grep 'dropbear'" "$chk"
  950. chk=$(echo "$text" | grep 'authorized_keys')
  951. count=$(echo "$chk" | wc -l)
  952. [ "$count" -eq 1 ] || { echo "$text"; die 'authorized_keys missing in initramfs image'; }
  953. _display_file "lsinitramfs /boot/initrd.img* | grep 'authorized_keys'" "$chk"
  954. }
  955. set -e
  956. }
  957. configure_target() {
  958. [ "$PARTITION_ONLY" ] && return
  959. mount_target
  960. _set_target_vars
  961. copy_etc_files
  962. copy_etc_files_distro_specific
  963. edit_initramfs_conf
  964. edit_initramfs_modules
  965. [ "$IP_ADDRESS" == 'none' ] || copy_authorized_keys
  966. create_etc_crypttab
  967. create_fstab
  968. edit_dropbear_cfg
  969. [ "$IP_ADDRESS" == 'none' ] || create_cryptroot_unlock_sh
  970. edit_armbianEnv
  971. _debug_pause
  972. _show_output # this must be done before entering chroot
  973. /bin/cp $0 $TARGET_ROOT
  974. export 'ROOTFS_NAME' 'IP_ADDRESS' 'target_distro' 'ROOTENC_TESTING' 'ROOTENC_PAUSE' 'ROOTENC_IGNORE_APT_ERRORS' 'APT_UPGRADE'
  975. chroot $TARGET_ROOT "./$PROGNAME" $ORIG_OPTS 'in_target'
  976. /bin/cp -a '/etc/resolv.conf' "$TARGET_ROOT/etc" # this could be a symlink
  977. /bin/rm "$TARGET_ROOT/$PROGNAME"
  978. _add_state_file 'target_configured' 'target'
  979. }
  980. _set_env_vars() {
  981. shopt -s extglob
  982. local name val
  983. while [ $# -gt 0 ]; do
  984. name=${1%=?*} val=${1#+([A-Z_])=}
  985. [ "$name" == "$1" -o "$val" == "$1" ] && die "$1: illegal argument (must be in format 'NAME=value')"
  986. eval "$name=$val"
  987. shift
  988. done
  989. shopt -u extglob
  990. }
  991. # begin execution
  992. set -e
  993. while getopts hCdmfFpRsuvz OPT
  994. do
  995. case "$OPT" in
  996. h) print_help; exit ;;
  997. C) NO_CLEANUP='y' ;;
  998. F) FORCE_REBUILD='y' ;;
  999. f) FORCE_RECONFIGURE='y' ;;
  1000. m) ADD_ALL_MODS='y' ;;
  1001. p) PARTITION_ONLY='y' ;;
  1002. R) FORCE_REFORMAT_ROOT='y' ;;
  1003. s) USE_LOCAL_AUTHORIZED_KEYS='y' ;;
  1004. u) APT_UPGRADE='y' ;;
  1005. d) DEBUG='y' ;&
  1006. v) VERBOSE='y' RSYNC_VERBOSITY='--verbose' ;;
  1007. z) ERASE='y' ;;
  1008. *) exit ;;
  1009. esac
  1010. ORIG_OPTS+="-$OPT "
  1011. done
  1012. shift $((OPTIND-1))
  1013. trap '_return_handler' RETURN
  1014. trap '_sigint_handler' INT
  1015. trap '_error_handler' ERR
  1016. set -o functrace
  1017. set -o errtrace
  1018. exec {stdout_dup}>&1
  1019. exec {stderr_dup}>&2
  1020. [ $UID == 0 -o $EUID == 0 ] || die 'This program must be run as root!'
  1021. export HOME='/root'
  1022. [ "$DEBUG" ] && set -x
  1023. ARG1=$1; shift
  1024. _set_env_vars $@
  1025. if [ "$ARG1" == 'in_target' ]; then
  1026. SCRIPT_DESC='Target script'
  1027. _hide_output
  1028. [ "$target_distro" == 'bionic' ] && {
  1029. echo 'export CRYPTSETUP=y' > '/etc/initramfs-tools/conf.d/cryptsetup'
  1030. }
  1031. apt_install_target
  1032. [ "$initramfs_updated" ] || update_initramfs
  1033. check_initramfs
  1034. else
  1035. SCRIPT_DESC='Host script'
  1036. _do_header
  1037. _set_host_vars
  1038. get_armbian_image
  1039. apt_install_host # we need cryptsetup in next cmd
  1040. _preclean
  1041. check_sdcard_name_and_params $ARG1
  1042. _get_user_vars
  1043. _test_sdcard_mounted
  1044. _warn_user_opts
  1045. _confirm_user_vars
  1046. [ "$IP_ADDRESS" == 'none' ] || get_authorized_keys
  1047. create_build_dir
  1048. [ "$NO_CLEANUP" ] || trap '_clean' EXIT
  1049. setup_loopmount
  1050. _debug_pause
  1051. check_install_state
  1052. _hide_output
  1053. [ "$card_partitioned" == 'n' ] && _do_partition
  1054. _debug_pause
  1055. [ "$bootpart_copied" == 'n' ] && copy_system_boot
  1056. [ "$bootpart_label_created" == 'n' ] && create_bootpart_label
  1057. [ "$rootpart_copied" == 'n' ] && copy_system_root
  1058. [ "$target_configured" == 'n' ] && configure_target
  1059. sync
  1060. gmsg 'All done!'
  1061. if [ "$IP_ADDRESS" != 'none' ]; then
  1062. imsg "To unlock the target disk, execute the following from the unlocking host:"
  1063. imsg " ssh -p 2222 root@${IP_ADDRESS/dhcp/TARGET_IP}"
  1064. fi
  1065. fi