01-30-2023 04:17 AM
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)
Solved! Go to Solution.
01-30-2023 05:51 AM
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?
01-30-2023 05:52 AM
You can't update, you have to set all rules again when you create a new rule.
01-30-2023 05:52 AM
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.
01-30-2023 05:53 AM
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?
01-30-2023 05:39 AM
https://developer.cisco.com/meraki/build/mx-firewall-control-python-script/
01-30-2023 05:57 AM
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?
01-30-2023 06:00 AM
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.
01-30-2023 07:27 AM
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.
01-30-2023 07:50 AM
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)
01-30-2023 08:39 AM
As mentioned by @dajirku 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
02-07-2023 02:14 AM
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.
02-07-2023 02:38 AM
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.
02-07-2023 03:36 AM
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)
Discover and save your favorite ideas. Come back to expert answers, step-by-step guides, recent topics, and more.
New here? Get started with these tips. How to use Community New member guide