This is an automated email from the ASF dual-hosted git repository.

zwoop pushed a commit to branch 8.1.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit 6319f29c8ff5d106ec0b68ed0c17ad2d33ec09d8
Author: Susan Hinrichs <shinr...@oath.com>
AuthorDate: Wed Jan 16 20:50:27 2019 +0000

    Add valid_tls_protocols_in to allow for per-domain protocols.
    
    (cherry picked from commit 34d9c4eaac536a2f27debd15326c58da91d711af)
    
    Conflicts:
        doc/admin-guide/files/ssl_server_name.yaml.en.rst
        iocore/net/SSLSNIConfig.cc
        iocore/net/SSLUtils.cc
        iocore/net/YamlSNIConfig.cc
        iocore/net/YamlSNIConfig.h
---
 build/crypto.m4                                   |  41 ++++++++
 configure.ac                                      |   3 +
 doc/admin-guide/files/ssl_server_name.yaml.en.rst |   9 ++
 include/tscore/ink_config.h.in                    |   1 +
 iocore/net/P_SNIActionPerformer.h                 |  26 +++++
 iocore/net/P_SSLNetVConnection.h                  |   5 +-
 iocore/net/P_SSLUtils.h                           |   1 +
 iocore/net/SSLSNIConfig.cc                        |   3 +
 iocore/net/SSLUtils.cc                            |  95 ++++++++++++++++--
 iocore/net/YamlSNIConfig.cc                       |  52 +++++++++-
 iocore/net/YamlSNIConfig.h                        |  20 ++--
 tests/gold_tests/tls/tls_client_versions.test.py  | 112 ++++++++++++++++++++++
 12 files changed, 350 insertions(+), 18 deletions(-)

diff --git a/build/crypto.m4 b/build/crypto.m4
index e3ac31b..248be9e 100644
--- a/build/crypto.m4
+++ b/build/crypto.m4
@@ -144,6 +144,47 @@ AC_DEFUN([TS_CHECK_CRYPTO_CERT_CB], [
   AC_SUBST(use_cert_cb)
 ])
 
+AC_DEFUN([TS_CHECK_CRYPTO_HELLO_CB], [
+  _hello_saved_LIBS=$LIBS
+  enable_hello_cb=yes
+
+  TS_ADDTO(LIBS, [$OPENSSL_LIBS])
+  AC_CHECK_HEADERS(openssl/ssl.h openssl/ts.h)
+  AC_CHECK_HEADERS(openssl/tls1.h, [], [],
+[ #if HAVE_OPENSSL_SSL_H
+#include <openssl/ssl.h>
+#include <openssl/tls1.h>
+#endif ])
+
+  AC_MSG_CHECKING([for SSL_CTX_set_client_hello_cb])
+  AC_LINK_IFELSE(
+  [
+    AC_LANG_PROGRAM([[
+#if HAVE_OPENSSL_SSL_H
+#include <openssl/ssl.h>
+#endif
+#if HAVE_OPENSSL_TLS1_H
+#include <openssl/tls1.h>
+#endif
+      ]],
+      [[SSL_CTX_set_client_hello_cb(NULL, NULL, NULL);]])
+  ],
+  [
+    AC_MSG_RESULT([yes])
+  ],
+  [
+    AC_MSG_RESULT([no])
+    enable_hello_cb=no
+  ])
+
+  LIBS=$_hello_saved_LIBS
+
+  AC_MSG_CHECKING(whether to enable TLS client hello callback support)
+  AC_MSG_RESULT([$enable_hello_cb])
+  TS_ARG_ENABLE_VAR([use], [hello-cb])
+  AC_SUBST(use_hello_cb)
+])
+
 AC_DEFUN([TS_CHECK_CRYPTO_SET_RBIO], [
   _rbio_saved_LIBS=$LIBS
   enable_set_rbio=yes
diff --git a/configure.ac b/configure.ac
index 0a19722..0cd0f72 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1196,6 +1196,9 @@ TS_CHECK_CRYPTO_EC_KEYS
 # Check for the presense of the certificate callback in the ssl library
 TS_CHECK_CRYPTO_CERT_CB
 
+# Check for the client hello callback
+TS_CHECK_CRYPTO_HELLO_CB
+
 #
 # Check for SSL_set0_rbio call
 TS_CHECK_CRYPTO_SET_RBIO
diff --git a/doc/admin-guide/files/ssl_server_name.yaml.en.rst 
b/doc/admin-guide/files/ssl_server_name.yaml.en.rst
index 4da0c0f..3778f1f 100644
--- a/doc/admin-guide/files/ssl_server_name.yaml.en.rst
+++ b/doc/admin-guide/files/ssl_server_name.yaml.en.rst
@@ -70,6 +70,15 @@ disable_h2              :code:`true` or :code:`false`.
                         for proxy ports on which HTTP/2 is not enabled.
 
 tunnel_route            Destination as an FQDN and port, separated by a colon 
``:``.
+
+valid_tls_versions_in     This specifies the list of TLS protocols that will 
be offered to user agents during
+                          the TLS negotiaton.  This replaces the global 
settings in :ts:cv:`proxy.config.ssl.TSLv1`,
+                          :ts:cv:`proxy.config.ssl.TLSv1_1`, 
:ts:cv:`proxy.config.ssl.TLSv1_2`,
+                          and :ts:cv:`proxy.config.ssl.TLSv1_3`. The potential 
values are TLSv1, TLSv1_1, TLSv1_2, and
+                          TLSv1_3.  You must list all protocols that |TS| 
should offer to the client when using
+                          this key.  This key is only valid for openssl 1.1.0 
and later. Older versions of openssl do not
+                          provide a hook early enough to update the SSL 
object.  It is a syntax error for |TS| built
+                          against earlier versions.
 ======================= 
==============================================================================
 
 Client verification, via ``verify_client``, correponds to setting
diff --git a/include/tscore/ink_config.h.in b/include/tscore/ink_config.h.in
index d8d0c56..04b1f56 100644
--- a/include/tscore/ink_config.h.in
+++ b/include/tscore/ink_config.h.in
@@ -73,6 +73,7 @@
 #define TS_USE_TLS_ALPN @use_tls_alpn@
 #define TS_USE_TLS_ASYNC @use_tls_async@
 #define TS_USE_CERT_CB @use_cert_cb@
+#define TS_USE_HELLO_CB @use_hello_cb@
 #define TS_USE_SET_RBIO @use_set_rbio@
 #define TS_USE_GET_DH_2048_256 @use_dh_get_2048_256@
 #define TS_USE_TLS_ECKEY @use_tls_eckey@
diff --git a/iocore/net/P_SNIActionPerformer.h 
b/iocore/net/P_SNIActionPerformer.h
index c8fce5d..2385b73 100644
--- a/iocore/net/P_SNIActionPerformer.h
+++ b/iocore/net/P_SNIActionPerformer.h
@@ -92,6 +92,32 @@ public:
   }
 };
 
+class TLSValidProtocols : public ActionItem
+{
+  bool unset;
+  unsigned long protocol_mask;
+
+public:
+#ifdef SSL_OP_NO_TLSv1_3
+  static const unsigned long max_mask = SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | 
SSL_OP_NO_TLSv1_2 | SSL_OP_NO_TLSv1_3;
+#else
+  static const unsigned long max_mask = SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | 
SSL_OP_NO_TLSv1_2;
+#endif
+  TLSValidProtocols() : unset(true), protocol_mask(max_mask) {}
+  TLSValidProtocols(unsigned long protocols) : unset(false), 
protocol_mask(protocols) {}
+  int
+  SNIAction(Continuation *cont) override
+  {
+    if (!unset) {
+      auto ssl_vc = dynamic_cast<SSLNetVConnection *>(cont);
+      Debug("ssl_sni", "TLSValidProtocol param 0%x", static_cast<unsigned 
int>(this->protocol_mask));
+      ssl_vc->protocol_mask_set = true;
+      ssl_vc->protocol_mask     = protocol_mask;
+    }
+    return SSL_TLSEXT_ERR_OK;
+  }
+};
+
 class SNI_IpAllow : public ActionItem
 {
   IpMap ip_map;
diff --git a/iocore/net/P_SSLNetVConnection.h b/iocore/net/P_SSLNetVConnection.h
index ff7a801..531abdb 100644
--- a/iocore/net/P_SSLNetVConnection.h
+++ b/iocore/net/P_SSLNetVConnection.h
@@ -318,7 +318,7 @@ public:
   ink_hrtime sslHandshakeEndTime   = 0;
   ink_hrtime sslLastWriteTime      = 0;
   int64_t sslTotalBytesSent        = 0;
-  char *serverName                 = nullptr;
+  const char *serverName           = nullptr;
 
   /// Set by asynchronous hooks to request a specific operation.
   SslVConnOp hookOpRequested = SSL_HOOK_OP_DEFAULT;
@@ -327,6 +327,9 @@ public:
   SSLNetVConnection(const SSLNetVConnection &) = delete;
   SSLNetVConnection &operator=(const SSLNetVConnection &) = delete;
 
+  bool protocol_mask_set = false;
+  unsigned long protocol_mask;
+
 private:
   std::string_view map_tls_protocol_to_tag(const char *proto_string) const;
   bool update_rbio(bool move_to_socket);
diff --git a/iocore/net/P_SSLUtils.h b/iocore/net/P_SSLUtils.h
index 4f829d1..c0737b3 100644
--- a/iocore/net/P_SSLUtils.h
+++ b/iocore/net/P_SSLUtils.h
@@ -175,6 +175,7 @@ void SSLNetVCDetach(SSL *ssl);
 SSLNetVConnection *SSLNetVCAccess(const SSL *ssl);
 
 void setClientCertLevel(SSL *ssl, uint8_t certLevel);
+void setTLSValidProtocols(SSL *ssl, unsigned long proto_mask, unsigned long 
max_mask);
 
 namespace ssl
 {
diff --git a/iocore/net/SSLSNIConfig.cc b/iocore/net/SSLSNIConfig.cc
index a0d5f5b..90e71a7 100644
--- a/iocore/net/SSLSNIConfig.cc
+++ b/iocore/net/SSLSNIConfig.cc
@@ -77,6 +77,9 @@ SNIConfigParams::loadSNIConfig()
     } else {
       sni_action_map.put(ats_strdup(servername), aiVec);
     }
+    if (!item.protocol_unset) {
+      aiVec->push_back(new TLSValidProtocols(item.protocol_mask));
+    }
 
     if (item.tunnel_destination.length()) {
       TunnelMap.emplace(item.fqdn.data(), item.tunnel_destination);
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index 7fe3a74..888ffee 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -46,6 +46,7 @@
 #include <termios.h>
 #include <vector>
 #include "P_SNIActionPerformer.h"
+#include "P_SSLSNI.h"
 
 #if HAVE_OPENSSL_EVP_H
 #include <openssl/evp.h>
@@ -406,6 +407,72 @@ ssl_verify_client_callback(int preverify_ok, 
X509_STORE_CTX *ctx)
   return SSL_TLSEXT_ERR_OK;
 }
 
+static int
+PerformAction(Continuation *cont, const char *servername)
+{
+  SNIConfig::scoped_config params;
+  const actionVector *actionvec = params->get(servername);
+  if (!actionvec) {
+    Debug("ssl_sni", "%s not available in the map", servername);
+  } else {
+    for (auto &&item : *actionvec) {
+      auto ret = item->SNIAction(cont);
+      if (ret != SSL_TLSEXT_ERR_OK) {
+        return ret;
+      }
+    }
+  }
+  return SSL_TLSEXT_ERR_OK;
+}
+
+#if TS_USE_HELLO_CB
+// Pausable callback
+static int
+ssl_client_hello_callback(SSL *s, int *al, void *arg)
+{
+  const char *servername = nullptr;
+  const unsigned char *p;
+  size_t remaining, len;
+  if (SSL_client_hello_get0_ext(s, TLSEXT_TYPE_server_name, &p, &remaining) || 
remaining <= 2) {
+    // Parse to get to the name, originally from test/handshake_helper.c in 
openssl tree
+    /* Extract the length of the supplied list of names. */
+    len = *(p++) << 8;
+    len += *(p++);
+    if (len + 2 == remaining) {
+      remaining = len;
+      /*
+       * The list in practice only has a single element, so we only consider
+       * the first one.
+       */
+      if (remaining != 0 && *p++ == TLSEXT_NAMETYPE_host_name) {
+        remaining--;
+        /* Now we can finally pull out the byte array with the actual 
hostname. */
+        if (remaining > 2) {
+          len = *(p++) << 8;
+          len += *(p++);
+          if (len + 2 <= remaining) {
+            remaining  = len;
+            servername = reinterpret_cast<const char *>(p);
+          }
+        }
+      }
+    }
+  }
+
+  SSLNetVConnection *netvc = SSLNetVCAccess(s);
+
+  netvc->serverName = servername ? servername : "";
+  int ret           = PerformAction(netvc, netvc->serverName);
+  if (ret != SSL_TLSEXT_ERR_OK) {
+    return SSL_TLSEXT_ERR_ALERT_FATAL;
+  }
+  if (netvc->protocol_mask_set) {
+    setTLSValidProtocols(s, netvc->protocol_mask, TLSValidProtocols::max_mask);
+  }
+  return 1;
+}
+#endif
+
 // Use the certificate callback for openssl 1.0.2 and greater
 // otherwise use the SNI callback
 #if TS_USE_CERT_CB
@@ -448,17 +515,19 @@ extern SNIActionPerformer sni_action_performer;
 static int
 ssl_servername_only_callback(SSL *ssl, int * /* ad */, void * /*arg*/)
 {
-  int ret                  = SSL_TLSEXT_ERR_OK;
   SSLNetVConnection *netvc = SSLNetVCAccess(ssl);
-  const char *servername   = SSL_get_servername(ssl, 
TLSEXT_NAMETYPE_host_name);
-  Debug("ssl", "Requested servername is %s", servername);
-  if (servername != nullptr) {
-    ret = sni_action_performer.PerformAction(netvc, servername);
+  netvc->callHooks(TS_EVENT_SSL_SERVERNAME);
+
+  netvc->serverName = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+  if (nullptr == netvc->serverName) {
+    netvc->serverName = "";
   }
-  if (ret != SSL_TLSEXT_ERR_OK)
-    return SSL_TLSEXT_ERR_ALERT_FATAL;
 
-  netvc->callHooks(TS_EVENT_SSL_SERVERNAME);
+  // Rerun the actions in case a plugin changed the server name
+  int ret = PerformAction(netvc, netvc->serverName);
+  if (ret != SSL_TLSEXT_ERR_OK) {
+    return SSL_TLSEXT_ERR_ALERT_FATAL;
+  }
   return SSL_TLSEXT_ERR_OK;
 }
 
@@ -1563,6 +1632,16 @@ ssl_set_handshake_callbacks(SSL_CTX *ctx)
 #else
   SSL_CTX_set_tlsext_servername_callback(ctx, 
ssl_servername_and_cert_callback);
 #endif
+#if TS_USE_HELLO_CB
+  SSL_CTX_set_client_hello_cb(ctx, ssl_client_hello_callback, nullptr);
+#endif
+}
+
+void
+setTLSValidProtocols(SSL *ssl, unsigned long proto_mask, unsigned long 
max_mask)
+{
+  SSL_set_options(ssl, proto_mask);
+  SSL_clear_options(ssl, max_mask & ~proto_mask);
 }
 
 void
diff --git a/iocore/net/YamlSNIConfig.cc b/iocore/net/YamlSNIConfig.cc
index 6f31777..1b64a47 100644
--- a/iocore/net/YamlSNIConfig.cc
+++ b/iocore/net/YamlSNIConfig.cc
@@ -26,10 +26,12 @@
 #include <string_view>
 
 #include <yaml-cpp/yaml.h>
+#include <openssl/ssl.h>
 
 #include "tscore/Diags.h"
 #include "tscore/EnumDescriptor.h"
 #include "tsconfig/Errata.h"
+#include "P_SNIActionPerformer.h"
 
 ts::Errata
 YamlSNIConfig::loader(const char *cfgFilename)
@@ -57,8 +59,47 @@ YamlSNIConfig::loader(const char *cfgFilename)
 
 TsEnumDescriptor LEVEL_DESCRIPTOR = {{{"NONE", 0}, {"MODERATE", 1}, {"STRICT", 
2}}};
 
-std::set<std::string> valid_sni_config_keys = {
-  TS_fqdn, TS_disable_H2, TS_verify_client, TS_tunnel_route, 
TS_verify_origin_server, TS_client_cert, TS_ip_allow};
+void
+YamlSNIConfig::Item::EnableProtocol(YamlSNIConfig::TLSProtocol proto)
+{
+  if (proto <= YamlSNIConfig::TLSProtocol::TLS_MAX) {
+    if (protocol_unset) {
+      protocol_mask  = TLSValidProtocols::max_mask;
+      protocol_unset = false;
+    }
+    switch (proto) {
+    case YamlSNIConfig::TLSProtocol::TLSv1:
+      protocol_mask &= ~SSL_OP_NO_TLSv1;
+      break;
+    case YamlSNIConfig::TLSProtocol::TLSv1_1:
+      protocol_mask &= ~SSL_OP_NO_TLSv1_1;
+      break;
+    case YamlSNIConfig::TLSProtocol::TLSv1_2:
+      protocol_mask &= ~SSL_OP_NO_TLSv1_2;
+      break;
+    case YamlSNIConfig::TLSProtocol::TLSv1_3:
+#ifdef SSL_OP_NO_TLSv1_3
+      protocol_mask &= ~SSL_OP_NO_TLSv1_3;
+#endif
+      break;
+    }
+  }
+}
+
+TsEnumDescriptor TLS_PROTOCOLS_DESCRIPTOR = {{{"TLSv1", 0}, {"TLSv1_1", 1}, 
{"TLSv1_2", 2}, {"TLSv1_3", 3}}};
+
+std::set<std::string> valid_sni_config_keys = {TS_fqdn,
+                                               TS_disable_H2,
+                                               TS_verify_client,
+                                               TS_tunnel_route,
+                                               TS_verify_origin_server,
+                                               TS_client_cert,
+                                               TS_ip_allow
+#if TS_USE_HELLO_CB
+                                               ,
+                                               TS_valid_tls_versions_in
+#endif
+};
 
 namespace YAML
 {
@@ -112,6 +153,13 @@ template <> struct convert<YamlSNIConfig::Item> {
     if (node[TS_ip_allow]) {
       item.ip_allow = node[TS_ip_allow].as<std::string>();
     }
+    if (node[TS_valid_tls_versions_in]) {
+      for (unsigned int i = 0; i < node[TS_valid_tls_versions_in].size(); i++) 
{
+        auto value   = node[TS_valid_tls_versions_in][i].as<std::string>();
+        int protocol = TLS_PROTOCOLS_DESCRIPTOR.get(value);
+        item.EnableProtocol(static_cast<YamlSNIConfig::TLSProtocol>(protocol));
+      }
+    }
     return true;
   }
 };
diff --git a/iocore/net/YamlSNIConfig.h b/iocore/net/YamlSNIConfig.h
index cb57d5b..7bdd619 100644
--- a/iocore/net/YamlSNIConfig.h
+++ b/iocore/net/YamlSNIConfig.h
@@ -26,13 +26,14 @@
 
 #include "tsconfig/Errata.h"
 
-constexpr char TS_fqdn[]                 = "fqdn";
-constexpr char TS_disable_H2[]           = "disable_h2";
-constexpr char TS_verify_client[]        = "verify_client";
-constexpr char TS_tunnel_route[]         = "tunnel_route";
-constexpr char TS_verify_origin_server[] = "verify_origin_server";
-constexpr char TS_client_cert[]          = "client_cert";
-constexpr char TS_ip_allow[]             = "ip_allow";
+constexpr char TS_fqdn[]                  = "fqdn";
+constexpr char TS_disable_H2[]            = "disable_h2";
+constexpr char TS_verify_client[]         = "verify_client";
+constexpr char TS_tunnel_route[]          = "tunnel_route";
+constexpr char TS_verify_origin_server[]  = "verify_origin_server";
+constexpr char TS_client_cert[]           = "client_cert";
+constexpr char TS_ip_allow[]              = "ip_allow";
+constexpr char TS_valid_tls_versions_in[] = "valid_tls_versions_in";
 
 const int start = 0;
 struct YamlSNIConfig {
@@ -45,6 +46,7 @@ struct YamlSNIConfig {
 
   };
   enum class Level { NONE = 0, MODERATE, STRICT };
+  enum class TLSProtocol : uint8_t { TLSv1 = 0, TLSv1_1, TLSv1_2, TLSv1_3, 
TLS_MAX = TLSv1_3 };
 
   YamlSNIConfig() {}
 
@@ -56,6 +58,10 @@ struct YamlSNIConfig {
     uint8_t verify_origin_server = 0;
     std::string client_cert;
     std::string ip_allow;
+    bool protocol_unset = true;
+    unsigned long protocol_mask;
+
+    void EnableProtocol(YamlSNIConfig::TLSProtocol proto);
   };
 
   ts::Errata loader(const char *cfgFilename);
diff --git a/tests/gold_tests/tls/tls_client_versions.test.py 
b/tests/gold_tests/tls/tls_client_versions.test.py
new file mode 100644
index 0000000..dc83455
--- /dev/null
+++ b/tests/gold_tests/tls/tls_client_versions.test.py
@@ -0,0 +1,112 @@
+'''
+'''
+#  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 os
+Test.Summary = '''
+Test TLS protocol offering  based on SNI
+'''
+
+# By default only offer TLSv1_2
+# for special doman foo.com only offer TLSv1 and TLSv1_1
+
+# need Curl
+Test.SkipUnless(
+    Condition.HasProgram("curl", "Curl need to be installed on system for this 
test to work"),
+    Condition.HasOpenSSLVersion("1.1.1")
+)
+
+# Define default ATS
+ts = Test.MakeATSProcess("ts", select_ports=False)
+server = Test.MakeOriginServer("server", ssl=True)
+
+request_foo_header = {"headers": "GET / HTTP/1.1\r\n\r\n", "timestamp": 
"1469733493.993", "body": ""}
+response_foo_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: 
close\r\n\r\n", "timestamp": "1469733493.993", "body": "foo ok"}
+server.addResponse("sessionlog.json", request_foo_header, response_foo_header)
+
+# add ssl materials like key, certificates for the server
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+
+ts.Variables.ssl_port = 4443
+
+# Need no remap rules.  Everything should be proccessed by ssl_server_name
+
+# Make sure the TS server certs are different from the origin certs
+ts.Disk.ssl_multicert_config.AddLine(
+    'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+)
+
+ts.Disk.records_config.update({
+    'proxy.config.diags.debug.enabled': 0,
+    'proxy.config.diags.debug.tags': 'http|ssl',
+    'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
+    'proxy.config.ssl.server.private_key.path': 
'{0}'.format(ts.Variables.SSLDir),
+    # enable ssl port
+    'proxy.config.http.server_ports': '{0} 
{1}:proto=http2;http:ssl'.format(ts.Variables.port, ts.Variables.ssl_port),
+    'proxy.config.ssl.server.cipher_suite': 
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
+    'proxy.config.ssl.client.CA.cert.path': '{0}'.format(ts.Variables.SSLDir),
+    'proxy.config.url_remap.pristine_host_hdr': 1,
+    'proxy.config.ssl.TLSv1': 0,
+    'proxy.config.ssl.TLSv1_1': 0,
+    'proxy.config.ssl.TLSv1_2': 1
+})
+
+# foo.com should only offer the older TLS protocols
+# bar.com should terminate.
+# empty SNI should tunnel to server_bar
+ts.Disk.ssl_server_name_yaml.AddLines([
+  '- fqdn: foo.com',
+  '  valid_tls_versions_in: [ TLSv1, TLSv1_1 ]'
+])
+
+# Target foo.com for TLSv1_2.  Should fail
+tr = Test.AddTestRun("foo.com TLSv1_2")
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(Test.Processes.ts, 
ready=When.PortOpen(ts.Variables.ssl_port))
+tr.Processes.Default.Command = "curl -v --tls-max 1.2 --tlsv1.2 --resolve 
'foo.com:{0}:127.0.0.1' -k  https://foo.com:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 35
+tr.StillRunningAfter = ts
+tr.Processes.Default.TimeOut = 5
+tr.TimeOut = 5
+tr.Processes.Default.Streams.All += 
Testers.ContainsExpression("ssl_choose_client_version:unsupported protocol", 
"Should not allow TLSv1_2")
+
+# Target foo.com for TLSv1.  Should succeed
+tr = Test.AddTestRun("foo.com TLSv1")
+tr.Processes.Default.Command = "curl -v --tls-max 1.0 --tlsv1 --resolve 
'foo.com:{0}:127.0.0.1' -k  https://foo.com:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = ts
+tr.Processes.Default.TimeOut = 5
+tr.TimeOut = 5
+
+# Target bar.com for TLSv1.  Should fail
+tr = Test.AddTestRun("bar.com TLSv1")
+tr.Processes.Default.Command = "curl -v --tls-max 1.0 --tlsv1 --resolve 
'bar.com:{0}:127.0.0.1' -k  https://bar.com:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 35
+tr.StillRunningAfter = ts
+tr.Processes.Default.TimeOut = 5
+tr.Processes.Default.Streams.All += Testers.ContainsExpression("alert protocol 
version", "Should not allow TLSv1_0")
+tr.TimeOut = 5
+
+# Target bar.com for TLSv1_2.  Should succeed
+tr = Test.AddTestRun("bar.com TLSv1_2")
+tr.Processes.Default.Command = "curl -v --tls-max 1.2 --tlsv1.2 --resolve 
'bar.com:{0}:127.0.0.1' -k  https://bar.com:{0}".format(ts.Variables.ssl_port)
+tr.ReturnCode = 0
+tr.StillRunningAfter = ts
+tr.Processes.Default.TimeOut = 5
+tr.TimeOut = 5
+

Reply via email to