summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Freed <patrick.freed@mongodb.com>2018-11-15 18:51:35 -0500
committerPatrick Freed <patrick.freed@mongodb.com>2018-11-20 11:39:08 -0500
commitf137cdd0e7298bae36ff1e7507c783a01b262828 (patch)
tree737e10ae7c9c37c509cd9177601b13be0bb9b07c
parentd75be58dd0dda5fe1190e8d48d91d6eda8e21acd (diff)
downloadmongo-f137cdd0e7298bae36ff1e7507c783a01b262828.tar.gz
SERVER-38155 Make ssl_ECDHE_suites.js use native Python ssl module
-rw-r--r--buildscripts/resmokeconfig/suites/ssl.yml2
-rw-r--r--etc/burn_in_tests.yml3
-rw-r--r--etc/pip/components/platform.req6
-rw-r--r--etc/pip/constraints.txt6
-rw-r--r--jstests/ssl/ssl_ECDHE_suites.js137
-rw-r--r--jstests/ssl/sslyze_tester.py52
-rw-r--r--jstests/ssl/tls_enumerator.py75
7 files changed, 133 insertions, 148 deletions
diff --git a/buildscripts/resmokeconfig/suites/ssl.yml b/buildscripts/resmokeconfig/suites/ssl.yml
index 60aa5d163a7..caaf406d4f9 100644
--- a/buildscripts/resmokeconfig/suites/ssl.yml
+++ b/buildscripts/resmokeconfig/suites/ssl.yml
@@ -4,8 +4,6 @@ selector:
roots:
- jstests/ssl/*.js
exclude_files:
- # TODO: Unblacklist after we've removed sslyze dependency in SERVER-38155
- - jstests/ssl/ssl_ECDHE_suites.js
# ssl tests start their own mongod's.
executor:
diff --git a/etc/burn_in_tests.yml b/etc/burn_in_tests.yml
index 5827d35c622..c6d63048621 100644
--- a/etc/burn_in_tests.yml
+++ b/etc/burn_in_tests.yml
@@ -6,5 +6,4 @@ selector:
# Exclude list of etc/evergreen.yml task names.
exclude_tasks:
# Exclude list of jstests file names.
- exclude_tests:
- - jstests/ssl/ssl_ECDHE_suites.js
+ exclude_tests: \ No newline at end of file
diff --git a/etc/pip/components/platform.req b/etc/pip/components/platform.req
index 324995955fa..26138b57c99 100644
--- a/etc/pip/components/platform.req
+++ b/etc/pip/components/platform.req
@@ -1,8 +1,4 @@
# Platform-specific components
pypiwin32==219; sys_platform == "win32" and python_version < "3"
pypiwin32==223; sys_platform == "win32" and python_version > "3"
-subprocess32==3.5.2; os_name == "posix" and platform_release != "2.6.18-194.el5xen" and platform_release != "2.6.18-274.el5xen" and python_version < "3"
-# Needed for ssl_ECDHE_suites.js test - testing the TLS suites enabled on each platform
-sslyze==2.0.1; python_version >= "3.6" and sys_platform == "win32" and (platform_machine == "x86_64" or platform_machine == "AMD64")
-sslyze==2.0.1; python_version >= "3.6" and sys_platform == "cygwin" and (platform_machine == "x86_64" or platform_machine == "AMD64")
-sslyze==2.0.1; python_version >= "3.6" and sys_platform == "linux" and (platform_machine == "x86_64" or platform_machine == "i686")
+subprocess32==3.5.2; os_name == "posix" and platform_release != "2.6.18-194.el5xen" and platform_release != "2.6.18-274.el5xen" and python_version < "3" \ No newline at end of file
diff --git a/etc/pip/constraints.txt b/etc/pip/constraints.txt
index 10dabb89676..27662e16b20 100644
--- a/etc/pip/constraints.txt
+++ b/etc/pip/constraints.txt
@@ -65,8 +65,4 @@ typed-ast==1.1.0; python_version > "3"
# Platform-specific components
pypiwin32==219; sys_platform == "win32" and python_version < "3"
pypiwin32==223; sys_platform == "win32" and python_version > "3"
-subprocess32==3.5.2; os_name == "posix" and platform_release != "2.6.18-194.el5xen" and platform_release != "2.6.18-274.el5xen" and python_version < "3"
-# Needed for ssl_ECDHE_suites.js test - testing the TLS suites enabled on each platform
-sslyze==2.0.1; python_version >= "3.6" and sys_platform == "win32" and (platform_machine == "x86_64" or platform_machine == "AMD64")
-sslyze==2.0.1; python_version >= "3.6" and sys_platform == "cygwin" and (platform_machine == "x86_64" or platform_machine == "AMD64")
-sslyze==2.0.1; python_version >= "3.6" and sys_platform == "linux" and (platform_machine == "x86_64" or platform_machine == "i686")
+subprocess32==3.5.2; os_name == "posix" and platform_release != "2.6.18-194.el5xen" and platform_release != "2.6.18-274.el5xen" and python_version < "3" \ No newline at end of file
diff --git a/jstests/ssl/ssl_ECDHE_suites.js b/jstests/ssl/ssl_ECDHE_suites.js
index bd01638f395..0e9839cae56 100644
--- a/jstests/ssl/ssl_ECDHE_suites.js
+++ b/jstests/ssl/ssl_ECDHE_suites.js
@@ -1,104 +1,77 @@
-// Test that a client can authenticate against the server with roles.
-// Also validates RFC2253
+// Test that the server supports at least one ECDHE tls cipher suite.
+
load('jstests/ssl/libs/ssl_helpers.js');
(function() {
"use strict";
+ // Need to use toolchain python, which is unsupported on Windows
+ if (_isWindows()) {
+ return;
+ }
+
// Amazon linux does not currently support ECDHE
const EXCLUDED_BUILDS = ['amazon', 'amzn64'];
- const suites = [
- "SSLV2 Cipher Suites",
- "SSLV3 Cipher Suites",
- "TLSV1_0 Cipher Suites",
- "TLSV1_1 Cipher Suites",
- "TLSV1_2 Cipher Suites",
- "TLSV1_3 Cipher Suites"
- ];
const SERVER_CERT = "jstests/libs/server.pem";
+ const OUTFILE = 'jstests/ssl/ciphers.json';
- function runSSLYze(port) {
- let target_os = buildInfo().buildEnvironment.target_os;
- let target_arch = buildInfo().buildEnvironment.target_arch;
-
- if (target_os === "macOS" || target_arch !== "x86_64") {
- return null;
- }
+ const suites = [
+ 'sslv2',
+ 'sslv3',
+ 'tls1',
+ 'tls1_1',
+ 'tls1_2',
+ ];
- let python = "/usr/bin/env python3";
- let sslyze = " jstests/ssl/sslyze_tester.py ";
+ const x509_options = {
+ tlsMode: 'requireTLS',
+ tlsCAFile: CA_CERT,
+ tlsPEMKeyFile: SERVER_CERT,
+ ipv6: "",
+ bind_ip_all: ""
+ };
- if (_isWindows()) {
- const paths = ["c:\\python36\\python.exe", "c:\\python\\python36\\python.exe"];
- for (let p of paths) {
- if (fileExists(p)) {
- python = p;
- }
- }
- }
+ const mongod = MongoRunner.runMongod(x509_options);
- const python_command = python + sslyze + "--port=" + port;
- let ret = 0;
- if (_isWindows()) {
- ret = runProgram('cmd.exe', '/c', python_command);
- } else {
- ret = runProgram('/bin/sh', '-c', python_command);
- }
- assert.eq(ret, 0);
+ // Run the tls cipher suite enumerator
+ let python = "/usr/bin/env /opt/mongodbtoolchain/v2/bin/python3";
+ let enumerator = " jstests/ssl/tls_enumerator.py ";
+ const python_command = python + enumerator + "--port=" + mongod.port + " --cafile=" + CA_CERT +
+ ' --cert=' + CLIENT_CERT + ' --outfile=' + OUTFILE;
+ assert.eq(runProgram('/bin/sh', '-c', python_command), 0);
- try {
- let ciphers = cat("jstests/ssl/ciphers.json");
- return JSON.parse(ciphers);
- } catch (e) {
- jsTestLog("Failed to parse ciphers.json");
- throw e;
- } finally {
- const python_delete_command = python + sslyze + "--delete";
- if (_isWindows()) {
- ret = runProgram('cmd.exe', '/c', python_delete_command);
- } else {
- ret = runProgram('/bin/sh', '-c', python_delete_command);
- }
- assert.eq(ret, 0);
- }
+ // Parse its output
+ let cipherDict = {};
+ try {
+ cipherDict = JSON.parse(cat(OUTFILE));
+ } catch (e) {
+ jsTestLog("Failed to parse ciphers.json");
+ throw e;
+ } finally {
+ const delete_command = 'rm ' + OUTFILE;
+ assert.eq(runProgram('/bin/sh', '-c', delete_command), 0);
}
- function testSSLYzeOutput(cipherDict) {
- // Checking that SSL 1.0, 2.0, 3.0 and TLS 1.0 are not accepted
- suites.slice(0, 3).forEach(tlsVersion => assert(cipherDict[tlsVersion].length === 0));
+ // Checking that SSLv2, SSLv3 and TLS 1.0 are not accepted
+ suites.slice(0, suites.indexOf('tls1'))
+ .forEach(tlsVersion => assert(cipherDict[tlsVersion].length === 0));
- // Printing TLS 1.1, 1.2, and 1.3 suites that are accepted
- let hasECDHE = false;
- suites.slice(3, 6).forEach(tlsVersion => {
- print('*************************\n' + tlsVersion + ": ");
- cipherDict[tlsVersion].forEach(cipher => {
- print(cipher);
- if (cipher.includes('ECDHE'))
- hasECDHE = true;
- });
+ // Printing TLS 1.1 and 1.2 suites that are accepted
+ let hasECDHE = false;
+ suites.slice(suites.indexOf('tls1_1')).forEach(tlsVersion => {
+ print('*************************\n' + tlsVersion + ": ");
+ cipherDict[tlsVersion].forEach(cipher => {
+ print(cipher);
+ if (cipher.includes('ECDHE'))
+ hasECDHE = true;
});
+ });
- // All platforms except Amazon Linux 1 should support ECDHE
- if (!EXCLUDED_BUILDS.includes(buildInfo().buildEnvironment.distmod)) {
- assert(hasECDHE, 'Supports at least one ECDHE cipher suite');
- }
+ // All platforms except Amazon Linux 1 should support ECDHE
+ if (!EXCLUDED_BUILDS.includes(buildInfo().buildEnvironment.distmod)) {
+ assert(hasECDHE, 'Supports at least one ECDHE cipher suite');
}
- print("1. Testing x.509 auth to mongod");
- {
- const x509_options = {
- tlsMode: "preferTLS",
- tlsCAFile: CA_CERT,
- tlsPEMKeyFile: SERVER_CERT,
- ipv6: "",
- bind_ip_all: ""
- };
- let mongod = MongoRunner.runMongod(x509_options);
- const cipherDict = runSSLYze(mongod.port);
- if (cipherDict !== null) {
- testSSLYzeOutput(cipherDict);
- }
- MongoRunner.stopMongod(mongod);
- }
+ MongoRunner.stopMongod(mongod);
}());
diff --git a/jstests/ssl/sslyze_tester.py b/jstests/ssl/sslyze_tester.py
deleted file mode 100644
index 70dda5f8c62..00000000000
--- a/jstests/ssl/sslyze_tester.py
+++ /dev/null
@@ -1,52 +0,0 @@
-#! /usr/bin/env python3
-
-# This is a wrapper file around the SSLYze package
-# The SSLYze package is used for testing the TLS/SSL
-# suites that we allow for connections.
-import os, logging, json, argparse, urllib.parse
-from sslyze.synchronous_scanner import SynchronousScanner
-from sslyze.plugins import openssl_cipher_suites_plugin
-from sslyze.plugins.openssl_cipher_suites_plugin import *
-from sslyze.server_connectivity_tester import ServerConnectivityTester, ServerConnectivityError
-
-path = str(os.path.dirname(os.path.abspath(__file__)))
-filename = f'{path}/ciphers.json'
-
-def server_scanner(p):
- try:
- logger = logging.getLogger(__name__)
- logger.info("Opening file")
- server_tester = ServerConnectivityTester(
- hostname = 'localhost',
- port=p
- )
- server_info = server_tester.perform()
- scanner = SynchronousScanner()
-
- results = dict()
- suite_names = ['SSLV2 Cipher Suites', 'SSLV3 Cipher Suites', 'TLSV1_0 Cipher Suites', 'TLSV1_1 Cipher Suites', 'TLSV1_2 Cipher Suites', 'TLSV1_3 Cipher Suites']
- suites = [Sslv20ScanCommand, Sslv30ScanCommand, Tlsv10ScanCommand, Tlsv11ScanCommand, Tlsv12ScanCommand, Tlsv13ScanCommand]
-
- logger.info("Scanning SSL/TLS suites, writing to ciphers.json")
- for suite_name, suite in zip(suite_names, suites):
- scan = scanner.run_scan_command(server_info, suite())
- results[suite_name] = [cipher.name for cipher in scan.accepted_cipher_list]
-
- with open(filename, 'w') as outfile:
- json.dump(results, outfile)
-
- except ServerConnectivityError as e:
- raise RuntimeError(f'Could not connect to {e.server_info.hostname}: {e.error_message}')
-
-def main():
- parser = argparse.ArgumentParser(description='MongoDB SSL/TLS Suite Validation.')
- parser.add_argument('-p', '--port', type=int, default=27017, help="Port to listen on")
- parser.add_argument('-d', '--delete', action='store_true', help="Delete the ciphers.json file")
- args = parser.parse_args()
- if args.delete:
- os.remove(filename)
- else:
- return server_scanner(args.port)
-
-if __name__ == '__main__':
- main()
diff --git a/jstests/ssl/tls_enumerator.py b/jstests/ssl/tls_enumerator.py
new file mode 100644
index 00000000000..29b0e81190f
--- /dev/null
+++ b/jstests/ssl/tls_enumerator.py
@@ -0,0 +1,75 @@
+import ssl
+import socket
+import json
+import argparse
+
+
+def enumerate_tls_ciphers(protocol_options, host, port, cert, cafile):
+ root_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
+ root_context.options |= protocol_options
+ root_context.set_ciphers('ALL:COMPLEMENTOFALL:-PSK:-SRP')
+
+ ciphers = set([cipher['name'] for cipher in root_context.get_ciphers()])
+
+ accepted_ciphers = []
+
+ for cipher_name in ciphers:
+ context = ssl.SSLContext(root_context.protocol)
+ context.set_ciphers(cipher_name)
+ context.options = root_context.options
+ context.load_verify_locations(cafile=cafile)
+ context.load_cert_chain(certfile=cert)
+
+ with socket.socket(socket.AF_INET) as sock:
+ with context.wrap_socket(sock, server_hostname=host) as conn:
+ try:
+ conn.connect((host, port))
+ except Exception as e:
+ continue
+ accepted_ciphers.append(cipher_name)
+
+ return sorted(accepted_ciphers)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='MongoDB TLS Cipher Suite Enumerator')
+ parser.add_argument('--port', type=int, default=27017, help='Port to connect to')
+ parser.add_argument('-o', '--outfile', type=str, default='ciphers.json', help='file to write the output to')
+ parser.add_argument('--host', type=str, default='localhost', help='host to connect to')
+ parser.add_argument('--cafile', type=str, help='Path to CA certificate')
+ parser.add_argument('--cert', type=str, help='Path to client certificate')
+ args = parser.parse_args()
+
+ exclude_ops = {
+ ssl.OP_NO_SSLv2,
+ ssl.OP_NO_SSLv3,
+ ssl.OP_NO_TLSv1,
+ ssl.OP_NO_TLSv1_1,
+ ssl.OP_NO_TLSv1_2,
+ }
+
+ def exclude_except(op):
+ option = 0
+ for other_op in exclude_ops - {op}:
+ option |= other_op
+ return option
+
+ suites = {
+ 'sslv2': exclude_except(ssl.OP_NO_SSLv2),
+ 'sslv3': exclude_except(ssl.OP_NO_SSLv3),
+ 'tls1': exclude_except(ssl.OP_NO_TLSv1),
+ 'tls1_1': exclude_except(ssl.OP_NO_TLSv1_1),
+ 'tls1_2': exclude_except(ssl.OP_NO_TLSv1_2),
+ }
+
+ results = {
+ key: enumerate_tls_ciphers(protocol_options=proto,
+ host=args.host,
+ port=args.port,
+ cafile=args.cafile,
+ cert=args.cert)
+ for key, proto in suites.items()
+ }
+
+ with open(args.outfile, 'w+') as outfile:
+ json.dump(results, outfile)