Connect CISCO Anyconnect VPN via bash

前端 未结 5 1634
滥情空心
滥情空心 2020-12-25 08:52

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          


        
相关标签:
5条回答
  • 2020-12-25 09:07

    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.
    0 讨论(0)
  • 2020-12-25 09:10

    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
    
    
    0 讨论(0)
  • 2020-12-25 09:16

    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>"
    

    Real easy! :D

    0 讨论(0)
  • 2020-12-25 09:20

    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! ...");
    
    0 讨论(0)
  • 2020-12-25 09:27

    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.

    0 讨论(0)
提交回复
热议问题