def setUp(self): """ Create a temporary file and launch a simple server in the current working directory. """ unittest_toolbox.Modified_TestCase.setUp(self) # Making a temporary file. current_dir = os.getcwd() target_filepath = self.make_temp_data_file(directory=current_dir) self.target_fileobj = open(target_filepath, 'r') self.target_data = self.target_fileobj.read() self.target_data_length = len(self.target_data) # Launch a SimpleHTTPServer (serves files in the current dir). self.server_process_handler = utils.TestServerProcess(log=logger) rel_target_filepath = os.path.basename(target_filepath) self.url = 'http://localhost:' \ + str(self.server_process_handler.port) + '/' + rel_target_filepath # Computing hash of target file data. m = hashlib.md5() m.update(self.target_data.encode('utf-8')) digest = m.hexdigest() self.target_hash = {'md5': digest}
def setUpClass(cls): # Create a temporary directory to store the repository, metadata, and target # files. 'temporary_directory' must be deleted in TearDownModule() so that # temporary files are always removed, even when exceptions occur. cls.temporary_directory = tempfile.mkdtemp(dir=os.getcwd()) # Launch a SimpleHTTPServer (serves files in the current directory). Test # cases will request metadata and target files that have been pre-generated # in 'tuf/tests/repository_data', which will be served by the # SimpleHTTPServer launched here. The test cases of # 'test_key_revocation.py' assume the pre-generated metadata files have a # specific structure, such as a delegated role, three target files, five # key files, etc. cls.server_process_handler = utils.TestServerProcess(log=logger)
def _start_slow_server(self, mode): # Launch a SimpleHTTPServer (serves files in the current directory). # Test cases will request metadata and target files that have been # pre-generated in 'tuf/tests/repository_data', which will be served by the # SimpleHTTPServer launched here. The test cases of this unit test assume # the pre-generated metadata files have a specific structure, such # as a delegated role 'targets/role1', three target files, five key files, # etc. self.server_process_handler = utils.TestServerProcess( log=logger, server='slow_retrieval_server.py', port=self.SERVER_PORT, timeout=0, extra_cmd_args=[mode]) logger.info('Slow Retrieval Server process started.') # NOTE: Following error is raised if a delay is not long enough: # <urlopen error [Errno 111] Connection refused> # or, on Windows: # Failed to establish a new connection: [Errno 111] Connection refused' # 1s led to occasional failures in automated builds on AppVeyor, so # increasing this to 3s, sadly. time.sleep(3)
def setUp(self): # We are inheriting from custom class. unittest_toolbox.Modified_TestCase.setUp(self) self.temporary_directory = tempfile.mkdtemp(dir=os.getcwd()) # Copy the original repository files provided in the test folder so that # any modifications made to repository files are restricted to the copies. # The 'repository_data' directory is expected to exist in 'tuf/tests/'. original_repository_files = os.path.join(os.getcwd(), 'repository_data') self.temporary_repository_root = self.make_temp_directory( directory=self.temporary_directory) # The original repository, keystore, and client directories will be copied # for each test case. original_repository = os.path.join(original_repository_files, 'repository') original_client = os.path.join(original_repository_files, 'client', 'test_repository1') original_keystore = os.path.join(original_repository_files, 'keystore') original_map_file = os.path.join(original_repository_files, 'map.json') # Save references to the often-needed client repository directories. # Test cases need these references to access metadata and target files. self.repository_directory = os.path.join( self.temporary_repository_root, 'repository_server1') self.repository_directory2 = os.path.join( self.temporary_repository_root, 'repository_server2') # Setting 'tuf.settings.repositories_directory' with the temporary client # directory copied from the original repository files. tuf.settings.repositories_directory = self.temporary_repository_root self.repository_name = 'test_repository1' self.repository_name2 = 'test_repository2' self.client_directory = os.path.join(self.temporary_repository_root, self.repository_name) self.client_directory2 = os.path.join(self.temporary_repository_root, self.repository_name2) self.keystore_directory = os.path.join(self.temporary_repository_root, 'keystore') self.map_file = os.path.join(self.client_directory, 'map.json') self.map_file2 = os.path.join(self.client_directory2, 'map.json') # Copy the original 'repository', 'client', and 'keystore' directories # to the temporary repository the test cases can use. shutil.copytree(original_repository, self.repository_directory) shutil.copytree(original_repository, self.repository_directory2) shutil.copytree(original_client, self.client_directory) shutil.copytree(original_client, self.client_directory2) shutil.copyfile(original_map_file, self.map_file) shutil.copyfile(original_map_file, self.map_file2) shutil.copytree(original_keystore, self.keystore_directory) # Launch a SimpleHTTPServer (serves files in the current directory). # Test cases will request metadata and target files that have been # pre-generated in 'tuf/tests/repository_data', which will be served by the # SimpleHTTPServer launched here. The test cases of this unit test assume # the pre-generated metadata files have a specific structure, such # as a delegated role 'targets/role1', three target files, five key files, # etc. self.SERVER_PORT = random.SystemRandom().randint(30000, 45000) self.SERVER_PORT2 = random.SystemRandom().randint(30000, 45000) # Avoid duplicate port numbers, to prevent multiple localhosts from # listening on the same port. while self.SERVER_PORT == self.SERVER_PORT2: self.SERVER_PORT2 = random.SystemRandom().randint(30000, 45000) # Needed because in some tests simple_server.py cannot be found. # The reason is that the current working directory # has been changed when executing a subprocess. SIMPLE_SERVER_PATH = os.path.join(os.getcwd(), 'simple_server.py') # Creates a subprocess running server and uses temp file for logging. self.server_process_handler = utils.TestServerProcess( log=logger, port=self.SERVER_PORT, server=SIMPLE_SERVER_PATH, popen_cwd=self.repository_directory) logger.debug('Server process started.') # Creates a subprocess running server and uses temp file for logging. self.server_process_handler2 = utils.TestServerProcess( log=logger, port=self.SERVER_PORT2, server=SIMPLE_SERVER_PATH, popen_cwd=self.repository_directory2) logger.debug('Server process 2 started.') url_prefix = 'http://localhost:' + str(self.SERVER_PORT) url_prefix2 = 'http://localhost:' + str(self.SERVER_PORT2) self.repository_mirrors = { 'mirror1': { 'url_prefix': url_prefix, 'metadata_path': 'metadata', 'targets_path': 'targets' } } self.repository_mirrors2 = { 'mirror1': { 'url_prefix': url_prefix2, 'metadata_path': 'metadata', 'targets_path': 'targets' } } # Create the repository instances. The test cases will use these client # updaters to refresh metadata, fetch target files, etc. self.repository_updater = updater.Updater(self.repository_name, self.repository_mirrors) self.repository_updater2 = updater.Updater(self.repository_name2, self.repository_mirrors2)
def test_https_connection(self): """ Try various HTTPS downloads using trusted and untrusted certificates with and without the correct hostname listed in the SSL certificate. """ # Make a temporary file to be served to the client. current_directory = os.getcwd() target_filepath = self.make_temp_data_file(directory=current_directory) with open(target_filepath, 'r') as target_file_object: target_data_length = len(target_file_object.read()) # These cert files provide various test cases: # good: A valid cert from an older generation of test_download.py tests. # good2: A valid cert made simultaneous to the bad certs below, with the # same settings otherwise, tested here in case the difference # between the way the new bad certs and the old good cert were # generated turns out to matter at some point. # bad: An otherwise-valid cert with the wrong hostname. The good certs # list "localhost", but this lists "notmyhostname". # expired: An otherwise-valid cert but which is expired (no valid dates # exist, fwiw: startdate > enddate). good_cert_fname = os.path.join('ssl_certs', 'ssl_cert.crt') good2_cert_fname = os.path.join('ssl_certs', 'ssl_cert_2.crt') bad_cert_fname = os.path.join('ssl_certs', 'ssl_cert_wronghost.crt') expired_cert_fname = os.path.join('ssl_certs', 'ssl_cert_expired.crt') # Launch four HTTPS servers (serve files in the current dir). # 1: we expect to operate correctly # 2: also good; uses a slightly different cert (controls for the cert # generation method used for the next two, in case it comes to matter) # 3: run with an HTTPS certificate with an unexpected hostname # 4: run with an HTTPS certificate that is expired # Be sure to offset from the port used in setUp to avoid collision. port1 = self.server_process_handler.port + 1 port2 = self.server_process_handler.port + 2 port3 = self.server_process_handler.port + 3 port4 = self.server_process_handler.port + 4 good_https_server_handler = utils.TestServerProcess( log=logger, server='simple_https_server.py', port=port1, extra_cmd_args=[good_cert_fname]) good2_https_server_handler = utils.TestServerProcess( log=logger, server='simple_https_server.py', port=port2, extra_cmd_args=[good2_cert_fname]) bad_https_server_handler = utils.TestServerProcess( log=logger, server='simple_https_server.py', port=port3, extra_cmd_args=[bad_cert_fname]) expd_https_server_handler = utils.TestServerProcess( log=logger, server='simple_https_server.py', port=port4, extra_cmd_args=[expired_cert_fname]) suffix = '/' + os.path.basename(target_filepath) good_https_url = 'https://localhost:' + str(port1) + suffix good2_https_url = 'https://localhost:' + str(port2) + suffix bad_https_url = 'https://localhost:' + str(port3) + suffix expired_https_url = 'https://localhost:' + str(port4) + suffix # Download the target file using an HTTPS connection. # Use try-finally solely to ensure that the server processes are killed. try: # Trust the certfile that happens to use a different hostname than we # will expect. os.environ['REQUESTS_CA_BUNDLE'] = bad_cert_fname # Clear sessions to ensure that the certificate we just specified is used. # TODO: Confirm necessity of this session clearing and lay out mechanics. tuf.download._sessions = {} # Try connecting to the server process with the bad cert while trusting # the bad cert. Expect failure because even though we trust it, the # hostname we're connecting to does not match the hostname in the cert. logger.info('Trying HTTPS download of target file: ' + bad_https_url) with warnings.catch_warnings(): # We're ok with a slightly fishy localhost cert warnings.filterwarnings( 'ignore', category=urllib3.exceptions.SubjectAltNameWarning) with self.assertRaises(requests.exceptions.SSLError): download.safe_download(bad_https_url, target_data_length) with self.assertRaises(requests.exceptions.SSLError): download.unsafe_download(bad_https_url, target_data_length) # Try connecting to the server processes with the good certs while not # trusting the good certs (trusting the bad cert instead). Expect failure # because even though the server's cert file is otherwise OK, we don't # trust it. logger.info('Trying HTTPS download of target file: ' + good_https_url) with self.assertRaises(requests.exceptions.SSLError): download.safe_download(good_https_url, target_data_length) with self.assertRaises(requests.exceptions.SSLError): download.unsafe_download(good_https_url, target_data_length) logger.info('Trying HTTPS download of target file: ' + good2_https_url) with self.assertRaises(requests.exceptions.SSLError): download.safe_download(good2_https_url, target_data_length) with self.assertRaises(requests.exceptions.SSLError): download.unsafe_download(good2_https_url, target_data_length) # Configure environment to now trust the certfile that is expired. os.environ['REQUESTS_CA_BUNDLE'] = expired_cert_fname # Clear sessions to ensure that the certificate we just specified is used. # TODO: Confirm necessity of this session clearing and lay out mechanics. tuf.download._sessions = {} # Try connecting to the server process with the expired cert while # trusting the expired cert. Expect failure because even though we trust # it, it is expired. logger.info('Trying HTTPS download of target file: ' + expired_https_url) with self.assertRaises(requests.exceptions.SSLError): download.safe_download(expired_https_url, target_data_length) with self.assertRaises(requests.exceptions.SSLError): download.unsafe_download(expired_https_url, target_data_length) # Try connecting to the server processes with the good certs while # trusting the appropriate good certs. Expect success. # TODO: expand testing to switch expected certificates back and forth a # bit more while clearing / not clearing sessions. os.environ['REQUESTS_CA_BUNDLE'] = good_cert_fname # Clear sessions to ensure that the certificate we just specified is used. # TODO: Confirm necessity of this session clearing and lay out mechanics. tuf.download._sessions = {} logger.info('Trying HTTPS download of target file: ' + good_https_url) download.safe_download(good_https_url, target_data_length).close() download.unsafe_download(good_https_url, target_data_length).close() os.environ['REQUESTS_CA_BUNDLE'] = good2_cert_fname # Clear sessions to ensure that the certificate we just specified is used. # TODO: Confirm necessity of this session clearing and lay out mechanics. tuf.download._sessions = {} logger.info('Trying HTTPS download of target file: ' + good2_https_url) download.safe_download(good2_https_url, target_data_length).close() download.unsafe_download(good2_https_url, target_data_length).close() finally: for proc_handler in [ good_https_server_handler, good2_https_server_handler, bad_https_server_handler, expd_https_server_handler ]: # Logs stdout and stderr from the server subprocess and then it # kills it and closes the temp file used for logging. proc_handler.clean()
def setUpClass(cls): """ Setup performed before the first test function (TestWithProxies class method) runs. Launch HTTP, HTTPS, and proxy servers in the current working directory. We'll set up four servers: - HTTP server (simple_server.py) - HTTPS server (simple_https_server.py) - HTTP proxy server (proxy_server.py) (that supports HTTP CONNECT to funnel HTTPS connections) - HTTPS proxy server (proxy_server.py) (trusted by the client to intercept and resign connections) """ unittest_toolbox.Modified_TestCase.setUpClass() if not six.PY2: raise NotImplementedError( "TestWithProxies only works with Python 2" " (proxy_server.py is Python2 only)") # Launch a simple HTTP server (serves files in the current dir). cls.http_server_handler = utils.TestServerProcess(log=logger) # Launch an HTTPS server (serves files in the current dir). cls.https_server_handler = utils.TestServerProcess( log=logger, server='simple_https_server.py', port=cls.http_server_handler.port + 1) # Launch an HTTP proxy server derived from inaz2/proxy2. # This one is able to handle HTTP CONNECT requests, and so can pass HTTPS # requests on to the target server. cls.http_proxy_handler = utils.TestServerProcess( log=logger, server='proxy_server.py', port=cls.http_server_handler.port + 2) # Note that the HTTP proxy server's address uses http://, regardless of the # type of connection used with the target server. cls.http_proxy_addr = 'http://127.0.0.1:' + str( cls.http_proxy_handler.port) # Launch an HTTPS proxy server, also derived from inaz2/proxy2. # (An HTTPS proxy performs its own TLS connection with the client and must # be trusted by it, and is capable of tampering.) # We instruct the proxy server to expect certain certificates from the # target server. # 1st arg: port # 2nd arg: whether to intercept (HTTPS proxy) or relay (TCP tunnel using # HTTP CONNECT verb, to facilitate an HTTPS connection between the client # and server which the proxy cannot inspect) # 3rd arg: (optional) certificate file for telling the proxy what target # server certs to accept in its HTTPS connection to the target server. # This is only relevant if the proxy is in intercept mode. good_cert_fpath = os.path.join('ssl_certs', 'ssl_cert.crt') cls.https_proxy_handler = utils.TestServerProcess( log=logger, server='proxy_server.py', port=cls.http_server_handler.port + 3, extra_cmd_args=['intercept', good_cert_fpath]) # Note that the HTTPS proxy server's address uses https://, regardless of # the type of connection used with the target server. cls.https_proxy_addr = 'https://localhost:' + str( cls.https_proxy_handler.port)