CISCO ACI Fabric Authentication via REST API

Modern networking devices expose REST API to provide a standard and consistent way to interact with the devices, simplifying configuration, monitoring, and management tasks.

Many vendor switches are capable of exposing REST API. Some examples are:

  • Cisco:
    • Cisco Nexus Series: Nexus 9000, 7000, 3000, and 2000 series.
    • Cisco Catalyst Series: Catalyst 9000, 3850, 3650, and 2960 series.
    • Cisco Meraki: All Meraki switches have REST API support through the Meraki Dashboard.
  • Arista Networks:
    • Arista 7000 Series: 7500R, 7280R, 7050X, and 7020R series.
  • Juniper Networks:
    • Juniper EX Series: EX9200, EX4600, EX4300, and EX2300 series.
    • Juniper QFX Series: QFX10000, QFX5200, and QFX5100 series.

Cisco APIC REST API Overview

Cisco Nexus 9000 can operate in ACI mode, of the SDDC (Software-Defined Data Center) solutions provided by Cisco. The Cisco Nexus 9000 switches are part of an ACI Fabric and are configured, monitored, and managed by the Application Policy Infrastructure Controller (APIC). The APIC can be a physical or virtual machine that facilitates network provisioning and control based on application needs, providing a unified operations model and simplifying management. It offers northbound REST APIs and operates as a distributed system with multiple controller instances. The APIC REST API accepts and returns HTTP or HTTPS messages that contain JavaScript Object Notation (JSON) or Extensible Markup Language (XML) documents.

APIC Log in via REST API

Before starting to use the APIC REST API for any purpose, it is a must first log in with the name and password of a configured user. In the section Authenticating and Maintaining an API Session it is possible to find all the details about APIC REST API for login.

When a login message is accepted, the API returns a data structure that includes a session timeout period in seconds and a token that represents the session. The default session timeout period is 10 minutes, which means that after 10 minutes of inactivity, the session is terminated. The token is also returned as a cookie in the HTTP response header.

In the next paragraphs, we explore 2 practical examples of how to use the REST API with CURL and POSTMAN. Then a Python class example is provided which can be used for further automation. Finally, a basic setup for APIC login and configuration with Ansible is shown.

Login with CURL

We need to use the POST method aaaLogin which logs in a user and opens a session. The message body contains an aaa:User object with the name and password attributes in XML format payload.

curl -X POST -k https://APIC_IP_ADDRESS/api/aaaLogin.xml -d '<aaaUser name="USERNAME" pwd="PASSWORD"/>' -c cookie.txt

The APIC controller returns a token stored in a cookie.txt file. It will last for 10 minutes. The cookie must be used to run any following REST API.

Example

For example, the REST API that uses XML format payload and creates a new Tenant, a new Application Profile, and a new EPG will look like this:

curl -b cookie.txt -X POST -k https://APIC_IP_ADDRESS/api/node/mo/uni.xml -d '<polUni> <fvTenant name="TENANT-NAME" descr="TENANT-NAME-DESC" status=""> <fvAp name="APP-PROF-NAME"> <fvAEPg name="NEW-EPG-NAME" > </fvAEPg> </fvAp> </fvTenant> </polUni>' -c cookie.txt

Login with POSTMAN

The login with PostMan will need to use these settings:

Method: POST
Body RAW, Type XML:

https://IP_ADDRESS_APIC/api/mo/aaaLogin.xml

<aaaUser name='USERNAME' pwd='PASSWORD'/>

You will receive a token stored in a cookie which PostMan will store for you, it will last 10 minutes.

PostMan usage example PostMan usage example PostMan Variable settings PostMan Variable settings

After that, you can run REST API.

Example

Method: GET

https://IP_ADDRESS_APIC/api/class/datetimeNtpProvider.xml

To get all the NTP providers configured in the fabric.

If you prefer JSON format, then change REST URL to:

Method: GET

https://IP_ADDRESS_APIC/api/class/datetimeNtpProvider.json

Login and Automation with Python

For automating the REST API usage with Python the class Session can be used, which is also available on GitHub.

import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

class Session(object):
    def __init__(self, apic_ip, apic_port, user, passwd):
        self.ip = apic_ip
        self.port = apic_port
        self.user = user
        self.passwd = passwd
        self.cookie = ''

    def set_cookie(self, cookie):
        self.cookie = cookie

    def get_cookie(self):
        try:
            auth_url = "https://%s:%s/api/aaaLogin.json" % (self.ip, self.port)
            auth_json = '{"aaaUser": {"attributes": {"name": "%s", "pwd": "%s"}}}'  % (self.user, self.passwd)
            print("Getting cookie from %s" % self.ip)
            session = requests.post(auth_url, data=auth_json, verify=False, timeout=5)
            session_json = session.json()
            token = session_json["imdata"][0]["aaaLogin"]["attributes"]["token"]
            return {'APIC-cookie': token}
        except requests.exceptions.Timeout:
            print("APIC %s timed out " % (self.ip))
        except requests.exceptions.ConnectionError:
            print("Connection error can't log in to APIC %s with user %s" % (self.ip, self.user))

    def apic_json_get(self, url):
        get_json_url = "https://%s:%s/api/class/%s.json" % (self.ip, self.port, url)
        json_get = requests.get(get_json_url, cookies=self.cookie, verify=False)
        print(get_json_url)
        if json_get.status_code != 200:
            print(json_get.text)
        else:
            return json_get.text

    def apic_json_post(self, url, data):
        post_json_url = "https://%s:%s/%s.json" % (self.ip, self.port, url)
        json_post = requests.post(post_json_url, cookies=self.cookie, data=data, verify=False)
        print(post_json_url)
        if json_post.status_code != 200:
            print(json_post.text)
        else:
            return json_post.text

The Session class allows users to login with the APIC and interact with it using GET and POST methods with JSON format payload and the cookie provided by the login. It uses the Python requests library.

Usage Example

apic_ip = 'APIC_IP_ADDRESS'
apic_port = '443'  # Default port for APIC for HTTPS
username = 'USERNAME'
password = 'PASSWORD'

# Create a session instance
session = Session(apic_ip, apic_port, username, password)

# Get the cookie
cookie = session.get_cookie()

# Set the cookie in the session
session.set_cookie(cookie)

# URL for the POST request
url = 'api/node/mo/uni/'

# Data for the POST request (JSON format)
data = '''
{
    "polUni": {
        "fvTenant": {
            "attributes": {
                "name": "TENANT-NAME",
                "descr": "TENANT-NAME-DESC",
                "status": ""
            },
            "children": [
                {
                    "fvAp": {
                        "attributes": {
                            "name": "APP-PROF-NAME"
                        },
                        "children": [
                            {
                                "fvAEPg": {
                                    "attributes": {
                                        "name": "NEW-EPG-NAME"
                                    }
                                }
                            }
                        ]
                    }
                }
            ]
        }
    }
}
'''

# Perform the POST request using the session
response = session.apic_json_post(url, data)

print(response)

Authentication and Configuration with Ansible

The Ansible ACI modules provide a user-friendly interface for managing the ACI Fabric environment using Ansible playbooks. On my GitHub a repository is available with a practical example.

In most cases, Ansible modules for network devices do not run on the network devices or controller but talk directly to the APIC's REST interface. This is also the case of the APIC.

Using aci_rest Module

A lot of ACI modules exist in the Ansible distribution, and the most common actions can be performed with these existing modules, there's always something that may not be possible with off-the-shelf modules.

For this purpose, the aci_rest module provides direct access to the APIC REST API and enables you to perform any task not already covered by the existing modules.

The aci_rest module accepts the native XML and JSON payloads and can accept the inline YAML payload. The XML payload requires you to use a path ending with .xml whereas JSON or YAML require the path to end with .json.

Modifications require the use of POST or DELETE methods, whereas doing just queries requires the GET method.

Password-based authentication is very simple to work with, but it is not the most efficient form of authentication from ACI's point-of-view as it requires a login every time an Ansible task is executed and an open session to work. Password-based authentication may trigger anti-DoS measures in recent ACI versions that result in HTTP 503 errors and login failures. To avoid this, it is possible to use the more efficient Signature-based authentication.

Example

The Ansible playbook is written in YAML. It creates a tenant using signature-based authentication and querying it afterward using password authentication:

- name: ACI Configuration
  hosts: localhost
  gather_facts: no
  vars:
    apic_host: 'APIC_IP_ADDRESS'
    apic_username: 'USERNAME'
    apic_password: 'PASSWORD'
    apic_private_key: 'pki/admin.key'
    apic_validate_certs: false

  tasks:
    - name: Add a tenant using inline YAML
      cisco.aci.aci_rest:
        host: '{{ apic_host }}'  # The IP address or hostname of your APIC
        username: '{{ apic_username }}'  # The username for authentication
        private_key: '{{ apic_private_key }}'  # The private key of the user for authentication
        validate_certs: false  # Whether to validate SSL certificates
        path: /api/mo/uni.json  # The API endpoint for creating a tenant
        method: post  # The HTTP method to use
        content:  # The content of the request, in YAML format
          fvTenant:
            attributes:
              name: TENANT-NAME # The name of the tenant to create
              descr: TENANT-NAME-DESC  # The description of the tenant
      delegate_to: localhost  # Run this task on the local machine

    - name: Get tenants using password authentication
      cisco.aci.aci_rest:
        host: '{{ apic_host }}'  # The IP address or hostname of your APIC
        username: '{{ apic_username }}'  # The username for authentication
        password: '{{ apic_password }}'  # The password for authentication
        method: get  # The HTTP method to use
        path: /api/node/class/fvTenant.json  # The API endpoint for querying tenants
      delegate_to: localhost  # Run this task on the local machine
      register: query_result  # Register the output of the query in a variable
    

Note that the SSL certificate has nothing to do with the authentication certificate.

SSL certificate is used between APIC and client for secure communication between them by encrypting the data transmitted over the network. The parameter apic_validate_certs set to false avoid the client to validate the certificate provided by the APIC against a trusted CA.

An authentication certificate is used to verify the client's identity to the APIC without using a password. The APIC contains the self-signed certificate for that specific local user. The ansible task uses the user's private key for authentication. The APIC challenges the client by asking to encrypt data by using its private key contained in the file pki/admin.key. The client sends the encrypted data to the APIC. The APIC can decrypt the data with the user public key in the admin.crt file.

Usage

Save the playbook to a file, for example, aci_config.yml.

Run the playbook using the ansible-playbook command:

# ansible-playbook aci_config.yml
Security Best Practice: Never hardcode passwords. Use environment variables, Ansible Vault, or external secret management systems.

References

#aci #restapi #python #ansible