set .htpasswd with bash expect - bash

I'm trying to automate setting a user's password in .htpasswd for Apache, but this script doesn't seem to actually set the password. What am I missing? Thanks!
#!/bin/bash
PASSWORD=`tr -dc a-z0-9_ < /dev/urandom | head -c 10`
cat << EOF | /usr/bin/expect
spawn /usr/bin/htpasswd -c /var/.htpasswd testuser
expect "assword:"
send "$PASSWORD\r"
expect "assword:"
send "$PASSWORD\r"
EOF
echo -e "\nPassword set to: $PASSWORD\n"

You can specify a password on the command line using the -b flag.
#!/bin/bash
PASSWORD=`tr -dc a-z0-9_ < /dev/urandom | head -c 10`
htpasswd -cb /var/.htpasswd testuser $PASSWORD
echo Password set to: $PASSWORD
# output:
# Adding password for user testuser
# Password set to: gzu00n4lp8
For clarification, here's a version using expect:
#!/bin/bash
PASSWORD=`tr -dc a-z0-9_ < /dev/urandom | head -c 10`
expect << EOF
spawn htpasswd -c /var/.htpasswd testuser
expect {
"New password:" { send "$PASSWORD\r"; exp_continue }
"Re-type new password:" { send "$PASSWORD\r"; exp_continue }
}
EOF
echo Password set to: $PASSWORD

Related

sftp bash script doesnt downloads files

I have a problem with my bash script.
It connects to my sftp server.
Gets the list of files to download.
Should download the files. But it doesnt do this. I can see the commands. if I write the commands manually, it works.
You can see the script and the log files here:
#!/bin/bash
LOCALDIR="/data/IMPORT/$(date +%Y%m%d)"
REMOTEDIR="/EXPORT"
FILELIST="$LOCALDIR/filelist.txt"
FILELIST2="$LOCALDIR/filelist2.txt"
SFTP="sftp -P 1234 -i /var/xxxxxx.pem -oStrictHostKeyChecking=no user#xxxxx.xxxx"
PASSPHRASE="xxxxxxxxxxxxxxxx"
mkdir -p $LOCALDIR
rm $FILELIST
rm $FILELIST2
allfilenames=()
function readFileList {
expect -c "
spawn $SFTP
expect \"assphrase\"
send \"$PASSPHRASE\r\"
expect \"sftp>\"
send \"lcd $LOCALDIR\r\"
send \"ls -l $REMOTEDIR/*\r\"
expect \"sftp>\"
send \"exit\r\"
interact " > $FILELIST
}
function getFiles {
myfilenames=("$#")
expect -c "
spawn $SFTP
expect \"assphrase\"
send \"$PASSPHRASE\r\"
expect \"sftp>\"
send \"lcd $LOCALDIR\r\"
expect \"sftp>\"
send \"cd $REMOTEDIR\r\"
expect \"sftp>\"
" >> $FILELIST2
for filepath in "${myfilenames[#]}"
do
file="$(basename -- $filepath)"
expect -c "
send \"get -P $file\r\n\"
sleep 3
expect \"sftp>\"
" >> $FILELIST2
done
expect -c "
send \"exit\r\"
" >> $FILELIST2
}
readFileList
c=0
if [[ -f "$FILELIST" ]]; then
while read line; do
filename=$(echo $line | awk '{ print $9 }')
if [[ "$filename" =~ ^$REMOTEDIR ]] ; then
allfilenames+=($filename)
fi
done < $FILELIST
fi
getFiles "${allfilenames[#]}"
filelist.txt looks like:
spawn sftp -P 1234 -i /var/xxxxxx.pem -oStrictHostKeyChecking=no user#xxxxx.xxxx
Enter passphrase for key '/var/xxxxxx.pem':
Connected to xxxxx.xxxx.
sftp> lcd /data/IMPORT/20200401
sftp> ls -l /EXPORT/*
-rw-r--r-- 0 1000472 1000472 3681 Mar 31 22:31 /EXPORT/file1.txt
-rw-r--r-- 0 1000472 1000472 14537 Mar 31 22:34 /EXPORT/file2.txt
-rw-r--r-- 0 1000472 1000472 5932 Mar 31 22:34 /EXPORT/file3.txt
sftp> exit
filelist2.txt looks like:
spawn sftp -P 1234 -i /var/xxxxxx.pem -oStrictHostKeyChecking=no user#xxxxx.xxxx
Enter passphrase for key '/var/xxxxxx.pem':
Connected to xxxxx.xxxx.
sftp> lcd /data/IMPORT/20200401
sftp> cd /EXPORT
sftp> get -P file1.txt
get -P file2.txt
get -P file3.txt
exit
expect -c 'spawn sftp ...'
expect -c 'send "get file\r" '
Here when the first expect -c completes, the SFTP connection will be closed so the second expect -c would not work. You have to use one single expect -c for one SFTP session.
It's like when you manually sftp to the server, you cannot temporarily go back to Bash and come back to sftp later.

How to loop Bash array in Expect script

I have an array in my bash script:
my_file=("a.txt" "b.txt" "c.txt" "d.txt" "e.txt")
Now in the same file I want to use Expect and make a looping to get some files in sftp. This is my code
/usr/bin/expect <<EOF
set timeout -1
array set param ${!my_file[#]}
spawn sftp $sftp_option $user#$host
expect "Password:"
send "$pswd\r"
expect "sftp>"
for arg in $param; do
send "mget $arg*\r"
expect "sftp>"
done
send "bye\r"
EOF
With that code I can't make a loop with that array above. And I got error like this:
wrong # args: should be "array set arrayName list"
while executing
"array set param 0 1"
Full Code:
#!/bin/bash
export PATH=$PATH:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/root/bin;
homeDir=/home/jlo_apps/daemon/jlo_ex/collection/wssh;
filePID=$homeDir/rsync_rfs_bbni.pid;
trap "rm $filePID; exit" SIGHUP SIGINT SIGTERM;
if [ -e $filePID ]; then
datenow=`date`;
echo "[ Rsync ] There are rsync processes still running...";
echo "========================= $datenow =========================";
exit 1;
else
echo $$ > $filePID;
fi
. $homeDir/get_rfs_bni.conf;
strconn="$dbuser/$dbpass#$dbhost:$dbport/$dbsid";
profile=`$sqlldr_path -s $strconn <<< "select host|| '|' ||username|| '|' ||password|| '|' ||port|| '|' ||outdir from p_epdp where bank='BBNI';" | tail -2 | head -1`;
mapfile mid < <($sqlldr_path -s $strconn <<< "select distinct(substr(mid, 0, 13)) from p_mid where kode_bank = 'BBNI';" | tail -3 | head -2);
host=$(echo $profile | cut -d"|" -f1);
user=$(echo $profile | cut -d"|" -f2);
pswd=$(echo $profile | cut -d"|" -f3);
port=$(echo $profile | cut -d"|" -f4);
outdir=$(echo $profile | cut -d"|" -f5);
/usr/bin/expect <<EOF
set timeout -1
spawn sftp $sftp_option $user#$host
expect "Password:"
send "$pswd\r"
expect "sftp>"
send "cd $outdir\r"
expect "sftp>"
send "lcd $rfshome\r"
expect "sftp>"
foreach arg {${mid[#]}} {
send "mget $arg*\r"
expect "sftp>"
}
send "bye\r"
EOF
rm $filePID;
sleep 10;
datenow=`date`;
echo "========================= $datenow =========================";
Is there any solution for this problem without separate file between bash and Expect?
The correct syntax to interpolate the array's values would be
array set param ${my_file[#]}
without a !, but this produces
array set param a.txt b.txt c.txt d.txt e.txt
However, the Expect syntax for creating an array looks like
array set param {one one.txt two two.txt}
with alternating keys and values (more like an associative array than just a list of values). But then you still can't use a shell for loop inside the Expect script; the language uses a completely different syntax (Expect is based on TCL, not shell script).
Probably you are looking for something like
/usr/bin/expect <<EOF
set timeout -1
spawn sftp $sftp_option $user#$host
expect "Password:"
send "$pswd\r"
expect "sftp>"
foreach arg {${my_file[#]}} {
send "mget $arg*\r"
expect "sftp>"
}
send "bye\r"
EOF
where I cribbed the proper TCL loop syntax from Pass bash array to expect script
It's a bit awkward looking, but you can do this:
$ my_file=("a.txt" "b.txt" "c.txt" "d.txt" "e.txt")
$ expect -f - -- "${my_file[#]}" <<'END'
foreach arg $argv {puts $arg}
END
a.txt
b.txt
c.txt
d.txt
e.txt
The -f - tells expect to use stdin for the script file.
Then -- ends the command line options, and the remaining arguments go into the argv list.
Don't forget to use a quoted heredoc, like I demonstrate. Otherwise the shell will expand the expect variables.

Password protected shell script

I want to make my script password protected. If I use this code it works:
ACTUAL="sam123"
read -s -p "Password: " enteredpass
I also want to protect the script from being read with cat and vi. I tried to use vim -x <script> to encrypt it but then it won't allow me to run it.
I am using a generic user and haven't gotten anywhere.
You can't do this securely without your sysadmin's help, but you can do something sorta-kinda-maybe-not-really-adequate without it.
So, let's say you create your script like so:
cat >myscript <<EOF
echo "Doing something super secret here"
EOF
...but you don't want anyone who doesn't know the password to run it, even if they're using a shared account. You can do this by encrypting it:
gpg -ac <myscript >myscript.asc
...and then embedding that plaintext into a script:
#!/usr/bin/env bash
{ gpg -d | bash -s "$#"; } <<'EOF'
-----BEGIN PGP MESSAGE-----
jA0EBwMCBogTuO9LcuZg0lsB2wqrsPU8Bw2DRzAZr+hiecYTOe//ajXfcjPI4G6c
P3anEYb0N4ng6gsOhKqOYpZU9JzVVkxeL73CD1GSpcQS46YlKWJI8FKcPckR6BE+
7vqkcPWwcS7oy4H2
=gmFu
-----END PGP MESSAGE-----
EOF
That said, other users in the shared account can still collect your password if they connect to and trace your process while it's running -- running strace on the copy of bash -s will show the text being fed into its stdin. In general, you shouldn't rely on shared accounts for anything that needs to remain confidential.
Late answer for posterity, how about using openssl? here's my scriptencrypt.sh
It generates a new .sh file that requires a password
#!/bin/bash
if [ -z "$1" ]; then echo "usage: $(basename $0) script"; exit 1; fi
script=$(cat "$1")
checksum="$(echo "$script" | md5sum | awk '{ print $1 }')"
extension=$([[ "$(basename $1)" =~ .\.. ]] && echo ".${1##*.}" || echo "")
cat << EOF > "${1%.*}.enc${extension}"
#!/bin/bash
read -r -d '' encrypted_script << EOF2
$(openssl aes-256-cbc -a -salt -in /dev/stdin -out /dev/stdout <<< "${script}")
EOF2
read -s -p "Enter script password: " password
echo
unencrypted_script=\$(openssl aes-256-cbc -d -a -salt -in /dev/stdin -out /dev/stdout <<< "\${encrypted_script}" -pass pass:"\${password}" 2>/dev/null | tr -d '\000')
clear
checksum="\$(echo "\$unencrypted_script" | md5sum | awk '{ print \$1 }')"
if [ "\${checksum}" = "${checksum}" ]; then
eval "\${unencrypted_script}"
exit 0
else
echo "Wrong password inserted"
exit 1
fi
EOF

Expect deletes ^H after spawn bash -c

I have an expect script that grabs a bunch of configs and outputs them to separate files. The files though have a bunch of ^H's though I'd like to clean out. I used the CtrlV+CtrlH to put it in the expect script, but when it runs it just does it. The sed works fine in bash, but fails in expect. How can I delete the ^H from the files?
foreach host $ip {
set output [ open "$host" w ]
set timeout 2
spawn ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 -o "StrictHostKeyChecking no" -oHostKeyAlgorithms=+ssh-dss $username#$host
expect {
eof {wait; spawn telnet $host;
expect "ame:";
send "$username\r"}
}
expect "word:"
send "$password\r"
expect "#"
send "$command\r"
sleep 2
expect {
"ore--" { send -- " "; puts $output $expect_out(buffer); exp_continue}
"#" {send -- "exit\r"}
}
puts $output $expect_out(buffer)
close $output
close
}
foreach host $ip {
spawn bash -c "sed -i 's/^H//gi; s/--More--//g' ./$host"
}
close
Here's what it looks like when it runs:
spawn bash -c sed -i 's//gi; s/--More--//g' ./192.168.50.1
spawn bash -c sed -i 's//gi; s/--More--//g' ./192.168.51.1
spawn bash -c sed -i 's//gi; s/--More--//g' ./192.168.52.1
Assuming an ASCII-based locale, this should work:
tr -d '\010' < file > file.new
\010 is octal 8, which is the ASCII equivalent of backspace.
For calling out to sed, since you don't need to interact with the process, you can use exec instead:
foreach host $ip {
exec sed -i {s/^H//gi; s/--More--//g} ./$host
}
Using Tcl's version of single quotes, which are {}

How to auto input the password when run vncserver :1?

I'm writing a bash script that will connect to my remote machine then run some commands, one of them is vncserver :1, but this command need to input a password. How can I do it in my shell script? (I just need to run the script only, don't need to input the password)
This is my script:
ssh -i $pem -o StrictHostKeyChecking=no -o 'IdentitiesOnly yes' admin#$ip -f '
pkill vnc ;
vncserver :1 ;
'
Thanks all,
It's ok now:
ssh -i $pem -o StrictHostKeyChecking=no -o 'IdentitiesOnly yes' admin#$ip -f '
pkill vnc ;
expect -c "
spawn vncserver :1;
expect -nocase \"password:\" {
send \"$pass\r\";
expect -nocase \"Verify:\" {
send \"$pass\r\";
expect -nocase \"Would you like to enter a view-only password \(y\/n\)\?\" {
send \"n\r\";
expect eof }; }; interact } ;
"
'

Resources