From e9c321001bb517ba57aa6269a9fa07e8276e6dff Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Wed, 31 Dec 2025 09:31:56 +0100 Subject: [PATCH 1/3] easyinstall: refactor script to be invoked as root Rather than running sudo inline for every command, require that the script is invoked as a root user This allows the script to run cleanly in chroot, while eliminating the need for sudo caching logic --- docker/backend/Dockerfile | 2 +- docker/web/Dockerfile | 6 +- easyinstall.sh | 316 +++++++++++++++--------------- shell_scripts/netatalk_install.sh | 70 ++++--- 4 files changed, 205 insertions(+), 189 deletions(-) diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile index dd72413f9a..725f01a482 100644 --- a/docker/backend/Dockerfile +++ b/docker/backend/Dockerfile @@ -13,7 +13,7 @@ COPY --chown=pi:pi easyinstall.sh . COPY --chown=pi:pi cpp cpp COPY --chown=pi:pi doc doc COPY --chown=pi:pi proto proto -RUN ./easyinstall.sh --run_choice=15 --cores=`nproc` +RUN sudo ./easyinstall.sh --run_choice=15 --cores=`nproc` FROM debian:bullseye-slim AS runner USER root diff --git a/docker/web/Dockerfile b/docker/web/Dockerfile index dcd151697e..336a92caeb 100644 --- a/docker/web/Dockerfile +++ b/docker/web/Dockerfile @@ -31,17 +31,17 @@ COPY --chown=pi:pi python/web python/web COPY --chown=pi:pi python/common python/common # Install standalone PiSCSI Web UI -RUN ./easyinstall.sh --run_choice=12 \ +RUN sudo ./easyinstall.sh --run_choice=12 \ && sudo apt-get remove build-essential --yes \ && sudo apt autoremove -y \ && sudo apt-get clean \ && sudo rm -rf /var/lib/apt/lists/* # Enable web UI authentication -RUN ./easyinstall.sh --run_choice=14 +RUN sudo ./easyinstall.sh --run_choice=14 # Setup wired network bridge -RUN ./easyinstall.sh --run_choice=5 --headless +RUN sudo ./easyinstall.sh --run_choice=5 --headless USER root WORKDIR /home/pi diff --git a/easyinstall.sh b/easyinstall.sh index 1a6851e754..2af02be06a 100755 --- a/easyinstall.sh +++ b/easyinstall.sh @@ -57,8 +57,16 @@ elif [ $CORES -lt 1 ]; then fi USER=$(whoami) BASE=$(dirname "$(readlink -f "${0}")") -VIRTUAL_DRIVER_PATH="$HOME/images" -CFG_PATH="$HOME/.config/piscsi" + +# Detect actual user home directory when running as root via sudo +if [ -n "$SUDO_USER" ]; then + ACTUAL_USER_HOME=$(eval echo "~$SUDO_USER") +else + ACTUAL_USER_HOME="$HOME" +fi + +VIRTUAL_DRIVER_PATH="$ACTUAL_USER_HOME/images" +CFG_PATH="$ACTUAL_USER_HOME/.config/piscsi" WEB_INSTALL_PATH="$BASE/python/web" OLED_INSTALL_PATH="$BASE/python/oled" CTRLBOARD_INSTALL_PATH="$BASE/python/ctrlboard" @@ -69,8 +77,8 @@ SSL_KEYS_PATH="/etc/ssl/private" HFDISK_BIN=/usr/bin/hfdisk TOKEN="" AUTH_GROUP="piscsi" -SECRET_FILE="$HOME/.config/piscsi/secret" -FILE_SHARE_PATH="$HOME/shared_files" +SECRET_FILE="$ACTUAL_USER_HOME/.config/piscsi/secret" +FILE_SHARE_PATH="$ACTUAL_USER_HOME/shared_files" FILE_SHARE_NAME="Pi File Server" APT_PACKAGES_COMMON="bridge-utils build-essential ca-certificates git protobuf-compiler rsyslog" @@ -84,40 +92,41 @@ set -e # checks to run before entering the script main menu function initialChecks() { - if [ "root" == "$USER" ]; then - echo "Do not run this script as $USER or with 'sudo'." + if [ "$EUID" -ne 0 ]; then + echo "This script must be run with root privileges." + echo "Please run: sudo $0 $*" exit 1 fi -} -# Only to be used for pi-gen automated install -function sudoCache() { - echo "Caching sudo password" - echo raspberry | sudo -v -S + # Detect the actual user who invoked sudo + if [ -n "$SUDO_USER" ]; then + ACTUAL_USER="$SUDO_USER" + else + # If not invoked via sudo, we're directly root - use a fallback + ACTUAL_USER="$USER" + fi } -# checks that the current user has sudoers privileges -function sudoCheck() { - if [[ $HEADLESS ]]; then - echo "Skipping password check in headless mode" - return 0 +# Ensures a directory is owned by the actual user, not root +function ensureUserOwnership() { + local dir_path="$1" + if [ -d "$dir_path" ] || [ -f "$dir_path" ]; then + chown -R "$ACTUAL_USER:$ACTUAL_USER" "$dir_path" fi - echo "Input your password to allow this script to make the above changes." - sudo -v } # Delete file if it exists function deleteFile() { - if sudo test -f "$1/$2"; then - sudo rm "$1/$2" || exit 1 + if test -f "$1/$2"; then + rm "$1/$2" || exit 1 echo "Deleted file $1/$2" fi } # Delete dir if it exists function deleteDir() { - if sudo test -d "$1"; then - sudo rm -rf "$1" || exit 1 + if test -d "$1"; then + rm -rf "$1" || exit 1 echo "Deleted directory $1" fi } @@ -128,7 +137,7 @@ function updateAptSources() { echo "Skipping package update" return 0 fi - sudo apt-get update + apt-get update } # install Debian packages for PiSCSI backend @@ -137,7 +146,7 @@ function installPackagesBackend() { echo "Skipping package installation" return 0 fi - sudo DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends --assume-yes -qq \ + DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends --assume-yes -qq \ $APT_PACKAGES_COMMON \ $APT_PACKAGES_BACKEND } @@ -148,13 +157,13 @@ function installPackagesWeb() { echo "Skipping package installation" return 0 fi - sudo DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends --assume-yes -qq \ + DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends --assume-yes -qq \ $APT_PACKAGES_COMMON \ $APT_PACKAGES_PYTHON \ $APT_PACKAGES_WEB if ! command -v hformat >/dev/null 2>&1 ]; then - if ! sudo apt-get install --no-install-recommends --assume-yes -qq hfsutils; then + if ! apt-get install --no-install-recommends --assume-yes -qq hfsutils; then echo "hfsutils package not found in apt repositories; compiling from source..." installHfsutils fi @@ -177,16 +186,16 @@ function compilePiscsi() { # install the PiSCSI binaries and modify the service configuration function installPiscsi() { - sudo make install CONNECT_TYPE="$CONNECT_TYPE" "$SECRET_FILE" # Make the secret file owned and only readable by root - sudo chown root:root "$SECRET_FILE" - sudo chmod 600 "$SECRET_FILE" + chown root:root "$SECRET_FILE" + chmod 600 "$SECRET_FILE" - sudo sed -i "s@^ExecStart.*@& -P $SECRET_FILE@" "$SYSTEMD_PATH/piscsi.service" + sed -i "s@^ExecStart.*@& -P $SECRET_FILE@" "$SYSTEMD_PATH/piscsi.service" echo "" echo "Configured PiSCSI to use $SECRET_FILE for authentication. This file is readable by root only." @@ -293,10 +304,10 @@ function configureTokenAuth() { # Enables and starts the piscsi service function enablePiscsiService() { - sudo systemctl daemon-reload - sudo systemctl restart rsyslog - sudo systemctl enable piscsi # start piscsi at boot - sudo systemctl start piscsi + systemctl daemon-reload + systemctl restart rsyslog + systemctl enable piscsi # start piscsi at boot + systemctl start piscsi } # Modifies and installs the piscsi-web service @@ -308,30 +319,30 @@ function installWebInterfaceService() { fi echo "Installing the piscsi-web.service configuration..." - sudo cp -f "$WEB_INSTALL_PATH/service-infra/piscsi-web.service" "$SYSTEMD_PATH/piscsi-web.service" - sudo sed -i /^ExecStart=/d "$SYSTEMD_PATH/piscsi-web.service" + cp -f "$WEB_INSTALL_PATH/service-infra/piscsi-web.service" "$SYSTEMD_PATH/piscsi-web.service" + sed -i /^ExecStart=/d "$SYSTEMD_PATH/piscsi-web.service" if [ ! -z "$TOKEN" ]; then - sudo sed -i "8 i ExecStart=$WEB_INSTALL_PATH/start.sh --password=$TOKEN" "$SYSTEMD_PATH/piscsi-web.service" + sed -i "8 i ExecStart=$WEB_INSTALL_PATH/start.sh --password=$TOKEN" "$SYSTEMD_PATH/piscsi-web.service" # Make the service file readable by root only, to protect the token string - sudo chmod 600 "$SYSTEMD_PATH/piscsi-web.service" + chmod 600 "$SYSTEMD_PATH/piscsi-web.service" echo "Granted access to the Web Interface with the token password that you configured for PiSCSI." else - sudo sed -i "8 i ExecStart=$WEB_INSTALL_PATH/start.sh" "$SYSTEMD_PATH/piscsi-web.service" + sed -i "8 i ExecStart=$WEB_INSTALL_PATH/start.sh" "$SYSTEMD_PATH/piscsi-web.service" fi - sudo systemctl daemon-reload - sudo systemctl enable piscsi-web - sudo systemctl start piscsi-web + systemctl daemon-reload + systemctl enable piscsi-web + systemctl start piscsi-web } # Stops a service if it is running function stopService() { if [[ -f "$SYSTEMD_PATH/$1.service" ]]; then SERVICE_RUNNING=0 - sudo systemctl is-active --quiet "$1.service" >/dev/null 2>&1 || SERVICE_RUNNING=$? + systemctl is-active --quiet "$1.service" >/dev/null 2>&1 || SERVICE_RUNNING=$? if [[ $SERVICE_RUNNING -eq 0 ]]; then - sudo systemctl stop "$1.service" + systemctl stop "$1.service" fi fi } @@ -340,9 +351,9 @@ function stopService() { function disableService() { if [ -f "$SYSTEMD_PATH/$1.service" ]; then SERVICE_ENABLED=0 - sudo systemctl is-enabled --quiet "$1.service" >/dev/null 2>&1 || SERVICE_ENABLED=$? + systemctl is-enabled --quiet "$1.service" >/dev/null 2>&1 || SERVICE_ENABLED=$? if [[ $SERVICE_ENABLED -eq 0 ]]; then - sudo systemctl disable "$1.service" + systemctl disable "$1.service" fi fi } @@ -351,7 +362,7 @@ function disableService() { function isPiscsiScreenInstalled() { SERVICE_PISCSI_OLED_ENABLED=0 if [[ -f "$SYSTEMD_PATH/piscsi-oled.service" ]]; then - sudo systemctl is-enabled --quiet piscsi-oled.service >/dev/null 2>&1 || SERVICE_PISCSI_OLED_ENABLED=$? + systemctl is-enabled --quiet piscsi-oled.service >/dev/null 2>&1 || SERVICE_PISCSI_OLED_ENABLED=$? else SERVICE_PISCSI_OLED_ENABLED=1 fi @@ -363,7 +374,7 @@ function isPiscsiScreenInstalled() { function isPiscsiCtrlBoardInstalled() { SERVICE_PISCSI_CTRLBOARD_ENABLED=0 if [[ -f "$SYSTEMD_PATH/piscsi-ctrlboard.service" ]]; then - sudo systemctl is-enabled --quiet piscsi-ctrlboard.service >/dev/null 2>&1 || SERVICE_PISCSI_CTRLBOARD_ENABLED=$? + systemctl is-enabled --quiet piscsi-ctrlboard.service >/dev/null 2>&1 || SERVICE_PISCSI_CTRLBOARD_ENABLED=$? else SERVICE_PISCSI_CTRLBOARD_ENABLED=1 fi @@ -375,7 +386,7 @@ function isPiscsiCtrlBoardInstalled() { function isPiscsiScreenRunning() { SERVICE_PISCSI_OLED_RUNNING=0 if [[ -f "$SYSTEMD_PATH/piscsi-oled.service" ]]; then - sudo systemctl is-active --quiet piscsi-oled.service >/dev/null 2>&1 || SERVICE_PISCSI_OLED_RUNNING=$? + systemctl is-active --quiet piscsi-oled.service >/dev/null 2>&1 || SERVICE_PISCSI_OLED_RUNNING=$? else SERVICE_PISCSI_OLED_RUNNING=1 fi @@ -387,7 +398,7 @@ function isPiscsiScreenRunning() { function isPiscsiCtrlBoardRunning() { SERVICE_PISCSI_CTRLBOARD_RUNNING=0 if [[ -f "$SYSTEMD_PATH/piscsi-ctrlboard.service" ]]; then - sudo systemctl is-active --quiet piscsi-ctrlboard.service >/dev/null 2>&1 || SERVICE_PISCSI_CTRLBOARD_RUNNING=$? + systemctl is-active --quiet piscsi-ctrlboard.service >/dev/null 2>&1 || SERVICE_PISCSI_CTRLBOARD_RUNNING=$? else SERVICE_PISCSI_CTRLBOARD_RUNNING=1 fi @@ -399,7 +410,7 @@ function isPiscsiCtrlBoardRunning() { # Starts the piscsi-oled service if installed function startPiscsiScreen() { if [[ $(isPiscsiScreenInstalled) -eq 0 ]] && [[ $(isPiscsiScreenRunning) -ne 1 ]]; then - sudo systemctl start piscsi-oled.service + systemctl start piscsi-oled.service showServiceStatus "piscsi-oled" fi } @@ -407,7 +418,7 @@ function startPiscsiScreen() { # Starts the piscsi-ctrlboard service if installed function startPiscsiCtrlBoard() { if [[ $(isPiscsiCtrlBoardInstalled) -eq 0 ]] && [[ $(isPiscsiCtrlBoardRunning) -ne 1 ]]; then - sudo systemctl start piscsi-ctrlboard.service + systemctl start piscsi-ctrlboard.service showServiceStatus "piscsi-ctrlboard" fi } @@ -415,7 +426,7 @@ function startPiscsiCtrlBoard() { # Starts the macproxy service if installed function startMacproxy() { if [ -f "$SYSTEMD_PATH/macproxy.service" ]; then - sudo systemctl start macproxy.service + systemctl start macproxy.service showServiceStatus "macproxy" fi } @@ -436,7 +447,7 @@ function installHfdisk() { cd "hfdisk-$HFDISK_VERSION" || exit 1 make - sudo cp hfdisk "$HFDISK_BIN" + cp hfdisk "$HFDISK_BIN" echo "Installed $HFDISK_BIN" fi @@ -444,7 +455,7 @@ function installHfdisk() { # Clone, compile and install 'hfsutils', HFS disk image tools function installHfsutils() { - sudo apt-get install --no-install-recommends --assume-yes -qq autoconf automake libtool m4 > /etc/dhcpcd.conf' + bash -c 'echo "denyinterfaces '$LAN_INTERFACE'" >> /etc/dhcpcd.conf' echo "Modified /etc/dhcpcd.conf" # default config file is made for eth0, this will set the right net interface - sudo bash -c 'sed s/eth0/'"$LAN_INTERFACE"'/g '"$BASE"'/os_integration/piscsi_bridge > /etc/network/interfaces.d/piscsi_bridge' + bash -c 'sed s/eth0/'"$LAN_INTERFACE"'/g '"$BASE"'/os_integration/piscsi_bridge > /etc/network/interfaces.d/piscsi_bridge' echo "Modified /etc/network/interfaces.d/piscsi_bridge" echo "Configuration completed!" @@ -541,7 +552,7 @@ function setupWiredNetworking() { echo "Rebooting..." sleep 3 - sudo reboot + reboot } # Modifies system configurations for a wireless network bridge with NAT @@ -593,7 +604,7 @@ function setupWirelessNetworking() { echo "Press enter to continue or CTRL-C to exit" read REPLY else - sudo bash -c 'echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf' + bash -c 'echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf' echo "Modified /etc/sysctl.conf" fi @@ -601,24 +612,24 @@ function setupWirelessNetworking() { if [ `apt-cache policy iptables | grep Installed | grep -c "(none)"` -eq 0 ]; then echo "iptables is already installed" else - sudo apt-get install iptables --assume-yes --no-install-recommends /dev/null || true wget -O vsftpd.wbm.tgz "https://github.com/rdmark/vsftpd-webmin/releases/download/$WEBMIN_VSFTPD_MODULE_VERSION/vsftpd-$WEBMIN_VSFTPD_MODULE_VERSION.wbm.gz" /dev/null + } | tee -a "$AFPCONF" > /dev/null echo "Added share section for $AFP_SHARE_NAME to afp.conf" else echo "Share section [$AFP_SHARE_NAME] already exists; not updating afp.conf" @@ -157,7 +171,7 @@ function installNetatalk() { echo "[$ADDITIONAL_SHARE_NAME]" echo "path = $ADDITIONAL_SHARE_PATH" echo "volume name = $ADDITIONAL_SHARE_NAME" - } | sudo tee -a "$AFPCONF" > /dev/null + } | tee -a "$AFPCONF" > /dev/null echo "Added share section for $ADDITIONAL_SHARE_NAME to afp.conf" else echo "Share section [$ADDITIONAL_SHARE_NAME] already exists; not updating afp.conf" @@ -166,26 +180,26 @@ function installNetatalk() { if [[ "$APPLETALK_INTERFACE" ]]; then echo "$NETATALK_CONFDIR/atalkd.conf:" - echo "$APPLETALK_INTERFACE" | sudo tee -a "$NETATALK_CONFDIR/atalkd.conf" + echo "$APPLETALK_INTERFACE" | tee -a "$NETATALK_CONFDIR/atalkd.conf" fi echo "$NETATALK_CONFDIR/papd.conf:" - echo "cupsautoadd:op=root:" | sudo tee -a "$NETATALK_CONFDIR/papd.conf" - sudo usermod -a -G lpadmin $USER - sudo cupsctl --remote-admin WebInterface=yes - if [[ `sudo grep -c "PreserveJobHistory" /etc/cups/cupsd.conf` -eq 0 ]]; then + echo "cupsautoadd:op=root:" | tee -a "$NETATALK_CONFDIR/papd.conf" + usermod -a -G lpadmin $ACTUAL_USER + cupsctl --remote-admin WebInterface=yes + if [[ `grep -c "PreserveJobHistory" /etc/cups/cupsd.conf` -eq 0 ]]; then echo "/etc/cups/cupsd.conf:" - sudo sed -i "/MaxLogSize/a PreserveJobHistory\ No" /etc/cups/cupsd.conf + sed -i "/MaxLogSize/a PreserveJobHistory\ No" /etc/cups/cupsd.conf fi if [ $START_SERVICES ]; then echo echo "Starting systemd services... (this may take a while)" - sudo systemctl start netatalk atalkd papd timelord a2boot cups + systemctl start netatalk atalkd papd timelord a2boot cups echo echo "Netatalk daemons are now installed and running, and should be discoverable by your Macs." - echo "To authenticate with the file server, use the current username ("$USER") and password." + echo "To authenticate with the file server, use the current username ("$ACTUAL_USER") and password." echo echo "To learn more about Netatalk and its capabilities, visit https://netatalk.io" echo "Enjoy AFP file sharing!" From ab4b6312011d3e8432bd85cf9effb52f9f1eb1f6 Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Wed, 31 Dec 2025 14:50:11 +0100 Subject: [PATCH 2/3] testing --- easyinstall.sh | 1 - shell_scripts/netatalk_install.sh | 1 - 2 files changed, 2 deletions(-) diff --git a/easyinstall.sh b/easyinstall.sh index 2af02be06a..0265ca3433 100755 --- a/easyinstall.sh +++ b/easyinstall.sh @@ -95,7 +95,6 @@ function initialChecks() { if [ "$EUID" -ne 0 ]; then echo "This script must be run with root privileges." echo "Please run: sudo $0 $*" - exit 1 fi # Detect the actual user who invoked sudo diff --git a/shell_scripts/netatalk_install.sh b/shell_scripts/netatalk_install.sh index 6a09cf9167..057817fd37 100755 --- a/shell_scripts/netatalk_install.sh +++ b/shell_scripts/netatalk_install.sh @@ -42,7 +42,6 @@ function initialChecks() { if [ "$EUID" -ne 0 ]; then echo "This script must be run with root privileges." echo "Please run: sudo $0 $*" - exit 1 fi echo "Netatalk install script for Debian Linux." From d7f7f1a07c0769b0c3ba5899e9f7411cfcbe6b5c Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Thu, 1 Jan 2026 01:46:17 +0100 Subject: [PATCH 3/3] fixes --- easyinstall.sh | 8 +++++++- shell_scripts/netatalk_install.sh | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/easyinstall.sh b/easyinstall.sh index 0265ca3433..81db83cea9 100755 --- a/easyinstall.sh +++ b/easyinstall.sh @@ -109,8 +109,14 @@ function initialChecks() { # Ensures a directory is owned by the actual user, not root function ensureUserOwnership() { local dir_path="$1" + + # Only attempt to change ownership if the user exists on the system if [ -d "$dir_path" ] || [ -f "$dir_path" ]; then - chown -R "$ACTUAL_USER:$ACTUAL_USER" "$dir_path" + if id "$ACTUAL_USER" >/dev/null 2>&1; then + chown -R "$ACTUAL_USER:$ACTUAL_USER" "$dir_path" + else + echo "Note: User '$ACTUAL_USER' does not exist in this environment, skipping ownership change for $dir_path" + fi fi } diff --git a/shell_scripts/netatalk_install.sh b/shell_scripts/netatalk_install.sh index 057817fd37..60eb4751e6 100755 --- a/shell_scripts/netatalk_install.sh +++ b/shell_scripts/netatalk_install.sh @@ -32,8 +32,14 @@ fi # Ensures a directory is owned by the actual user, not root function ensureUserOwnership() { local dir_path="$1" + + # Only attempt to change ownership if the user exists on the system if [ -d "$dir_path" ] || [ -f "$dir_path" ]; then - chown -R "$ACTUAL_USER:$ACTUAL_USER" "$dir_path" + if id "$ACTUAL_USER" >/dev/null 2>&1; then + chown -R "$ACTUAL_USER:$ACTUAL_USER" "$dir_path" + else + echo "Note: User '$ACTUAL_USER' does not exist in this environment, skipping ownership change for $dir_path" + fi fi }