Configuring Cisco ASA firewall with REST API

In this scenario I use Python requests library to add and verify new Network Objects and Access Rules in an ASA firewall running on GNS3.

GNS3 Topology:

Enabling REST API on Cisco ASA firewall:

interface Management0/0
 nameif MGMT
 security-level 0
 ip address 192.168.240.202 255.255.255.0

username automation password P@ssw0rd 15

aaa authentication http console LOCAL

http server enable
http 192.168.240.0 255.255.255.0 MGMT

rest-api image boot:/asa-restapi-132300-lfbff-k8.SPA
rest-api agent

I have created an inventory file for the devices in this scenario and include it in my Python script:

iosxrv_1 = {
    "ip": "192.168.240.201",
    "netconf_port": "830",
    "restconf_port": "443",
    "username": "automation",
    "password": "P@ssw0rd",
    "netconf_device_params": {'name':'csr'}
}

asav_2 = {
    "ip": "192.168.240.202",
    "netconf_port": None,
    "restconf_port": "443",
    "username": "automation",
    "password": "P@ssw0rd",
    "netconf_device_params": None
}

This is a simple template file for Network Object and Access Rule configuration using REST API on Cisco ASA firewall. I will include this file in my Python script as well:

read_network_object = {
    "api" : "/api/objects/networkobjects/{NAME}"
}

add_network_object = {
    "api" : "/api/objects/networkobjects/{NAME}",
    "data" : """{{
  "host": {{
    "kind": "{KIND}",
    "value": "{IP}"
  }},
  "kind": "object#NetworkObj",
  "name": "{NAME}",
  "objectId": "{ID}"
}}"""
}

read_acl = {
    "api" : "/api/objects/extendedacls/{NAME}"
}


read_ace = {
    "api" : "/api/objects/extendedacls/{ACL}/aces/{ID}"
}

add_ace = {
    "api" : "/api/objects/extendedacls/{ACL}/aces",
    "data" : """
{{
  "destinationAddress": {{
    "kind": "objectRef#NetworkObj",
    "objectId": "{DEST_OBJECT}"
  }},
  "destinationService": {{
    "kind": "NetworkProtocol",
    "value": "{DEST_SERVICE}"
  }},
  "permit": {PERMIT},
  "active": {ACTIVE},
  "sourceAddress": {{
    "kind": "objectRef#NetworkObj",
    "objectId": "{SRC_OBJECT}"
  }}
}}"""
}

write_memory = {
    "api" : "/api/commands/writemem"
}

And here is the script:

from inventory import asav_2
import requests
import asa_rc_templates as templates

def restconf_init (device):
    restconf_base_url = "https://{IP}:{PORT}".format(IP=device["ip"],
                                                     PORT=device["restconf_port"])
    restconf_auth = (device["username"],
                     device["password"])
    restconf_headers = {'Content-Type': 'application/json'}
    return restconf_base_url, restconf_auth, restconf_headers

def add_network_object (device,object):
    restconf_base_url, restconf_auth, restconf_headers = restconf_init(device)
    restconf_api = templates.add_network_object["api"].format(**object)
    restconf_url = restconf_base_url + restconf_api
    restconf_data = templates.add_network_object["data"].format(**object)

    return requests.post(restconf_url,
                         restconf_data,
                         headers=restconf_headers,
                         auth=restconf_auth,
                         verify=False)

def read_network_object(device,name=""):
    restconf_base_url, restconf_auth, restconf_headers = restconf_init(device)
    restconf_api = templates.read_network_object["api"].format(NAME=name)
    restconf_url = restconf_base_url + restconf_api

    return requests.get(restconf_url,
                        headers=restconf_headers,
                        auth=restconf_auth,
                        verify=False)

def write_memory(device):
    restconf_base_url, restconf_auth, restconf_headers = restconf_init(device)
    restconf_api = templates.write_memory["api"]
    restconf_url = restconf_base_url + restconf_api

    return requests.post(restconf_url,
                         headers=restconf_headers,
                         auth=restconf_auth,
                         verify=False)

def read_acl(device,name=""):
    restconf_base_url, restconf_auth, restconf_headers = restconf_init(device)
    restconf_api = templates.read_acl["api"].format(NAME=name)
    restconf_url = restconf_base_url + restconf_api

    return requests.get(restconf_url,
                        headers=restconf_headers,
                        auth=restconf_auth,
                        verify=False)

def read_ace (device,acl,id=""):
    restconf_base_url, restconf_auth, restconf_headers = restconf_init(device)
    restconf_api = templates.read_ace["api"].format(ACL=acl,ID=id)
    restconf_url = restconf_base_url + restconf_api

    return requests.get(restconf_url,
                        headers=restconf_headers,
                        auth=restconf_auth,
                        verify=False)

def add_ace (device,rule):
    restconf_base_url, restconf_auth, restconf_headers = restconf_init(device)
    restconf_api = templates.add_ace["api"].format(**rule)
    restconf_url = restconf_base_url + restconf_api
    restconf_data = templates.add_ace["data"].format(**rule)

    return requests.post(restconf_url,
                         restconf_data,
                         headers=restconf_headers,
                         auth=restconf_auth,
                         verify=False)

new_rule = {
    "ACL": "inside_access_in_1",
    "SRC_OBJECT": "NOBJ-IOSXRv-1-LO_1",
    "DEST_SERVICE": "tcp",
    "DEST_OBJECT": "NOBJ-PC-3",
    "PERMIT": "true",
    "ACTIVE": "true"}

new_object = {"NAME" : "NOBJ-IOSXRv-1-LO_1",
              "ID" : "NOBJ-IOSXRv-1-LO_0",
              "KIND" : "IPv4Address",
              "IP" :"10.0.1.1"}

response = add_network_object(asav_2,new_object)
response = add_ace(asav_2,new_rule)
response = write_memory(asav_2)

The only point is how to create the JSON formatted REST API template file. We need to use the documentation page available on the ASA firewall (https://<server-ip>/doc/) to create the JSON code and turn the JSON code to a simple template by substituting the values with template {PARAMETERS}.

One thought on “Configuring Cisco ASA firewall with REST API”

  1. Very nice code, clean and well structured.

    I am unable to get my write_memory request working properly, always responds with 400.

    I am using, from the requests package, the command:

    req.request(‘POST’, ‘https:///api/commands/writemem’, headers=AuthToken, verify=False)

    Have tried your way, same result.
    Have also added the {‘Content-Type’: ‘application/json’}, REST API agent headers, and so forth with no improvement.

    Any thoughts?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.