I have a script to automatically connect to a VPN, because I often have to connect and disconnect several times a day. (Certain things like ScreenHero and GoToMeeting fail on the VPN, and Mail servers and other things are blocked while on the VPN, but I can't connect to the Git server unless I'm on the VPN, and the app I'm working on can't reach certain back-end services unless I'm on the VPN, so development is limited when disconnected.)
The script lessens the hassle, but I have a $
in my password (which is available in the environment through the environment variable VPN_PASSWORD
1), and the password value gets interpolated before it is passed to the VPN program by expect.
#!/bin/bash
{
/usr/bin/expect << END_OF_LOGIN_SESSION
set timeout 30
spawn /opt/cisco/anyconnect/bin/vpn -s connect blah.blahblah.com
expect "Username:*"
send "\r"
expect "Password:*"
send "$VPN_PASSWORD\r"
expect "accept? \[y/n\]:*"
send "y\r"
expect eof
END_OF_LOGIN_SESSION
}
How do I pass a password literally, without letting it be subject to string interpolation?
Changing my password is a cop-out. Saving it with a \
is also a cop-out (and horrible UX, since I have to enter it every time I source my .project
file for this project2).
It seems like expect must have a solution for this.
Yes I know someone who steals my laptop could try to log in, but (a) they'd have to get into my account first, and (b) they'd fail without my PIN, entered by phone, anyway.
No, the password is not saved to disk. I enter it once per shell session related to this project.
You have the shell variable $VPN_PASSWORD
in an unquoted heredoc, so the shell will substitute the value. Suppose VPN_PASSWORD='foo$bar'
=> Then expect will see: send "foo$bar\r"
and you'll get can't read "bar": no such variable
. The solution is to use {braces}
instead of double quotes, so that expect will not attempt to expand the "inner" variable.
send {$VPN_PASSWORD}; send "\r"
You need a separate send "\r"
because putting \r
inside the braces will remove its special meaning, and Tcl won't let you do send {$VPN_PASSWORD}"\r"
Here's a demo:
$ VPN_PASSWORD='foo$bar'
$ expect <<END
send_user "$VPN_PASSWORD\n"
END
can't read "bar": no such variable
while executing
"send_user "foo$bar\n""
$ expect <<END
send_user {$VPN_PASSWORD}; send_user "\n"
END
foo$bar
In Tcl, braces act like single quotes in the shell: everything inside them are literal characters.
It might be cleaner to use the environment to pass the values. Here it is implemented as a shell function
vpnconnect() {
expect <<'END'
set timeout 30
spawn /opt/cisco/anyconnect/bin/vpn -s connect $env(VPN_HOST)
expect "Username:*"
send "\r"
expect "Password:*"
send "$env(VPN_PASSWORD)\r"
expect {accept? [y/n]:*}
send "y\r"
expect eof
END
}
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments