correct endpoint for firewall rules?

SOLVED
Adrian4
A model citizen

correct endpoint for firewall rules?

Hi,

 

New to API's and coding in general.

 

I am trying to write a script that adds a new firewall rule to a network (I'm using a test site atm).

However I am getting a 404 error which I assume means my URL is wrong?

I got this from the Meraki reference guide but I'm not sure its correct.

/networks/{network_id}/appliance/firewall/l3firewallRules

 

 

Also - I read somewhere that adding a new rule would overwrite all the existing rules! Is that true and if so is there a way to add a rule so that it doesn't effect any existing rules?

 

Thanks!

 

import requests
import security
import json


api_key = security.MERAKI_API_KEY
organizationId = security.ORG_ID
network_id = "xxxxxxxxx"

rule = {
'name': 'Test Rule',
'policy': 'deny',
'protocol': 'any',
'srcPort': 'any',
'srcCidr': '1.1.1.1/24',
'dstPort': 'any',
'dstCidr': '0.0.0.0/0',
}


url = f"https://api.meraki.com/api/v1/networks/{network_id}/appliance/firewall/l3firewallRules"


headers = {
'X-Cisco-Meraki-API-Key': api_key,
'Content-Type': 'application/json'
}


response = requests.post(url, headers=headers, data=json.dumps(rule), verify=False)

print(response.status_code)
1 ACCEPTED SOLUTION
RaphaelL
Kind of a big deal
Kind of a big deal

HTTP400 is sent because the dashboard doesn't like your request. Doesn't mean you are missing a parameter. 

 

Using the Meraki SDK would be easier to troubleshoot. 

 

The issue is within the payload.  

data=json.dumps(rule)

View solution in original post

27 REPLIES 27
alemabrahao
Kind of a big deal
Kind of a big deal

Here is the correct URL:

 

https://developer.cisco.com/meraki/api/#!update-network-l-3-firewall-rules

I am not a Cisco Meraki employee. My suggestions are based on documentation of Meraki best practices and day-to-day experience.

Please, if this post was useful, leave your kudos and mark it as solved.

Hi,

Thanks for the reply, but Im still getting a 404 error 😞

am I getting the url wrong or can something else cause this issue?

 

url = f"https://api.meraki.com/api/v1/networks/{network_id}/l3FirewallRules"



response = requests.post(url, headers=headers, data=json.dumps(rule), verify=False)
alemabrahao
Kind of a big deal
Kind of a big deal

404 - Not Found The requested resource doesn't exist or incorrect API key

 

You can test It on the Meraki developer Hub before.

I am not a Cisco Meraki employee. My suggestions are based on documentation of Meraki best practices and day-to-day experience.

Please, if this post was useful, leave your kudos and mark it as solved.
RaphaelL
Kind of a big deal
Kind of a big deal

I would troubleshoot this issue with these steps :

 

1- Create a dummy rule from the dashboard

2- GET the rule via the API

3- PUT the exact same response received from step #2.

 

You have to be Org admin / Net admin to do that. 

huh - if I try to use the PUT operation from the dashboard - its gets a 404 there too

Adrian4_0-1675085603833.png

 

alemabrahao
Kind of a big deal
Kind of a big deal

Have you enabled API access?

 

https://documentation.meraki.com/General_Administration/Other_Topics/Cisco_Meraki_Dashboard_API#:~:t....

I am not a Cisco Meraki employee. My suggestions are based on documentation of Meraki best practices and day-to-day experience.

Please, if this post was useful, leave your kudos and mark it as solved.

Yes, as mentioned, moments before i tried the PUT I did a successful GET

alemabrahao
Kind of a big deal
Kind of a big deal

Use this one:

 

https://developer.cisco.com/meraki/api-latest/

I am not a Cisco Meraki employee. My suggestions are based on documentation of Meraki best practices and day-to-day experience.

Please, if this post was useful, leave your kudos and mark it as solved.
Adrian4
A model citizen

If I run the exact same script as a GET - it works ok. I can also run the get from the dashboard.

 

I noticed I was using POST rather than PUT so made that change but if I do that I get a 400 error instead 😞

url = f"https://api.meraki.com/api/v1/networks/{network_id}/appliance/firewall/l3FirewallRules"



response = requests.put(url, headers=headers, data=json.dumps(rule), verify=False)
alemabrahao
Kind of a big deal
Kind of a big deal

400 - Bad Request The request was unacceptable, often due to missing a required parameter.

 

 

I am not a Cisco Meraki employee. My suggestions are based on documentation of Meraki best practices and day-to-day experience.

Please, if this post was useful, leave your kudos and mark it as solved.

shouldn't the dashboard tell me if there are any required parameters?

RaphaelL
Kind of a big deal
Kind of a big deal

HTTP400 is sent because the dashboard doesn't like your request. Doesn't mean you are missing a parameter. 

 

Using the Meraki SDK would be easier to troubleshoot. 

 

The issue is within the payload.  

data=json.dumps(rule)
RaphaelL
Kind of a big deal
Kind of a big deal

Not pretty , but should work. 

 

 

 

import os
import json
import requests
import codecs

base_url_v1 = 'https://api.meraki.com/api/v1'
NetworkID = XXXXXXXXXXX
headers = {
	'x-cisco-meraki-api-key': format(str(apikey)),
	'Content-Type': 'application/json'
}
def __returnhandler(statuscode, returntext):
    if str(statuscode) == '200':
        return returntext
    else:
        print('HTTP Status Code: {0}\n'.format(statuscode))
def getL3FirewallRules():
    geturl = '{0}/networks/{1}/appliance/firewall/l3FirewallRules'.format(str(base_url_v1), str(NetworkID))
    dashboard = requests.get(geturl, headers=headers,verify=False)
    result = __returnhandler(dashboard.status_code, dashboard.text)
    return result 

L3FWRules = getL3FirewallRules()

payload = L3FWRules
url = 'https://api.meraki.com/api/v1/networks/{0}/appliance/firewall/l3FirewallRules'.format(NetworkID)
response = requests.request('PUT', url, headers=headers, data = payload,verify=False)
print(response.text.encode('utf8'))

 

 

Thanks you!

This put me on the right track. I changed 

data=json.dumps(rule)

to 

data=rule,


and then changed the rule paramater to this

rule = '''{
"rules": [
{
"comment": "Test Rule.",
"policy": "allow",
"protocol": "tcp",
"destPort": "443",
"destCidr": "192.168.1.0/24",
"srcPort": "Any",
"srcCidr": "Any",
"syslogEnabled": false
}
]
}'''


(slight syntax change)


just my second issue now - this seems to overwrite all the existing rules rather than just add this to the list.

how do I add things without deleting all the others?

 

alemabrahao
Kind of a big deal
Kind of a big deal

You can't update, you have to set all rules again when you create a new rule.

I am not a Cisco Meraki employee. My suggestions are based on documentation of Meraki best practices and day-to-day experience.

Please, if this post was useful, leave your kudos and mark it as solved.
RaphaelL
Kind of a big deal
Kind of a big deal

You can't append a rule. You have to push the whole rule base.

 

So I would suggest doing a GET first , append your modifications , then PUT the whole rule base.

am I going to have to call the existing rules, then add them along with the new rule to the update call and basically re-write all the rules each time?

alemabrahao
Kind of a big deal
Kind of a big deal

 

https://developer.cisco.com/codeexchange/github/repo/CiscoSE/AddMerakiMXL3FirewallRuleToNetworks#use...

https://developer.cisco.com/meraki/build/mx-firewall-control-python-script/

I am not a Cisco Meraki employee. My suggestions are based on documentation of Meraki best practices and day-to-day experience.

Please, if this post was useful, leave your kudos and mark it as solved.
Adrian4
A model citizen

regarding updating the rules and having to get a copy of existing rules and then adding those to the new update request - this is a little terrifying - what if it misses a rule or something, id have no way of checking 😕

I guess I could get it to export a json file after the get request as something I could look at to make sure its all ok right?

Yes. The order of firewall rules is important, and Dashboard can't assume placement. Placing a rule at the end may not actually do anyty, thus it is up to you to place a rule in the correct position. 

sungod
Head in the Cloud

Another approach to consider is that if you want  maintain rules via the API, it may be worth keeping the 'master copy' in a local database, then just write the whole set each time.

 

Then if you need to check rules, it's an API read and compare against the local copy.

 

Adrian4
A model citizen

I'm having a problem appending the new rule list to the existing list :(|

I think its because I have created the existing rule list as a dictionary. However if I try to re-write the dictionary as a list (with square brackets [] ) the code stops working. "Invalid syntax"


import requests
import security
import json

# Replace with your Meraki API key and network ID
api_key = security.MERAKI_API_KEY
organizationId = security.ORG_ID
network_id = "xxxxxxxxxxxxxxxxx"



url = f"https://api.meraki.com/api/v1/networks/{network_id}/appliance/firewall/l3FirewallRules"



headers = {
'X-Cisco-Meraki-API-Key': api_key,
'Content-Type': 'application/json'
}


response = requests.get(url, headers=headers, verify=False)

existingrules = response.json()

print(response.status_code)
print(response.text.encode('utf8'))

with open('ExistingRules.json', 'w') as json_file:
json.dump(response.json(), json_file, indent=4)


newrule = '''{
"rules": [
{
"comment": "Test RuleNEW.",
"policy": "deny",
"protocol": "any",
"destPort": "any",
"destCidr": "192.200.1.0/24",
"srcPort": "Any",
"srcCidr": "Any",
"syslogEnabled": false
}
]
}'''

existingrules.append(newrule)

response = requests.put(url, headers=headers, json=existingrules, verify=False)
sungod
Head in the Cloud

As mentioned by @David_Jirku it is probable that appending is not going to behave as you wish.

 

Usually the last rule in the list is 'allow any-any', if you append a stricter rule it will be ignored as it will be lower in precedence than 'allow any-any'

 

To add a rule to existingrules, you should be adding an item to the rules array within it, not direct to existingrules.

 

Have a look at...

https://github.com/CiscoSE/AddMerakiMXL3FirewallRuleToNetworks/blob/master/AddRulesToMXL3Firewall.py

 

rhbirkelund
Kind of a big deal

You could do something along the lines of

# Constants
netowrk_id = "xxxx"
TargetComment = "Google DNS"

# Get Rules for network
FirewallRules = dashboard.appliance.getNetworkApplianceFirewallL3FirewallRules(network_id)
# Determine idx where rule should be updated
target_idx = next((idx for idx, item in enumerate(FirewallRules['rules']) if item['comment'] == TargetComment), None)
# Update rule
FirewallRules['rules'][target_idx].update({
    "comment": TargetComment
    "policy": "deny",
    "protocol": "any",
    "destPort": "any",
    "destCidr": "192.200.1.0/24",
    "srcPort": "Any",
    "srcCidr": "Any",
    "syslogEnabled": false
})
# Remove Default Rule entry
FirewallRules['rules'].pop(-1)
# Update Rules
NewSetOfRules = dashboard.appliance.updateNetworkApplianceFirewallL3FirewallRules(
    networkId=network_id,
    rules=FirewallRules['rules']
)

 

What happens is, that I search through all the elements in FirewallRules for the index of the element with the comment, of the rule I wan't to update.

With the index in hand, I can either update the rule, or use the index to inject a new rule. In the above example I use it to update a specific rule.

 

Modify it to suit your implementation.

LinkedIn ::: https://blog.rhbirkelund.dk/

Like what you see? - Give a Kudo ## Did it answer your question? - Mark it as a Solution 🙂

All code examples are provided as is. Responsibility for Code execution lies solely your own.

thanks. I have a working script now which does a similar thing. It injects the new rule to the existing but the rules are still updated by completely overwriting all existing rules with the list I send in - apparently that's the way it works, rather than just adding in any new items in the index.

It seems to work ok but I found that when you do the GET, the list includes the default allow rule. Then when you go to PUT the updated list - that Allow rule gets added like it was a custom rule so you end up with the default and an extra identical copy.

You get an extra Allow rule every time you do an update. I fixed this by searching the list index for that rule and deleting it (luckily the comment is "default Rule" which is nicely unique) before adding my new rule and sending it back.

rhbirkelund
Kind of a big deal

Or use the a posteriori knowledge, that the default rule is always at the end of the list and simpy "pop it". 😉

 

# Remove Default Rule entry
FirewallRules['rules'].pop(-1)

 

 

LinkedIn ::: https://blog.rhbirkelund.dk/

Like what you see? - Give a Kudo ## Did it answer your question? - Mark it as a Solution 🙂

All code examples are provided as is. Responsibility for Code execution lies solely your own.
Get notified when there are additional replies to this discussion.