Python3 connection to SonicOS API
Does anyone have any working Python3 code that can successfully connect to the SonicOS API?
I have made several attempts using both the "requests" and "urllib" Python modules, however in all cases, the SonicWALL API returns a "406 Not Acceptable" response.
My Code:
import requests
import urllib3
urllib3.disable_warnings()
class sonicapi:
def __init__(self, hostname, port, username, password):
self.baseurl = 'https://{0}:{1}/api/sonicos/'.format(hostname, str(port))
self.authinfo = (username, password)
self.headers = {
'Connection': None,
'Accept-Encoding': None,
'Content-Length': None,
}
def auth(self):
controller = 'auth'
url = self.baseurl + controller
r = requests.post(url, auth=self.authinfo, headers=self.headers, verify=False)
if r.status_code != 200:
return r.status_code
else:
response = r.json().pop('data')
return response
def getIPv4AddressObjects(self):
controller = 'address-objects/ipv4'
url = self.baseurl + controller
r = requests.get(url, auth=self.authinfo, headers=self.headers, verify=False)
if r.status_code != 200:
return r.status_code
else:
response = r.json().pop('data')
return response
def main():
s = sonicapi('192.168.168.168', 4343, 'admin', 'password')
s.auth()
s.getIPv4AddressObjects()
if __name__ == "__main__":
main()
The above code returns: 406 Not Acceptable
If I use curl from within bash, it seemingly works fine:
curl -k -i -u admin:password -X POST https://192.168.168.168:4343/api/sonicos/auth
HTTP/1.1 200 Connection established
HTTP/1.0 200 OK
Server: SonicWALL
Expires: -1
Cache-Control: no-cache
Content-type: application/json; charset=UTF-8
X-Content-Type-Options: nosniff
{
"status": {
"success": true,
"info": [
{ "level": "info", "code": "E_OK", "message": "Success." }
]
}
}
I have set up mitmproxy and tested both curl and Python, the only difference is that Python is sending an implicit "Accept-Encoding: identity" header, which curl otherwise is not.
I have opened a case as well after spending about 6 hours on this, using different model SonicWALLs with different OS Versions, I have been on the #Python IRC channel, and have exhausted all resources - it seems crazy to me that the SonicOS API would not be RFC Compliant or or otherwise incompatible with Python3.
My Environment:
SonicWALL NSA6600: SonicOS Enhanced 6.5.4.5-53n
SonicWALL NSv200(KVM): SonicOS Enhanced 6.5.4.4-44v-21-757-3d1fff93
Python version 3.6.10, requests module version 2.18.4. Ubuntu 18.04
I have also tested with Python 3.8.2, requests 2.22.0 on Ubuntu 20.04 with the same results.
Best Answer
-
CORRECT ANSWER
Jaime
SonicWall Employee
In your class:
class sonicapi: def __init__(self, hostname, port, username, password): self.baseurl = 'https://{0}:{1}/api/sonicos/'.format(hostname, str(port)) self.authinfo = (username, password) self.headers = OrderedDict([ ('Accept', 'application/json'), ('Content-Type', 'application/json'), ('Accept-Encoding', 'application/json'), ('charset', 'UTF-8')])7
Answers
Adding @Jaime and @skesarwa to this thread as they are experts on this.
Thanks!
Shipra Sahu
Technical Support Advisor, Premier Services
Following this thread.
Hi @hbonath , try set the following headers:
headers={
'Accept-Encoding': '*/*',
'Accept': '*/*',
},
I actually think this a bug, but this has been working for us.
Hi,
Please give this a try and let me know if the response is any different:
from collections import OrderedDict headers = OrderedDict([ ('Accept', 'application/json'), ('Content-Type', 'application/json'), ('Accept-Encoding', 'application/json'), ('charset', 'UTF-8')])Thanks.
Jaime
@Jaime -
Thank you, that is exactly what needed to be in there, specifically the 'Accept-Encoding' and 'Accept' headers being set to 'application/json'
The code is now working, and I am able to successfully connect to all of the devices I have been testing with and pull a list of Address Objects.
Here's the updated functional script for future reference:
import requests import urllib3 from collections import OrderedDict urllib3.disable_warnings() class sonicapi: def __init__(self, hostname, port, username, password): self.baseurl = 'https://{0}:{1}/api/sonicos/'.format(hostname, str(port)) self.authinfo = (username, password) self.headers = OrderedDict([ ('Accept', 'application/json'), ('Content-Type', 'application/json'), ('Accept-Encoding', 'application/json'), ('Charset', 'UTF-8')]) def auth(self): controller = 'auth' url = self.baseurl + controller r = requests.post(url, auth=self.authinfo, headers=self.headers, verify=False) if r.status_code != 200: return r.status_code else: response = r.json() return response def getIPv4AddressObjects(self): controller = 'address-objects/ipv4' url = self.baseurl + controller r = requests.get(url, auth=self.authinfo, headers=self.headers, verify=False) if r.status_code != 200: return r.status_code else: response = r.json() return response def main(): s = sonicapi('192.168.168.168', 443, 'admin', 'password') print(s.auth()) print(s.getIPv4AddressObjects()) if __name__ == "__main__": main()This is great! I'll have to try this out. Thanks for the collaboration!
@micah - SonicWall's Self-Service Sr. Manager
I've created a new repo to keep this at where this can be eventually built upon to be imported into other projects.