"Cache" credentials on a bash script - linux

I have created a bash script to run a few js scripts into MongoDB. Basically, what I am doing is, running the bash script and passing a parameter in this case called version, example:
./script.sh 1.0
That will go and execute all scripts for the version 1.0. Now, it is possible that MongoDB requires authentication user/pass, so I have an option in the script execution that will ask the user if it requires authentication.
read -p "Username: " mongo_user; read -s -p "Password: " mongo_pass;
My question is: what would be the best way to kind cache the same credentials to call the script multiple times? For example:
./script.sh 1.0
./script.sh 1.1
./script.sh 1.2 and on..
I don't want to type in the same credentials every time the script runs.

Caio,
As stated in my comment here's how I did it:
Thanks for Charles Duffy for the printf solution:
#!/bin/bash
ePass() {
read -sp "Password: " pass
echo ""
printf '%s\n' "$pass" | perl -e 'chomp($passwd=<>); chomp($encoded=pack("u",$passwd));print "$encoded\n"' > .pswd
cat .pswd
}
dPass() {
dPass=`cat .pswd | perl -e 'chomp($encoded=<>); chomp($passwd=unpack("u",$encoded)); print "$passwd\n"'`
echo $dPass
}
ePass
dPass
You can add these functions to your script. When you want to set the password ePass will do:
[KUBO#home ~]$ ./test.sh
Password:
It will mask the input to avoid over-the-shoulder reading. Then it will echo the encoded output (remove after testing):
Password: Hello >>> %2&5L;&\`
Then you dPass:
Hello
So when you call your mongo scripts you can use the dPass output as your arg.

The best way would be setting an environment variable and reading that variable every time.
comp#rangeesh:~$ export USERNAME=user
comp#rangeesh:~$ export PASSWORD=pass
comp#rangeesh:~$ echo $USERNAME
user
comp#rangeesh:~$ echo $PASSWORD
pass
comp#rangeesh:~$
using the same way you could use the environment variable for a valid session or global var

Related

Execute Bash script remotely via cURL

I have a simple Bash script that takes in inputs and prints a few lines out with that inputs
fortinetTest.sh
read -p "Enter SSC IP: $ip " ip && ip=${ip:-1.1.1.1}
printf "\n"
#check IP validation
if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "SSC IP: $ip"
printf "\n"
else
echo "Enter a valid SSC IP address. Ex. 1.1.1.1"
exit
fi
I tried to upload them into my server, then try to run it via curl
I am not sure why the input prompt never kick in when I use cURL/wget.
Am I missing anything?
With the curl ... | bash form, bash's stdin is reading the script, so stdin is not available for the read command.
Try using a Process Substitution to invoke the remote script like a local file:
bash <( curl -s ... )
Your issue can be simply be reproduced by run the script like below
$ cat test.sh | bash
Enter a valid SSC IP address. Ex. 1.1.1.1
This is because the bash you launch with a pipe is not getting a TTY, when you do a read -p it is read from stdin which is content of the test.sh in this case. So the issue is not with curl. The issue is not reading from the tty
So the fix is to make sure you ready it from tty
read < /dev/tty -p "Enter SSC IP: $ip " ip && ip=${ip:-1.1.1.1}
printf "\n"
#check IP validation
if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "SSC IP: $ip"
printf "\n"
else
echo "Enter a valid SSC IP address. Ex. 1.1.1.1"
exit
fi
Once you do that even curl will start working
vagrant#vagrant:/var/www/html$ curl -s localhost/test.sh | bash
Enter SSC IP: 2.2.2.2
SSC IP: 2.2.2.2
I personally prefer source <(curl -s localhost/test.sh) option. While it is similar to bash ..., the one significant difference is how processes handled.
bash will result in a new process being spun up, and that process will evoke commands from the script.
source on the other hand will use current process to evoke commands from the script.
In some cases that can play a key role. I admit that is not very often though.
To demonstrate do the following:
### Open Two Terminals
# In the first terminal run:
echo "sleep 5" > ./myTest.sh
bash ./myTest.sh
# Switch to the second terminal and run:
ps -efjh
## Repeat the same with _source_ command
# In the first terminal run:
source ./myTest.sh
# Switch to the second terminal and run:
ps -efjh
Results should look similar to this:
Before execution:
Running bash (main + two subprocesses):
Running source (main + one subprocess):
UPDATE:
Difference in use variable usage by bash and source:
source command will use your current environment. Meaning that upon execution all changes and variable declarations, made by the script, will be available in your prompt.
bash on the other hand will be running in as a different process; therefore, all variables will be discarded when process exits.
I think everyone will agree that there are benefits and drawbacks to each method. You just have to decide which one is better for your use case.
## Test for variables declared by the script:
echo "test_var3='Some Other Value'" > ./myTest3.sh
bash ./myTest3.sh
echo $test_var3
source ./myTest3.sh
echo $test_var3
## Test for usability of current environment variables:
test_var="Some Value" # Setting a variable
echo "echo $test_var" > myTest2.sh # Creating a test script
chmod +x ./myTest2.sh # Adding execute permission
## Executing:
. myTest2.sh
bash ./myTest2.sh
source ./myTest2.sh
./myTest2.sh
## All of the above results should print the variable.
I hope this helps.

How to enter pass and username inside script

I wrote a shell script and ı will set it into crontab but ı have a problem. When crontab run my script, running scp command inside script and get some informations from different server so ı have to enter password of other server inside my script. How to enter password inside script ?
my script:
scp username#host:/Products/data/bridge/control.txt .
????? ( have to automatically enter password)
b=$(more control.txt | wc -l)
if [$b = 1]; then
echo " OK "
fi
You could use expect, for example:
my_password="123456"
expect -c "spawn scp username#host:/Products/data/bridge/control.txt .; expect \"password\" {send -- \"${my_password}\r\"; expect eof;};"

Having an issue passing variables to subshell

So here is my problem, I have this script I wrote where I'm exporting two variables however they're not making it into the subshell.
The point of this script is to change a users password and clear out their pam_tally for CentOS and Ubuntu hosts.
A little background is that this environment's users are managed by puppet but the passwords are all local, ssh keys are not allowed either (this is set in stone and can't be changed so I have to work with what I got) and the reason is that every log in has to be manual (even number of sessions are limited to two so you can't even user csshX effectively).
Here is my script
#!/bin/bash
echo "Please enter user whose password you want to change"
read NEWUSER
echo "Please enter new password for user"
read -s -p "Temp Password:" TEMPPASSWORD
PASSWORD=$TEMPPASSWORD
export PASSWORD
NEWUSER2=$NEWUSER
export NEWUSER2
for i in HOST{cluster1,cluster2,cluster3}0{1..9}
do
ping -c 2 $i && (echo $i ; ssh -t $i '
sudo pam_tally2 --user=$NEWUSER2 --reset
echo -e "$PASSWORD\n$PASSWORD" | sudo passwd $NEWUSER2
sudo chage -d 0 $NEWUSER2
')
done
You are using ssh to connect to a remote host and run a script on that host. ssh does not export the local environment to the remote session but instead performs a login on the remote host which sets the environment according to the remote user's configuration on the remote host.
I suggest you pass all needed values via the command you want to execute. This could be done like this:
ssh -t $i '
sudo pam_tally2 --user='"$NEWUSER2"' --reset
echo -e "'"$PASSWORD"'\n'"$PASSWORD"'" | sudo passwd '"$NEWUSER2"'
sudo chage -d 0 '"$NEWUSER2"
Watch closely how this uses quotes. At each occasion where you used a variable, I terminate the single-quoted string (using '), then add a double-quoted use of the variable (e. g. "$PASSWORD") and then start the single-quoted string again (using ' again). This way, the shell executing the ssh command will expand the variables already, so you have no need to pass them into the remote shell.
But be aware that special characters in the password (like " or ' or or maybe a bunch of other characters) can mean trouble using this simple mechanism. To be safe against this as well, you would need to use the %q format specifier of the printf command to quote your values before passing them:
ssh -t "$i" "$(printf '
sudo pam_tally2 --user=%q --reset
{ echo %q; echo %q; } | sudo passwd %q
sudo chage -d 0 %q' \
"$NEWUSER2" "$PASSWORD" "$PASSWORD" "$NEWUSER2" "$NEWUSER2")"

Bash Script Here Documents FTP

I recently discovered "here" statements while working with a bash script for automating an ftp process.
Reference for Here Documents: http://tldp.org/LDP/abs/html/here-docs.html
The ftp process takes fairly long in the bash script, and I wanted specifically to run it in the background and have the next line of the bash script continue after the ftp process. How would I do this for "here" documents?
FTP snippet:
USER="test"
PASSWD="test"
ftp -n $HOST <<END_SCRIPT
quote USER $USER
quote PASS $PASSWD
quit
END_SCRIPT
For example:
I want to be able to do this:
run ftp snippet &
run other shell commands
but I'm not quite sure where to put the &
I have tried so far:
Attempt 1: (I believe that this is syntactically incorrect and does not work):
function do_ftp() {
ftp -n $HOST <<END_SCRIPT
quote USER $USER
quote PASS $PASSWD
quit
END_SCRIPT
}
do_ftp &
//additional commands
Attempt 2:
USER="test"
PASSWD="test"
ftp -n $HOST <<END_SCRIPT
quote USER $USER
quote PASS $PASSWD
quit
END_SCRIPT &
Attempt 3 :
USER="test"
PASSWD="test"
ftp -n $HOST & <<END_SCRIPT
quote USER $USER
quote PASS $PASSWD
quit
END_SCRIPT
I haven't tested it on ftp BUT:
I know when you want to put variables on HEREDOC you should do something else
For example I tried the following for ssh command:
while read pass port user ip fileinput fileoutput filetemp; do
sshpass -p$pass ssh -o 'StrictHostKeyChecking no' -p $port $user#$ip fileinput=$fileinput fileoutput=$fileoutput filetemp=$filetemp 'bash -s'<<ENDSSH1
python /path/to/f.py $fileinput $fileoutput $filetemp
ENDSSH1
done <<____HERE1
PASS PORT USER IP FILE-INPUT FILE-OUTPUT FILE-TEMP
____HERE1
May be something like this is needed by you...

Run scripts remotely via SSH

I need to collect user information from 100 remote servers. We have public/private key infrastructure for authentication, and I have configured ssh-agent command to forward key, meaning i can login on any server without password prompt (auto login).
Now I want to run a script on all server to collect user information (how many user account we have on all servers).
This is my script to collect user info.
#!/bin/bash
_l="/etc/login.defs"
_p="/etc/passwd"
## get mini UID limit ##
l=$(grep "^UID_MIN" $_l)
## get max UID limit ##
l1=$(grep "^UID_MAX" $_l)
awk -F':' -v "min=${l##UID_MIN}" -v "max=${l1##UID_MAX}" '{ if ( $3 >= min && $3 <= max && $7 != "/sbin/nologin" ) print $0 }' "$_p"
I don't know how to run this script using ssh without interaction??
Since you need to log into the remote machine there is AFAICT no way to do this "without ssh". However, ssh accepts a command to execute on the remote machine once logged in (instead of the shell it would start). So if you can save your script on the remote machine, e.g. as ~/script.sh, you can execute it without starting an interactive shell with
$ ssh remote_machine ~/script.sh
Once the script terminates the connection will automatically be closed (if you didn't configure that away purposely).
Sounds like something you can do using expect.
http://linux.die.net/man/1/expect
Expect is a program that "talks" to other interactive programs according to a script. Following the script, Expect knows what can be expected from a program and what the correct response should be.
If you've got a key on each machine and can ssh remotehost from your monitoring host, you've got all that's required to collect the information you've asked for.
#!/bin/bash
servers=(wopr gerty mother)
fmt="%s\t%s\t%s\n"
printf "$fmt" "Host" "UIDs" "Highest"
printf "$fmt" "----" "----" "-------"
count='awk "END {print NR}" /etc/passwd' # avoids whitespace problems from `wc`
highest="awk -F: '\$3>n&&\$3<60000{n=\$3} END{print n}' /etc/passwd"
for server in ${servers[#]}; do
printf "$fmt" "$server" "$(ssh "$server" "$count")" "$(ssh "$server" "$highest")"
done
Results for me:
$ ./doit.sh
Host UIDs Highest
---- ---- -------
wopr 40 2020
gerty 37 9001
mother 32 534
Note that this makes TWO ssh connections to each server to collect each datum. If you'd like to do this a little more efficiently, you can bundle the information into a single, slightly more complex collection script:
#!/usr/local/bin/bash
servers=(wopr gerty mother)
fmt="%s\t%s\t%s\n"
printf "$fmt" "Host" "UIDs" "Highest"
printf "$fmt" "----" "----" "-------"
gather="awk -F: '\$3>n&&\$3<60000{n=\$3} END{print NR,n}' /etc/passwd"
for server in ${servers[#]}; do
read count highest < <(ssh "$server" "$gather")
printf "$fmt" "$server" "$count" "$highest"
done
(Identical results.)
ssh remoteserver.example /bin/bash < localscript.bash
(Note: the "proper" way to authenticate without manually entering in password is to use SSH keys. Storing password in plaintext even in your local scripts is a potential security vulnerability)
You can run expect as part of your bash script. Here's a quick example that you can hack into your existing script:
login=user
IP=127.0.0.1
password='your_password'
expect_sh=$(expect -c "
spawn ssh $login#$IP
expect \"password:\"
send \"$password\r\"
expect \"#\"
send \"./$remote_side_script\r\"
expect \"#\"
send \"cd /lib\r\"
expect \"#\"
send \"cat file_name\r\"
expect \"#\"
send \"exit\r\"
")
echo "$expect_sh"
You can also use pscp to copy files back and forth as part of a script so you don't need to manually supply the password as part of the interaction:
Install putty-tools:
$ sudo apt-get install putty-tools
Using pscp in your script:
pscp -scp -pw $password file_to_copy $login#$IP:$dest_dir
maybe you'd like to try the expect command as following
#!/usr/bin/expect
set timeout 30
spawn ssh -p ssh_port -l ssh_username ssh_server_host
expect "password:"
send "your_passwd\r"
interact
the expect command will catch the "password:" and then auto fill the passwd your send by above.
Remember that replace the ssh_port, ssh_username, ssh_server_host and your_passwd with your own configure

Resources