Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions examples/aio_ips2firewall.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import csv
from datetime import datetime, timedelta
import os
import asyncio
import argparse
import ipaddress
from typing import Dict,List
import sys

import meraki.aio

# Either input your API key below, or set an environment variable
# for example, in Terminal on macOS: export MERAKI_DASHBOARD_API_KEY=66839003d2861bc302b292eb66d3b247709f2d0d
api_key = ""

def removeSmallAmounts( ip_counts:Dict[str,int], filter:int):
ret = ip_counts.copy()
for k,v in ip_counts.items():
if v < filter:
ret.pop(k)

return ret

async def analyzeOrganization(aiomeraki: meraki.aio.AsyncDashboardAPI, orgId:str, days:int) -> Dict[str,int]:
ret = {}
timespan = days * 24 * 60 * 60
events = await aiomeraki.security_events.getOrganizationSecurityEvents(orgId, timespan=timespan,total_pages=-1)
for e in events:
ip, port = e["srcIp"].rsplit(":",1)
ip = ip.strip("[]") # remove brackets in case of ipv6
if not ipaddress.ip_address(ip).is_private: # dont block private ip addresses on the public ip of the firewall
ret[ip] = ret.get(ip, 0) + 1

return ret

async def updateFirewallrules(aiomeraki: meraki.aio.AsyncDashboardAPI, networkId:str, ip_list:List[str]):
rules = await aiomeraki.mx_l7_firewall.getNetworkL7FirewallRules(networkId)

rules=rules["rules"]
#get the currently blocked ip ranges
current_blocks = [x["value"] for x in rules if x["type"] == "ipRange"]

new_blocks = current_blocks + list(set(ip_list)-set(current_blocks))
new_blocks = sorted(new_blocks)

#generate new rules based on the list of total ip ranges to block
rules_to_add = [{"policy":"deny", "type":"ipRange", "value":x} for x in new_blocks]

#remove all currently blocked ip ranges
rules = [x for x in rules if x["type"] != "ipRange"]

rules = rules + rules_to_add

await aiomeraki.mx_l7_firewall.updateNetworkL7FirewallRules(networkId,rules=rules)

async def main():

parser = argparse.ArgumentParser(description='Block IP Addresses based on security events')
parser.add_argument('-o','--organization', type=str, nargs='+', dest="organizations", required=True,
help='the name/id of the organization(s) you want to analyze/secure')
parser.add_argument("-f",'--filter', dest='filter', type=int, default=5,
help='how often must an attack be listed before it gets blocked')
parser.add_argument("-s",'--save', dest='save', action='store_true',
help='write the blocklist to all networks in the organization.')
parser.add_argument("-d",'--days', dest='days', default=31, type=int,
help='How many days should be analyzed.')

if len(sys.argv) < 3:
parser.print_help()
return

try:
args = parser.parse_args()
if args.days >= 365:
print("days must be < 365")
parser.print_help()
return
except SystemExit:
return
except:
print("could not parse arguments")
parser.print_help()
return

# Instantiate a Meraki dashboard API session
# NOTE: you have to use "async with" so that the session will be closed correctly at the end of the usage
async with meraki.aio.AsyncDashboardAPI(
api_key,
base_url="https://api.meraki.com/api/v0",
log_file_prefix=__file__[:-3],
print_console=False,
) as aiomeraki:
# Get list of organizations to which API key has access
organizations = await aiomeraki.organizations.getOrganizations()
for x in organizations:
if x["id"] in args.organizations or x["name"] in args.organizations:
print(f"Analyzing organization {x['name']}")
result = await analyzeOrganization(aiomeraki, x["id"], args.days)
result = removeSmallAmounts(result, args.filter)
sum = 0

for k,v in result.items():
print(f"{k} attacked {v} times.")
sum = sum + v
print(f"Total attacks: {sum} from {len(result)} different IP adresses")

#apply the found ip ranges to the firewall
if args.save:
for n in await aiomeraki.networks.getOrganizationNetworks(x["id"]):
print(f"Updating Network {n['name']}")
await updateFirewallrules(aiomeraki,n["id"], result.keys())

print("Script complete!")


if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())

137 changes: 137 additions & 0 deletions examples/aio_org_wide_clients.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import csv
from datetime import datetime
import os
import asyncio

import meraki.aio

# Either input your API key below, or set an environment variable
# for example, in Terminal on macOS: export MERAKI_DASHBOARD_API_KEY=093b24e85df15a3e66f1fc359f4c48493eaa1b73
api_key = ""


async def listNetworkClients(aiomeraki: meraki.aio.AsyncDashboardAPI, folder_name, network):
print(f'Finding clients in network {network["name"]}')
try:
# Get list of clients on network, filtering on timespan of last 14 days
clients = await aiomeraki.clients.getNetworkClients(
network["id"],
timespan=60 * 60 * 24 * 14,
perPage=1000,
total_pages="all",
)
except meraki.AsyncAPIError as e:
print(f"Meraki API error: {e}")
except Exception as e:
print(f"some other error: {e}")
else:
if clients:
# Write to file
file_name = f'{network["name"]}.csv'
output_file = open(
f"{folder_name}/{file_name}", mode="w", newline="\n"
)
field_names = clients[0].keys()
csv_writer = csv.DictWriter(
output_file,
field_names,
delimiter=",",
quotechar='"',
quoting=csv.QUOTE_ALL,
)
csv_writer.writeheader()
csv_writer.writerows(clients)
output_file.close()
print(
f"Successfully output {len(clients)} clients' data to file {file_name}"
)
return network["name"], field_names
return network["name"], None


async def listOrganization(aiomeraki: meraki.aio.AsyncDashboardAPI, org):
print(f'Analyzing organization {org["name"]}:')
org_id = org["id"]

# Get list of networks in organization
try:
networks = await aiomeraki.networks.getOrganizationNetworks(org_id)
except meraki.AsyncAPIError as e:
print(f"Meraki API error: {e}")
return org["name"]
except Exception as e:
print(f"some other error: {e}")
return org["name"]

# Create local folder
todays_date = f"{datetime.now():%Y-%m-%d}"
folder_name = f"Org {org_id} clients {todays_date}"
if folder_name not in os.listdir():
os.mkdir(folder_name)

# Iterate through networks
total = len(networks)
print(f"Iterating through {total} networks in organization {org_id}")

# create a list of all networks in the organization so we can call them all concurrently
networkClientsTasks = [listNetworkClients(aiomeraki, folder_name, net) for net in networks]
for task in asyncio.as_completed(networkClientsTasks):
networkname, field_names = await task
print(f"finished network: {networkname}")

# Stitch together one consolidated CSV per org
output_file = open(f"{folder_name}.csv", mode="w", newline="\n")
field_names = list(field_names)
field_names.insert(0, "Network Name")
field_names.insert(1, "Network ID")
csv_writer = csv.DictWriter(
output_file,
field_names,
delimiter=",",
quotechar='"',
quoting=csv.QUOTE_ALL,
)
csv_writer.writeheader()
for net in networks:
file_name = f'{net["name"]}.csv'
if file_name in os.listdir(folder_name):
with open(f"{folder_name}/{file_name}") as input_file:
csv_reader = csv.DictReader(
input_file,
delimiter=",",
quotechar='"',
quoting=csv.QUOTE_ALL,
)
next(csv_reader)
for row in csv_reader:
row["Network Name"] = net["name"]
row["Network ID"] = net["id"]
csv_writer.writerow(row)
return org["name"]


async def main():
# Instantiate a Meraki dashboard API session
# NOTE: you have to use "async with" so that the session will be closed correctly at the end of the usage
async with meraki.aio.AsyncDashboardAPI(
api_key,
base_url="https://api.meraki.com/api/v0",
log_file_prefix=__file__[:-3],
print_console=False,
) as aiomeraki:
# Get list of organizations to which API key has access
organizations = await aiomeraki.organizations.getOrganizations()

# create a list of all organizations so we can call them all concurrently
organizationTasks = [listOrganization(aiomeraki, org) for org in organizations]
for task in asyncio.as_completed(organizationTasks):
# as_completed returns an iterator, so we just have to await the iterator and not call it
organizationName = await task
print(f"finished organization: {organizationName}")

print("Script complete!")


if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
14 changes: 10 additions & 4 deletions examples/org_wide_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,25 @@

import meraki

# Either input your API key below by uncommenting line 10 and changing line 15 to api_key=api_key,
# Either input your API key below by uncommenting line 10 and changing line 16 to api_key=API_KEY,
# or set an environment variable (preferred) to define your API key. The former is insecure and not recommended.
# For example, in Linux/macOS: export MERAKI_DASHBOARD_API_KEY=093b24e85df15a3e66f1fc359f4c48493eaa1b73
# api_key = '093b24e85df15a3e66f1fc359f4c48493eaa1b73'
# API_KEY = '093b24e85df15a3e66f1fc359f4c48493eaa1b73'


def main():
# Instantiate a Meraki dashboard API session
dashboard = meraki.DashboardAPI(api_key=None, base_url='https://api.meraki.com/api/v0/', log_file_prefix=__file__[:-3], print_console=False)
dashboard = meraki.DashboardAPI(
api_key='',
base_url='https://api-mp.meraki.com/api/v0/',
log_file_prefix=os.path.basename(__file__)[:-3],
log_path='',
print_console=False
)

# Get list of organizations to which API key has access
organizations = dashboard.organizations.getOrganizations()

# Iterate through list of orgs
for org in organizations:
print(f'\nAnalyzing organization {org["name"]}:')
Expand Down
28 changes: 21 additions & 7 deletions meraki/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
from .api.admins import Admins
from .api.alert_settings import AlertSettings
from .api.bluetooth_clients import BluetoothClients
from .api.bluetooth_settings import BluetoothSettings
from .api.camera_quality_retention_profiles import CameraQualityRetentionProfiles
from .api.cameras import Cameras
from .api.change_log import ChangeLog
from .api.clients import Clients
from .api.config_templates import ConfigTemplates
from .api.connectivity_monitoring_destinations import ConnectivityMonitoringDestinations
Expand Down Expand Up @@ -49,6 +51,7 @@
from .api.malware_settings import MalwareSettings
from .api.management_interface_settings import ManagementInterfaceSettings
from .api.meraki_auth_users import MerakiAuthUsers
from .api.monitored_media_servers import MonitoredMediaServers
from .api.named_tag_scope import NamedTagScope
from .api.netflow_settings import NetFlowSettings
from .api.networks import Networks
Expand Down Expand Up @@ -79,9 +82,10 @@
from .api.wireless_settings import WirelessSettings
from .config import (
API_KEY_ENVIRONMENT_VARIABLE, DEFAULT_BASE_URL, SINGLE_REQUEST_TIMEOUT, CERTIFICATE_PATH, WAIT_ON_RATE_LIMIT,
MAXIMUM_RETRIES, OUTPUT_LOG, LOG_FILE_PREFIX, PRINT_TO_CONSOLE, SIMULATE_API_CALLS
MAXIMUM_RETRIES, OUTPUT_LOG, LOG_PATH, LOG_FILE_PREFIX, PRINT_TO_CONSOLE, SIMULATE_API_CALLS
)

__version__ = '0.90.1'

class DashboardAPI(object):
"""
Expand All @@ -92,38 +96,45 @@ class DashboardAPI(object):
- single_request_timeout (integer): maximum number of seconds for each API call
- certificate_path (string): path for TLS/SSL certificate verification if behind local proxy
- wait_on_rate_limit (boolean): retry if 429 rate limit error encountered?
- maximum_retries_on_rate_limit (integer): retry up to this many times when encountering 429s or other server-side errors
- maximum_retries (integer): retry up to this many times when encountering 429s or other server-side errors
- output_log (boolean): create an output log file?
- log_path (string): path to output log; by default, working directory of script if not specified
- log_file_prefix (string): log file name appended with date and timestamp
- print_console (boolean): if output log used, output to console too?
- print_console (boolean): print logging output to console?
- simulate (boolean): simulate POST/PUT/DELETE calls to prevent changes?
"""

def __init__(self, api_key=None, base_url=DEFAULT_BASE_URL, single_request_timeout=SINGLE_REQUEST_TIMEOUT,
certificate_path=CERTIFICATE_PATH, wait_on_rate_limit=WAIT_ON_RATE_LIMIT,
maximum_retries=MAXIMUM_RETRIES, output_log=OUTPUT_LOG, log_file_prefix=LOG_FILE_PREFIX,
print_console=PRINT_TO_CONSOLE, simulate=SIMULATE_API_CALLS):
maximum_retries=MAXIMUM_RETRIES, output_log=OUTPUT_LOG, log_path=LOG_PATH,
log_file_prefix=LOG_FILE_PREFIX, print_console=PRINT_TO_CONSOLE, simulate=SIMULATE_API_CALLS):
# Check API key
api_key = api_key or os.environ.get(API_KEY_ENVIRONMENT_VARIABLE)
if not api_key:
raise APIKeyError()

# Configure logging
self._logger = logging.getLogger(__name__)
self._log_file = f'{log_file_prefix}_log__{datetime.now():%Y-%m-%d_%H-%M-%S}.log'
if log_path and log_path[-1] != '/':
log_path += '/'
self._log_file = f'{log_path}{log_file_prefix}_log__{datetime.now():%Y-%m-%d_%H-%M-%S}.log'
if output_log:
logging.basicConfig(
filename=self._log_file,
level=logging.DEBUG,
format='%(asctime)s %(name)12s: %(levelname)8s > %(message)s',
datefmt='%Y-%m-%d %H:%M:%S')

if print_console:
console = logging.StreamHandler()
console.setLevel(logging.INFO)
formatter = logging.Formatter('%(name)12s: %(levelname)8s > %(message)s')
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)
elif print_console:
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s %(name)12s: %(levelname)8s > %(message)s',
datefmt='%Y-%m-%d %H:%M:%S')

# Creates the API session
self._session = RestSession(
Expand All @@ -143,8 +154,10 @@ def __init__(self, api_key=None, base_url=DEFAULT_BASE_URL, single_request_timeo
self.admins = Admins(self._session)
self.alert_settings = AlertSettings(self._session)
self.bluetooth_clients = BluetoothClients(self._session)
self.bluetooth_settings = BluetoothSettings(self._session)
self.camera_quality_retention_profiles = CameraQualityRetentionProfiles(self._session)
self.cameras = Cameras(self._session)
self.change_log = ChangeLog(self._session)
self.clients = Clients(self._session)
self.config_templates = ConfigTemplates(self._session)
self.connectivity_monitoring_destinations = ConnectivityMonitoringDestinations(self._session)
Expand Down Expand Up @@ -183,6 +196,7 @@ def __init__(self, api_key=None, base_url=DEFAULT_BASE_URL, single_request_timeo
self.malware_settings = MalwareSettings(self._session)
self.management_interface_settings = ManagementInterfaceSettings(self._session)
self.meraki_auth_users = MerakiAuthUsers(self._session)
self.monitored_media_servers = MonitoredMediaServers(self._session)
self.named_tag_scope = NamedTagScope(self._session)
self.netflow_settings = NetFlowSettings(self._session)
self.networks = Networks(self._session)
Expand Down
Loading