From 4913e2d286d892d07a56c35fccec489465d48f03 Mon Sep 17 00:00:00 2001 From: Stadicus Date: Thu, 28 Nov 2019 07:40:59 +0100 Subject: [PATCH 1/4] rename reset-token, add factory maintenance-token Because: * The initial reset token system stays in place, but is extended to serve general maintenance purposes, including factory setup. * The factory token is deleted manually or on first setup. This commit: * renames reset-token to maintenance-token * adds a Shift factory token (clearly marked and to be deleted) --- .../config/signatures/maintenance-token-hashes | 1 + armbian/base/customize-armbian-rockpro64.sh | 3 +++ armbian/base/scripts/bbb-cmd.sh | 18 ++++++++++-------- .../scripts/systemd-startup-after-redis.sh | 10 +++++----- 4 files changed, 19 insertions(+), 13 deletions(-) create mode 100644 armbian/base/config/signatures/maintenance-token-hashes diff --git a/armbian/base/config/signatures/maintenance-token-hashes b/armbian/base/config/signatures/maintenance-token-hashes new file mode 100644 index 00000000..7363f1f4 --- /dev/null +++ b/armbian/base/config/signatures/maintenance-token-hashes @@ -0,0 +1 @@ +657861a460401b2ec32183ea5b3b4d12ce80aea8c9be57c245a033dae776adf7 Shift factory token diff --git a/armbian/base/customize-armbian-rockpro64.sh b/armbian/base/customize-armbian-rockpro64.sh index 825bfb11..ced9bea0 100755 --- a/armbian/base/customize-armbian-rockpro64.sh +++ b/armbian/base/customize-armbian-rockpro64.sh @@ -348,6 +348,9 @@ mkdir -p /data_source/ ln -sfn /data_source /data touch /data/.datadir_set_up +# import presync authorization token, will be deleted after presync or on initial setup +cp /opt/shift/config/signatures/maintenance-token-hashes /data_source + ## install Redis apt-get install -y --no-install-recommends redis mkdir -p /data/redis/ diff --git a/armbian/base/scripts/bbb-cmd.sh b/armbian/base/scripts/bbb-cmd.sh index d26d19f4..dc52e144 100755 --- a/armbian/base/scripts/bbb-cmd.sh +++ b/armbian/base/scripts/bbb-cmd.sh @@ -320,18 +320,20 @@ case "${MODULE}" in fi echo "OK: backup created as /mnt/backup/bbb-backup.rdb" - # add Factory Reset token - RESET_TOKEN="$(< /dev/urandom tr -dc A-Za-z0-9 | head -c64)" - RESET_TOKEN_HASH=$(echo -n "${RESET_TOKEN}" | sha256sum | tr -d "[:space:]-") + # add maintenance token + MAINTENANCE_TOKEN="$(< /dev/urandom tr -dc A-Za-z0-9 | head -c64)" + MAINTENANCE_TOKEN_HASH=$(echo -n "${MAINTENANCE_TOKEN}" | sha256sum | tr -d "[:space:]-") - # write reset token to usb drive, no linebreak allowed - printf "%s" "${RESET_TOKEN}" > /mnt/backup/.reset-token + # write maintenance token to usb drive, no linebreak allowed + printf "%s" "${MAINTENANCE_TOKEN}" > /mnt/backup/.maintenance-token # append reset token hash for permission check locally - echo "${RESET_TOKEN_HASH}" >> /data/reset-token-hashes - chmod 600 /data/reset-token-hashes - echo "OK: reset token created on flashdrive" + echo "${MAINTENANCE_TOKEN_HASH}" >> /data/maintenance-token-hashes + chmod 600 /data/maintenance-token-hashes + echo "OK: maintenance token created on flashdrive" + # make sure the Shift factory token is removed on first backup + sed -i '/factory token/d' /data/maintenance-token-hashes ;; # backup c-lightning on-chain keys in 'hsm_secret' into Redis database diff --git a/armbian/base/scripts/systemd-startup-after-redis.sh b/armbian/base/scripts/systemd-startup-after-redis.sh index 0c3e8047..cec8ab1a 100755 --- a/armbian/base/scripts/systemd-startup-after-redis.sh +++ b/armbian/base/scripts/systemd-startup-after-redis.sh @@ -45,12 +45,12 @@ if FLASHDRIVE="$(/opt/shift/scripts/bbb-cmd.sh flashdrive check)"; then echo "RESET: flashdrive mounted" # are all necessary files for a reset present? - if [[ -f /mnt/backup/.reset-token ]] && [[ -f /data/reset-token-hashes ]]; then - FLASHDRIVE_TOKEN_HASH="$(sha256sum /mnt/backup/.reset-token | cut -f 1 -d " ")" - echo "RESET: reset token present on flashdrive, hashed value: ${FLASHDRIVE_TOKEN_HASH}" + if [[ -f /mnt/backup/.maintenance-token ]] && [[ -f /data/maintenance-token-hashes ]]; then + MAINTENANCE_TOKEN_HASH="$(sha256sum /mnt/backup/.maintenance-token | cut -f 1 -d " ")" + echo "RESET: reset token present on flashdrive, hashed value: ${MAINTENANCE_TOKEN_HASH}" # is hashed reset token present on Base? - if grep -q "${FLASHDRIVE_TOKEN_HASH}" /data/reset-token-hashes; then + if grep -q "${MAINTENANCE_TOKEN_HASH}" /data/maintenance-token-hashes; then echo "RESET: valid reset token found" if [[ -f /mnt/backup/reset-base-auth ]]; then @@ -74,7 +74,7 @@ if FLASHDRIVE="$(/opt/shift/scripts/bbb-cmd.sh flashdrive check)"; then mv /mnt/backup/reset-base-image /mnt/backup/reset-base-image.done fi else - echo "RESET: reset token on flashdrive does not match authorized tokens on the Base" + echo "RESET: maintenance token on flashdrive does not match authorized tokens on the BitBoxBase" fi else echo "RESET: not all files for a reset present, doing nothing." From b0ba38bf6aad262997aa07b7e4785c952c37b06d Mon Sep 17 00:00:00 2001 From: Stadicus Date: Thu, 28 Nov 2019 07:45:09 +0100 Subject: [PATCH 2/4] add Maintenance menu Initial implementation of a "Maintenance Menu" that is displayed (without the need to log in) if an authorized maintenance token is found on the USB flashdrive. A maintenance token can be: * a Shift factory token, its hash added in the initial build and removed later (either from the maintenance menu, or on initial setup) * a user token that is written to the flashdrive as part of the Backup process The maintenancen menu is displayed over HDMI on tty2 and allows: * creating and applying Bitcoin block snapshots * finishing the factory setup by deleting any temporary user files * Factory reset... * Authentication: user can reset the password from the BitBoxApp * Configuration reset: factory defaults are restored (Redis database, new SSH and SSL keys...) * Disk image reset: user can flash official, signed BitBoxBase image from USB flashdrive This commit: * The backup to USB flashdrive also creates a maintenance token. * adds /opt/shift/scripts/systemd-startup-maintenance.sh that contains the menu structure and calls bbb-cmd.sh * extends 'bbb-cmd.sh' with * presync create/restore * reset auth/config * during build, the packages 'console-setup' (for command 'chvt') and 'dialog' are no longer removed * adds the systemd unit 'startup-maintenance.service' * adds dialog configuration as '.dialogrc' --- armbian/base/config/dialog/.dialogrc | 144 ++++++++++ .../config/templates/bashrc-custom.template | 4 +- armbian/base/customize-armbian-rockpro64.sh | 12 +- .../system/startup-maintenance.service | 14 + armbian/base/scripts/bbb-cmd.sh | 124 +++++++- .../base/scripts/create-presync-archive.sh | 59 ---- .../scripts/systemd-startup-maintenance.sh | 272 ++++++++++++++++++ 7 files changed, 558 insertions(+), 71 deletions(-) create mode 100644 armbian/base/config/dialog/.dialogrc create mode 100644 armbian/base/rootfs/etc/systemd/system/startup-maintenance.service delete mode 100644 armbian/base/scripts/create-presync-archive.sh create mode 100755 armbian/base/scripts/systemd-startup-maintenance.sh diff --git a/armbian/base/config/dialog/.dialogrc b/armbian/base/config/dialog/.dialogrc new file mode 100644 index 00000000..e96ee261 --- /dev/null +++ b/armbian/base/config/dialog/.dialogrc @@ -0,0 +1,144 @@ +# +# Run-time configuration file for dialog +# +# Automatically generated by "dialog --create-rc " +# +# +# Types of values: +# +# Number - +# String - "string" +# Boolean - +# Attribute - (foreground,background,highlight?) + +# Set aspect-ration. +aspect = 0 + +# Set separator (for multiple widgets output). +separate_widget = "" + +# Set tab-length (for textbox tab-conversion). +tab_len = 0 + +# Make tab-traversal for checklist, etc., include the list. +visit_items = OFF + +# Shadow dialog boxes? This also turns on color. +use_shadow = OFF + +# Turn color support ON or OFF +use_colors = ON + +# Screen color +screen_color = (BLUE,BLACK,ON) + +# Shadow color +shadow_color = (BLACK,BLACK,ON) + +# Dialog box color +dialog_color = (BLACK,WHITE,OFF) + +# Dialog box title color +title_color = (BLUE,WHITE,ON) + +# Dialog box border color +border_color = (WHITE,WHITE,ON) + +# Active button color +button_active_color = (WHITE,BLUE,ON) + +# Inactive button color +button_inactive_color = dialog_color + +# Active button key color +button_key_active_color = button_active_color + +# Inactive button key color +button_key_inactive_color = (RED,WHITE,OFF) + +# Active button label color +button_label_active_color = (YELLOW,BLUE,ON) + +# Inactive button label color +button_label_inactive_color = (BLACK,WHITE,ON) + +# Input box color +inputbox_color = dialog_color + +# Input box border color +inputbox_border_color = dialog_color + +# Search box color +searchbox_color = dialog_color + +# Search box title color +searchbox_title_color = title_color + +# Search box border color +searchbox_border_color = border_color + +# File position indicator color +position_indicator_color = title_color + +# Menu box color +menubox_color = dialog_color + +# Menu box border color +menubox_border_color = border_color + +# Item color +item_color = dialog_color + +# Selected item color +item_selected_color = button_active_color + +# Tag color +tag_color = title_color + +# Selected tag color +tag_selected_color = button_label_active_color + +# Tag key color +tag_key_color = button_key_inactive_color + +# Selected tag key color +tag_key_selected_color = (RED,BLUE,ON) + +# Check box color +check_color = dialog_color + +# Selected check box color +check_selected_color = button_active_color + +# Up arrow color +uarrow_color = (GREEN,WHITE,ON) + +# Down arrow color +darrow_color = uarrow_color + +# Item help-text color +itemhelp_color = (WHITE,BLACK,OFF) + +# Active form text color +form_active_text_color = button_active_color + +# Form text color +form_text_color = (WHITE,CYAN,ON) + +# Readonly form item color +form_item_readonly_color = (CYAN,WHITE,ON) + +# Dialog box gauge color +gauge_color = title_color + +# Dialog box border2 color +border2_color = dialog_color + +# Input box border2 color +inputbox_border2_color = dialog_color + +# Search box border2 color +searchbox_border2_color = dialog_color + +# Menu box border2 color +menubox_border2_color = dialog_color diff --git a/armbian/base/config/templates/bashrc-custom.template b/armbian/base/config/templates/bashrc-custom.template index b66743f6..cb8c0ec5 100644 --- a/armbian/base/config/templates/bashrc-custom.template +++ b/armbian/base/config/templates/bashrc-custom.template @@ -1,7 +1,7 @@ {{ #output: /home/base/.bashrc-custom }} export LS_OPTIONS='--color=auto' -alias l='ls $LS_OPTIONS -l' -alias ll='ls $LS_OPTIONS -la' +alias l='ls $LS_OPTIONS -lh' +alias ll='ls $LS_OPTIONS -lah' # Bitcoin alias bcli='bitcoin-cli -conf=/etc/bitcoin/bitcoin.conf -rpcuser=base -rpcpassword={{ bitcoind:rpcpassword }}' diff --git a/armbian/base/customize-armbian-rockpro64.sh b/armbian/base/customize-armbian-rockpro64.sh index ced9bea0..6a3ed697 100755 --- a/armbian/base/customize-armbian-rockpro64.sh +++ b/armbian/base/customize-armbian-rockpro64.sh @@ -291,11 +291,11 @@ apt-get -y --fix-broken install ## remove unnecessary packages (only when building image, not ondevice) if [[ "${BASE_BUILDMODE}" != "ondevice" ]] && [[ "${BASE_MINIMAL}" == "true" ]]; then pkgToRemove="git libllvmkk build-essential libtool autotools-dev automake pkg-config gcc gcc-6 libgcc-6-dev - alsa-utils* autoconf* bc* bison* bridge-utils* btrfs-tools* bwm-ng* cmake* command-not-found* console-setup* - console-setup-linux* crda* dconf-gsettings-backend* dconf-service* debconf-utils* device-tree-compiler* dialog* dirmngr* + alsa-utils* autoconf* bc* bison* bridge-utils* btrfs-tools* bwm-ng* cmake* command-not-found* + crda* dconf-gsettings-backend* dconf-service* debconf-utils* device-tree-compiler* dirmngr* dnsutils* dosfstools* ethtool* evtest* f2fs-tools* f3* fancontrol* figlet* fio* flex* fping* glib-networking* glib-networking-services* gnome-icon-theme* gnupg2* gsettings-desktop-schemas* gtk-update-icon-cache* haveged* hdparm* hostapd* html2text* ifenslave* iotop* - iperf3* iputils-arping* iw* kbd* libatk1.0-0* libcroco3* libcups2* libdbus-glib-1-2* libgdk-pixbuf2.0-0* libglade2-0* libnl-3-dev* + iperf3* iputils-arping* iw* libatk1.0-0* libcroco3* libcups2* libdbus-glib-1-2* libgdk-pixbuf2.0-0* libglade2-0* libnl-3-dev* libpango-1.0-0* libpolkit-agent-1-0* libpolkit-backend-1-0* libpolkit-gobject-1-0* libpython-stdlib* libpython2.7-stdlib* libssl-dev* man-db* ncurses-term* psmisc* pv* python-avahi* python-pip* python2.7-minimal screen* shared-mime-info* unattended-upgrades* unicode-data* unzip* vim* wireless-regdb* wireless-tools* wpasupplicant* " @@ -449,6 +449,8 @@ ln -sfn /mnt/ssd/system/journal /var/log/journal mkdir -p /etc/mender generateConfig mender.conf.template # --> /etc/mender/mender.conf + + ## configure swap file (disable Armbian zram, configure custom swapfile on ssd) if [[ -f /etc/default/armbian-zram-config ]] || [[ "${BASE_BUILDMODE}" != "ondevice" ]]; then sed -i '/ENABLED=/Ic\ENABLED=false' /etc/default/armbian-zram-config @@ -467,6 +469,10 @@ systemctl enable startup-after-redis.service importFile /etc/systemd/system/update-checks.service systemctl enable update-checks.service +## maintenance menu +importFile /etc/systemd/system/startup-maintenance.service +systemctl enable startup-maintenance.service + ## disable ssh login messages echo "MOTD_DISABLE='header tips updates armbian-config'" >> /etc/default/armbian-motd diff --git a/armbian/base/rootfs/etc/systemd/system/startup-maintenance.service b/armbian/base/rootfs/etc/systemd/system/startup-maintenance.service new file mode 100644 index 00000000..f7df8686 --- /dev/null +++ b/armbian/base/rootfs/etc/systemd/system/startup-maintenance.service @@ -0,0 +1,14 @@ +[Unit] +Description=BitBoxBase: Maintenance menu +After=getty@tty2.service + +[Service] +Type=oneshot +ExecStart=/opt/shift/scripts/systemd-startup-maintenance.sh +StandardInput=tty +TTYPath=/dev/tty2 +TTYReset=yes +TTYVHangup=yes + +[Install] +WantedBy=default.target diff --git a/armbian/base/scripts/bbb-cmd.sh b/armbian/base/scripts/bbb-cmd.sh index dc52e144..540db561 100755 --- a/armbian/base/scripts/bbb-cmd.sh +++ b/armbian/base/scripts/bbb-cmd.sh @@ -18,6 +18,7 @@ possible commands: restore reset mender-update + presync " } @@ -79,6 +80,7 @@ fi MODULE="${1:-}" COMMAND="${2:-}" ARG="${3:-}" +ARG2="${4:-}" MODULE="$(tr '[:lower:]' '[:upper:]' <<< "${MODULE}")" COMMAND="$(tr '[:lower:]' '[:upper:]' <<< "${COMMAND}")" @@ -110,14 +112,14 @@ case "${MODULE}" in ln -sfn /mnt/ssd/data / echo "OK: (DATADIR) symlink /data --> /mnt/ssd/data created in OVERLAYROOTFS" - if [ ! -f /data/.datadir_set_up ]; then - cp -r /data_source/* /data - echo "OK: (DATADIR) /data_source/ copied to /data/" - fi - else - ln -sfn /data_source /data - echo "OK: (DATADIR) symlink /data/ --> /data_source/ created" + mkdir -p /data + echo "OK: (DATADIR) directory /data/ created" + fi + + if [ ! -f /data/.datadir_set_up ]; then + cp -r /data_source/* /data + echo "OK: (DATADIR) /data_source/ copied to /data/" fi fi else @@ -506,12 +508,120 @@ case "${MODULE}" in echo "OK: middleware authentication reset, setup wizard can be run again." ;; + CONFIG) + # stop services + if redis-cli save; then + systemctl stop redis.service + fi + + # delete old data + rm -rf /data/bbbmiddleware + rm -rf /data/redis + rm -rf /data/ssh + rm -rf /data/ssl + rm /data/.datadir_set_up + + # re-initialize data dir + /opt/shift/scripts/bbb-cmd.sh setup datadir + + # start services + systemctl start redis.service + + # recreate directories and certificates (like on first boot) + /opt/shift/scripts/systemd-startup-checks.sh + ;; + *) echo "Invalid argument for module ${MODULE}: command ${COMMAND} unknown." errorExit CMD_SCRIPT_INVALID_ARG esac ;; + PRESYNC) + # check and mount external drive + if ! lsblk -o NAME,TYPE -abrnp -e 1,7,31,179,252 | grep part | grep -q "${ARG} " || [ -z "${ARG}" ]; then + echo "ERR: external drive partition not found (specify e.g. '/dev/sda1')" + errorExit PRESYNC_EXTERNAL_DRIVE_NOT_FOUND + fi + + mkdir -p /mnt/ext + if mountpoint /mnt/ext -q; then + umount /mnt/ext + fi + mount "${ARG}" /mnt/ext + + # stop bitcoin services + systemctl stop bitcoind + systemctl stop lightningd + systemctl stop electrs + + case "${COMMAND}" in + # create snapshot of blockchain data + # bbb-cmd.sh presync create /dev/sdb1 + CREATE) + checkMockMode + + if [ ! -d /mnt/ssd/bitcoin/.bitcoin ] || [ ! -d /mnt/ssd/electrs/db/mainnet ]; then + echo "ERR: required directories not found (run with --help for additional details)" >&2 + sleep 5 + errorExit PRESYNC_DIRECTORIES_NOT_FOUND + fi + + # freespace=$(df -k /mnt/ssd | awk '/[0-9]%/{print $(NF-2)}') + # if [[ ${freespace} -lt 400000000 ]]; then + # echo "ERR: not enough disk space, should at least have 400 GB" >&2 + # errorExit PRESYNC_NOT_ENOUGH_DISKSPACE + # fi + + tar cvfW /mnt/ext/bbb-presync-ssd-"$(date '+%Y%m%d-%H%M')".tar \ + -C /mnt/ssd/ \ + bitcoin/.bitcoin/chainstate \ + bitcoin/.bitcoin/blocks \ + bitcoin/.bitcoin/chainstate \ + --exclude='IDENTITY' \ + --exclude='LOG*' \ + --exclude='*.log' \ + electrs/db/mainnet + + echo + echo "OK: Presync archive created." + ls -lh /mnt/ssd/bbb-presync* + ;; + + # restore snapshot of blockchain data + # bbb-cmd.sh presync restore /dev/sdb1 /mnt/ext/bbb-presync-ssd-20191126-2248.tar + RESTORE) + checkMockMode + + echo "${ARG2}" + ls -la "${ARG2}" + + if [[ ! -f ${ARG2} ]]; then + echo "ERR: file ${ARG2} not found" + errorExit PRESYNC_RESTORE_ARCHIVE_NOT_FOUND + fi + + # TODO(Stadicus) + # rm -rf /mnt/ssd/bitcoin/.bitcoin/blocks/* + # rm -rf /mnt/ssd/bitcoin/.bitcoin/chainstate/* + # rm -rf /mnt/ssd/electrs/db/mainnet + + tar xvf "${ARG2}" -C /mnt/ssd + + echo + echo "OK: Presync archive restored." + ;; + + *) + echo "Invalid argument for module ${MODULE}: command ${COMMAND} unknown." + errorExit CMD_SCRIPT_INVALID_ARG + + umount /mnt/ext + + esac + ;; + + *) echo "Invalid argument: module ${MODULE} unknown." errorExit CMD_SCRIPT_INVALID_ARG diff --git a/armbian/base/scripts/create-presync-archive.sh b/armbian/base/scripts/create-presync-archive.sh deleted file mode 100644 index dcdd4c08..00000000 --- a/armbian/base/scripts/create-presync-archive.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/bash -# -# create BitBoxBase presync archive -# -set -eu - -# print usage information for script -usage() { - echo "BitBoxBase: create presync archive -usage: create-presync-archive.sh [--help] - -This script needs to be run on the BitBoxBase and creates an uncompressed archive -with Bitcoin Core blocks/chainstate and Electrs database for presynced devices. - -Requirements: -- Bitcoin Core datadir at /mnt/ssd/bitcoin/.bitcoin -- Electrs database at /mnt/ssd/electrs/db/mainnet - -The presync archive will be created as /mnt/ssd/bbb-presync-ssd-YYYYMMDD-hhmm.tar - -" -} - -if [[ ${#} -ne 0 ]]; then - usage - exit 0 -fi - -if [ ! -d /mnt/ssd/bitcoin/.bitcoin ] || [ ! -d /mnt/ssd/electrs/db/mainnet ]; then - echo "ERR: required directories not found (run with --help for additional details)" >&2 - exit 1 -fi - -if [[ ${UID} -ne 0 ]]; then - echo "ERR: needs to be run as superuser." >&2 - exit 1 -fi - -freespace=$(df -k /mnt/ssd | awk '/[0-9]%/{print $(NF-2)}') -if [[ ${freespace} -lt 400000000 ]]; then - echo "ERR: not enough disk space, should at least have 400 GB" >&2 - exit 1 -fi - -cd /mnt/ssd || exit - -tar cvfW bbb-presync-ssd-"$(date '+%Y%m%d-%H%M')".tar \ - bitcoin/.bitcoin/blocks \ - bitcoin/.bitcoin/chainstate \ - --exclude='IDENTITY' \ - --exclude='LOG*' \ - --exclude='*.log' \ - electrs/db/mainnet - -echo -echo "Archive created:" -echo -ls -lh /mnt/ssd/bbb-presync* -echo diff --git a/armbian/base/scripts/systemd-startup-maintenance.sh b/armbian/base/scripts/systemd-startup-maintenance.sh new file mode 100755 index 00000000..796d45b2 --- /dev/null +++ b/armbian/base/scripts/systemd-startup-maintenance.sh @@ -0,0 +1,272 @@ +#!/bin/bash + +abort() { + sleep 5 + clear + exit 0 +} +trap 'abort' SIGHUP SIGINT SIGTERM + +if [[ ${UID} -ne 0 ]]; then + echo "ERR: script needs to be run as superuser." + exit 1 +fi + +DIALOGRC='/opt/shift/config/dialog/.dialogrc' +export DIALOGRC + +backtitle="BitBoxBase: Maintenance" +box_h=18 +box_w=60 + +popup_h=14 +popup_w=40 + +# +# check credentials +# ----------------------------------------------------------------------------- + +# exit if no flashdrive found +if ! /opt/shift/scripts/bbb-cmd.sh flashdrive mount &>/dev/null; then + echo "MAINTENANCE: no flashdrive found" + exit +fi +echo "MAINTENANCE: flashdrive mounted to check credentials" + +# exit if no token found +if [[ ! -f /mnt/backup/.maintenance-token ]] || [[ ! -f /data/maintenance-token-hashes ]]; then + echo "MAINTENANCE: flashdrive does not contain a valid maintenance token" + exit +fi +private_token_hash="$(sha256sum /mnt/backup/.maintenance-token | cut -f 1 -d " ")" +echo "MAINTENANCE: found maintenance token (hash (${private_token_hash})" + +# exit if token hash not authorized +if ! grep -q "${private_token_hash}" /data/maintenance-token-hashes; then + echo "MAINTENANCE: maintenance token not authorized" + exit +fi + +echo "MAINTENANCE: valid token found, starting maintenance menu on tty2" + +sleep 5 +chvt 2 + +# +# Submenu: SSD presync +# ----------------------------------------------------------------------------- +submenu_presync() { + while true; do + if ! menuitem=$(dialog --title "SSD presync" --backtitle "${backtitle}" --menu "\nPlease choose maintenance task" ${box_h} ${box_w} 10 \ + 1 "CREATE snapshot on external storage" \ + 2 "IMPORT snapshot to internal ssd" \ + 3>&1 1>&2 2>&3) + then break + fi + + count=0 + unset options + + case $menuitem in + 1) # create snapshot + while read -r partition; do + count=$((count + 1)); + onoff="off" + if [[ count -eq 1 ]]; then + onoff="on" + fi + options[$count]="${partition} ${onoff}" + done <<< "$(lsblk -o NAME,SIZE,TYPE -arnp -e 1,7,31,179,252 | grep part | cut -f 1,2 -d " ")" + + options=(${options[@]}) + cmd=(dialog --title "Create snapshot" --backtitle "${backtitle}" --radiolist "Select target drive:" 22 76 16) + target_drive=$("${cmd[@]}" "${options[@]}" 3>&1 1>&2 2>&3) + + if ! bbb-cmd.sh presync create "${target_drive}"; then + echo + read -rp "An error occurred. Press [Enter] to continue..." + else + dialog --title "Snapshot created" --msgbox "\nPresync snapshot created." ${popup_h} ${popup_w} + fi + ;; + + 2) # import snapshot + # select source drive + while read -r partition; do + count=$((count + 1)); + onoff="off" + if [[ count -eq 1 ]]; then + onoff="on" + fi + options[$count]="${partition} ${onoff}" + done <<< "$(lsblk -o NAME,SIZE,TYPE -arnp -e 1,7,31,179,252 | grep part | cut -f 1,2 -d " ")" + + options=(${options[@]}) + cmd=(dialog --title "Restore snapshot" --backtitle "${backtitle}" --radiolist "Select source drive:" 22 76 16) + source_drive=$("${cmd[@]}" "${options[@]}" 3>&1 1>&2 2>&3) + + # select file + count=0 + unset options + + while read -r partition; do + count=$((count + 1)); + onoff="off" + if [[ count -eq 1 ]]; then + onoff="on" + fi + options[$count]="${partition} ${onoff}" + done <<< "$(stat -c "%n %s" /mnt/ext/bbb-presync*)" + + options=(${options[@]}) + cmd=(dialog --title "Restore snapshot" --backtitle "${backtitle}" --radiolist "Select target drive:" 22 76 16) + source_file=$("${cmd[@]}" "${options[@]}" 3>&1 1>&2 2>&3) + + if ! bbb-cmd.sh presync restore "${source_drive}" "${source_file}"; then + echo + read -rp "An error occurred. Press [Enter] to continue..." + else + dialog --title "Snapshot restored" --msgbox "\nPresync snapshot restored." ${popup_h} ${popup_w} + fi + ;; + *) + break + esac + done +} + +# +# Submenu: Factory reset +# ----------------------------------------------------------------------------- +submenu_reset() { + while true; do + if ! menuitem=$(dialog --title "Factory reset" --backtitle "${backtitle}" --menu "\nPlease choose maintenance task" ${box_h} ${box_w} 10 \ + 1 "AUTHENTICATION reset" \ + 2 "CONFIGURATION reset..." \ + 3 "DISK IMAGE reset..." \ + 3>&1 1>&2 2>&3) + then break + fi + + case $menuitem in + 1) action_confirmation="The AUTHENTICATION credentials will be reset.";; + 2) action_confirmation="The CONFIGURATION will be reset to factory defaults.";; + 3) action_confirmation="The DISK IMAGE on the USB flashdrive will be written to your BitBoxBase.\n\nIt needs to be signed and named 'update.base'.";; + 4) action_confirmation="The SSD including all data (Bitcoin, Lightning, Electrum) will be wiped permanently.";; + esac + + if dialog --title "${backtitle}" --yesno "\n${action_confirmation}\n\nContinue?" 12 50; then + case $menuitem in + 1) # auth + if bbb-cmd.sh reset auth --assume-yes; then + dialog --title "Factory reset" --msgbox "\nOK: Authentication reset.\n\nUse BitBoxApp to set management password again." ${popup_h} ${popup_w} + else + read -rp "An error occurred. Press [Enter] to continue..." + fi + ;; + + 2) # config + if bbb-cmd.sh reset config --assume-yes; then + dialog --title "Configuration reset" --msgbox "\nOK: Configuration reset to factory defaults." ${popup_h} ${popup_w} + while ! bbb-cmd.sh flashdrive check; do + dialog --title "Configuration reset" --msgbox "\nInsert USB flashdrive to store new maintenance token." ${popup_h} ${popup_w} + done + + if bbb-cmd.sh flashdrive mount && bbb-cmd.sh backup sysconfig; then + dialog --title "Configuration reset" --msgbox "\nOK: New maintenance token created on USB flashdrive." ${popup_h} ${popup_w} + else + read -rp "An error occurred. Press [Enter] to continue..." + fi + bbb-cmd.sh flashdrive unmount || true + + else + read -rp "An error occurred. Press [Enter] to continue..." + fi + ;; + + 3) # disk image + if bbb-cmd.sh flashdrive mount && bbb-cmd.sh mender-update install flashdrive; then + dialog --title "Disk Image reset" --msgbox "\nOK: Updated from USB disk image, please reboot." ${popup_h} ${popup_w} + else + read -rp "An error occurred. Press [Enter] to continue..." + fi + bbb-cmd.sh flashdrive unmount || true + ;; + esac + fi + + done +} + +# +# Main menu +# ----------------------------------------------------------------------------- +while true; do + +# Main menu +if ! menuitem=$(dialog --title "Main menu" --backtitle "${backtitle}" --menu "\nPlease choose maintenance task" ${box_h} ${box_w} 10 \ + 1 "SSD presync data..." \ + 2 "Finish factory setup" \ + 3 "Factory reset..." \ + 4 "Shutdown" \ + 3>&1 1>&2 2>&3) + then abort +fi + +case $menuitem in + 1) # presync + submenu_presync + ;; + + 2) # finish factory setup + if dialog --title "${backtitle}" --yesno "\nThis will delete all user data on the SSD, preparing the device for shipping.\n\nIt also deletes the factory setup credentials.\n\nContinue?" 12 50; then + + bbb-systemctl.sh stop + + # first, cleanup ssd data + rm -f /mnt/ssd/bitcoin/.bitcoin/*.log + rm -f /mnt/ssd/bitcoin/.bitcoin/*.dat + rm -f /mnt/ssd/bitcoin/.bitcoin/.cookie* + rm -f /mnt/ssd/bitcoin/.bitcoin/.lock + rm -rf /mnt/ssd/bitcoin/.bitcoin/onion_private_key + rm -rf /mnt/ssd/bitcoin/.bitcoin/testnet3 + rm -rf /mnt/ssd/bitcoin/.lightning* + rm -f /mnt/ssd/electrs/db/mainnet/LOG* + rm -f /mnt/ssd/electrs/db/mainnet/LOCK + rm -rf /mnt/ssd/lost+found + rm -rf /mnt/ssd/system + rm -rf /mnt/ssd/prometheus + + swapoff -a + rm -f /mnt/ssd/swapfile + + systemctl start redis + systemctl start bbbmiddleware + + # remove credentials + if sed -i '/factory token/d' /data/maintenance-token-hashes; then + dialog --title "OK" --msgbox "\nFactory setup credentials deleted." ${popup_h} ${popup_w} + else + dialog --title "ERR" --msgbox "\nError: could not delete factory setup credentials." ${popup_h} ${popup_w} + fi + fi + ;; + + 3) # factory reset + submenu_reset + ;; + + 4) # shutdown + if dialog --title "${backtitle}" --yesno "\n Shut down BitBoxBase?" 8 40; then + shutdown now + fi + ;; +esac + +done + +# ----------------------------------------------------------------------------- + +exit 0 + +clear \ No newline at end of file From 77dbe4a38e3980f78436518870e6083000ec8239 Mon Sep 17 00:00:00 2001 From: Stadicus Date: Thu, 28 Nov 2019 07:54:41 +0100 Subject: [PATCH 3/4] give NVMe drive priority over USB Because: * If a unformatted NVMe is present, but a formatted USB drive is connected on first boot, e.g. for factory maintenance, the USB drive is added to /etc/fstab * It is necessary to give the NVMe ssd always precedence over attached USB drives. This commit: * checks if a NVMe drive is present and, if true, no longer checks for any USB drives, formatted or not. --- armbian/base/scripts/systemd-startup-checks.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/armbian/base/scripts/systemd-startup-checks.sh b/armbian/base/scripts/systemd-startup-checks.sh index 95b67e67..b19ee189 100755 --- a/armbian/base/scripts/systemd-startup-checks.sh +++ b/armbian/base/scripts/systemd-startup-checks.sh @@ -28,12 +28,15 @@ if ! grep -q '/mnt/ssd ' /etc/fstab ; then if lsblk | grep -q 'nvme0n1p1' && [[ $(lsblk -o NAME,SIZE -abrnp | grep nvme0n1p1 | cut -f 2 -d " ") -gt 400000000000 ]]; then exec_overlayroot all-layers "echo '/dev/nvme0n1p1 /mnt/ssd ext4 rw,nosuid,dev,noexec,noatime,nodiratime,auto,nouser,async,nofail 0 2' >> /etc/fstab" - elif lsblk | grep -q 'sda1' && [[ $(lsblk -o NAME,SIZE -abrnp | grep sda1 | cut -f 2 -d " ") -gt 400000000000 ]]; then + # ignore USB drives if NVMe SSD present + elif ! lsblk | grep -q 'nvme0n1' && lsblk | grep -q 'sda1' && [[ $(lsblk -o NAME,SIZE -abrnp | grep sda1 | cut -f 2 -d " ") -gt 400000000000 ]]; then exec_overlayroot all-layers "echo '/dev/sda1 /mnt/ssd ext4 rw,nosuid,dev,noexec,noatime,nodiratime,auto,nouser,async,nofail 0 2' >> /etc/fstab" + elif ! lsblk | grep -q 'nvme0n1' && lsblk | grep -q 'sdb1' && [[ $(lsblk -o NAME,SIZE -abrnp | grep sdb1 | cut -f 2 -d " ") -gt 400000000000 ]]; then + exec_overlayroot all-layers "echo '/dev/sdb1 /mnt/ssd ext4 rw,nosuid,dev,noexec,noatime,nodiratime,auto,nouser,async,nofail 0 2' >> /etc/fstab" + else ## if no valid partition present, is image configured for autosetup of SSD? - if ! mountpoint /mnt/ssd -q && [ -f /opt/shift/config/.autosetup-ssd ]; then # run ssd autosetup, and disable it afterwards on success if /opt/shift/scripts/autosetup-ssd.sh format auto --assume-yes From 62bbe1f5c2aaf9d63772e0ce247f4c43ef1da43b Mon Sep 17 00:00:00 2001 From: Stadicus Date: Tue, 17 Dec 2019 15:36:26 +0100 Subject: [PATCH 4/4] build: add MAINTENANCEMENU option The new maintenance menu needs to be tested in real builds but should not yet be enabled in production builds. By adding this feature, but disabled by default, it can be go into a more extensive security review. This commit: * adds the option BASE_MAINTENANCEMENU, default: 'false' * `startup-maintenance.service` is only enabled if set to true --- armbian/base/build.conf | 4 ++++ armbian/base/customize-armbian-rockpro64.sh | 7 ++++++- armbian/base/scripts/bbb-cmd.sh | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/armbian/base/build.conf b/armbian/base/build.conf index edbe22e4..a8536ddb 100644 --- a/armbian/base/build.conf +++ b/armbian/base/build.conf @@ -60,3 +60,7 @@ # Make root filesystem read-only and overlay it with a temporary filesystem. # All changes are lost on reboot, guaranteeing a safe state. #BASE_OVERLAYROOT="true" + +# Start Maintenance Menu if authorized maintenance token +# is found on USB flasdrive on boot +#BASE_MAINTENANCEMENU="false" diff --git a/armbian/base/customize-armbian-rockpro64.sh b/armbian/base/customize-armbian-rockpro64.sh index 6a3ed697..fb8d4144 100755 --- a/armbian/base/customize-armbian-rockpro64.sh +++ b/armbian/base/customize-armbian-rockpro64.sh @@ -81,6 +81,7 @@ CONFIGURATION: AUTOSETUP SSD: ${BASE_AUTOSETUP_SSD} BITCOIN SERVICES ENABLED: ${BASE_ENABLE_BITCOIN_SERVICES} + MAINTENANCE MENU: ${BASE_MAINTENANCEMENU} ================================================================================ BUILD OPTIONS: @@ -179,6 +180,7 @@ BASE_DASHBOARD_HDMI_ENABLED="false" BASE_HDMI_BUILD="false" BASE_MINIMAL="true" BASE_OVERLAYROOT="true" +BASE_MAINTENANCEMENU="false" # TODO(Stadicus): set "true" by default after further review # Overwrite defaults if BASE_PRODUCTION_IMAGE set to "false" if [[ ${BASE_PRODUCTION_IMAGE} == "false" ]]; then @@ -471,7 +473,10 @@ systemctl enable update-checks.service ## maintenance menu importFile /etc/systemd/system/startup-maintenance.service -systemctl enable startup-maintenance.service + +if [[ "${BASE_MAINTENANCEMENU}" == "true" ]]; then + systemctl enable startup-maintenance.service +fi ## disable ssh login messages echo "MOTD_DISABLE='header tips updates armbian-config'" >> /etc/default/armbian-motd diff --git a/armbian/base/scripts/bbb-cmd.sh b/armbian/base/scripts/bbb-cmd.sh index 540db561..694c69e9 100755 --- a/armbian/base/scripts/bbb-cmd.sh +++ b/armbian/base/scripts/bbb-cmd.sh @@ -16,7 +16,7 @@ possible commands: flashdrive backup restore - reset + reset mender-update presync