Linux AnyConnect Overview & Ansible Automation

"The What?" - In this blog I want to cover a brief overview of one way to install AnyConnect (AC) on a linux client running a supported OS. Once I cover the overview I intend on covering a few Ansible playbooks that will automate several of the tasks included in the provided manual install overview.


"The Why?" - Sharing my experiences and growth to assist others with similar AnyConnect desires from an automation perspective as well as understanding the install process on Linux clients.


"The How?" - First I will cover the step-by-step manual process on how to install and setup AC. Note that I am not sharing the VPN concentrator side config in this blog as my desire is to share ways to automate the install and setup process with Ansible. Just know that the remote device is setup to support certificate authc against an ASA, with username/pass against ISE for proper authz. The username/pass is actually an Active Directory account which ISE maps to. So on client side, the VPN onboarding process involves linux client certificate auth followed by username and pass auth.


Manual overview is as follows:

-Prepping the client:

- The following files must be placed on the client in their respective directories:

  • anyconnect-linux64-4.10.XXX-predeploy-k9.tar.gz (use latest & greatest & whatever is supported on VPN concentrator)

  • CA Cert Chains (destination /opt/.cisco/certificates/ca/)

  • CA Root Cert (destination /opt/.cisco/certificates/ca/)

  • CA Int Cert (destination /opt/.cisco/certificates/ca/)

  • hostname.key (destination /opt/.cisco/certificates/client/private/)

  • hostname.pem (destination /opt/.cisco/certificates/client/)

  • LINUX_VPN.xml (destination /opt/cisco/anyconnect/profile/)

Both the client side and VPN client side cert chains need to be imported. All chains need to be in .pem format.


-Install the software:

[Hostname]$ tar -xvf anyconnect-linux64-4.10.XXX-predeploy-k9.tar.gz

Change to the VPN folder location to run the install

[Hostname]$ cd anyconnect-linux64-4.10.XXX/vpn/

Install the VPN core module with the following command (elevated permission required)

[Hostname]$ ./vpn_install.sh

Installing Cisco AnyConnect Secure Mobility Client...

Once completed, you should see the Cisco AnyConnect in you available software under Applications – Internet or within you application search tool.


-Setup client for identity certificate:

Create a Private Key first.

In order to successfully VPN, you will need to generate a private key, extract a CSR, and provide it to the PKI team to generate a certificate or whomever manages your certificates.


Identify your computers hostname

[Hostname]$ hostname

[Hostname]$ openssl genrsa –aes256 -out hostname.key 2048

Generating RSA private key, 2048 bit long modulus

..................................+++

............................................+++

e is 65537 (0x10001)

Enter pass phrase for hostname.key:

Verifying - Enter pass phrase for hostname.key:

[Hostname]$ openssl req -new -key hostname.key -out hostname.csr (You will input your key password to generate the csr)

Country Name (2 letter code) [XX]:US

State or Province Name (full name) []:xx

Locality Name (eg, city) [Default City]:xx

Organization Name (eg, company) [Default Company Ltd]:xx

Organizational Unit Name (eg, section) []:xx

Common Name (eg, your name or your server's hostname) []:”your FQDN”

Email Address []:

Please enter the following 'extra' attributes

to be sent with your certificate request

A challenge password []:

An optional company name []:


You will now provide the hostname.csr file to the team to generate a computer identity certificate.


-Copy the CA certificates to the ca store:

[Hostname]$ sudo cp ca_root.pem /opt/.cisco/certificates/ca/

[Hostname]$ sudo cp ca_sub.pem /opt/.cisco/certificates/ca/

-Copy your computers private key to the private key store:

Note: By default, the path for installing client certificate and the private key is not present. It needs to be manually created with the following command.

[Hostname]$ sudo mkdir -p /opt/.cisco/certificates/client/private/

[Hostname]$ sudo cp hostname.key /opt/.cisco/certificates/client/private/


-Copy the VPN profile to the profile folder

[Hostname]$ sudo cp LINUX_VPN.xml /opt/cisco/anyconnect/profile/


-Copy the provided client identity cert pem file to the client store

[Hostname]$ sudo cp hostname.pem /opt/.cisco/certificates/client/

Once the final client certificate has been copied to the client store, you are ready to test your VPN connection.


-Finally at this point the client is ready to attempt to establish a VPN connection.


Ok so yeah, that is a lot of manual steps to ensure a linux client is properly provisioned. Now I want to cover 3 separate Ansible playbooks that can be used as a boiler template to automate some of the processes listed above.


So the first part, automating the AC install process with Ansible:

Couple notes: Inventory must be setup properly, the ac variable needs to be passed (the exact file.tar.gz) and you will want to update the dirs to match the proper directory. Follow the names to understand what each play is doing.

---
- name: Linux AC Install
  hosts: all
  become: yes
  become_user: root
  become_method: su
  tasks:
     - name: copy Archive file to system
       copy:
         src: /usr/share/ansible/ac_installer/{{ ac }}
         dest: /opt
         mode: '0755'

     - name: Change directory to package locate and Extract Archive
       unarchive:
         src: /opt/{{ ac }}
         dest: /opt
         owner: root
         group: root
         remote_src: yes

     - name: Delete AC Archive
       shell: "rm -f any*.tar.gz"
       args:
         chdir: /opt/

     - name: Change file permissions
       file:
         path: /opt/anyconnect-linux64-4.10.XXX/vpn/vpn_install.sh
         mode: '0755'

     - name: Execute the script
       shell: "echo 'y' | /opt/anyconnect-linux64-4.10.XXX/vpn/vpn_install.sh"
       args:
         chdir: /opt/anyconnect-linux64-4.10.XXX/vpn/
       register: output

     - name: Print output
       debug:
         msg: "{{ output.stdout_lines }}"

Second part, automating client certificate chain and vpn profile setup:

Note: tweak inventory as needed, there is a conditional to move VPN profile based on user input, & of course tweak other plays as needed to meet your environment.

---
- name: Move CA Certs and VPN Profile
  hosts: all
  gather_facts: yes
  become: yes
  become_user: root
  become_method: su
  tasks: 
    - name: 
      set_fact:
       profile: "Linux_1.xml"
      when: group_id == "Linux_1"
  
    - name: 
      set_fact:
       profile: "Linux_2.xml"
      when: group_id == "Linux_2"

    - name: Copy CA Certs to Client
      copy:
        src: "{{ item.src }}"
        dest: /opt/.cisco/certificates/ca/
        mode: '0644'
        force: no
      with_items:
        - src: /usr/share/ansible/certs/ca_root.pem 
        - src: /usr/share/ansible/certs/ca_sub.pem

    - name: Verify Certs
      shell: "ls | grep pem"
      args:
       chdir: /opt/.cisco/certificates/ca
      register: certs_output

    - name: Printing Certs Verification
      debug:
        msg: "{{ certs_output.stdout_lines }}"

    - name: Move VPN Client Profile
      copy:
        src: /usr/share/ansible/vpn_profs/{{ profile }}
        dest: /opt/cisco/anyconnect/profile/
        mode: '0644'
        force: no

    - name: Verify VPN Profile
      shell: "ls | grep xml"
      args:
       chdir: /opt/cisco/anyconnect/profile/
      register: vpn_prof_output

    - name: Printing VPN Prof Verification
      debug:
        msg: "{{ vpn_prof_output.stdout_lines }}"

Lastly, automate SOME of the identity cert processes:

The job here is to automate the openssl items, manipulate some directories, and generate a csr. Pay attention to the vars required. Also, the openssl conf file is about as basic as it gets to automate some of that stuff.

---
- name: Linux Client Key and CSR Generation
  hosts: all
  gather_facts: yes
  become: yes
  become_user: root
  become_method: su
  tasks:
     - name: Copy cert.cfg to Client
       copy:
         src: /usr/share/ansible/client_cert.cfg
         dest: /opt
         mode: '0755'

     - name: Generate Private Key
       command: "openssl genrsa -aes256 -passout pass:{{ password_var }} -out {{ ansible_hostname }}.key 2048"
       args:
         chdir: /opt

     - name: Generate CSR
       command: "openssl req -new -key {{ ansible_hostname }}.key -config client_cert.cfg -passin pass:{{ password_var }} -out {{ ansible_hostname }}.csr"
       args:
         chdir: /opt

     - name: Mkdir Private
       command: "mkdir -p /opt/.cisco/certificates/client/private/"
     
     - name: Move Private Key
       command: "mv {{ansible_hostname }}.key /opt/.cisco/certificates/client/private/"
       args:
         chdir: /opt

     - name: Verify Key and CSR Generation
       shell: "ls /opt/ | grep -E 'key|csr'"
       register: output

     - name: Key and CSR Verification
       debug:
           msg: "{{ output.stdout_lines }}"

Quick overview of the client_cert.cfg (update accordingly):

[req]
distinguished_name = req_distinguished_name
prompt = no
[req_distinguished_name]
C = XX
ST = XX
L = XX
O = XX
OU = XX
CN = "$HOSTNAME.domain"

Alright there you have it. As mentioned earlier you would definitely have to tweak the playbooks to meet your environment. My main idea here was to share Ansible boiler templates for several items relating to Linux AnyConnect so that you can automate a rather tedious manual install/setup (shared first above). Cheers and I hope this helps you in your journey!


0 comments

Recent Posts

See All

Using a Custom Ansible Module for ISE API Interaction

"The What?" - In this post I am going to provide a general understanding on how to use a custom Ansible module. A module is essentially a reusable standalone script that Ansible will run on your beha

Ansible URI Module Breakdown & ISE Example

"The What?" - In this blog I want to cover an important Ansible module for those looking to gain an understanding of how to use Ansible to consume APIs without the need for custom modules. The URI mo