Part 1 – Give Your Playbook a Performance Increase by Using Signature-based Authentication.

If you’re using Ansible to monitor, maintain, or configure your Cisco Application Centric Infrastructure (ACI) fabric, chances are you’ve seen or are using tasks that use password-based authentication methods. While this isn’t bad on its own and will work for a small playbook, it’s not efficient when your playbook makes a lot of API calls to your APIC. Cisco introduced an anti-DoS mechanism in ACI version 3.1 which you can possibly trigger by sending a large number of password-based login requests to an APIC. This session throttling can cause login failures, HTTP 503 errors, or cause your playbook to fail or perform slowly.

Enter signature-based authentication. By leveraging signature-based authentication, you rely on certificates to handle the authentication part of a session/request. Very simply put, you generate a private and public key pair and place a copy of your public key on the server. Messages can only be deciphered if the public and private keys are correctly matched up during a session between the server and user. This makes for a more efficient and potentially more secure playbook. Keys can be upwards of 4096 bits in length, as opposed to someone’s 8-character, easily brute-forced password.   

Below, I’ll show you how I’ve used Cisco ACI modules for Ansible to programmatically create a local user on the APIC, generate a certificate for said user, and assign that certificate to the user created on the APIC. I’ll also show you how to remove the programmatically created user when your playbook is complete, if you so desire.  

Pre-requisites

You’ll need the pyopenssl module for this to work.

Mac & Ubuntu

sudo -H pip3 install pyopenssl

Windows

HAHAHA, funny.  Use WSL (Windows Subsystem for Linux)

First, start by generating a certificate for your user account to use. Don’t forget to define the variable “cert_user” somewhere in your playbook if you’re copy/pasting this.

– name: Generating Certificate
command: “openssl req -new -newkey rsa:2048 -days 36500 -nodes -x509 -keyout {{ cert_user }}.key -out {{ cert_user }}.crt -subj ‘/CN={{ cert_user }}/O=Lumos Cloud/C=US'”

Next, while using password-based authentication, we’ll create our signature based auth user for the other tasks to use.

- name: Creating Certificate Based Authentication User {{ cert_user }}
  aci_rest:
    host: "{{ inventory_hostname }}"
    username: "{{ user }}"
    password: "{{ pass }}"
    use_ssl: yes
    validate_certs: false
    path: /api/mo/uni.json
    method: post
    content: {
            "aaaUser": {
              "attributes": {
                "accountStatus": "active",
                "clearPwdHistory": "yes",
                "dn": "uni/userext/user-{{ cert_user }}",
                "expiration": "never",
                "expires": "no",
                "name": "{{ cert_user }}",
                "pwdLifeTime": "no-password-expire",
                "status": "created,modified",
                "pwd": "{{ pass }}"
              },
              "children": [
                {
                  "aaaUserDomain": {
                    "attributes": {
                      "annotation": "",
                      "childAction": "",
                      "descr": "",
                      "name": "all",
                      "nameAlias": "",
                      "ownerKey": "",
                      "ownerTag": "",
                      "rn": "userdomain-all",
                      "status": "created,modified"
                    },
                    "children": [
                      {
                        "aaaUserRole": {
                          "attributes": {
                            "annotation": "",
                            "childAction": "",
                            "descr": "",
                            "name": "read-all",
                            "nameAlias": "",
                            "ownerKey": "",
                            "ownerTag": "",
                            "privType": "readPriv",
                            "rn": "role-read-all",
                            "status": "created,modified"
                          }
                        }
                      },
                      {
                        "aaaUserRole": {
                          "attributes": {
                            "annotation": "",
                            "descr": "",
                            "name": "admin",
                            "nameAlias": "",
                            "ownerKey": "",
                            "ownerTag": "",
                            "privType": "writePriv",
                            "rn": "role-admin",
                            "status": "created,modified"
                          }
                        }
                      }
                    ]
                  }
                }
              ]
            }
          }

Once the user has been created and assigned to the appropriate roles, we’ll attach the certificate we generated previously.

- name: Installing Certificate For {{ cert_user }}
  aci_aaa_user_certificate:
    host: "{{ inventory_hostname }}"
    username: "{{ user }}"
    password: "{{ pass }}"
    use_ssl: yes
    validate_certs: false
    aaa_user: "{{ cert_user }}"
    certificate_name: "{{ cert_user }}"
    certificate: "{{ lookup('file', '{{ cert_user }}.crt') }}"

Now that a user with a valid certificate has been created on the APIC, all remaining tasks can use this user/cert for API calls.

- name: Task that will query AEP's using signature-based authentication
  aci_rest:
    host: "{{ inventory_hostname }}"
    username: "{{ cert_user }}"
    private_key: "{{ cert_user }}.key"
    certificate_name: "{{ cert_user }}" 
    use_ssl: yes
    validate_certs: false
    path: /api/node/class/infraAttEntityP.json
    method: get

Removing the programmatically created user is as simple as changing the json “status” attribute to “deleted”.

- name: Removing Certificate Based Authentication User
  aci_rest:
    host: "{{ inventory_hostname }}"
    username: "{{ user }}"
    password: "{{ pass }}"
    use_ssl: yes
    validate_certs: false
    path: /api/mo/uni.json
    method: post
    content: {
            "aaaUser": {
              "attributes": {
                "accountStatus": "active",
                "clearPwdHistory": "yes",
                "dn": "uni/userext/user-{{ cert_user }}",
                "expiration": "never",
                "expires": "no",
                "name": "{{ cert_user }}",
                "pwdLifeTime": "no-password-expire",
                "status": "deleted",
                "pwd": "{{ pass }}"
              }
          }
      }

Stay Tuned
We’re just getting started with demystifying Ansible for Cisco Application Centric Infrastructure (ACI). Check back soon for part two of our series! In the meantime, if you have questions about Cisco ACI Modules for Ansible, feel free to reach out; we’d be happy to help!

Ref: https://docs.ansible.com/ansible/latest/scenario_guides/guide_aci.html
sig-based-auth (1).txt
Displaying sig-based-auth (1).txt.