Connect CISCO Anyconnect VPN via bash
Asked Answered
O

5

16

As title says, trying to connect vpn via bash. The following script seemed closest to the answer I'm looking for:

#!/bin/bash
/opt/cisco/anyconnect/bin/vpn -s << EOF
connect https://your.cisco.vpn.hostname/vpn_name
here_goes_your_username
here_goes_your_passwordy
EOF

When I run this the vpn starts but then exits without an error and without connecting. This seems to be caused by the -s. If I remove this parameter the VPN will start but none of the commands (ie connect vpn, username, password) will be entered. From what I read the -s option will allow the username/password to be passed. Help!

Oppenheimer answered 13/6, 2014 at 16:29 Comment(3)
You might want to look into expect(1) if you're trying to script interactive programs.Jadeite
Thanks! Got it working using expect. If anybody else has a similar problem here is the tutorial I used (I didn't even know about expect so its pretty basic): thegeekstuff.com/2010/10/expect-examplesOppenheimer
This is also something you can trivially do with OpenConnect rather than using the official AnyConnect client.Sewerage
O
19

I had to download the expect packages (yum install expect). Here is the code I used to automate vpn connection

#!/usr/bin/expect

eval spawn /opt/cisco/anyconnect/bin/vpn connect vpn.domain.com

expect "Username: " { send "username\r" }
expect "Password: " { send "password\r" }

set timeout 60
expect "VPN>"
Oppenheimer answered 17/6, 2014 at 21:10 Comment(0)
R
13

Although expect can be cleaner, it is not strictly necessary. Assuming /opt/cisco/anyconnect/bin/vpnagentd is running as it automatically should be:

To connect:

printf "USERNAME\nPASSWORD\ny" | /opt/cisco/anyconnect/bin/vpn -s connect HOST

Replace USERNAME, PASSWORD, and HOST. The \ny at the end is to accept the login banner - this is specific to my host, and so you may not need it.

I understand that there are obvious security concerns with this method; it's for illustration purposes only.

To get state:

/opt/cisco/anyconnect/bin/vpn state

To disconnect:

/opt/cisco/anyconnect/bin/vpn disconnect

This was tested with AnyConnect v3.1.05160.

Resurrectionist answered 9/11, 2014 at 5:51 Comment(3)
Reference: On my vpn cli manual option, "-s read commands from standard input Example: vpncli.exe -s < MyScript.txt"Otiliaotina
I am curious on how the pipe "|" worked here. How come "vpn connect HOST" will read from the pipe for password? I tried using the same method for ssh. but it did not work. "printf "password" | ssh user@HOST " give error "Pseudo-terminal will not be allocated because stdin is not a terminal."Otiliaotina
Later I figured out that, for SSH, it's because its security feature prevents it from reading password from insecure source, like a file or printlnOtiliaotina
P
3

If you are using macOS, I recommend to save your vpn password in Keychain, then request it from your Anyconnect script.

For example, say I want to connect to foo.bar.com with account foo and password bar.

  1. Save foo and bar pair in Keychain (login not iCloud) with name fookey
  2. Run the following bash script to connect
/opt/cisco/anyconnect/bin/vpn connect foo.bar.com -s << EOM
0    # foo.bar.com doesn't require two factor authorization
foo  # vpn account
$(sudo security find-generic-password -ws fookey)  # vpn password
EOM

Using this approach, you don't need to type in your vpn password every time, and you won't write your password to files without encryption.

If you are not familiar with bash script, read below for explanation:


  • /opt/cisco/anyconnect/bin/vpn connect -s enters non-interactivel mode.
  • << EOM ... EOM is called here-docs, which uses a string to replace a file. It is very useful to script interactive CLI, by writing each respond as a new line.
  • security is a nice tool to access your Keychain from the command line.
Parasynapsis answered 23/8, 2019 at 3:21 Comment(0)
M
1

c# solution ... in this case profile is the group name.

//file = @"C:\Program Files (x86)\Cisco\Cisco AnyConnect Secure Mobility Client\vpncli.exe"
var file = vpnInfo.ExecutablePath;
var host = vpnInfo.Host;
var profile = vpnInfo.ProfileName;
var user = vpnInfo.User;
var pass = vpnInfo.Password;
var confirm = "y";

var proc = new Process
{
    StartInfo = new ProcessStartInfo
    {
        FileName = file,
        Arguments = string.Format("-s"),
        UseShellExecute = false,
        RedirectStandardInput = true,
        RedirectStandardOutput = true,
        RedirectStandardError = true,
    }
};

proc.OutputDataReceived += (s, a) => stdOut.AppendLine(a.Data);
proc.ErrorDataReceived += (s, a) => stdOut.AppendLine(a.Data);

//make sure it is not running, otherwise connection will fail
var procFilter = new HashSet<string>() { "vpnui", "vpncli" };
var existingProcs = Process.GetProcesses().Where(p => procFilter.Contains(p.ProcessName));
if (existingProcs.Any())
{
    foreach (var p in existingProcs)
    {
        p.Kill();
    }
}

proc.Start();
proc.BeginOutputReadLine();

//simulate profile file
var simProfile = string.Format("{1}{0}{2}{0}{3}{0}{4}{0}{5}{0}"
    , Environment.NewLine
    , string.Format("connect {0}", host)
    , profile
    , user
    , pass
    , confirm
    );

proc.StandardInput.Write(simProfile);
proc.StandardInput.Flush();

//todo: these should be configurable values
var waitTime = 500; //in ms
var maxWait = 10;

var count = 0;
var output = stdOut.ToString();
while (!output.Contains("state: Connected"))
{
    if (count > maxWait)
        throw new Exception("Unable to connect to VPN.");

    Thread.Sleep(waitTime);
    output = stdOut.ToString();
    count++;
}
stdOut.Append("VPN connection established! ...");
Meltwater answered 6/10, 2015 at 14:2 Comment(0)
O
1

Building on Brayden Hancock's answer, I built a solution that reads the password from the macOS Keychain. As a first step, I added a new password item with the account field set to mycompany-vpn via the Keychain Access app. The first part of the script reads that item back from the keychain and extracts the password using the ruby snippet, the expect script section does the rest.

#!/usr/bin/env bash
get_pw () {
    security 2>&1 >/dev/null find-generic-password -ga mycompany-vpn \
    |ruby -e 'print $1 if STDIN.gets =~ /^password: "(.*)"$/'
}

USER=username
ADDR=vpn.company.com
PASSWORD=$(get_pw)

/usr/bin/expect -f - <<EOD
set timeout 10

spawn /opt/cisco/anyconnect/bin/vpn connect $ADDR
expect "\r\nUsername:*" {send -- "$USER\r"}
expect "Password: " {send -- "$PASSWORD\r"}
expect "Connected"
EOD

Oletta answered 11/6, 2019 at 15:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.