API Conversations and Why They're Important

API Interactions are designed to be easy

Believe it or not, the IT infrastructure industry is trying to make things easier by building API access out.

Programmatic interfaces are a new mental model competing for brain-space with GUI and CLI implementations; we need to play to its strengths:

  • Parsing output from a computer automatically
  • Easy batch entry
  • Ensuring that a thing configured matches the thing requested
  • (Usually) easy pass/fail responses based on HTTP response codes

API Conversation Structure

<a href="conversing_apis_001.svg">API Conversations</a>

RESTful, SOAP, and NetCONF interfaces all interleave the concept of a conversation with the Hypertext Transfer Protocol (HTTP) standards:

  • Identify Yourself
  • Ask for things
  • Get a Response

NB: Most API implementers (vendors) will implement API standards very loosely!

NB: RESTful interfaces will be used for the examples in this post, as it's quickly becoming the most common.

Authentication

Authentication is where most new users get stuck. It's complicated, but usually an API provider will also leverage an SDK to simplify the authentication process when you "graduate" to a programming language.

This only covers what a client has to do to perform API work - implementing an API (and authorization with it) is considerably more complex.

Basic Authentication

The title says it all, this authentication schema just uses Base64 encoding (ASCII-formatted binary) to place your username and password in an HTTP header:

1Authorization: Basic {{ Base64 String }}

If this approach makes you feel a little uncomfortable, it should. This is not a secure way to execute commands; it puts your credentials at risk. There are a few ways to mitigate the security risks:

  • Ensure that any endpoint you interact with uses strong cryptography and has a valid certificate
    • Postman and cURL do half of that by default
    • If it's an API you manage, issue it a client-recognizable certificate and tune cryptography upwards
  • Try using a "read-only" account unless a change is required
  • Use Basic Authentication as a method to establish a session token

Most new API sessions will at least start using Basic authentication, so these guidelines will apply unless client certificate authentication is used.

Here are some examples of client authentication use:

cURL
1curl -u {{ username }} -p {{ password }} https://{{ api_endpoint }}/get_stuff
2curl --header 'Authorization: Basic {{ string }}' https://{{ api_endpoint }}/get_stuff
Python 3
 1import requests
 2import sys
 3
 4try:
 5    # This example is to generate a bearer token with Cisco's Firepower Threat Defense
 6    do_api_url = "https://{{fmc_ip}}/api/fmc_platform/v1/auth/generatetoken"
 7    # The Requests library supports converting to Base64 from a tuple to keep things simple
 8    do_api_request = requests.request(
 9        "POST",
10        url= do_api_url,
11        auth=(username, password)
12    )
13    do_api_request.raise_for_status()
14except requests.Timeout:
15    sys.exit("TCP Timeout!")
16except HTTPError as e:
17    sys.exit("HTTP Error Found! " + do_api_request.status_code + " " + str(e))

Token Authentication

Most API Providers will require you to use safer authentication for continued requests. This is a good thing - but it does add of work. Usually, Basic or Certificate authentication is used to establish a timeboxed token for future authentication.

These tokens have several forms:

  • Bearer Token: Simple. They'll use the Authorization header per the HTTP standard:
    • `Authorization: Bearer '{{ token }}'
  • JSON Web Token: Standardized and much more robust. Base64 encodes a JSON payload. It supports signing of all requests and provides context in all future requests, which makes things easier to parse on the server side
  • I'll just call this third category weird stuff. Vendors loosely follow standards, so they'll typically have headers that are specific to their platform. The mechanics remain the same, but the header name won't be Authorization

Here are some examples:

cURL
 1# GitHub follows the Bearer standard
 2curl --request GET \
 3--url "https://api.github.com/octocat" \
 4--header "Authorization: Bearer YOUR-TOKEN" \
 5--header "X-GitHub-Api-Version: 2022-11-28"
 6# VMware vSphere uses `vmware-api-session-id` as an API key
 7curl --location --globoff 'https://{{vsphere_vcenter}}/api/content/library/{{vsphere_base_images_library}}' \
 8--header 'vmware-api-session-id: {{vsphere_key}}'
 9# Cisco FirePower uses `X-auth-access-token` as a custom header
10curl --location --globoff 'https://{{fmc_ip}}/api/fmc_config/v1/domain/{{domain_uuid}}/devices/devicerecords' \
11--header 'X-auth-access-token: {{auth_token}}'
Python 3
 1# Cisco DNA Center uses `X-Auth-Token` as a custom header
 2import requests
 3import json
 4import sys
 5
 6url = "https://sandboxdnac2.cisco.com/dna/intent/api/v1/site"
 7
 8payload = {}
 9headers = {
10  'Content-Type': 'application/json',
11  'X-Auth-Token': '{{ token }}'
12}
13
14try:
15    response = requests.request("GET", url, headers=headers, data=payload)
16    response.raise_for_status()
17    print(response.text)
18except requests.Timeout:
19    sys.exit("TCP Timeout!")
20except HTTPError as e:
21    sys.exit("HTTP Error Found! " + do_api_request.status_code + " " + str(e))

Verbs

In API Terminology, clients (and servers) should identify what type of operation they intend to execute with a matching HTTP Method:

  • GET: Read only actions, typically without a submitted payload.
  • POST: Frequently (mis)used. Can be read only, can be a non-idemopotent change, or it could be an idempotent change. By standard, POST methods create an object in a tree or change the state machine - but nearly every vendor defaults to this method.
  • PUT: Meant to be a "safe" method that either updates an existing record, or creates a new object. Usually, if an API provider uses this verb, they're using it properly.
  • PATCH: PUT is idempotent, which is safe from an API perspective, but not from a production service perspective. It will wipe and recreate a service - the PATCH verb indicates a messier but production safe action.
  • DELETE: Idempotent, but it does delete a resource, so I'd be careful considering it production-safe.

Why?

Infrastructure operators benefit from programmatic interface usage, but the pattern differs.

Researching secure, well-managed authentication methods leaves a lot of room for improvement with the commodity resources available today, and a security engineer with even some basic API knowledge can quickly and easily secure resources with these rules. It's easy to imagine how firewalling an API service can quickly become secure:

1allow user any GET under /api/v3/healthcheck
2allow user admin PUT under /*
3deny user off-net any under /*
4deny user any DELETE under /*

Essentially, any load balancer can become a highly granular firewall for an API.

APIs are easy to parse due to their dictionary formats. Prior to their inception, network engineers had to write screen scrapers like Scrapli and "guess" what value in a given table has what meaning. YAML and JSON formats allow language-native association between values not normally present in a tab-separated table like a routing table.

The most important advantage to API automation, though, is for change safety. No matter what discipline you follow, invasive IT work is completed at night and with a strict schedule - one that does not promote thoroughness. Leverage APIs to check if your system is healthy - only you as the engineer know how to do that - and make change windows less stressful.

Some Tips

  • jq is a command-line utility that parses JSON - and lets you navigate or prune it to relevant data. Think of it like grep for JSON.
  • If you do complex or scaled work with Python, check out tqdm to build progress bars for your work.

Posts in this Series