Introdução
Este documento descreve um exemplo do uso do Python para fazer chamadas à API Rest.
Pré-requisitos
As ferramentas e dispositivos usados no guia são:
- Defesa contra ameaças (FTD) do Cisco Firepower
- Gerenciamento de dispositivos Cisco Firepower (FDM)
- SO Mac
- Texto Sublime
Requisitos
A Cisco recomenda que você tenha conhecimento destes tópicos:
- HTTPS
- API Rest
- Python
- Json
- Gerenciamento de dispositivos Firepower
- LDAP
Componentes Utilizados
As informações neste documento são baseadas nestas versões de software e hardware:
- Python 3. 11. 4
- API do FDM versão 6
- FTDv 7.3.1
As informações neste documento foram criadas a partir de dispositivos em um ambiente de laboratório específico. Todos os dispositivos utilizados neste documento foram iniciados com uma configuração (padrão) inicial. Se a rede estiver ativa, certifique-se de que você entenda o impacto potencial de qualquer comando.
Informações de Apoio
A intenção principal deste documento é guiá-lo pelas etapas de criação de um script Python para fazer chamadas à API.
O recurso específico a ser ajustado e criado é o LDAP e os mapas de atributos.
As chamadas de API feitas no guia são:
GET: Coletar informações do servidor
POST: criar um novo objeto no servidor
PUT: Atualizar um objeto existente no servidor
DELETE: Remove um objeto existente do servidor
Configurar
GET
Exemplo de Rest API GET para coletar dados em objetos existentes:
import requests
import json
import certifi
import urllib3
from pprint import pprint
from getpass import getpass
urllib3.disable_warnings()
#Data collection
u = input ('Input username: ')
p = getpass(prompt='Input password: ')
url = input('Input Hostname or IP address: ')
protocol = 'https://'
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def auth():
uri = '/api/fdm/v6/fdm/token'
token_url = protocol+url+uri
payload = {
'grant_type' : 'password',
'username' : u,
'password': p
}
response = requests.post(token_url, data=payload, verify=False)
#ONlY USE "verify=False" in a lab setting!
#Error Checking
if response.status_code == 400:
raise Exception("Error Received: {}".format(response.content))
else:
access_token = response.json()['access_token']
return access_token
token = auth()
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def getactivedirectory():
uri = "/api/fdm/v6/object/realms"
ad_url = protocol+url+uri
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization":"Bearer {}".format(token)
}
response = requests.get(ad_url, headers=headers, verify=False)
if response.status_code == 200:
AD = response.json()
return AD
else:
print(response.status_code)
pass
pprint(getactivedirectory())
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def revoke():
uri = '/api/fdm/v6/fdm/token'
token_url = protocol+url+uri
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization":"Bearer"
}
payload = {
'grant_type' : 'revoke_token',
"access_token": token,
"token_to_revoke": token,
}
response = requests.post(token_url, data=payload, verify=False)
if response.status_code == 200:
print("Access token revoked")
else:
print(response.status_code)
revoke()
POST
Esta seção descreve um exemplo de solicitação REST API POST para criar um novo objeto de mapa de atributos LDAP:
import requests
import json
import certifi
import urllib3
from pprint import pprint
from getpass import getpass
urllib3.disable_warnings()
#Data collection
u = input ('Input username: ')
p = getpass(prompt='Input password: ')
url = input('Input Hostname or IP address: ')
protocol = 'https://'
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def auth():
uri = '/api/fdm/v6/fdm/token'
token_url = protocol+url+uri
payload = {
'grant_type' : 'password',
'username' : u,
'password': p
}
response = requests.post(token_url, data=payload, verify=False)
#WARNING ONLY USE "verify=False" in a lab setting!
#Error Checking
if response.status_code == 400:
raise Exception("Error Received: {}".format(response.content))
else:
access_token = response.json()['access_token']
return access_token
token = auth()
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def postattributemap():
uri = "/api/fdm/v6/object/ldapattributemaps"
ad_url = protocol+url+uri
name = input('Object name> ')
group_policy = input('Group-Policy> ')
base = input('LDAP DN> ')
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization":"Bearer {}".format(token)
}
payload = {
"name": name,
"ldapAttributeMaps": [
{
"ldapName": "memberOf",
"ciscoName": "GROUP_POLICY",
"valueMappings": [
{
"ldapValue": base,
"ciscoValue": group_policy,
"type": "ldaptociscovaluemapping"
}
],
"type": "ldapattributemapping"
}
],
"type": "ldapattributemap"
}
data = json.dumps(payload)
response = requests.post(ad_url, headers=headers, data=data, verify=False)
if response.status_code == 200:
print(response.status_code)
print("Created LDAP attribute map")
map = response.json
return map
else:
print(response.status_code)
pprint(response.content)
pass
postattributemap()
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def revoke():
uri = '/api/fdm/v6/fdm/token'
token_url = protocol+url+uri
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization":"Bearer"
}
payload = {
'grant_type' : 'revoke_token',
"access_token": token,
"token_to_revoke": token,
}
response = requests.post(token_url, data=payload, verify=False)
if response.status_code == 200:
print("Access token revoked")
else:
print(response.status_code)
revoke()
PUT
Esta seção mostra um exemplo de um script Python fazendo uma chamada à API PUT Rest. Esta função adiciona o mapa de atributos LDAP às configurações existentes do Ative Diretory.
Observação: Observação: antes de continuar, as informações necessárias para atualizar o objeto devem ser coletadas por meio da função GET.
Territórios do Ative Diretory da URL1: /api/fdm/v6/object/realms/
Mapa de atributos LDAP URL2: /api/fdm/v6/object/ldapatributemaps
import requests
import json
import certifi
import urllib3
from pprint import pprint
from getpass import getpass
urllib3.disable_warnings()
#Data collection
u = input ('Input username: ')
p = getpass(prompt='Input password: ')
url = input('Input Hostname or IP address: ')
protocol = 'https://'
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def auth():
uri = '/api/fdm/v6/fdm/token'
token_url = protocol+url+uri
payload = {
'grant_type' : 'password',
'username' : u,
'password': p
}
response = requests.post(token_url, data=payload, verify=False)
#WARNING ONLY USE "verify=False" in a lab setting!
#Error Checking
if response.status_code == 400:
raise Exception("Error Received: {}".format(response.content))
else:
access_token = response.json()['access_token']
return access_token
token = auth()
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def put():
objId = input('object id of active directory object> ')
uri = "/api/fdm/v6/object/realms/"
ad_url = protocol+url+uri+objId
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization":"Bearer {}".format(token)
}
#Place the GET response from the active directory here in "payload" along with the added "ldapAttributeMap", "id" and ""type": "ldapattributemap""
payload = {
"version": "p6ueo2w2aulkf",
"name": "Test",
"directoryConfigurations": [
{
"hostname": "{omitted}",
"port": 389,
"encryptionProtocol": "NONE",
"encryptionCert": None,
"interface": {
"version": "mjvylmnd52agk",
"name": "diagnostic",
"hardwareName": "Management0/0",
"id": "a46ef70c-06ca-11ee-9be1-bd712e622992",
"type": "physicalinterface"
},
"type": "directoryconfiguration"
}
],
"enabled": True,
"realmId": 3,
"dirUsername": "{omitted}",
"dirPassword": "*********",
"baseDN": "dc={omitted},dc=com",
"ldapAttributeMap": {
"id": "7fbf5798-27c9-11ee-a635-a1f4b2c2e66b",
"type": "ldapattributemap"
},
"adPrimaryDomain": "{omitted}.com",
"id": "5957a304-2662-11ee-a635-a5df7d28e8c4",
"type": "activedirectoryrealm"
}
data = json.dumps(payload)
response = requests.put(ad_url, headers=headers, data=data, verify=False)
if response.status_code == 200:
print("Updated Object")
else:
pprint("Error Received: {}".format(response.content))
pass
put()
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def revoke():
uri = '/api/fdm/v6/fdm/token'
token_url = protocol+url+uri
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization":"Bearer"
}
payload = {
'grant_type' : 'revoke_token',
"access_token": token,
"token_to_revoke": token,
}
response = requests.post(token_url, data=payload, verify=False)
if response.status_code == 200:
print("Access token revoked")
else:
print(response.status_code)
revoke()
DELETE
Esta seção descreve um exemplo de Rest API DELETE para remover o objeto do Ative Diretory:
import requests
import json
import certifi
import urllib3
from pprint import pprint
from getpass import getpass
urllib3.disable_warnings()
#Data collection
u = input ('Input username: ')
p = getpass(prompt='Input password: ')
url = input('Input Hostname or IP address: ')
protocol = 'https://'
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def auth():
uri = '/api/fdm/v6/fdm/token'
token_url = protocol+url+uri
payload = {
'grant_type' : 'password',
'username' : u,
'password': p
}
response = requests.post(token_url, data=payload, verify=False)
#ONlY USE "verify=False" in a lab setting!
#Error Checking
if response.status_code == 400:
raise Exception("Error Received: {}".format(response.content))
else:
access_token = response.json()['access_token']
return access_token
token = auth()
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def delete():
objId = input('object id to delete> ')
uri = "/api/fdm/v6/object/realms/"
ad_url = protocol+url+uri+objId
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization":"Bearer {}".format(token)
}
response = requests.delete(ad_url, headers=headers, verify=False)
if response.status_code == 204:
print('Object removed')
else:
print("Error Received: {}".format(response.content))
pass
delete()
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def revoke():
uri = '/api/fdm/v6/fdm/token'
token_url = protocol+url+uri
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization":"Bearer"
}
payload = {
'grant_type' : 'revoke_token',
"access_token": token,
"token_to_revoke": token,
}
response = requests.post(token_url, data=payload, verify=False)
if response.status_code == 200:
print("Access token revoked")
else:
print(response.status_code)
revoke()
IMPLANTAR
Esta seção descreve um exemplo de implantação de alterações de configuração por meio de uma chamada de API REST POST e da confirmação do status de implantações com uma solicitação GET.
import requests
import json
import certifi
import urllib3
from pprint import pprint
from getpass import getpass
urllib3.disable_warnings()
#Data collection
u = input ('Input username: ')
p = getpass(prompt='Input password: ')
url = input('Input Hostname or IP address: ')
protocol = 'https://'
x="0"
attempts=0
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def auth():
uri = '/api/fdm/v6/fdm/token'
token_url = protocol+url+uri
payload = {
'grant_type' : 'password',
'username' : u,
'password': p
}
response = requests.post(token_url, data=payload, verify=False)
#ONlY USE "verify=False" in a lab setting!
#Error Checking
if response.status_code == 400:
raise Exception("Error Received: {}".format(response.content))
else:
access_token = response.json()['access_token']
return access_token
token = auth()
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def deploy():
uri = '/api/fdm/v6/operational/deploy'
deploy = protocol+url+uri
headers = headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization":"Bearer {}".format(token)
}
payload = {
"statusMessage": "string",
"cliErrorMessage": "string",
"state": "QUEUED",
"queuedTime": 0,
"startTime": 0,
"endTime": 0,
"statusMessages": [
"string"
],
"id": "string",
"name": "string",
"modifiedObjects": {},
"forceRefreshDeploymentData": False,
"type": "deploymentstatus"
}
data = json.dumps(payload)
response = requests.get(deploy, headers=headers, data=data,verify=False)
if response.status_code == 200:
print(response.status_code)
status = response.json()['items'][0]['statusMessage']
return status
else:
print(response.status_code)
pass
status = deploy()
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def deploy2():
uri = '/api/fdm/v6/operational/deploy'
deploy = protocol+url+uri
headers = headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization":"Bearer {}".format(token)
}
payload = {
"statusMessage": "string",
"cliErrorMessage": "string",
"state": "QUEUED",
"queuedTime": 0,
"startTime": 0,
"endTime": 0,
"statusMessages": [
"string"
],
"id": "string",
"name": "string",
"modifiedObjects": {},
"forceRefreshDeploymentData": False,
"type": "deploymentstatus"
}
data = json.dumps(payload)
response = requests.post(deploy, headers=headers, data=data,verify=False)
if response.status_code == 200:
print(response.status_code)
print('Deploying')
else:
print(response.status_code)
pass
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def revoke():
uri = '/api/fdm/v6/fdm/token'
token_url = protocol+url+uri
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization":"Bearer"
}
payload = {
'grant_type' : 'revoke_token',
"access_token": token,
"token_to_revoke": token,
}
response = requests.post(token_url, data=payload, verify=False)
if response.status_code == 200:
print("Access token revoked")
else:
print(response.status_code)
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
while True:
x = input("""
Press 1 for deployment status
Press 2 to deploy
Enter Exit to exit
> """).lower()
if x == "1":
deploy()
pprint('Status is: ' + status)
elif x == "2":
deploy2()
elif x == "exit":
break
revoke()
elif x != "1" or "2" or "exit":
attempts += 1
print("The options are 1, 2 or Exit only!")
if attempts == 3:
print('Closing the program')
revoke()
break
else:
raise Exception("Program shutting down")
Verificar
GET
Com esse script, uma resposta de código HTTP "200" é recebida, e a saída é apresentada, como mostrado na imagem abaixo.
Saída do script
PUT
Uma nova implantação está pronta na GUI do FDM com tentativas bem-sucedidas, como mostrado na imagem.
Captura de tela da página de administração do FDM
A confirmação também pode ser confirmada pelo recebimento de um código de resposta HTTP 200.
DELETE
Uma nova implantação está pronta na GUI do FDM com tentativas bem-sucedidas.
A confirmação também pode ser confirmada pelo recebimento do código de resposta HTTP 204.
Troubleshooting
Esta seção descreve como solucionar problemas.
O código de resposta HTTP pode informar sobre os problemas específicos observados:
Erros do cliente 4XX
400- Solicitação incorreta
401- Não autorizado
403- Proibido
404- Não encontrado
408- Timeout de Solicitação
415- Tipo de mídia sem suporte
Erros do Servidor 5xx
500- Erro interno do servidor
501- Não Implementado
502- Gateway com problema
503- Serviço Indisponível
Informações Relacionadas
RFC 2616: protocolo de transferência de hipertexto — HTTP/1.1
Introdução à API REST do Firepower Threat Defense - Referência FTD-API v6(FTD v7.2) - Documento - Desenvolvedor da Cisco