Skip to content
This repository has been archived by the owner on Oct 10, 2023. It is now read-only.

Commit

Permalink
Merge pull request #6 from MetLife/develop
Browse files Browse the repository at this point in the history
v1.1
  • Loading branch information
jeffmurr committed Apr 28, 2020
2 parents e047d52 + 0e0d767 commit 73b9b1a
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 11 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Changelog for SSLChecker

# v1.0.0
- Initial Release
- Initial Release

# v1.1.0
- Added the ability to pass a port to SSLScanner
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@ Deployment - As part of the above setup, you will be able to deploy to Azure usi

Invoke the function on the command line using curl:

``` curl http://<functionname>.azurewebsite.net/api/{scan:alpha}/{view:alpha}/{name}```
``` curl http://<functionname>.azurewebsite.net/api/{scan:alpha}/{view:alpha}/{name}/{port}```

There are three parts to pass to the URI: scan, view, and name.
There are four parts to pass to the URI: scan, view, name and port.

"scan" is the type of scan: policy or full. Currently, the default policy prohibits using SSL 2.0/3.0 and TLS 1.0, so the policy scan will identify which unsupported ciphers are in use, if any. A full scan will report back all supported ciphers. In a future release I will make this configurable.

Since corporations often use [split-view DNS](https://en.wikipedia.org/wiki/Split-horizon_DNS), "view" in this context is the network viewpoint you want to scan, either internal or external. This is accomplished by specifying a valid DNS server to use for name resolution. The default value for external will use OpenDNS (e.g. 208.67.222.222). The default for internal will be 0.0.0.0 and will result in an error if a scan is attempted and no internal DNS server is specified.
Since corporations often use [split-view DNS](https://en.wikipedia.org/wiki/Split-horizon_DNS), "view" in this context is the network viewpoint you want to scan, either internal or external. This is accomplished by specifying a valid DNS server to use for name resolution. The default value for external will use OpenDNS (e.g. 208.67.222.222). The default for internal will be 0.0.0.0 and will result in an error if a scan is attempted and no internal DNS server is specified. Please modify the config.ini file to use an internal DNS server.

"name" should be the DNS domain name you would like to scan (i.e., github.com).

"port" is optional and if omitted will default to TCP 443.

## A Note on Authentication

Microsoft has extensive documentation on how to secure an HTTP endpoint in Azure Functions [here](https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook-trigger?tabs=csharp#secure-an-http-endpoint-in-production). There are two main ways to secure a function: Turn on App Service Authentication/Authorization for the function app, or use Azure API Management (APIM) to authenticate requests. Additionally, Azure functions support API key authorization that you can supply either as a query string variable or in a HTTP header. Microsoft states that API key authorization is not intended as a way to secure an HTTP trigger in production
Expand Down
14 changes: 13 additions & 1 deletion SSLChecker/SSLChecker/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ def main(req: func.HttpRequest) -> func.HttpResponse:
scan_type = req.route_params.get('scan')
view = req.route_params.get('view')
name = req.route_params.get('name')
port = req.route_params.get('port')

# Port is optional and will default to 443 if none is provided
if port is None:
port = 443
elif port.isnumeric() is False:
error = {"Message": ("Please pass a valid port")}
return json.dumps(error)
elif int(port) > 65535:
error = {"Message": ("Please pass a valid port"
" in range 0-65535")}
return json.dumps(error)

""" Check to ensure ALL parameters were passed in the URI.
If you mark the route parameters in function.json as mandatory,
Expand Down Expand Up @@ -82,7 +94,7 @@ def main(req: func.HttpRequest) -> func.HttpResponse:
return json.dumps(error)

# Run the scan
scanjob = scanner.scan(name, ip, view, scan_type)
scanjob = scanner.scan(name, ip, port, view, scan_type)
elapsedtime = process_time() - starttime
logging.info(f'{name} processed for {elapsedtime}')
return json.dumps(scanjob)
2 changes: 1 addition & 1 deletion SSLChecker/SSLChecker/function.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"type": "httpTrigger",
"direction": "in",
"name": "req",
"route": "{scan:alpha?}/{view:alpha?}/{name?}",
"route": "{scan:alpha?}/{view:alpha?}/{name?}/{port?}",
"methods": [
"get"
]
Expand Down
10 changes: 5 additions & 5 deletions SSLChecker/sharedcode/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@
SynchronousScanner.DEFAULT_NETWORK_TIMEOUT = 3


def scan(name, ip, view, suite):
""" Two inputs: web site name and ip
TODO: extend this to include port """
def scan(name, ip, port, view, suite):
""" Five inputs: web site name, ip, port
split-dns view, and cipher suite """

try:
server_tester = ServerConnectivityTester(
hostname=name,
ip_address=ip,
port=443,
port=port,
tls_wrapped_protocol=TlsWrappedProtocolEnum.HTTPS
)
# This line checks to see if the host is online
Expand All @@ -41,7 +41,7 @@ def scan(name, ip, view, suite):
# Could not establish an SSL connection to the server
except ConnectionToServerTimedOut:
error = results.set_error(f'{name}',
"Connection to TCP 443 timed-out")
f"Connection to TCP {port} timed-out")
return error
except ServerConnectivityError:
error = results.set_error(f'{name}',
Expand Down
66 changes: 66 additions & 0 deletions tests/test_SSLChecker.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,69 @@ def test_missing_policy_view_dns_name():
" valid scan type: policy or full, "
"valid DNS view: internal or external, "
"and a valid DNS domain name")


def test_external_bad_port():
# Construct a mock HTTP request
req = func.HttpRequest(
method='GET',
body=None,
url='/api/',
route_params={'scan': 'policy',
'view': 'external',
'name': 'yahoo.com',
'port': 'a'}
)

# Call the function
resp = main(req)

# Convert resp string to dict
results = json.loads(resp)

# Check the output to ensure the DNS name could not resolve
assert results["Message"] == 'Please pass a valid port'


def test_external_port_timeout():
# Construct a mock HTTP request
req = func.HttpRequest(
method='GET',
body=None,
url='/api/',
route_params={'scan': 'policy',
'view': 'external',
'name': 'yahoo.com',
'port': '8443'}
)

# Call the function
resp = main(req)

# Convert resp string to dict
results = json.loads(resp)

# Check the output to ensure the DNS name could not resolve
assert results["Message"] == 'Connection to TCP 8443 timed-out'


def test_external_port_not_in_range():
# Construct a mock HTTP request
req = func.HttpRequest(
method='GET',
body=None,
url='/api/',
route_params={'scan': 'policy',
'view': 'external',
'name': 'espn.com',
'port': '123456'}
)

# Call the function
resp = main(req)

# Convert resp string to dict
results = json.loads(resp)

# Check the output to ensure the DNS name could not resolve
assert results["Message"] == 'Please pass a valid port in range 0-65535'

0 comments on commit 73b9b1a

Please sign in to comment.