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..81db83cea9 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,46 @@ 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'." - exit 1 + if [ "$EUID" -ne 0 ]; then + echo "This script must be run with root privileges." + echo "Please run: sudo $0 $*" 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" + + # Only attempt to change ownership if the user exists on the system + if [ -d "$dir_path" ] || [ -f "$dir_path" ]; then + 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 - 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 +142,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 +151,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 +162,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 +191,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 +309,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 +324,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 +356,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 +367,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 +379,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 +391,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 +403,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 +415,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 +423,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 +431,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 +452,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 +460,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 +557,7 @@ function setupWiredNetworking() { echo "Rebooting..." sleep 3 - sudo reboot + reboot } # Modifies system configurations for a wireless network bridge with NAT @@ -593,7 +609,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 +617,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 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 +} + # checks to run before installation function initialChecks() { - if [ "root" == "$USER" ]; then - echo "Do not run this script as $USER or with 'sudo'." - exit 1 + if [ "$EUID" -ne 0 ]; then + echo "This script must be run with root privileges." + echo "Please run: sudo $0 $*" fi echo "Netatalk install script for Debian Linux." @@ -51,36 +74,32 @@ function initialChecks() { echo " - Install configuration files to $SYSCONFDIR" echo " - Install the CUPS printing system and modify its configuration" echo - echo "Input your password to allow this script to make the above changes." - sudo -v - - echo - echo "IMPORTANT: "$USER" needs to have a password of 8 chars or less due to Classic Mac OS limitations." + echo "IMPORTANT: "$ACTUAL_USER" needs to have a password of 8 chars or less due to Classic Mac OS limitations." echo "Do you want to change your password now? [y/N]" read -r REPLY if [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then - passwd + passwd "$ACTUAL_USER" fi } function installNetatalk() { echo "Checking for previous versions of Netatalk..." - sudo systemctl stop atalkd afpd netatalk || true + systemctl stop atalkd afpd netatalk || true if [ -f /etc/init.d/netatalk ]; then echo echo "WARNING: Legacy init scripts for a previous version of Netatalk was detected on your system. It is recommended to back up you configuration files and shared files before proceeding. Press CTRL-C to exit, or any other key to proceed." read - sudo /etc/init.d/netatalk stop || true + /etc/init.d/netatalk stop || true fi if [ -f /var/log/afpd.log ]; then echo "Cleaning up /var/log/afpd.log..." - sudo rm /var/log/afpd.log + rm /var/log/afpd.log fi if [[ `grep -c netatalk /etc/rc.local` -eq 1 ]]; then - sudo sed -i "/netatalk/d" /etc/rc.local + sed -i "/netatalk/d" /etc/rc.local echo "Removed Netatalk from /etc/rc.local -- use systemctl to control Netatalk from now on." fi @@ -89,17 +108,17 @@ function installNetatalk() { echo "Found a $AFP_SHARE_PATH directory; will use it for file sharing." else echo "Creating the $AFP_SHARE_PATH directory and granting read/write permissions to all users..." - sudo mkdir -p "$AFP_SHARE_PATH" - sudo chown -R "$USER:$USER" "$AFP_SHARE_PATH" + mkdir -p "$AFP_SHARE_PATH" chmod -R 2775 "$AFP_SHARE_PATH" fi + ensureUserOwnership "$AFP_SHARE_PATH" fi if [ $INSTALL_PACKAGES ]; then echo echo "Installing dependencies..." - sudo apt-get update || true - sudo apt-get install cmark-gfm gcc libdb-dev libcups2-dev cups libavahi-client-dev libevent-dev libgcrypt20-dev libiniparser-dev libpam0g-dev libsqlite3-dev meson ninja-build pkg-config --assume-yes --no-install-recommends /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 +176,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 +185,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!"