2

I'd like to remove the need for the user to type a password multiple times for a bash script that runs sudo commands. How can I loop for password until something like sudo echo returns 0, and then store the password in a variable to use script-wide?

Eliah Kagan
  • 117,780
maqp
  • 173
  • 4
    That's generally considered an unwise design. Instead, run the entire script using sudo once up front. – user535733 Oct 27 '17 at 23:32
  • 4
    Generally you write the script and run the script as root , sudo /path/to/script , IMO, any script run as root should be owned and rw only by root. Depending on what you are doing, however, you can either configure sudo to run without a password or use expect - http://www.admin-magazine.com/Articles/Automating-with-Expect-Scripts . All 3 methods are preferable to using passwords in scripts. – Panther Oct 28 '17 at 01:18

2 Answers2

2

It is certainly possible to remove the need to type sudo password for specific commands or even whole scripts.

Your suggested path of storing the password in a local variable in the script is rather unwise. Anyone with read rights to /proc/<pid>/environ can read the local variables. Anyone who gains read access to a script, with hard coded credentials, could use those credentials to escalate privileges to root and own your system. On a single user system this is still unwise as there are increasing remote attacks targeting Linux users.

Without the specifics of your script I can only advise you to read Ubuntu Community Wiki - Sudoers. Then modify your sudoers file by running sudo visudo to add the specific commands to the file.

Likely you should add two lines one to add a command alias, and the second to authorize users to run those commands.

An example of allowing specific user ie, bob, to run selected shutdown commands without a password.

Cmnd_Alias SHUTDOWN_CMDS = /sbin/poweroff, /sbin/halt, /sbin/reboot
bob ALL=(ALL) NOPASSWD: SHUTDOWN_CMDS

The following script is an example of how to include in a script the ability to install, uninstall, or edit the example lines in /etc/sudoers.

#!/bin/bash

#Set Script Name variable
SCRIPT=`basename ${BASH_SOURCE[0]}`

#Initialize variables to default values.
OPT_i=i
OPT_u=u
OPT_e=e
OPT_m=m

#Set fonts for Help.
NORM=`tput sgr0`
BOLD=`tput bold`
REV=`tput smso`

#Help function
function HELP {
  echo -e \\n"Help documentation for ${BOLD}${SCRIPT}.${NORM}"\\n
  echo -e "${REV}Basic usage:${NORM} ${BOLD}$SCRIPT file.ext${NORM}"\\n
  echo "Command line switches are optional. The following switches are recognized."
  echo "${REV}-1${NORM}  --Installs lines in /etc/sudoers to allow script to be run without entering password multiple times."
  echo "${REV}-u${NORM}  --Unistalls lines in /etc/sudoers."
  echo "${REV}-e${NORM}  --Launces visudo to edit /etc/sudoers."
  echo "${REV}-m${NORM}  --Launces main."
  echo -e "${REV}-h${NORM}  --Displays this help message. No further functions are performed."\\n
  exit 1
}

#Install function
function INSTALL {
echo "launching Install"
echo -e '#script_append'\\n'Cmnd_Alias SHUTDOWN_CMDS = /sbin/poweroff, /sbin/halt, /sbin/reboot'\\n'bob ALL=(ALL) NOPASSWD: SHUTDOWN_CMDS' | sudo EDITOR='tee -a' visudo
}

#Unnstall function
function UNINSTALL {
echo "launching uninstall"
bash -c 'printf ",g/^#script_append$/d\nw\nq\n" | sudo EDITOR='ed' visudo'
bash -c 'printf ",g/^Cmnd_Alias.*reboot$/d\nw\nq\n" | sudo EDITOR='ed' visudo'
bash -c 'printf ",g/^bob ALL=(ALL) NOPASSWD: SHUTDOWN_CMDS$/d\nw\nq\n" | sudo EDITOR='ed' visudo'
}

#Main function
function MAIN {
echo "launching editor via main"
sudo visudo
}

#Check the number of arguments. If none are passed, print help and exit.
NUMARGS=$#
echo -e \\n"Number of arguments: $NUMARGS"
if [ $NUMARGS -eq 0 ]; then
  HELP
fi

### Start getopts code ###

#Parse command line flags
#If an option should be followed by an argument, it should be followed by a ":".
#Notice there is no ":" after "h". The leading ":" suppresses error messages from
#getopts. This is required to get my unrecognized option code to work.

while getopts ":iuemh" FLAG; do
  case "${FLAG}" in
    i)  #set option "i"
     OPT_i=${OPTARG}
      echo "-i used: $OPTARG"
      if sudo grep -q '#script_append' /etc/sudoers
                then 
                    echo "Sudoers apperes to have already been installed"
                    exit
                else 
                    INSTALL
            fi
      ;;
    u)  #set option "u"
      OPT_u=$OPTARG
      echo "-u used: $OPTARG"
      UNINSTALL
      ;;
    e)  #set option "e"
      OPT_e=$OPTARG
      echo "-e used: $OPTARG"
      sudo visudo
      ;;
    m)  #set option "m"
      OPT_m=$OPTARG
      echo "-m used: $OPTARG"
      MAIN
      ;;
    h)  #show help
      HELP
      ;;
    \?) #unrecognized option - show help
      echo -e \\n"Option -${BOLD}$OPTARG${NORM} not allowed."
      HELP
      #If you just want to display a simple error message instead of the full
      #help, remove the 2 lines above and uncomment the 2 lines below.
      #echo -e "Use ${BOLD}$SCRIPT -h${NORM} to see the help documentation."\\n
      #exit 2
      ;;
  esac
done

shift $((OPTIND-1))  #This tells getopts to move on to the next argument.

### End getopts code ###
J. Starnes
  • 1,969
  • I'm not storing the password into the script (which is an installer that needs to run some commands as sudo and some as user). I want to bother the user with password only once while the script runs. I tried multiple approaches with Tails OS (a supported OS) to disable password prompt but editing /etc/sudoers.d/always-ask-password did not fix it reliably. As for visudo, user would have to make edits themselves and I don't want that. I tried echo 'Defaults timestamp_timeout=-1' | (sudo su -c 'EDITOR="tee" visudo -f /etc/sudoers.d/always-ask-password') but that didn't work either. – maqp Oct 28 '17 at 18:01
  • "Anyone with read rights to /proc//environ can read the local variables." ... this is true, but only for the variables the process started with. Modifications or additions made by the process after that are not reflected in environ. Local un-exported variables wouldn't even be visible in a child process's environ. – muru Oct 30 '17 at 10:45
-1

My current approach that seems to be working is as follows:

read_sudo_pwd() {
    read -s -p "[sudo] password for $USER: " sudo_pwd
    until (echo $sudo_pwd | sudo -S echo '' 2>/dev/null)
    do
        echo -e '\nSorry, try again.'
        read -s -p "[sudo] password for $USER: " sudo_pwd
    done
}

read_sudo_pwd
echo $sudo_pwd | sudo -S echo 'Hello'
echo $sudo_pwd | sudo -S echo 'World'
maqp
  • 173
  • 2
    -1 Bad design: $PWD is an environment variable so it's best if you don't overwrite it. Use a more descriptive, lowercase variable like $password_sudo. – wjandrea Oct 29 '17 at 06:18