OLD | NEW |
(Empty) | |
| 1 # Copyright 2011 Google Inc. |
| 2 # |
| 3 # Permission is hereby granted, free of charge, to any person obtaining a |
| 4 # copy of this software and associated documentation files (the |
| 5 # "Software"), to deal in the Software without restriction, including |
| 6 # without limitation the rights to use, copy, modify, merge, publish, dis- |
| 7 # tribute, sublicense, and/or sell copies of the Software, and to permit |
| 8 # persons to whom the Software is furnished to do so, subject to the fol- |
| 9 # lowing conditions: |
| 10 # |
| 11 # The above copyright notice and this permission notice shall be included |
| 12 # in all copies or substantial portions of the Software. |
| 13 # |
| 14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| 15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- |
| 16 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT |
| 17 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| 18 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| 20 # IN THE SOFTWARE. |
| 21 |
| 22 """ |
| 23 Tests to validate correct validation of SSL server certificates. |
| 24 |
| 25 Note that this test assumes two external dependencies are available: |
| 26 - A http proxy, which by default is assumed to be at host 'cache' and port |
| 27 3128. This can be overridden with environment variables PROXY_HOST and |
| 28 PROXY_PORT, respectively. |
| 29 - An ssl-enabled web server that will return a valid certificate signed by one |
| 30 of the bundled CAs, and which can be reached by an alternate hostname that |
| 31 does not match the CN in that certificate. By default, this test uses host |
| 32 'www' (without fully qualified domain). This can be overridden with |
| 33 environment variable INVALID_HOSTNAME_HOST. If no suitable host is already |
| 34 available, such a mapping can be established by temporarily adding an IP |
| 35 address for, say, www.google.com or www.amazon.com to /etc/hosts. |
| 36 """ |
| 37 |
| 38 import os |
| 39 import ssl |
| 40 import unittest |
| 41 |
| 42 from nose.plugins.attrib import attr |
| 43 |
| 44 import boto |
| 45 from boto import exception, https_connection |
| 46 from boto.gs.connection import GSConnection |
| 47 from boto.s3.connection import S3Connection |
| 48 |
| 49 |
| 50 # File 'other_cacerts.txt' contains a valid CA certificate of a CA that is used |
| 51 # by neither S3 nor Google Cloud Storage. Validation against this CA cert should |
| 52 # result in a certificate error. |
| 53 DEFAULT_CA_CERTS_FILE = os.path.join( |
| 54 os.path.dirname(os.path.abspath(__file__ )), 'other_cacerts.txt') |
| 55 |
| 56 |
| 57 PROXY_HOST = os.environ.get('PROXY_HOST', 'cache') |
| 58 PROXY_PORT = os.environ.get('PROXY_PORT', '3128') |
| 59 |
| 60 # This test assumes that this host returns a certificate signed by one of the |
| 61 # trusted CAs, but with a Common Name that won't match host name 'www' (i.e., |
| 62 # the server should return a certificate with CN 'www.<somedomain>.com'). |
| 63 INVALID_HOSTNAME_HOST = os.environ.get('INVALID_HOSTNAME_HOST', 'www') |
| 64 |
| 65 |
| 66 @attr('notdefault', 'ssl') |
| 67 class CertValidationTest(unittest.TestCase): |
| 68 def setUp(self): |
| 69 # Clear config |
| 70 for section in boto.config.sections(): |
| 71 boto.config.remove_section(section) |
| 72 |
| 73 # Enable https_validate_certificates. |
| 74 boto.config.add_section('Boto') |
| 75 boto.config.setbool('Boto', 'https_validate_certificates', True) |
| 76 |
| 77 # Set up bogus credentials so that the auth module is willing to go |
| 78 # ahead and make a request; the request should fail with a service-level |
| 79 # error if it does get to the service (S3 or GS). |
| 80 boto.config.add_section('Credentials') |
| 81 boto.config.set('Credentials', 'gs_access_key_id', 'xyz') |
| 82 boto.config.set('Credentials', 'gs_secret_access_key', 'xyz') |
| 83 boto.config.set('Credentials', 'aws_access_key_id', 'xyz') |
| 84 boto.config.set('Credentials', 'aws_secret_access_key', 'xyz') |
| 85 |
| 86 def enableProxy(self): |
| 87 boto.config.set('Boto', 'proxy', PROXY_HOST) |
| 88 boto.config.set('Boto', 'proxy_port', PROXY_PORT) |
| 89 |
| 90 def assertConnectionThrows(self, connection_class, error): |
| 91 conn = connection_class() |
| 92 self.assertRaises(error, conn.get_all_buckets) |
| 93 |
| 94 def do_test_valid_cert(self): |
| 95 # When connecting to actual servers with bundled root certificates, no |
| 96 # cert errors should be thrown; instead we will get "invalid |
| 97 # credentials" errors since the config used does not contain any |
| 98 # credentials. |
| 99 self.assertConnectionThrows(S3Connection, exception.S3ResponseError) |
| 100 self.assertConnectionThrows(GSConnection, exception.GSResponseError) |
| 101 |
| 102 def test_valid_cert(self): |
| 103 self.do_test_valid_cert() |
| 104 |
| 105 def test_valid_cert_with_proxy(self): |
| 106 self.enableProxy() |
| 107 self.do_test_valid_cert() |
| 108 |
| 109 def do_test_invalid_signature(self): |
| 110 boto.config.set('Boto', 'ca_certificates_file', DEFAULT_CA_CERTS_FILE) |
| 111 self.assertConnectionThrows(S3Connection, ssl.SSLError) |
| 112 self.assertConnectionThrows(GSConnection, ssl.SSLError) |
| 113 |
| 114 def test_invalid_signature(self): |
| 115 self.do_test_invalid_signature() |
| 116 |
| 117 def test_invalid_signature_with_proxy(self): |
| 118 self.enableProxy() |
| 119 self.do_test_invalid_signature() |
| 120 |
| 121 def do_test_invalid_host(self): |
| 122 boto.config.set('Credentials', 'gs_host', INVALID_HOSTNAME_HOST) |
| 123 boto.config.set('Credentials', 's3_host', INVALID_HOSTNAME_HOST) |
| 124 self.assertConnectionThrows(S3Connection, ssl.SSLError) |
| 125 self.assertConnectionThrows(GSConnection, ssl.SSLError) |
| 126 |
| 127 def do_test_invalid_host(self): |
| 128 boto.config.set('Credentials', 'gs_host', INVALID_HOSTNAME_HOST) |
| 129 boto.config.set('Credentials', 's3_host', INVALID_HOSTNAME_HOST) |
| 130 self.assertConnectionThrows( |
| 131 S3Connection, https_connection.InvalidCertificateException) |
| 132 self.assertConnectionThrows( |
| 133 GSConnection, https_connection.InvalidCertificateException) |
| 134 |
| 135 def test_invalid_host(self): |
| 136 self.do_test_invalid_host() |
| 137 |
| 138 def test_invalid_host_with_proxy(self): |
| 139 self.enableProxy() |
| 140 self.do_test_invalid_host() |
| 141 |
OLD | NEW |