Skip to content
This repository was archived by the owner on Apr 24, 2019. It is now read-only.

Commit

Permalink
Merge pull request #2 from deep-security/deep-security-sdk-update
Browse files Browse the repository at this point in the history
Deep Security SDK Update & Ignoring SSL Certification Validation
  • Loading branch information
marknca committed Feb 19, 2016
2 parents 46e3e5b + 30abe1f commit 0e4712e
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 30 deletions.
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,37 @@ $ python ip_list_to_set.py -d 152 -u USERNAME -p PASSWORD -t TENANT --dryrun
Updated IP Set [AMAZON eu-west-1] with ID [9ee53a08-cdaf-4881-a111-3d99b58065e4]
```

Now the IP Set has been created in AWS WAF and can be used in a AWS WAC WACL rule as a matching condition. AWS was detailed documentation on [the next steps you need to take](http://docs.aws.amazon.com/waf/latest/developerguide/web-acl.html).
Now the IP Set has been created in AWS WAF and can be used in a AWS WAC WACL rule as a matching condition. AWS was detailed documentation on [the next steps you need to take](http://docs.aws.amazon.com/waf/latest/developerguide/web-acl.html).

## SSL Certificate Validation

If the Deep Security Manager (DSM) you're connecting to was installed via software of the AWS Marketplace, there's a chance that it is still using the default, self-signed SSL certificate. By default, python checks the certificate for validity which it cannot do with self-signed certificates.

If you are using self-signed certificates, please use the new ```--ignore-ssl-validation``` command line flag.

When you use this flag, you're telling python to ignore any certificate warnings. These warnings should be due to the self-signed certificate but *could* be for other reasons. It is strongly recommended that you have alternative mitigations in place to secure your DSM.

When the flag is set, you'll see this warning block;

```bash
***********************************************************************
* IGNORING SSL CERTIFICATE VALIDATION
* ===================================
* You have requested to ignore SSL certificate validation. This is a less secure method
* of connecting to a Deep Security Manager (DSM). Please ensure that you have other
* mitigations and security controls in place (like restricting IP space that can access
* the DSM, implementing least privilege for the Deep Security user/role accessing the
* API, etc).
*
* During script execution, you'll see a number of "InsecureRequestWarning" messages.
* These are to be expected when operating without validation.
***********************************************************************
```
And during execution you may see lines similar to;
```python
.../requests/packages/urllib3/connectionpool.py:789: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.org/en/latest/security.html
```
These are expected warnings. Can you tell that we (and the python core teams) are trying to tell you something? If you're interesting in using a valid SSL certificate, you can get one for free from [Let's Encrypt](https://letsencrypt.org), [AWS themselves](https://aws.amazon.com/certificate-manager/) (if your DSM is behind an ELB), or explore commercial options (like the [one from Trend Micro](http://www.trendmicro.com/us/enterprise/cloud-solutions/deep-security/ssl-certificates/)).
Empty file modified dsawswaf/deepsecurity/__init__.py
100644 → 100755
Empty file.
Empty file modified dsawswaf/deepsecurity/cloud_account.py
100644 → 100755
Empty file.
Empty file modified dsawswaf/deepsecurity/computer.py
100644 → 100755
Empty file.
Empty file modified dsawswaf/deepsecurity/computer_group.py
100644 → 100755
Empty file.
Empty file modified dsawswaf/deepsecurity/deepsecurity.latest.wsdl.xml
100644 → 100755
Empty file.
Empty file modified dsawswaf/deepsecurity/ip_list.py
100644 → 100755
Empty file.
61 changes: 34 additions & 27 deletions dsawswaf/deepsecurity/manager.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@
import computer_group
import ip_list
import policy
import soap_https_handler

class Manager(object):
"""
Class representing the Deep Security Manager and all of it's
functionality. Well, at least the functionality available via the
SOAP and REST APIs
"""
def __init__(self, username=None, password=None, tenant=None, dsm_hostname=None, start_session=True):
def __init__(self, username=None, password=None, tenant='Primary', dsm_hostname=None, dsm_port=443, start_session=True, ignore_ssl_validation=False, debug=False):
"""
Create a new reference to a Deep Security Manager
Expand All @@ -34,16 +35,18 @@ def __init__(self, username=None, password=None, tenant=None, dsm_hostname=None,
tenant = In a multi-tenant deployment (like Deep Security as a Service) this is the tenant/account name.
For non-multi tenant accounts this can be left blank or set to "primary"
dsm_hostname = The hostname of the Deep Security Manager to access, defaults to Deep Security as a Service
dsm_port = The port of the Deep Security Manager to access, defaults to Deep Security as a Service
start_session = Whether or not to automatically start a session with the specified Deep Security Manager
"""
self.version = '9.6'
self._hostname = 'app.deepsecurity.trendmicro.com' if not dsm_hostname else dsm_hostname # default to Deep Security as a Service
self._port = 443 # on-premise defaults to 4119
self._port = dsm_port # on-premise defaults to 4119
self.rest_api_path = 'rest'
self.soap_api_wsdl = 'webservice/Manager?WSDL'
self.session_id_rest = None
self.session_id_soap = None
self.soap_client = None
self.ignore_ssl_validation = ignore_ssl_validation

# Deep Security data
self.computer_groups = {}
Expand All @@ -54,12 +57,13 @@ def __init__(self, username=None, password=None, tenant=None, dsm_hostname=None,
self.ip_lists = {}

# Setup functions
self.debug = False
self._debug = debug
self.logger = self._setup_logging()
self._set_url()

# Try to start a session if possible
if username and password and start_session:
self.log("Attempting to start a session")
self.start_session(username=username, password=password, tenant=tenant)

def __del__(self):
Expand Down Expand Up @@ -105,6 +109,17 @@ def port(self, val):
self._port = val
self._set_url()

# Any change to debug requires that logging be reset
@property
def debug(self): return self._debug

@debug.setter
def debug(self, val):
"""
Reset the logging configuration on change
"""
self._setup_logging()

# *****************************************************************
# 'Private' methods
# *****************************************************************
Expand All @@ -115,16 +130,18 @@ def _setup_logging(self):

# Based on tips from http://www.blog.pythonlibrary.org/2012/08/02/python-101-an-intro-to-logging/
logging.basicConfig(level=logging.ERROR)
if self._debug:
logging.basicConfig(level=logging.DEBUG)

# turn down suds logging
logging.getLogger('suds.client').setLevel(logging.ERROR)
if self.debug:
if self._debug:
logging.getLogger('suds.client').setLevel(logging.DEBUG)

# setup module logging
logger = logging.getLogger("DeepSecurity.API")
logger.setLevel(logging.WARNING)
if self.debug:
if self._debug:
logger.setLevel(logging.DEBUG)

formatter = logging.Formatter('[%(asctime)s]\t%(message)s', '%Y-%m-%d %H:%M:%S')
Expand Down Expand Up @@ -159,25 +176,12 @@ def _get_soap_client(self, force_load_from_url=False):
"""
soap_client = None

# First, try to use a local WSDL
wsdl_path = None
current_path = os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))
found_wsdl = []
for fn in os.listdir(current_path):
if fn.endswith('.wsdl.xml'): found_wsdl.append(os.path.join(current_path, fn))

found_wsdl.sort()

if len(found_wsdl) > 0:
if 'deepsecurity.latest.wsdl.xml' in found_wsdl:
wsdl_path = 'file://deepsecurity.latest.wsdl.xml'
else:
wsdl_path = 'file://{}'.format(found_wsdl[0])

if not wsdl_path or force_load_from_url: wsdl_path = self.base_url_for_soap

try:
soap_client = suds.client.Client(wsdl_path)
if self.ignore_ssl_validation:
self.log("Ignoring SSL validation for SOAP API access")
soap_client = suds.client.Client(self.base_url_for_soap, transport=soap_https_handler.HTTPSIgnoreValidation())
else:
soap_client = suds.client.Client(self.base_url_for_soap)
except Exception, soap_err:
self.log("Could not create a SOAP client. Threw exception: %s" % soap_err)
soap_client = None
Expand Down Expand Up @@ -233,13 +237,13 @@ def _make_a_rest_call(self, call):
if call.has_key('query') and call['query'] and not call.has_key('data'):
# GET
try:
result = requests.get(full_url, headers=headers)
result = requests.get(full_url, headers=headers, verify=not self.ignore_ssl_validation)
except Exception, get_err:
self.log("Failed to get REST call [%s] with query string. Threw exception: /%s" % (call['method'].lstrip('/'), post_err))
elif call.has_key('data') and call['data']:
# POST
try:
result = requests.post(full_url, data=json.dumps(call['data']), headers=headers)
result = requests.post(full_url, data=json.dumps(call['data']), headers=headers, verify=not self.ignore_ssl_validation)
except Exception, post_err:
self.log("Failed to post REST call [%s]. Threw exception: /%s" % (call['method'].lstrip('/'), post_err))
else:
Expand Down Expand Up @@ -362,7 +366,7 @@ def start_session(self, username=None, password=None, tenant=None, force_new_ses
# We need to make different calls for tenants and the primary
soap_call = None
rest_call = None
if not tenant:
if not tenant or tenant.lower() == "primary":
soap_call = self._get_call_structure()
soap_call['auth'] = False
soap_call['method'] = 'authenticate'
Expand Down Expand Up @@ -414,7 +418,10 @@ def start_session(self, username=None, password=None, tenant=None, force_new_ses

# Do we have an existing REST session?
if not self.session_id_rest or force_new_session:
if rest_call: self.session_id_rest = (self._make_call(rest_call)).text
if rest_call:
response = self._make_call(rest_call)
if response:
self.session_id_rest = response.text

if self.session_id_rest:
self.log("Authenticated successfully, starting REST session [%s]" % self.session_id_rest)
Expand Down
Empty file modified dsawswaf/deepsecurity/policy.py
100644 → 100755
Empty file.
30 changes: 30 additions & 0 deletions dsawswaf/deepsecurity/soap_https_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import ssl
import urllib2
from suds.transport.http import HttpTransport, Reply, TransportError

class HTTPSIgnoreValidation(HttpTransport):
"""
An HTTPS Handler set to ignore SSL certificate validation
The software and AWS Marketplace installations of Deep Security default to using
a self-signed certificate and require this handler for SOAP API access
With help from;
@nitwit via http://stackoverflow.com/questions/6277027/suds-over-https-with-cert
@enno groper via http://stackoverflow.com/questions/19268548/python-ignore-certicate-validation-urllib2
"""
def __init__(self, *args, **kwargs): HttpTransport.__init__(self, *args, **kwargs)

def u2open(self, u2request):
"""
Open a connection.
@param u2request: A urllib2 request.
@type u2request: urllib2.Requet.
@return: The opened file-like urllib2 object.
@rtype: fp
"""
tm = self.options.timeout
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
return urllib2.urlopen(u2request, context=ctx)
1 change: 0 additions & 1 deletion dsawswaf/deepsecurity/version.txt

This file was deleted.

23 changes: 22 additions & 1 deletion dsawswaf/ip_list_to_set.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#! /usr/bin/env python

# Standard libraries
import argparse
import os
Expand Down Expand Up @@ -31,6 +33,7 @@ def parse_args(str_to_parse=None):
parser.add_argument('-u', '--username', action='store', required=True, help='The Deep Security username to access the IP Lists with. Should only have read-only rights to IP lists and API access')
parser.add_argument('-p', '--password', action='store', required=True, help='The password for the specified Deep Security username. Should only have read-only rights to IP lists and API access')
parser.add_argument('-t', '--tenant', action='store', required=False, default=None, help='The name of the Deep Security tenant/account')
parser.add_argument('--ignore-ssl-validation', action='store_true', dest='ignore_ssl_validation', required=False, help='Ignore SSL certification validation. Be careful when you use this as it disables a recommended security check. Required for Deep Security Managers using a self-signed SSL certificate')
parser.add_argument('--dryrun', action='store_true', required=False, help='Do a dry run of the command. This will not make any changes to your AWS WAF service')
parser.add_argument('--verbose', action='store_true', required=False, help='Enabled verbose output for the script. Useful for debugging')

Expand Down Expand Up @@ -97,11 +100,29 @@ def _get_aws_credentials(self):

def _connect_to_deep_security(self):
dsm = None
if self.args.ignore_ssl_validation:
self._log("""***********************************************************************
* IGNORING SSL CERTIFICATE VALIDATION
* ===================================
* You have requested to ignore SSL certificate validation. This is a less secure method
* of connecting to a Deep Security Manager (DSM). Please ensure that you have other
* mitigations and security controls in place (like restricting IP space that can access
* the DSM, implementing least privilege for the Deep Security user/role accessing the
* API, etc).
*
* During script execution, you'll see a number of "InsecureRequestWarning" messages.
* These are to be expected when operating without validation.
***********************************************************************""", priority=True)
try:
dsm = deepsecurity.manager.Manager(dsm_hostname=self.args.dsm, dsm_port=self.args.dsm_port, username=self.args.username, password=self.args.password, tenant=self.args.tenant)
dsm = deepsecurity.manager.Manager(dsm_hostname=self.args.dsm, dsm_port=self.args.dsm_port, username=self.args.username, password=self.args.password, tenant=self.args.tenant, ignore_ssl_validation=self.args.ignore_ssl_validation)
self._log("Connected to the Deep Security Manager at {}".format(self.args.dsm))
except Exception, err: pass # @TODO handle this exception gracefully

if not dsm.session_id_rest and not dsm.session_id_soap:
self._log("Unable to connect to the Deep Security Manager. Please check your settings")
if not self.args.ignore_ssl_validation:
self._log("You did not ask to ignore SSL certification validation. This is a common error when connect to a Deep Security Manager that was installed via software or the AWS Marketplace. Please set the flag (--ignore-ssl-validation), check your other settings, and try again")

return dsm

def _connect_to_aws_waf(self):
Expand Down

0 comments on commit 0e4712e

Please sign in to comment.