diff --git a/ci/new_tsqa/files/rsa_keys/README.rst b/ci/new_tsqa/files/rsa_keys/README.rst new file mode 100644 index 00000000000..f3510de96b5 --- /dev/null +++ b/ci/new_tsqa/files/rsa_keys/README.rst @@ -0,0 +1,5 @@ +All of these certificates are self-signed and are *not* secure. They are intended +only for use in testing. + +# generated using (make sure to set "hostname"): +openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -nodes && cat key.pem cert.pem > keypair.pem && rm key.pem cert.pem diff --git a/ci/new_tsqa/files/rsa_keys/www.example.com.pem b/ci/new_tsqa/files/rsa_keys/www.example.com.pem new file mode 100644 index 00000000000..a2ef5679d6c --- /dev/null +++ b/ci/new_tsqa/files/rsa_keys/www.example.com.pem @@ -0,0 +1,49 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCrS3J3uL2MNr1Q +9zL7ODukw8UOHN4AwuWsNSDJzaswrgxtFO3VfJbCfo7CUtojoJY39783m3HnLhPA +cFxfsSgQ0Z8TdUZYtq2I1p/eEp/A6kdQffHS4To0ueYQ8r1I6pMJTniaDi8ICD0d +837WwbUUaxx8IhzaFNPrvSdhJb1LGPxj8YvfKvHF9rkwqpJSEAXFCKTa/H0i8wb8 +h3u4cXZmYN6aooai3SY1KGlWZtAfbgTlMMtvB3ZE3JsNNwbL4gfzxYsfXTW9QUZ2 +kT3irmB80koQjzDiW6MpxmLpvDD/NpQWSiR9TDZYCJtACwWyoxqo8gHZVqsF/rkK +yqkg2cbJAgMBAAECggEAf/ZGtsUdZGdoGdnxDda+R6GvzZEnDy6JYJH3womP/zem +NL7TxQ3jmbvtbaFzL/ZBAeJjyGipOGglfTby6tFu+tF9oo2TVaZyEK00lDMZgIYD +bFAJnN2AG+9bvQF5AcWqveMPGRbLb5aoAX3rHQdr/KrfhqP9JbU1cv/FMT9+H2B7 +Spty/WJYOL6AhzN4H4YHJzfhn2e0iMfA3usu1hha84FWWAR3+Z4sphSCtY+edumT +ygES/j5TAX7nu5Eyqe2L4natuDLXiLEbKEpqyfAg1SmTDqAiHrMtWkWv/e+tdTs8 ++DE+wPVhRCjVyjejjvsgDV/d9B805bbpq7M4eyIh+QKBgQDhHPcd+b5KZdXU/PiC +GQ/5C9elW35t7D8gaCzLAzoxW7B/PnTmVyK+QPBsxaUNg87BxB0TTssNUulSY2nk +TVemOe57xdJKiX7nOG+9vIZFGxlzqywMz0o6kZe3No8PAcp91K7Xr3RkXdHHwyZY ++NDiUIeMptEnoeEbFZcv+YQ+8wKBgQDCzB/5tD6D8uGKfsiakMs5HGEsYyJNP2b5 +UQ6e1spFg1noMfc5Vj+Av5lF0AE6tELEvJe0J8z2rA9zfHC6JkY/zOGMXrILF0S7 +KSTcxfBUqeE0OUJMkDNy61lRGs5ISul0qcGU473EsBEaxi999GcqzLrB5V4CHZlJ +EUam9SSqUwKBgQCVkcE/UWh11iO1WD9lcXGDb7LgU2I1dvqadZ0NZh+MG/exE7Zo +NQ0Ii+0y2D9KM4F0jPEkmv2e5K/R5eu9nQXXlDY4Vr/adnCzAHR+BHzR/adziw/B +kxkmwQWk3cM/nVkFMgLZm+IhrZRsveUEyI1BUXA+q7fcNVpzvGyvm4GasQKBgEW9 +XMlCLYuB3ht+ToV/xzIYJfYFO9eaFly2F1zomxwN7ZdCpDcD2NJYRiCHWplQxgK3 +Xjyiby/048c9ywHqCAZ5bFqb4HQ2DWZQUaE0wFkfRMA0q7bLfY/sEFsIFMgvAavB +xstuSZdsTYNfZstaP8FD8KzQWDq7rBBLvhax90F/AoGBANNhkbNxWiUJX/+6VtRj +u7msBgrUpYQtLkyY+13Ry8cdf+8w1die0LZ4unYjIcAS1ro+XGOc0GASc6pb5dYG +X+RxTyZzoNazbC6JEsFx9IJLn/L0/8jsg368m1f0Dkptd2LzrLsw2zuY6wm8DrLH +Re4GALck6zlT+rZNLuN13p5Z +-----END PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIDizCCAnOgAwIBAgIJAPvhz+ZDGGDAMA0GCSqGSIb3DQEBBQUAMFwxCzAJBgNV +BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg +Q29tcGFueSBMdGQxGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTAeFw0xNTAxMjgx +ODU4NTVaFw0xNTAyMjcxODU4NTVaMFwxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxE +ZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQxGDAWBgNV +BAMMD3d3dy5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAKtLcne4vYw2vVD3Mvs4O6TDxQ4c3gDC5aw1IMnNqzCuDG0U7dV8lsJ+jsJS +2iOgljf3vzebcecuE8BwXF+xKBDRnxN1Rli2rYjWn94Sn8DqR1B98dLhOjS55hDy +vUjqkwlOeJoOLwgIPR3zftbBtRRrHHwiHNoU0+u9J2ElvUsY/GPxi98q8cX2uTCq +klIQBcUIpNr8fSLzBvyHe7hxdmZg3pqihqLdJjUoaVZm0B9uBOUwy28HdkTcmw03 +BsviB/PFix9dNb1BRnaRPeKuYHzSShCPMOJboynGYum8MP82lBZKJH1MNlgIm0AL +BbKjGqjyAdlWqwX+uQrKqSDZxskCAwEAAaNQME4wHQYDVR0OBBYEFDI+rlmBGnur +j67NWhWx/oyK+xYOMB8GA1UdIwQYMBaAFDI+rlmBGnurj67NWhWx/oyK+xYOMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKl1WYFnT3LYuh3FLvLarZd0 +MPAiTfbFbZBm7WWXm/ZPL+uvdtIQVD8VzGjA1Y3HfapQyVyqNh1BWmlY4oDFFGOF +VZrZZ9vLG5xqTMBfjm/u7kBlKoU7DKNyeLeHoaOtaJ8Gk0QzUuCXTCF4Nd6qOyz+ +zhGhUa+QeiGIcsQLqTco84KprxD2FXvgFcmDU/OQksXml+QVUSqbZw+P+G9suOq3 +n7zGO7ff6CWjB3bS5m5Wfo1hcLAtGMHr+ZkWLCV5xh7zn7GZOyEpFrbC7TUnee4Y ++CE3ISKrhIob4WUCd9r8GYGh21CavGCccPxqasdGqRS6ZQpQXopo/UhDR//UURo= +-----END CERTIFICATE----- diff --git a/ci/new_tsqa/files/rsa_keys/www.test.com.pem b/ci/new_tsqa/files/rsa_keys/www.test.com.pem new file mode 100644 index 00000000000..6bd3b10da69 --- /dev/null +++ b/ci/new_tsqa/files/rsa_keys/www.test.com.pem @@ -0,0 +1,49 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDUiFw0p0JOfm/G +0YHivkd5v5Xu5tkBWETu9NgKLr7MkEBeqb1fjkm8Gd1IsndoYh0R/tkRzdE6O+lN +WjMgEuCtz+5zblBQ9xDMwAKI+1YhudqrwIxqXBzpyvAAZwU5EHftog/WiEDvJPM0 +XEQsx6142kgVR5NfNX6HWHOL8H86j7Yfbdd0Knu0Qv9tulC8aeBbZ+pKU0/cBCVi +ljr8OFdrCZu1EkKIat3oBjdbEF4boh4gNSsxbX+89uW4SZYR/G4xEX4VZJLMsloG +YOkcOLTV90+VDbbdrZsnmgQgpKBlp2NBdSx7MZQRU5De3PMBzo2uoIvmwXS9ZxtT +Lr+/dpxZAgMBAAECggEANh1uVN3NrUzWSypnRwOqEV7t30GaOZRvIOTo6VbTsCR+ +r1vK4zzIm4N+a5c9fi+VNVLNlJHyV0CP++keoWkNGlSaY3vQKX1vIqM1Qgm0+atn ++Vlyp1ZC6miIyaFxnAEMeE5OeBKDbiDbaBaiKUDCc8Yomnp6FMD7MZ0c9qHK027z +DL0kYjxjVgYiDnE1fJ1ZV466IcyDnGDky2ebjjvvTI7c55tbrEc+VRZdC5Cn5Yb/ +desjXGAb5snEbdAiNqCgIG9bLw3hHWsn0wCkUPRcEMoSZ73taak54vC7cPda7/D5 +aetzsNQWCMZC5NZ74JD5kyC3xBr7TAarQ5OXsJ0DMQKBgQD8GTQdc2MHvA+4nCpp +0Xy3J7gOCI63FWETZgw22sboqk1toF/LFQHYY40EdvEkFsqc6zTm09QK8y8G48sC +j8pvskhO2M9EgLegzPQv6NY0pTqltI0Ye7w9I3FTKNZIJW+XokDEFFqu+lXqf80i +BRmoUrltoS8L5XB/z5GoNMdk7QKBgQDX0mZSYdjPgTDWDSTCdEOcGB4bjAZzXDTu +ukYhri0vuStZ51giAWUlbBnN/hlvln5bULqZFnB5svFRujTIwwevxzhSl45aiNl1 +vE8zsRS0bNeNyjjF6HX0HOEuAQtre/k+WHvEH3mnFR1Zngwbrt4CJyuadlCmj7yw +jv/DVIyznQKBgAbmobiUqgdSLJP/ImIXK/TPj4hCz7VPToL7biYqQvunfcsccsLa +ZlyIDRosL1mvjghRn/cZoVpTYdwsbCg7y2zXUodmA/Z6F4y9T4noM8TpKPvUP3CG +IpcB215NZeA/thhOhrtXW0wi6isrKHBf913WNeE8Yk9PDo9RHUmfeD3ZAoGBAJGn +t9LFopN4t0LfH/30hWSlijxBJmFYy4iKQqacbHaW28ETNxHMKz00Vb4GTZhX0vNB +6o1C7anUsLTdnJ4ZsehZ5ZMoIbTMQycIbdOPIVAbXOaeoe4/UsvrabWoktJ5mt8O +zIiyTWIMCADhf353Z/HACdd3HjsrKsdl2wsy1rqpAoGBAOaNsxoyw/BIn/wvkQRs +YyYXLb8zECO17ad4M+aFsnf7yLY8i3k80JRfjqxis/yrePNuVKHiZYgEvLNBMdJi +j3YSMgXHYSgYti73+zBcy1uVVlUR5Q2HnihFX5Ho1IAQbC+TXzUCnLOEoR/IIHIz ++qWsoHgJD98ie3cjys0WujpH +-----END PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIDhTCCAm2gAwIBAgIJAJ7iD8W9eXUHMA0GCSqGSIb3DQEBBQUAMFkxCzAJBgNV +BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg +Q29tcGFueSBMdGQxFTATBgNVBAMMDHd3dy50ZXN0LmNvbTAeFw0xNTAxMjgxODU5 +MTVaFw0xNTAyMjcxODU5MTVaMFkxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZh +dWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQxFTATBgNVBAMM +DHd3dy50ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANSI +XDSnQk5+b8bRgeK+R3m/le7m2QFYRO702AouvsyQQF6pvV+OSbwZ3Uiyd2hiHRH+ +2RHN0To76U1aMyAS4K3P7nNuUFD3EMzAAoj7ViG52qvAjGpcHOnK8ABnBTkQd+2i +D9aIQO8k8zRcRCzHrXjaSBVHk181fodYc4vwfzqPth9t13Qqe7RC/226ULxp4Ftn +6kpTT9wEJWKWOvw4V2sJm7USQohq3egGN1sQXhuiHiA1KzFtf7z25bhJlhH8bjER +fhVkksyyWgZg6Rw4tNX3T5UNtt2tmyeaBCCkoGWnY0F1LHsxlBFTkN7c8wHOja6g +i+bBdL1nG1Muv792nFkCAwEAAaNQME4wHQYDVR0OBBYEFDZ0k9VGIt+bls2dqEsk ++71jcD6uMB8GA1UdIwQYMBaAFDZ0k9VGIt+bls2dqEsk+71jcD6uMAwGA1UdEwQF +MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAITinBiCI4VJg/rgLsadx3VCeSfu0AgU +VnS5Z5FJ6HqL0xtRgUrwQgqIM72nf5x3MGcx70TJVv4hDHBPFTfaQ2c3AeGzlpnI +j46UTEcuHx2ilyXnAOW3Z7Ap08WrBRATwUK21IVqoj9yfKUQ9Y70PCq/wKbZPMbX +v8zSJ/B+CJcV3yUJKhFVyKJ+de3i36XtItXBUnLoTSByjdCvLDc/uv900kU06DQ1 +qLUVPA0sEYIrC1V3fDokZLHuXW8ZdknfFWHP514rZ++njdrs3D0hInSI/IYIvp0f +3aK15+uirKoXxjxlQN5zQC/GKCcSiJo2yJ/mvOxIzUjCmb8Zg2ynqsc= +-----END CERTIFICATE----- diff --git a/ci/new_tsqa/requirements.txt b/ci/new_tsqa/requirements.txt index a6aef769105..668de0d7134 100644 --- a/ci/new_tsqa/requirements.txt +++ b/ci/new_tsqa/requirements.txt @@ -3,3 +3,4 @@ # TODO: pin a specific version https://github.com/jacksontj/tsqa/archive/master.zip pyyaml +pyOpenSSL diff --git a/ci/new_tsqa/tests/test_https.py b/ci/new_tsqa/tests/test_https.py new file mode 100644 index 00000000000..be014c691a2 --- /dev/null +++ b/ci/new_tsqa/tests/test_https.py @@ -0,0 +1,93 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +from OpenSSL import SSL +import socket + +import helpers +import tsqa.utils + + +class TestSSL(helpers.EnvironmentCase): + @classmethod + def setUpEnv(cls, env): + ''' + This funciton is responsible for setting up the environment for this fixture + This includes everything pre-daemon start + ''' + + # add an SSL port to ATS + cls.ssl_port = tsqa.utils.bind_unused_port()[1] + cls.configs['records.config']['CONFIG']['proxy.config.http.server_ports'] += ' {0}:ssl'.format(cls.ssl_port) + + # configure SSL multicert + cls.configs['ssl_multicert.config'].add_line('dest_ip=127.0.0.2 ssl_cert_name={0}'.format(helpers.tests_file_path('rsa_keys/www.example.com.pem'))) + cls.configs['ssl_multicert.config'].add_line('dest_ip=127.0.0.2 ssl_cert_name={0}'.format(helpers.tests_file_path('rsa_keys/www.test.com.pem'))) + + cls.configs['ssl_multicert.config'].add_line('dest_ip=* ssl_cert_name={0}'.format(helpers.tests_file_path('rsa_keys/www.example.com.pem'))) + cls.configs['ssl_multicert.config'].add_line('dest_ip=* ssl_cert_name={0}'.format(helpers.tests_file_path('rsa_keys/www.test.com.pem'))) + + def _get_cert(self, addr, sni_name=None): + ''' + Return the certificate for addr. Optionally sending sni_name + ''' + ctx = SSL.Context(SSL.SSLv23_METHOD) + # Set up client + sock = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM)) + sock.connect(addr) + if sni_name is not None: + sock.set_tlsext_host_name(sni_name) + sock.do_handshake() + return sock.get_peer_certificate() + + def test_star_ordering(self): + ''' + We should be served the first match, since we aren't sending SNI headers + ''' + addr = ('127.0.0.1', self.ssl_port) + cert = self._get_cert(addr) + self.assertEqual(cert.get_subject().commonName.decode(), 'www.example.com') + + def test_star_sni(self): + ''' + Make sure we get the certificate we asked for if we pass in SNI headers + ''' + addr = ('127.0.0.1', self.ssl_port) + cert = self._get_cert(addr, sni_name='www.test.com') + self.assertEqual(cert.get_subject().commonName.decode(), 'www.test.com') + + cert = self._get_cert(addr, sni_name='www.example.com') + self.assertEqual(cert.get_subject().commonName.decode(), 'www.example.com') + + def test_ip_ordering(self): + ''' + We should be served the first match, since we aren't sending SNI headers + ''' + addr = ('127.0.0.2', self.ssl_port) + cert = self._get_cert(addr) + self.assertEqual(cert.get_subject().commonName.decode(), 'www.example.com') + + def test_ip_sni(self): + ''' + Make sure we get the certificate we asked for if we pass in SNI headers + ''' + addr = ('127.0.0.2', self.ssl_port) + cert = self._get_cert(addr, sni_name='www.test.com') + self.assertEqual(cert.get_subject().commonName.decode(), 'www.test.com') + + cert = self._get_cert(addr, sni_name='www.example.com') + self.assertEqual(cert.get_subject().commonName.decode(), 'www.example.com') diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc index 7c522346e2e..9d704d52b7f 100644 --- a/iocore/net/SSLUtils.cc +++ b/iocore/net/SSLUtils.cc @@ -1673,8 +1673,9 @@ ssl_store_ssl_context( // Index this certificate by the specified IP(v6) address. If the address is "*", make it the default context. if (sslMultCertSettings.addr) { if (strcmp(sslMultCertSettings.addr, "*") == 0) { - lookup->ssl_default = ctx; - lookup->insert(sslMultCertSettings.addr, SSLCertContext(ctx, sslMultCertSettings.opt)); + if (lookup->insert(sslMultCertSettings.addr, SSLCertContext(ctx, sslMultCertSettings.opt)) >= 0) { + lookup->ssl_default = ctx; + } } else { IpEndpoint ep;