はじめに
本シリーズでは、Cisco Secure Access APIを利用した運用自動化をテーマに、Pythonなどを用いた実践的な活用方法を紹介します。
「APIを使うと何ができるのか」「実際の運用でどのように活用できるのか」を、サンプルコードを交えながらわかりやすく解説していきます。
全4回の構成は以下のとおりです。
- 第1回:Cisco Secure Access APIを試してみる
- 第2回:PythonでCisco Secure Access APIを実行し、結果をCSVに出力してみる ※本記事
- 第3回:APIで運用を自動化してみる
- 第4回:Cisco XDRと連携してみる
前回はPostmanを利用してCisco Secure Access APIを実際に呼び出してみました。
今回は、同じ処理をPythonから実行し、取得したデータを加工してCSVファイルに出力してみます。
この記事を読むとできること
- PythonからCisco Secure Access APIに接続できる
- Client IDとClient Secretを使ってアクセストークンを取得できる
- APIレスポンスをJSONとして扱える
- APIから取得した情報をPythonで抽出・処理できる
- 取得したデータをCSVファイルに出力できる
今回の処理の流れ
今回作成するPythonプログラムでは、以下の流れでCisco Secure Access APIを呼び出します。
第1回ではPostmanがこの処理を行っていましたが、今回はPythonプログラムから同じ処理を実行します。さらに必要な情報のみを抽出しCSVに出力する処理を追加します。
CSVに出力しておくことで、Excelによる分析や、メール添付によるレポート配布などに活用できます。
PythonからAPIを利用するための事前準備
今回使用するものは以下のとおりです。
- Cisco Secure Access環境
- APIキー(Client ID)
- APIシークレット(Client Secret)
- Python
- requests
requestsがインストールされていない場合は、以下を実行してください。
pip install requests
APIキー/シークレットの作成方法については、第1回で紹介した手順を参照してください。
アクセストークンをPythonから取得する
第1回ではPostmanを利用してアクセストークンを取得しました。今回は、この処理をPythonで実装します。
実際にPythonで記述すると以下のコードとなります。
コード
import requests
# APIキー/シークレット
client_id = "xxxxxxxx"
client_secret = "xxxxxxxx"
# 認証用エンドポイント
url = "https://api.sse.cisco.com/auth/v2/token"
# アクセストークン取得APIを実行
response = requests.post(
url,
auth=(client_id, client_secret),
data={
"grant_type": "client_credentials"
}
)
# ステータスコードを表示
print(response.status_code)
# レスポンスを表示
print(response.json())
以下が返って来ればOKです。
{
"token_type": "bearer",
"access_token": "xxxxxxxx",
"expires_in": 3600
}
ここで必要なaccess_tokenのみを取り出して、変数tokenに保存するコードを追加します。
token = response.json()["access_token"]
補足
今回はAPIが正常に実行されたことを確認するため、ステータスコードを表示しています。エラー処理については、今後の運用自動化の中で扱います。
補足
運用で利用する場合は、 アクセストークンの有効期限(expires_in)を考慮し、 期限切れ時に再取得する処理を実装してください。
注意
実際の運用では、Client IDやClient SecretをPythonコードに直接記載することは避けてください。環境変数やSecrets Managerなどを利用して安全に管理することをおすすめします。今回は説明を簡単にするため、サンプルコード内に記載しています。
Cisco Secure Access APIを呼び出す
取得したアクセストークンを用いてAPIを呼び出します。変数tokenに格納したアクセストークンを利用しています。
コード
# アクセス数の多い宛先ドメインを取得するAPI
# クエリパラメータに取得日時(1日前から今まで)と、件数(5件)を追加
api_url = "https://api.sse.cisco.com/reports/v2/top-destinations/proxy?from=-1days&to=now&limit=5&offset=0"
# ヘッダーにアクセストークンを設定
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
# APIを呼び出し
api_response = requests.get(
api_url,
headers=headers
)
# ステータスコードを表示
print(api_response.status_code)
# レスポンスを”data"に格納し表示
data = api_response.json()
print(data)
補足
取得したアクセストークンtokenを、Authorizationヘッダーに設定して認証しています。
レスポンス
{
"meta": {},
"data": [
{
"count": 12345,
"bandwidth": 80000000,
"domain": "sensor.anz-prod.obsrvbl.com",
"policycategories": [],
"categories": [
{
"id": 167,
"type": "content",
"label": "Computers and Internet",
"integration": false,
"deprecated": false
},
{
"id": 25,
"type": "content",
"label": "Software/Technology",
"integration": false,
"deprecated": true
}
],
"counts": {
"requests": 12345,
"allowedrequests": 12345,
"blockedrequests": 0
},
"rank": 1
},
{
"count": 11234,
"bandwidth": 50000000,
"domain": "s3.ap-southeast-2.amazonaws.com",
"policycategories": [],
"categories": [
{
"id": 198,
"type": "content",
"label": "Cloud and Data Centers",
"integration": false,
"deprecated": false
}
],
"counts": {
"requests": 11234,
"allowedrequests": 11234,
"blockedrequests": 0
},
"rank": 2
},
{
(省略)
}
]
}
レスポンスには、第1回で確認した以下のような情報が含まれます。括弧内はJSON内のキー名を示しています。
- ドメイン名(domain)
- カテゴリ (categories)
- リクエスト総数 (counts > requests)
- 許可されたリクエスト数 (counts > allowedrequests)
- ブロックされたリクエスト数 (counts > blockedrequests)
- 順位 (rank)
JSONレスポンスをPythonで処理する
ここまででAPIからデータを取得できました。次は、このデータをCSVとして出力するための準備を行います。まずJSON全体の構造を確認し、実際に取得したデータが格納されているdata部分から必要な要な項目だけを取り出す処理を加えていきます。
1.JSONレスポンスからdata部分を抽出
JSONの全体像は以下の様な構造になっています。ここでは各ドメイン毎に必要な情報を抽出する処理を加えていきます。
data
├── meta
└── data ← ここに5件のデータが入っている
├── 1件目
│ ├── count
│ ├── bandwidth
│ ├── domain
│ ├── policycategories
│ ├── categories
│ ├── counts
│ └── rank
│
├── 2件目
├── 3件目
├── 4件目
└── 5件目
補足
変数dataに格納したJSONはPythonの辞書型(dict)として扱われます。辞書型は、キーと値のペアでデータを管理するデータ構造です。
Pythonで辞書型の中にあるリストからデータを抽出するには、以下のようにします。
コード
# typeを確認
print(type(data))
# 中身を表示
for item in data:
print(item)
レスポンス
<class 'dict'>
meta
data
今回のレスポンスでは、metaとdataという2つのキーがあります。変数dataは辞書型(dict)なので、for文で直接ループすると、上記のように値ではなくキーが取り出されます。
今回確認したいドメイン情報は、dataキーの値であるリストに格納されています。そのため、data["data"]を指定することでdataキー内の各データを取り出すことができます。
コード
# dataから値を取り出しitemに格納する
for item in data["data"]:
print(item)
レスポンス
{
"count": 12345,
"bandwidth": 80000000,
"domain": "sensor.anz-prod.obsrvbl.com",
"policycategories": [],
"categories": [
{
"id": 167,
"type": "content",
"label": "Computers and Internet",
"integration": false,
"deprecated": false
},
{
"id": 25,
"type": "content",
"label": "Software/Technology",
"integration": false,
"deprecated": true
}
],
"counts": {
"requests": 12345,
"allowedrequests": 12345,
"blockedrequests": 0
},
"rank": 1
}
(省略)
2.最終的に必要な情報のみを抽出
ここからさらに必要な値のみを抽出していく処理を加えます。
categoriesおよびcountsはそのままでは取り出せない構造となっており、少し工夫が必要です。
categoriesからカテゴリ名を取得する
categoriesは以下のような構造となっています。
"categories": [
{
"id": 167,
"type": "content",
"label": "Computers and Internet"
},
{
"id": 25,
"type": "content",
"label": "Software/Technology"
}
]
少し複雑ですが、リストの中に辞書型のデータが入っているという構造となり、複数のカテゴリー名となるパターンがあります。カテゴリー名を取得するなら以下のようなプログラムとなります。
for item in data["data"]:
# categoriesの中のlabelを取り出し表示
for category in item["categories"]:
print("Category:", category["label"])
さらに、今回はCSVの1行にカテゴリーをまとめて出力したいため、複数のカテゴリーをカンマ区切りの文字列に変換します。
# category名をカンマ区切り文字列に変換
categories = ", ".join(
category["label"]
for category in item["categories"]
)
countsからリクエスト情報を取得する
countsの中身は以下のようになっています。
"counts": {
"requests": 12345,
"allowedrequests": 12345,
"blockedrequests": 0
}
countsの中にさらに辞書が入っている構造となります。そのため、以下のようなプログラムとすることで目的の値を抽出できます。
for item in data["data"]:
print("Domain:", item["domain"])
print("Requests:", item["counts"]["requests"])
print("Allowed:", item["counts"]["allowedrequests"])
print("Blocked:", item["counts"]["blockedrequests"])
print("---")
上記をまとめると、最終的に以下となります。
コード
# カテゴリ名を抽出して変数categoriesに格納
for item in data["data"]:
categories = ", ".join(
category["label"] for category in item["categories"]
)
# 各項目を1行で表示
print(
item["rank"],
item["domain"],
categories,
item["counts"]["requests"],
item["counts"]["allowedrequests"],
item["counts"]["blockedrequests"]
)
レスポンス
1 sensor.anz-prod.obsrvbl.com Computers and Internet, Software/Technology 12345 12345 0
2 s3.ap-southeast-2.amazonaws.com Cloud and Data Centers 11234 11234 0
3 au.download.windowsupdate.com Software Updates 1234 1234 0
4 lfodown01-gyr-maverick.cloudsink.net Infrastructure and Content Delivery Networks 123 123 0
5 bolt.dropbox.com Application Allow, File Storage, Online Storage and Backup 12 12 0
取得したデータをCSVに出力する
ここまでで、Cisco Secure Accessから取得したJSONデータをPythonで必要な項目だけ取り出せるようになりました。最後に、このデータをCSVファイルに出力してみます。
今回はPython標準ライブラリのcsvを使ってシンプルにCSVに出力したいと思います。複雑な処理の場合は、pandasなどのライブラリを利用すると良いです。
今回取得したい項目を、
- Rank
- Domain
- Category
- Requests
- Allowed Requests
- Blocked Requests
として出力する場合、以下のようなコードとなります。
コード
import csv
# ファイルをオープン
with open("top_destinations.csv", "w", newline="", encoding="utf-8-sig") as csvfile:
writer = csv.writer(csvfile)
# ヘッダー
writer.writerow([
"Rank",
"Domain",
"Category",
"Requests",
"Allowed Requests",
"Blocked Requests"
])
# データ
for item in data["data"]:
# カテゴリ名を抽出して変数categoriesに格納
categories = ", ".join(
category["label"]
for category in item["categories"]
)
# CSVへ書き込み
writer.writerow([
item["rank"],
item["domain"],
categories,
item["counts"]["requests"],
item["counts"]["allowedrequests"],
item["counts"]["blockedrequests"]
])
encoding="utf-8-sig"としているのは、生成したCSVをExcelで開いたときに日本語が文字化けしにくくするためです。
これを実行すると、同じフォルダに
top_destinations.csv
が作成されます。
実際の出力結果は以下となりました。
Rank,Domain,Category,Requests,Allowed Requests,Blocked Requests
1,sensor.anz-prod.obsrvbl.com,"Computers and Internet, Software/Technology",12345,12345,0
2,s3.ap-southeast-2.amazonaws.com,Cloud and Data Centers,11234,11234,0
3,au.download.windowsupdate.com,Software Updates,1234,1234,0
4,lfodown01-gyr-maverick.cloudsink.net,Infrastructure and Content Delivery Networks,123,123,0
5,bolt.dropbox.com,"Application Allow, File Storage, Online Storage and Backup",12,12,0
まとめ
今回は以下の流れを実施しました。
Cisco Secure Access
↓
API実行
↓
JSONレスポンス
↓
Pythonでデータ抽出
↓
CSV出力
今回は手動でPythonプログラムを実行しましたが、ここまでの処理はすべてプログラムで実行できます。
つまり、「Cisco Secure Accessからデータを取得する」という作業を、人が管理コンソールを操作することなく自動化するための準備ができました。
次回予告
第3回では、今回作成したPythonプログラムをさらに発展させ、「APIで運用を自動化してみる」 をテーマに紹介します。
第2回では手動でPythonプログラムを実行しましたが、第3回では定期実行や通知を組み合わせ、日々の運用作業を自動化してみます。
次回のイメージ
定期実行
↓
APIからイベント取得
↓
必要なデータだけ抽出
↓
CSV生成
↓
メール / Teams通知
