diff --git a/configs/sni.yaml.default b/configs/sni.yaml.default index 6b9080c39e6..021312e8436 100644 --- a/configs/sni.yaml.default +++ b/configs/sni.yaml.default @@ -7,7 +7,7 @@ # YAML-based Configuration file # Format : # Actions available: -# disable_h2 - removes H2 from the protocol list advertised by ATS; parameter required = None, parameters = true or false +# http2 - adds or removes HTTP/2 (H2) from the protocol list advertised by ATS; parameter required = None, parameters = on or off # verify_client - sets the verification flag for verifying the client certificate; parameters = one of 'NONE', 'MODERATE' or 'STRICT' # verify_origin_server - sets the verification flag for verifying the server certificate; parameters = one of 'NONE', 'MODERATE' or 'STRICT' # client_cert - sets the client certificate to present to the server specified in dest_host; parameters = certificate file . @@ -19,7 +19,7 @@ # Example: # sni: # - fqdn: one.com -# disable_h2: true +# http2: off # verify_origin_server: STRICT # client_cert: somepem.pem # verify_client: MODERATE diff --git a/doc/admin-guide/files/sni.yaml.en.rst b/doc/admin-guide/files/sni.yaml.en.rst index 98f2dffebaa..8556b7ac433 100644 --- a/doc/admin-guide/files/sni.yaml.en.rst +++ b/doc/admin-guide/files/sni.yaml.en.rst @@ -97,16 +97,14 @@ client_key The file containing the client private key that corres |TS| tries to use a private key in client_cert. Otherwise, :ts:cv:`proxy.config.ssl.client.private_key.filename` is used. +http2 Indicates whether the H2 protocol should be added to or removed from the + protocol negotiation list. The valid values are :code:`on` or :code:`off`. -disable_h2 :code:`true` or :code:`false`. - - If :code:`false` then HTTP/2 is removed from - the valid next protocol list. It is not an error to set this to :code:`false` - for proxy ports on which HTTP/2 is not enabled. +disable_h2 Deprecated for the more general h2 setting. Setting disable_h2 + to :code:`true` is the same as setting http2 to :code:`on`. tunnel_route Destination as an FQDN and port, separated by a colon ``:``. - This will forward all traffic to the specified destination without first terminating the incoming TLS connection. @@ -175,7 +173,7 @@ Disable HTTP/2 for ``no-http2.example.com``. sni: - fqdn: no-http2.example.com - disable_h2: true + http2: off Require client certificate verification for ``example.com`` and any server name ending with ``.yahoo.com``. Therefore, client request for a server name ending with yahoo.com (e.g., def.yahoo.com, abc.yahoo.com etc.) will cause |TS| require and verify the client certificate. By contrast, |TS| will allow a client certificate to be provided for ``example.com`` and if it is, |TS| will require the certificate to be valid. diff --git a/doc/developer-guide/api/functions/TSProtoSet.en.rst b/doc/developer-guide/api/functions/TSProtoSet.en.rst deleted file mode 100644 index 8b57209c385..00000000000 --- a/doc/developer-guide/api/functions/TSProtoSet.en.rst +++ /dev/null @@ -1,46 +0,0 @@ -.. 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. - -.. include:: ../../../common.defs - -.. default-domain:: c - -TSProtoSet -****************** - -Synopsis -======== - -`#include ` - -.. function:: TSNextProtocolSet TSGetcloneProtoSet(TSAcceptor tna) -.. function:: TSNextProtocolSet TSUnregisterProtocol(TSNextProtocolSet protoset, const char* protocol) -.. function:: void TSRegisterProtocolSet(TSVConn sslp, TSNextProtocolSet ps) - -Description -=========== - -:func:`TSGetcloneProtoSet` makes a copy of the ProtocolSet to be advertised by the ssl connection associated with :arg:`tna`. This function -returns :type:`TSNextProtocolSet` object which points to a clone of the protocolset owned by :arg:`tna`. This type represents the protocolset -containing the protocols which are advertised by an ssl connection during ssl handshake. Each :type:`TSAcceptor` object is associated with a protocolset. - - -:func:`TSUnregisterProtocol` unregisters :arg:`protocol` from :arg:`protoset` and returns the protocol set. -The returned protocol set needs to be registered with the :type:`TSVConn` using :func:`TSRegisterProtocolSet` that will advertise the protocols. - - -:func:`TSRegisterProtocolSet` registers :arg:`ps` with :arg:`sslp`. This function clears the protocolset string created by the already registered -protocolset before registering the new protocolset. On Success, the ssl object associated with :arg:`sslp` will then advertise the protocols contained in :arg:`ps`. diff --git a/doc/developer-guide/api/functions/TSTypes.en.rst b/doc/developer-guide/api/functions/TSTypes.en.rst index 6738202697a..9ffc3185ad5 100644 --- a/doc/developer-guide/api/functions/TSTypes.en.rst +++ b/doc/developer-guide/api/functions/TSTypes.en.rst @@ -213,8 +213,6 @@ more widely. Those are described on this page. .. type:: TSAcceptor -.. type:: TSNextProtocolSet - .. cpp:class:: template LINK .. cpp:class:: VersionNumber diff --git a/doc/developer-guide/api/functions/TSVConnProtocol.en.rst b/doc/developer-guide/api/functions/TSVConnProtocol.en.rst new file mode 100644 index 00000000000..49d1b88d47d --- /dev/null +++ b/doc/developer-guide/api/functions/TSVConnProtocol.en.rst @@ -0,0 +1,51 @@ +.. 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. + +.. include:: ../../../common.defs + +.. default-domain:: c + +TSVConnProtocolEnable/Disable +***************************** + +Synopsis +======== + +`#include ` + +.. function:: TSReturnCode TSVConnProtocolEnable(TSVConn vconn, const char* protocol) +.. function:: TSReturnCode TSVConnProtocolDisable(TSVConn vconn, const char* protocol) + +Description +=========== + +:func:`TSVConnProtocolEnable` will enable the protocol specified by :arg:`protocol` to be advertised in the TLS protocol negotiation. + +Similarly, :func:`TSVConnProtocolDisable` will remove the protocol specified by :arg:`protocol` from the TLS protocol negotiation. + +To be effective, these calls must be made from the early TLS negotiation hooks like :member:`TS_SSL_CLIENT_HELLO_HOOK` or :member:`TS_SSL_SERVERNAME_HOOK`. + +Examples +======== + +The example below is excerpted from `example/plugins/c-api/disable_http2/disable_http2.cc` +in the Traffic Server source distribution. It shows how the :func:`TSVConnProtocolDisable` function +can be used in a plugin called from the :member:`TS_SSL_SERVERNAME_HOOK`. + +.. literalinclude:: ../../../../example/plugins/c-api/disable_http2/disable_http2.cc + :language: c + :lines: 41-54 + diff --git a/doc/developer-guide/api/types/TSHttpHookID.en.rst b/doc/developer-guide/api/types/TSHttpHookID.en.rst index dd09fbcc6fb..663759581fc 100644 --- a/doc/developer-guide/api/types/TSHttpHookID.en.rst +++ b/doc/developer-guide/api/types/TSHttpHookID.en.rst @@ -78,6 +78,8 @@ Enumeration Members .. c:macro:: TSHttpHookID TS_VCONN_OUTBOUND_CLOSE_HOOK +.. c:macro:: TSHttpHookID TS_SSL_CLIENT_HELLO_HOOK + .. c:macro:: TSHttpHookID TS_SSL_SNI_HOOK .. c:macro:: TSHttpHookID TS_SSL_CERT_HOOK diff --git a/example/plugins/c-api/disable_http2/disable_http2.cc b/example/plugins/c-api/disable_http2/disable_http2.cc index 4fd0fc596ee..0169bc4f05c 100644 --- a/example/plugins/c-api/disable_http2/disable_http2.cc +++ b/example/plugins/c-api/disable_http2/disable_http2.cc @@ -27,7 +27,6 @@ #include -#include #include #include #include @@ -35,9 +34,6 @@ #define PLUGIN_NAME "disable_http2" -typedef std::unordered_map AcceptorMapping; // stores protocolset keyed by NetAccept ID -AcceptorMapping AcceptorMap; - // Map of domains to tweak. using DomainSet = std::unordered_set; DomainSet Domains; @@ -51,10 +47,8 @@ CB_SNI(TSCont contp, TSEvent, void *cb_data) char const *sni = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if (sni) { if (Domains.find(sni) != Domains.end()) { - TSAcceptor na = TSAcceptorGet(vc); - int nid = TSAcceptorIDGet(na); - TSNextProtocolSet ps = AcceptorMap[nid]; // get our copy of the protocol set. - TSRegisterProtocolSet(vc, ps); // replace default protocol set with the copy. + TSDebug(PLUGIN_NAME, "Disable H2 for SNI=%s", sni); + TSVConnProtocolDisable(vc, TS_ALPN_PROTOCOL_HTTP_2_0); } } @@ -62,27 +56,6 @@ CB_SNI(TSCont contp, TSEvent, void *cb_data) return TS_SUCCESS; } -int -CB_NetAcceptReady(TSCont contp, TSEvent event, void *cb_data) -{ - switch (event) { - case TS_EVENT_LIFECYCLE_PORTS_READY: - // The accept objects are all created and ready at this point. We - // can now iterate over them. - for (int i = 0, totalNA = TSAcceptorCount(); i < totalNA; ++i) { - TSAcceptor netaccept = TSAcceptorGetbyID(i); - // get a clone of the protoset associated with the netaccept - TSNextProtocolSet nps = TSGetcloneProtoSet(netaccept); - TSUnregisterProtocol(nps, TS_ALPN_PROTOCOL_HTTP_2_0); - AcceptorMap[i] = nps; - } - break; - default: - break; - } - return 0; -} - void TSPluginInit(int argc, char const *argv[]) { @@ -109,9 +82,7 @@ TSPluginInit(int argc, char const *argv[]) Domains.emplace(std::string(argv[i], strlen(argv[i]))); } // These callbacks do not modify any state so no lock is needed. - TSCont cb_sni = TSContCreate(&CB_SNI, nullptr); - TSCont cb_netacc = TSContCreate(&CB_NetAcceptReady, nullptr); + TSCont cb_sni = TSContCreate(&CB_SNI, nullptr); TSHttpHookAdd(TS_SSL_SERVERNAME_HOOK, cb_sni); - TSLifecycleHookAdd(TS_LIFECYCLE_PORTS_READY_HOOK, cb_netacc); } diff --git a/include/ts/apidefs.h.in b/include/ts/apidefs.h.in index 9364adf8996..71dcd74dd73 100644 --- a/include/ts/apidefs.h.in +++ b/include/ts/apidefs.h.in @@ -913,7 +913,6 @@ typedef struct tsapi_bufferreader *TSIOBufferReader; typedef struct tsapi_hostlookupresult *TSHostLookupResult; typedef struct tsapi_aiocallback *TSAIOCallback; typedef struct tsapi_net_accept *TSAcceptor; -typedef struct tsapi_protocol_set *TSNextProtocolSet; typedef void *(*TSThreadFunc)(void *data); typedef int (*TSEventFunc)(TSCont contp, TSEvent event, void *edata); diff --git a/include/ts/ts.h b/include/ts/ts.h index 6c8aa05373d..71a663eb5da 100644 --- a/include/ts/ts.h +++ b/include/ts/ts.h @@ -1251,13 +1251,12 @@ tsapi TSReturnCode TSSslServerCertUpdate(const char *cert_path, const char *key_ tsapi TSSslContext TSSslServerContextCreate(TSSslX509 cert, const char *certname, const char *rsp_file); tsapi void TSSslContextDestroy(TSSslContext ctx); tsapi void TSSslTicketKeyUpdate(char *ticketData, int ticketDataLen); -tsapi TSNextProtocolSet TSUnregisterProtocol(TSNextProtocolSet protoset, const char *protocol); TSAcceptor TSAcceptorGet(TSVConn sslp); -TSNextProtocolSet TSGetcloneProtoSet(TSAcceptor tna); TSAcceptor TSAcceptorGetbyID(int ID); -void TSRegisterProtocolSet(TSVConn sslp, TSNextProtocolSet ps); int TSAcceptorCount(); int TSAcceptorIDGet(TSAcceptor acceptor); +TSReturnCode TSVConnProtocolDisable(TSVConn connp, const char *protocol_name); +TSReturnCode TSVConnProtocolEnable(TSVConn connp, const char *protocol_name); /* Returns 1 if the sslp argument refers to a SSL connection */ tsapi int TSVConnIsSsl(TSVConn sslp); diff --git a/iocore/net/ALPNSupport.cc b/iocore/net/ALPNSupport.cc new file mode 100644 index 00000000000..bf6874c7257 --- /dev/null +++ b/iocore/net/ALPNSupport.cc @@ -0,0 +1,82 @@ +/** @file + + ALPNSupport.cc provides implmentations for ALPNSupport methods + + @section license License + + 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. + */ + +#include "P_ALPNSupport.h" +#include "P_SSLNextProtocolSet.h" +#include "records/I_RecHttp.h" + +void +ALPNSupport::clear() +{ + if (npn) { + ats_free(npn); + npn = nullptr; + npnsz = 0; + } + npnSet = nullptr; + npnEndpoint = nullptr; +} + +bool +ALPNSupport::setSelectedProtocol(const unsigned char *proto, unsigned int len) +{ + // If there's no NPN set, we should not have done this negotiation. + ink_assert(this->npnSet != nullptr); + + this->npnEndpoint = this->npnSet->findEndpoint(proto, static_cast(len)); + this->npnSet = nullptr; + + if (this->npnEndpoint == nullptr) { + Error("failed to find registered SSL endpoint for '%.*s'", len, proto); + return false; + } + return true; +} + +void +ALPNSupport::disableProtocol(int idx) +{ + this->protoenabled.markOut(idx); + // Update the npn string + if (npnSet) { + npnSet->create_npn_advertisement(protoenabled, &npn, &npnsz); + } +} + +void +ALPNSupport::enableProtocol(int idx) +{ + this->protoenabled.markIn(idx); + // Update the npn string + if (npnSet) { + npnSet->create_npn_advertisement(protoenabled, &npn, &npnsz); + } +} + +void +ALPNSupport::registerNextProtocolSet(SSLNextProtocolSet *s, const SessionProtocolSet &protos) +{ + this->protoenabled = protos; + this->npnSet = s; + npnSet->create_npn_advertisement(protoenabled, &npn, &npnsz); +} diff --git a/iocore/net/Makefile.am b/iocore/net/Makefile.am index 898eb9b0eea..2ec6f4e668c 100644 --- a/iocore/net/Makefile.am +++ b/iocore/net/Makefile.am @@ -86,6 +86,7 @@ test_UDPNet_SOURCES = \ test_I_UDPNet.cc libinknet_a_SOURCES = \ + ALPNSupport.cc \ BIO_fastopen.cc \ BIO_fastopen.h \ Connection.cc \ @@ -102,6 +103,7 @@ libinknet_a_SOURCES = \ YamlSNIConfig.cc \ Net.cc \ NetVConnection.cc \ + P_ALPNSupport.h \ P_SNIActionPerformer.h \ P_CompletionUtil.h \ P_Connection.h \ diff --git a/iocore/net/P_ALPNSupport.h b/iocore/net/P_ALPNSupport.h new file mode 100644 index 00000000000..75970da679f --- /dev/null +++ b/iocore/net/P_ALPNSupport.h @@ -0,0 +1,71 @@ +/** @file + + ALPNSupport implements common methods and members to + support protocols for ALPN negotiation + + @section license License + + 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. + */ + +#pragma once +#include "records/I_RecHttp.h" + +class SSLNextProtocolSet; +class SSLNextProtocolAccept; +class Continuation; + +class ALPNSupport +{ +public: + void registerNextProtocolSet(SSLNextProtocolSet *, const SessionProtocolSet &protos); + void disableProtocol(int idx); + void enableProtocol(int idx); + void clear(); + bool setSelectedProtocol(const unsigned char *proto, unsigned int len); + + Continuation * + endpoint() const + { + return npnEndpoint; + } + + bool + getNPN(const unsigned char **out, unsigned int *outlen) const + { + if (this->npn && this->npnsz) { + *out = this->npn; + *outlen = this->npnsz; + return true; + } + return false; + } + + const SSLNextProtocolSet * + getNextProtocolSet() const + { + return npnSet; + } + +private: + const SSLNextProtocolSet *npnSet = nullptr; + SessionProtocolSet protoenabled; + // Local copies of the npn strings + unsigned char *npn = nullptr; + size_t npnsz = 0; + Continuation *npnEndpoint = nullptr; +}; diff --git a/iocore/net/P_QUICNetVConnection.h b/iocore/net/P_QUICNetVConnection.h index 8ca25880df5..925a4fed057 100644 --- a/iocore/net/P_QUICNetVConnection.h +++ b/iocore/net/P_QUICNetVConnection.h @@ -127,7 +127,11 @@ class SSLNextProtocolSet; * WRITE: * Do nothing **/ -class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, public QUICFrameGenerator, public RefCountObj +class QUICNetVConnection : public UnixNetVConnection, + public QUICConnection, + public QUICFrameGenerator, + public RefCountObj, + public ALPNSupport { using super = UnixNetVConnection; ///< Parent type. @@ -168,8 +172,8 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub int populate_protocol(std::string_view *results, int n) const override; const char *protocol_contains(std::string_view tag) const override; - // QUICNetVConnection - void registerNextProtocolSet(SSLNextProtocolSet *s); + int select_next_protocol(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, + unsigned inlen) const override; // QUICConnection QUICStreamManager *stream_manager() override; @@ -186,7 +190,6 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub const QUICFiveTuple five_tuple() const override; uint32_t pmtu() const override; NetVConnectionContext_t direction() const override; - SSLNextProtocolSet *next_protocol_set() const override; std::string_view negotiated_application_name() const override; bool is_closed() const override; @@ -235,8 +238,6 @@ class QUICNetVConnection : public UnixNetVConnection, public QUICConnection, pub uint32_t _pmtu = 1280; - SSLNextProtocolSet *_next_protocol_set = nullptr; - // TODO: use custom allocator and make them std::unique_ptr or std::shared_ptr // or make them just member variables. QUICHandshake *_handshake_handler = nullptr; diff --git a/iocore/net/P_QUICNextProtocolAccept.h b/iocore/net/P_QUICNextProtocolAccept.h index 5b03652b0ae..95752dd21e0 100644 --- a/iocore/net/P_QUICNextProtocolAccept.h +++ b/iocore/net/P_QUICNextProtocolAccept.h @@ -40,13 +40,10 @@ class QUICNextProtocolAccept : public SessionAccept // lifetime is at least as long as that of the acceptor. bool registerEndpoint(const char *protocol, Continuation *handler); - // Unregister the handler. Returns false if this protocol is not registered - // or if it is not registered for the specified handler. - bool unregisterEndpoint(const char *protocol, Continuation *handler); + void enableProtocols(const SessionProtocolSet &protos); SLINK(QUICNextProtocolAccept, link); SSLNextProtocolSet *getProtoSet(); - SSLNextProtocolSet *cloneProtoSet(); // noncopyable QUICNextProtocolAccept(const QUICNextProtocolAccept &) = delete; // disabled @@ -56,6 +53,7 @@ class QUICNextProtocolAccept : public SessionAccept int mainEvent(int event, void *netvc); SSLNextProtocolSet protoset; + SessionProtocolSet protoenabled; friend struct QUICNextProtocolTrampoline; }; diff --git a/iocore/net/P_SNIActionPerformer.h b/iocore/net/P_SNIActionPerformer.h index a04541040df..5e9f352ccd6 100644 --- a/iocore/net/P_SNIActionPerformer.h +++ b/iocore/net/P_SNIActionPerformer.h @@ -34,9 +34,6 @@ #include #include "P_SSLNextProtocolAccept.h" #include "tscore/ink_inet.h" -#include - -extern std::unordered_map snpsMap; class ActionItem { @@ -45,24 +42,28 @@ class ActionItem virtual ~ActionItem(){}; }; -class DisableH2 : public ActionItem +class ControlH2 : public ActionItem { public: - DisableH2() {} - ~DisableH2() override {} + ControlH2(bool turn_on) : enable_h2(turn_on) {} + ~ControlH2() override {} int SNIAction(Continuation *cont) const override { - auto ssl_vc = dynamic_cast(cont); - auto accept_obj = ssl_vc ? ssl_vc->accept_object : nullptr; - if (accept_obj && accept_obj->snpa && ssl_vc) { - if (auto it = snpsMap.find(accept_obj->id); it != snpsMap.end()) { - ssl_vc->registerNextProtocolSet(it->second); + auto ssl_vc = dynamic_cast(cont); + if (ssl_vc) { + if (!enable_h2) { + ssl_vc->disableProtocol(TS_ALPN_PROTOCOL_INDEX_HTTP_2_0); + } else { + ssl_vc->enableProtocol(TS_ALPN_PROTOCOL_INDEX_HTTP_2_0); } } return SSL_TLSEXT_ERR_OK; } + +private: + bool enable_h2 = false; }; class TunnelDestination : public ActionItem @@ -178,10 +179,3 @@ class SNI_IpAllow : public ActionItem } } }; - -class SNIActionPerformer -{ -public: - SNIActionPerformer() = default; - static int PerformAction(Continuation *cont, const char *servername); -}; diff --git a/iocore/net/P_SSLNetVConnection.h b/iocore/net/P_SSLNetVConnection.h index 952cccba374..0240ee7e908 100644 --- a/iocore/net/P_SSLNetVConnection.h +++ b/iocore/net/P_SSLNetVConnection.h @@ -42,6 +42,7 @@ #include "P_EventSystem.h" #include "P_UnixNetVConnection.h" #include "P_UnixNet.h" +#include "P_ALPNSupport.h" // These are included here because older OpenSSL libraries don't have them. // Don't copy these defines, or use their values directly, they are merely @@ -67,8 +68,6 @@ #define SSL_DEF_TLS_RECORD_BYTE_THRESHOLD 1000000 #define SSL_DEF_TLS_RECORD_MSEC_THRESHOLD 1000 -class SSLNextProtocolSet; -class SSLNextProtocolAccept; struct SSLCertLookup; typedef enum { @@ -87,7 +86,7 @@ enum SSLHandshakeStatus { SSL_HANDSHAKE_ONGOING, SSL_HANDSHAKE_DONE, SSL_HANDSHA // A VConnection for a network socket. // ////////////////////////////////////////////////////////////////// -class SSLNetVConnection : public UnixNetVConnection +class SSLNetVConnection : public UnixNetVConnection, public ALPNSupport { typedef UnixNetVConnection super; ///< Parent type. @@ -141,7 +140,6 @@ class SSLNetVConnection : public UnixNetVConnection int sslClientHandShakeEvent(int &err); void net_read_io(NetHandler *nh, EThread *lthread) override; int64_t load_buffer_and_write(int64_t towrite, MIOBufferAccessor &buf, int64_t &total_written, int &needs) override; - void registerNextProtocolSet(SSLNextProtocolSet *); void do_io_close(int lerrno = -1) override; //////////////////////////////////////////////////////////// @@ -155,12 +153,6 @@ class SSLNetVConnection : public UnixNetVConnection static int select_next_protocol(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned inlen, void *); - Continuation * - endpoint() const - { - return npnEndpoint; - } - bool getSSLClientRenegotiationAbort() const { @@ -449,15 +441,11 @@ class SSLNetVConnection : public UnixNetVConnection HANDSHAKE_HOOKS_DONE } sslHandshakeHookState = HANDSHAKE_HOOKS_PRE; - const SSLNextProtocolSet *npnSet = nullptr; - Continuation *npnEndpoint = nullptr; - SessionAccept *sessionAcceptPtr = nullptr; - bool sslTrace = false; - int64_t redoWriteSize = 0; - char *tunnel_host = nullptr; - in_port_t tunnel_port = 0; - bool tunnel_decrypt = false; - X509_STORE_CTX *verify_cert = nullptr; + int64_t redoWriteSize = 0; + char *tunnel_host = nullptr; + in_port_t tunnel_port = 0; + bool tunnel_decrypt = false; + X509_STORE_CTX *verify_cert = nullptr; }; typedef int (SSLNetVConnection::*SSLNetVConnHandler)(int, void *); diff --git a/iocore/net/P_SSLNextProtocolAccept.h b/iocore/net/P_SSLNextProtocolAccept.h index e9c2c8b573c..2c2c5de82a7 100644 --- a/iocore/net/P_SSLNextProtocolAccept.h +++ b/iocore/net/P_SSLNextProtocolAccept.h @@ -29,6 +29,7 @@ #include "P_SSLNetVConnection.h" #include "P_SSLNextProtocolSet.h" #include "I_IOBuffer.h" +#include "records/I_RecHttp.h" class SSLNextProtocolAccept : public SessionAccept { @@ -43,13 +44,10 @@ class SSLNextProtocolAccept : public SessionAccept // lifetime is at least as long as that of the acceptor. bool registerEndpoint(const char *protocol, Continuation *handler); - // Unregister the handler. Returns false if this protocol is not registered - // or if it is not registered for the specified handler. - bool unregisterEndpoint(const char *protocol, Continuation *handler); + void enableProtocols(const SessionProtocolSet &protos); SLINK(SSLNextProtocolAccept, link); SSLNextProtocolSet *getProtoSet(); - SSLNextProtocolSet *cloneProtoSet(); // noncopyable SSLNextProtocolAccept(const SSLNextProtocolAccept &) = delete; // disabled @@ -61,6 +59,7 @@ class SSLNextProtocolAccept : public SessionAccept MIOBuffer *buffer; // XXX do we really need this? Continuation *endpoint; SSLNextProtocolSet protoset; + SessionProtocolSet protoenabled; bool transparent_passthrough; friend struct SSLNextProtocolTrampoline; diff --git a/iocore/net/P_SSLNextProtocolSet.h b/iocore/net/P_SSLNextProtocolSet.h index 61d1ded9a8b..8b05b0a89c0 100644 --- a/iocore/net/P_SSLNextProtocolSet.h +++ b/iocore/net/P_SSLNextProtocolSet.h @@ -25,6 +25,7 @@ #include "tscore/List.h" #include "I_Net.h" +#include "records/I_RecHttp.h" class Continuation; @@ -35,12 +36,9 @@ class SSLNextProtocolSet ~SSLNextProtocolSet(); bool registerEndpoint(const char *, Continuation *); - bool unregisterEndpoint(const char *, Continuation *); - bool unregisterEndpoint(const char *proto); - bool advertiseProtocols(const unsigned char **out, unsigned *len) const; - SSLNextProtocolSet *clone() const; Continuation *findEndpoint(const unsigned char *, unsigned) const; + bool create_npn_advertisement(const SessionProtocolSet &enabled, unsigned char **npn, size_t *len) const; struct NextProtocolEndpoint { // NOTE: the protocol and endpoint are NOT copied. The caller is @@ -60,8 +58,5 @@ class SSLNextProtocolSet SSLNextProtocolSet &operator=(const SSLNextProtocolSet &) = delete; // disabled private: - mutable unsigned char *npn = nullptr; - mutable size_t npnsz = 0; - NextProtocolEndpoint::list_type endpoints; }; diff --git a/iocore/net/P_SSLSNI.h b/iocore/net/P_SSLSNI.h index 970c53af584..4cf71ff2a02 100644 --- a/iocore/net/P_SSLSNI.h +++ b/iocore/net/P_SSLSNI.h @@ -38,8 +38,6 @@ #include #include "YamlSNIConfig.h" -#include - // Properties for the next hop server struct NextHopProperty { std::string name; // name of the server @@ -99,9 +97,7 @@ struct NextHopItem : public namedElement { NextHopProperty prop; }; -// typedef HashMap SNIMap; typedef std::vector SNIList; -// typedef HashMap NextHopPropertyTable; typedef std::vector NextHopPropertyList; struct SNIConfigParams : public ConfigInfo { @@ -123,7 +119,6 @@ struct SNIConfig { static void reconfigure(); static SNIConfigParams *acquire(); static void release(SNIConfigParams *params); - static void cloneProtoSet(); // clones the protoset of all the netaccept objects created and unregisters h2 typedef ConfigProcessor::scoped_config scoped_config; diff --git a/iocore/net/QUICMultiCertConfigLoader.cc b/iocore/net/QUICMultiCertConfigLoader.cc index a948a6f8f2e..7a3fe2c0285 100644 --- a/iocore/net/QUICMultiCertConfigLoader.cc +++ b/iocore/net/QUICMultiCertConfigLoader.cc @@ -274,17 +274,11 @@ int QUICMultiCertConfigLoader::ssl_select_next_protocol(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned inlen, void *) { - const unsigned char *npn; - unsigned npnsz = 0; QUICConnection *qc = static_cast(SSL_get_ex_data(ssl, QUIC::ssl_quic_qc_index)); - qc->next_protocol_set()->advertiseProtocols(&npn, &npnsz); - if (SSL_select_next_proto((unsigned char **)out, outlen, npn, npnsz, in, inlen) == OPENSSL_NPN_NEGOTIATED) { - return SSL_TLSEXT_ERR_OK; + if (qc) { + return qc->select_next_protocol(ssl, out, outlen, in, inlen); } - - *out = nullptr; - *outlen = 0; return SSL_TLSEXT_ERR_NOACK; } diff --git a/iocore/net/QUICNetVConnection.cc b/iocore/net/QUICNetVConnection.cc index 5bbd25b8781..675570325b5 100644 --- a/iocore/net/QUICNetVConnection.cc +++ b/iocore/net/QUICNetVConnection.cc @@ -503,6 +503,7 @@ QUICNetVConnection::free(EThread *t) super::clear(); */ + ALPNSupport::clear(); this->_packet_handler->close_connection(this); } @@ -1014,10 +1015,26 @@ QUICNetVConnection::protocol_contains(std::string_view prefix) const return retval; } -void -QUICNetVConnection::registerNextProtocolSet(SSLNextProtocolSet *s) -{ - this->_next_protocol_set = s; +// ALPN TLS extension callback. Given the client's set of offered +// protocols, we have to select a protocol to use for this session. +int +QUICNetVConnection::select_next_protocol(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, + unsigned inlen) const +{ + const unsigned char *npnptr = nullptr; + unsigned int npnsize = 0; + if (this->getNPN(&npnptr, &npnsize)) { + // SSL_select_next_proto chooses the first server-offered protocol that appears in the clients protocol set, ie. the + // server selects the protocol. This is a n^2 search, so it's preferable to keep the protocol set short. + if (SSL_select_next_proto((unsigned char **)out, outlen, npnptr, npnsize, in, inlen) == OPENSSL_NPN_NEGOTIATED) { + Debug("ssl", "selected ALPN protocol %.*s", (int)(*outlen), *out); + return SSL_TLSEXT_ERR_OK; + } + } + + *out = nullptr; + *outlen = 0; + return SSL_TLSEXT_ERR_NOACK; } bool @@ -1026,12 +1043,6 @@ QUICNetVConnection::is_closed() const return this->handler == reinterpret_cast(&QUICNetVConnection::state_connection_closed); } -SSLNextProtocolSet * -QUICNetVConnection::next_protocol_set() const -{ - return this->_next_protocol_set; -} - QUICPacketNumber QUICNetVConnection::_largest_acked_packet_number(QUICEncryptionLevel level) const { @@ -2017,11 +2028,10 @@ QUICNetVConnection::_start_application() } if (netvc_context == NET_VCONNECTION_IN) { - Continuation *endpoint = this->_next_protocol_set->findEndpoint(app_name, app_name_len); - if (endpoint == nullptr) { + if (!this->setSelectedProtocol(app_name, app_name_len)) { this->_handle_error(std::make_unique(QUICTransErrorCode::VERSION_NEGOTIATION_ERROR)); } else { - endpoint->handleEvent(NET_EVENT_ACCEPT, this); + this->endpoint()->handleEvent(NET_EVENT_ACCEPT, this); } } else { this->action_.continuation->handleEvent(NET_EVENT_OPEN, this); diff --git a/iocore/net/QUICNextProtocolAccept.cc b/iocore/net/QUICNextProtocolAccept.cc index 1f721fa5bfc..53faf43073e 100644 --- a/iocore/net/QUICNextProtocolAccept.cc +++ b/iocore/net/QUICNextProtocolAccept.cc @@ -54,7 +54,7 @@ QUICNextProtocolAccept::mainEvent(int event, void *edata) switch (event) { case NET_EVENT_ACCEPT: ink_release_assert(netvc != nullptr); - netvc->registerNextProtocolSet(&this->protoset); + netvc->registerNextProtocolSet(&this->protoset, this->protoenabled); return EVENT_CONT; default: netvc->do_io_close(); @@ -75,10 +75,10 @@ QUICNextProtocolAccept::registerEndpoint(const char *protocol, Continuation *han return this->protoset.registerEndpoint(protocol, handler); } -bool -QUICNextProtocolAccept::unregisterEndpoint(const char *protocol, Continuation *handler) +void +QUICNextProtocolAccept::enableProtocols(const SessionProtocolSet &protos) { - return this->protoset.unregisterEndpoint(protocol, handler); + this->protoenabled = protos; } QUICNextProtocolAccept::QUICNextProtocolAccept() : SessionAccept(nullptr) @@ -92,10 +92,4 @@ QUICNextProtocolAccept::getProtoSet() return &this->protoset; } -SSLNextProtocolSet * -QUICNextProtocolAccept::cloneProtoSet() -{ - return this->protoset.clone(); -} - QUICNextProtocolAccept::~QUICNextProtocolAccept() {} diff --git a/iocore/net/SNIActionPerformer.cc b/iocore/net/SNIActionPerformer.cc deleted file mode 100644 index 842345e34b9..00000000000 --- a/iocore/net/SNIActionPerformer.cc +++ /dev/null @@ -1,58 +0,0 @@ -/** @file - - A brief file description - - @section license License - - 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. - */ - -/*************************** -*- Mod: C++ -*- ****************************** - SNIActionPerformer.cc - Created On : 05/02/2017 - - Description: - SNI based Configuration in ATS - ****************************************************************************/ -#include "P_SNIActionPerformer.h" -#include "tscore/ink_memory.h" -#include "P_SSLSNI.h" -#include "P_Net.h" -#include "P_SSLNextProtocolAccept.h" -#include "P_SSLUtils.h" - -extern std::unordered_map snpsMap; - -int -SNIActionPerformer::PerformAction(Continuation *cont, const char *servername) -{ - SNIConfig::scoped_config params; - auto actionvec = params->get(servername); - if (!actionvec) { - Debug("ssl_sni", "%s not available in the map", servername); - } else { - for (auto it : *actionvec) { - if (it) { - auto ret = it->SNIAction(cont); - if (ret != SSL_TLSEXT_ERR_OK) { - return ret; - } - } - } - } - return SSL_TLSEXT_ERR_OK; -} diff --git a/iocore/net/SSLNetProcessor.cc b/iocore/net/SSLNetProcessor.cc index bec9a3d0725..fd855084135 100644 --- a/iocore/net/SSLNetProcessor.cc +++ b/iocore/net/SSLNetProcessor.cc @@ -35,7 +35,6 @@ SSLNetProcessor ssl_NetProcessor; NetProcessor &sslNetProcessor = ssl_NetProcessor; -SNIActionPerformer sni_action_performer; #if TS_USE_TLS_OCSP struct OCSPContinuation : public Continuation { diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc index 0c374e1be43..309391c1a08 100644 --- a/iocore/net/SSLNetVConnection.cc +++ b/iocore/net/SSLNetVConnection.cc @@ -25,7 +25,6 @@ #include "tscore/EventNotify.h" #include "tscore/I_Layout.h" #include "tscore/TSSystemState.h" -#include "records/I_RecHttp.h" #include "InkAPIInternal.h" // Added to include the ssl_hook definitions #include "Log.h" @@ -34,7 +33,6 @@ #include "HttpConfig.h" #include "P_Net.h" -#include "P_SSLNextProtocolSet.h" #include "P_SSLUtils.h" #include "P_SSLConfig.h" #include "P_SSLClientUtils.h" @@ -42,6 +40,7 @@ #include "BIO_fastopen.h" #include "SSLStats.h" #include "SSLInternal.h" +#include "P_ALPNSupport.h" #include #include @@ -889,6 +888,7 @@ SSLNetVConnection::clear() SSL_free(ssl); ssl = nullptr; } + ALPNSupport::clear(); sslHandshakeStatus = SSL_HANDSHAKE_ONGOING; sslHandshakeBeginTime = 0; @@ -899,10 +899,7 @@ SSLNetVConnection::clear() curHook = nullptr; hookOpRequested = SSL_HOOK_OP_DEFAULT; - npnSet = nullptr; - npnEndpoint = nullptr; free_handshake_buffers(); - sslTrace = false; super::clear(); } @@ -1264,17 +1261,9 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err) } if (len) { - // If there's no NPN set, we should not have done this negotiation. - ink_assert(this->npnSet != nullptr); - - this->npnEndpoint = this->npnSet->findEndpoint(proto, len); - this->npnSet = nullptr; - - if (this->npnEndpoint == nullptr) { - Error("failed to find registered SSL endpoint for '%.*s'", (int)len, (const char *)proto); + if (!this->setSelectedProtocol(proto, len)) { return EVENT_ERROR; } - Debug("ssl", "client selected next protocol '%.*s'", len, proto); } else { Debug("ssl", "client did not select a next protocol"); @@ -1453,12 +1442,6 @@ SSLNetVConnection::sslClientHandShakeEvent(int &err) return EVENT_CONT; } -void -SSLNetVConnection::registerNextProtocolSet(SSLNextProtocolSet *s) -{ - this->npnSet = s; -} - // NextProtocolNegotiation TLS extension callback. The NPN extension // allows the client to select a preferred protocol, so all we have // to do here is tell them what out protocol set is. @@ -1468,11 +1451,11 @@ SSLNetVConnection::advertise_next_protocol(SSL *ssl, const unsigned char **out, SSLNetVConnection *netvc = SSLNetVCAccess(ssl); ink_release_assert(netvc != nullptr); - if (netvc->npnSet && netvc->npnSet->advertiseProtocols(out, outlen)) { + + if (netvc->getNPN(out, outlen)) { // Successful return tells OpenSSL to advertise. return SSL_TLSEXT_ERR_OK; } - return SSL_TLSEXT_ERR_NOACK; } @@ -1483,14 +1466,14 @@ SSLNetVConnection::select_next_protocol(SSL *ssl, const unsigned char **out, uns const unsigned char *in ATS_UNUSED, unsigned inlen ATS_UNUSED, void *) { SSLNetVConnection *netvc = SSLNetVCAccess(ssl); - const unsigned char *npn = nullptr; - unsigned npnsz = 0; ink_release_assert(netvc != nullptr); - if (netvc->npnSet && netvc->npnSet->advertiseProtocols(&npn, &npnsz)) { + const unsigned char *npnptr = nullptr; + unsigned int npnsize = 0; + if (netvc->getNPN(&npnptr, &npnsize)) { // SSL_select_next_proto chooses the first server-offered protocol that appears in the clients protocol set, ie. the // server selects the protocol. This is a n^2 search, so it's preferable to keep the protocol set short. - if (SSL_select_next_proto((unsigned char **)out, outlen, npn, npnsz, in, inlen) == OPENSSL_NPN_NEGOTIATED) { + if (SSL_select_next_proto((unsigned char **)out, outlen, npnptr, npnsize, in, inlen) == OPENSSL_NPN_NEGOTIATED) { Debug("ssl", "selected ALPN protocol %.*s", (int)(*outlen), *out); return SSL_TLSEXT_ERR_OK; } diff --git a/iocore/net/SSLNextProtocolAccept.cc b/iocore/net/SSLNextProtocolAccept.cc index ed3fc5ac09a..942bf0270c8 100644 --- a/iocore/net/SSLNextProtocolAccept.cc +++ b/iocore/net/SSLNextProtocolAccept.cc @@ -135,7 +135,7 @@ SSLNextProtocolAccept::mainEvent(int event, void *edata) // force the SSLNetVConnection to complete the SSL handshake. Don't tell // the endpoint that there is an accept to handle until the read completes // and we know which protocol was negotiated. - netvc->registerNextProtocolSet(&this->protoset); + netvc->registerNextProtocolSet(&this->protoset, this->protoenabled); netvc->do_io_read(new SSLNextProtocolTrampoline(this, netvc->mutex), 0, this->buffer); return EVENT_CONT; default: @@ -159,10 +159,10 @@ SSLNextProtocolAccept::registerEndpoint(const char *protocol, Continuation *hand return this->protoset.registerEndpoint(protocol, handler); } -bool -SSLNextProtocolAccept::unregisterEndpoint(const char *protocol, Continuation *handler) +void +SSLNextProtocolAccept::enableProtocols(const SessionProtocolSet &protos) { - return this->protoset.unregisterEndpoint(protocol, handler); + this->protoenabled = protos; } SSLNextProtocolAccept::SSLNextProtocolAccept(Continuation *ep, bool transparent_passthrough) @@ -177,12 +177,6 @@ SSLNextProtocolAccept::getProtoSet() return &this->protoset; } -SSLNextProtocolSet * -SSLNextProtocolAccept::cloneProtoSet() -{ - return this->protoset.clone(); -} - SSLNextProtocolAccept::~SSLNextProtocolAccept() { free_MIOBuffer(this->buffer); diff --git a/iocore/net/SSLNextProtocolSet.cc b/iocore/net/SSLNextProtocolSet.cc index 04c6330e3e0..ccd9941ca3d 100644 --- a/iocore/net/SSLNextProtocolSet.cc +++ b/iocore/net/SSLNextProtocolSet.cc @@ -25,6 +25,7 @@ #include "ts/apidefs.h" #include "tscore/ink_platform.h" #include "P_SSLNextProtocolSet.h" +#include "tscpp/util/TextView.h" // For currently defined protocol strings, see // http://technotes.googlecode.com/git/nextprotoneg.html. The OpenSSL @@ -42,8 +43,8 @@ append_protocol(const char *proto, unsigned char *buf) return buf + sz; } -static bool -create_npn_advertisement(const SSLNextProtocolSet::NextProtocolEndpoint::list_type &endpoints, unsigned char **npn, size_t *len) +bool +SSLNextProtocolSet::create_npn_advertisement(const SessionProtocolSet &enabled, unsigned char **npn, size_t *len) const { const SSLNextProtocolSet::NextProtocolEndpoint *ep; unsigned char *advertised; @@ -65,8 +66,10 @@ create_npn_advertisement(const SSLNextProtocolSet::NextProtocolEndpoint::list_ty } for (ep = endpoints.head; ep != nullptr; ep = endpoints.next(ep)) { - Debug("ssl", "advertising protocol %s, %p", ep->protocol, ep->endpoint); - advertised = append_protocol(ep->protocol, advertised); + if (enabled.contains(globalSessionProtocolNameRegistry.toIndex(ts::TextView{ep->protocol, strlen(ep->protocol)}))) { + Debug("ssl", "advertising protocol %s, %p", ep->protocol, ep->endpoint); + advertised = append_protocol(ep->protocol, advertised); + } } return true; @@ -78,31 +81,6 @@ create_npn_advertisement(const SSLNextProtocolSet::NextProtocolEndpoint::list_ty return false; } -// copies the protocols but not the endpoints - -SSLNextProtocolSet * -SSLNextProtocolSet::clone() const -{ - const SSLNextProtocolSet::NextProtocolEndpoint *ep; - SSLNextProtocolSet *newProtoSet = new SSLNextProtocolSet(); - for (ep = this->endpoints.head; ep != nullptr; ep = this->endpoints.next(ep)) { - newProtoSet->registerEndpoint(ep->protocol, ep->endpoint); - } - return newProtoSet; -} - -bool -SSLNextProtocolSet::advertiseProtocols(const unsigned char **out, unsigned *len) const -{ - if (npn && npnsz) { - *out = npn; - *len = npnsz; - return true; - } - - return false; -} - bool SSLNextProtocolSet::registerEndpoint(const char *proto, Continuation *ep) { @@ -115,36 +93,12 @@ SSLNextProtocolSet::registerEndpoint(const char *proto, Continuation *ep) if (!findEndpoint((const unsigned char *)proto, len)) { this->endpoints.push(new NextProtocolEndpoint(proto, ep)); - - if (npn) { - ats_free(npn); - npn = nullptr; - npnsz = 0; - } - create_npn_advertisement(this->endpoints, &npn, &npnsz); - return true; } return false; } -bool -SSLNextProtocolSet::unregisterEndpoint(const char *proto, Continuation *ep) -{ - for (NextProtocolEndpoint *e = this->endpoints.head; e; e = this->endpoints.next(e)) { - if (strcmp(proto, e->protocol) == 0 && (ep == nullptr || e->endpoint == ep)) { - // Protocol must be registered only once; no need to remove - // any more entries. - this->endpoints.remove(e); - create_npn_advertisement(this->endpoints, &npn, &npnsz); - return true; - } - } - - return false; -} - Continuation * SSLNextProtocolSet::findEndpoint(const unsigned char *proto, unsigned len) const { @@ -161,8 +115,6 @@ SSLNextProtocolSet::SSLNextProtocolSet() {} SSLNextProtocolSet::~SSLNextProtocolSet() { - ats_free(this->npn); - for (NextProtocolEndpoint *ep; (ep = this->endpoints.pop());) { delete ep; } diff --git a/iocore/net/SSLSNIConfig.cc b/iocore/net/SSLSNIConfig.cc index 895f6988850..32b88260664 100644 --- a/iocore/net/SSLSNIConfig.cc +++ b/iocore/net/SSLSNIConfig.cc @@ -40,8 +40,6 @@ #include static ConfigUpdateHandler *sniConfigUpdate; -struct NetAccept; -std::unordered_map snpsMap; const NextHopProperty * SNIConfigParams::getPropertyConfig(const std::string &servername) const @@ -66,8 +64,8 @@ SNIConfigParams::loadSNIConfig() Debug("ssl", "name: %s", item.fqdn.data()); // set SNI based actions to be called in the ssl_servername_only callback - if (item.disable_h2) { - ai->actions.push_back(std::make_unique()); + if (item.offer_h2.has_value()) { + ai->actions.push_back(std::make_unique(item.offer_h2.value())); } if (item.verify_client_level != 255) { ai->actions.push_back(std::make_unique(item.verify_client_level)); @@ -162,19 +160,6 @@ SNIConfig::startup() reconfigure(); } -void -SNIConfig::cloneProtoSet() -{ - SCOPED_MUTEX_LOCK(lock, naVecMutex, this_ethread()); - for (auto na : naVec) { - if (na->snpa) { - auto snps = na->snpa->cloneProtoSet(); - snps->unregisterEndpoint(TS_ALPN_PROTOCOL_HTTP_2_0, nullptr); - snpsMap.emplace(na->id, snps); - } - } -} - void SNIConfig::reconfigure() { diff --git a/iocore/net/YamlSNIConfig.cc b/iocore/net/YamlSNIConfig.cc index d2f6f9d8c7d..3ce5123a682 100644 --- a/iocore/net/YamlSNIConfig.cc +++ b/iocore/net/YamlSNIConfig.cc @@ -103,6 +103,7 @@ std::set valid_sni_config_keys = {TS_fqdn, TS_verify_server_properties, TS_client_cert, TS_client_key, + TS_http2, TS_ip_allow #if TS_USE_HELLO_CB , @@ -129,7 +130,10 @@ template <> struct convert { return false; // servername must be present } if (node[TS_disable_h2]) { - item.disable_h2 = node[TS_disable_h2].as(); + item.offer_h2 = false; + } + if (node[TS_http2]) { + item.offer_h2 = node[TS_http2].as(); } // enum diff --git a/iocore/net/YamlSNIConfig.h b/iocore/net/YamlSNIConfig.h index 0e6b8fc549f..d528388a10a 100644 --- a/iocore/net/YamlSNIConfig.h +++ b/iocore/net/YamlSNIConfig.h @@ -23,6 +23,7 @@ #include #include +#include #include "tscore/Errata.h" @@ -39,6 +40,7 @@ TSDECL(client_cert); TSDECL(client_key); TSDECL(ip_allow); TSDECL(valid_tls_versions_in); +TSDECL(http2); #undef TSDECL const int start = 0; @@ -50,18 +52,20 @@ struct YamlSNIConfig { forward_route, // decrypt data and then blind tunnel action verify_server_policy, // this applies to server side vc only verify_server_properties, // this applies to server side vc only - client_cert + client_cert, + h2 // this applies to client side only }; enum class Level { NONE = 0, MODERATE, STRICT }; enum class Policy : uint8_t { DISABLED = 0, PERMISSIVE, ENFORCED, UNSET }; enum class Property : uint8_t { NONE = 0, SIGNATURE_MASK = 0x1, NAME_MASK = 0x2, ALL_MASK = 0x3, UNSET }; enum class TLSProtocol : uint8_t { TLSv1 = 0, TLSv1_1, TLSv1_2, TLSv1_3, TLS_MAX = TLSv1_3 }; + enum class Control : uint8_t { NONE = 0, ENABLE, DISABLE }; YamlSNIConfig() {} struct Item { std::string fqdn; - bool disable_h2 = false; + std::optional offer_h2; // Has no value by default, so do not initialize! uint8_t verify_client_level = 255; std::string tunnel_destination; bool tunnel_decrypt = false; diff --git a/iocore/net/quic/Mock.h b/iocore/net/quic/Mock.h index 45f5e8f49b4..d4692b05d43 100644 --- a/iocore/net/quic/Mock.h +++ b/iocore/net/quic/Mock.h @@ -214,12 +214,6 @@ class MockQUICConnection : public QUICConnection return _direction; } - SSLNextProtocolSet * - next_protocol_set() const override - { - return nullptr; - } - void close(QUICConnectionErrorUPtr error) override { @@ -259,6 +253,13 @@ class MockQUICConnection : public QUICConnection return negotiated_application_name_sv; } + int + select_next_protocol(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, + unsigned inlen) const override + { + return SSL_TLSEXT_ERR_OK; + } + int _transmit_count = 0; int _retransmit_count = 0; Ptr _mutex; @@ -321,10 +322,11 @@ class MockQUICConnectionInfoProvider : public QUICConnectionInfoProvider return NET_VCONNECTION_OUT; } - SSLNextProtocolSet * - next_protocol_set() const override + int + select_next_protocol(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, + unsigned inlen) const override { - return nullptr; + return SSL_TLSEXT_ERR_OK; } bool diff --git a/iocore/net/quic/QUICConnection.h b/iocore/net/quic/QUICConnection.h index 36cc5fb987e..d2c3d56b78a 100644 --- a/iocore/net/quic/QUICConnection.h +++ b/iocore/net/quic/QUICConnection.h @@ -30,7 +30,6 @@ class QUICApplication; class QUICStreamManager; class UDPPacket; -class SSLNextProtocolSet; class QUICConnectionInfoProvider { @@ -44,7 +43,8 @@ class QUICConnectionInfoProvider virtual uint32_t pmtu() const = 0; virtual NetVConnectionContext_t direction() const = 0; - virtual SSLNextProtocolSet *next_protocol_set() const = 0; + virtual int select_next_protocol(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, + unsigned inlen) const = 0; virtual bool is_closed() const = 0; virtual std::string_view negotiated_application_name() const = 0; }; diff --git a/proxy/http/HttpProxyServerMain.cc b/proxy/http/HttpProxyServerMain.cc index 70f17b77427..48ea161c874 100644 --- a/proxy/http/HttpProxyServerMain.cc +++ b/proxy/http/HttpProxyServerMain.cc @@ -95,20 +95,6 @@ ssl_register_protocol(const char *protocol, Continuation *contp) return true; } -bool -ssl_unregister_protocol(const char *protocol, Continuation *contp) -{ - SCOPED_MUTEX_LOCK(lock, ssl_plugin_mutex, this_ethread()); - - for (SSLNextProtocolAccept *ssl = ssl_plugin_acceptors.head; ssl; ssl = ssl_plugin_acceptors.next(ssl)) { - // Ignore possible failure because we want to try to unregister - // from all SSL ports. - ssl->unregisterEndpoint(protocol, contp); - } - - return true; -} - ///////////////////////////////////////////////////////////////// // // main() @@ -236,21 +222,10 @@ MakeHttpProxyAcceptor(HttpProxyAcceptor &acceptor, HttpProxyPort &port, unsigned // the least important protocol first: // http/1.0, http/1.1, h2 - // HTTP - if (port.m_session_protocol_preference.contains(TS_ALPN_PROTOCOL_INDEX_HTTP_1_0)) { - ssl->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_1_0, http); - } - - if (port.m_session_protocol_preference.contains(TS_ALPN_PROTOCOL_INDEX_HTTP_1_1)) { - ssl->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_1_1, http); - } - - // HTTP2 - if (port.m_session_protocol_preference.contains(TS_ALPN_PROTOCOL_INDEX_HTTP_2_0)) { - Http2SessionAccept *acc = new Http2SessionAccept(accept_opt); - - ssl->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_2_0, acc); - } + ssl->enableProtocols(port.m_session_protocol_preference); + ssl->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_1_0, http); + ssl->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_1_1, http); + ssl->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_2_0, new Http2SessionAccept(accept_opt)); SCOPED_MUTEX_LOCK(lock, ssl_plugin_mutex, this_ethread()); ssl_plugin_acceptors.push(ssl); @@ -260,15 +235,13 @@ MakeHttpProxyAcceptor(HttpProxyAcceptor &acceptor, HttpProxyPort &port, unsigned } else if (port.isQUIC()) { QUICNextProtocolAccept *quic = new QUICNextProtocolAccept(); + quic->enableProtocols(port.m_session_protocol_preference); + // HTTP/3 - if (port.m_session_protocol_preference.contains(TS_ALPN_PROTOCOL_INDEX_HTTP_3)) { - quic->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_3, new Http3SessionAccept(accept_opt)); - } + quic->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_3, new Http3SessionAccept(accept_opt)); // HTTP/0.9 over QUIC (for interop only, will be removed) - if (port.m_session_protocol_preference.contains(TS_ALPN_PROTOCOL_INDEX_HTTP_QUIC)) { - quic->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_QUIC, new Http3SessionAccept(accept_opt)); - } + quic->registerEndpoint(TS_ALPN_PROTOCOL_HTTP_QUIC, new Http3SessionAccept(accept_opt)); quic->proxyPort = &port; acceptor._accept = quic; diff --git a/src/traffic_server/InkAPI.cc b/src/traffic_server/InkAPI.cc index 6d493034866..ecb21947bbb 100644 --- a/src/traffic_server/InkAPI.cc +++ b/src/traffic_server/InkAPI.cc @@ -7078,7 +7078,6 @@ TSNetAccept(TSCont contp, int port, int domain, int accept_threads) /* From proxy/http/HttpProxyServerMain.c: */ extern bool ssl_register_protocol(const char *, Continuation *); -extern bool ssl_unregister_protocol(const char *, Continuation *); TSReturnCode TSNetAcceptNamedProtocol(TSCont contp, const char *protocol) @@ -7088,7 +7087,6 @@ TSNetAcceptNamedProtocol(TSCont contp, const char *protocol) sdk_assert(sdk_sanity_check_continuation(contp) == TS_SUCCESS); if (!ssl_register_protocol(protocol, (INKContInternal *)contp)) { - ssl_unregister_protocol(protocol, (INKContInternal *)contp); return TS_ERROR; } @@ -9333,25 +9331,32 @@ TSSslTicketKeyUpdate(char *ticketData, int ticketDataLen) SSLTicketKeyConfig::reconfigure_data(ticketData, ticketDataLen); } -void -TSRegisterProtocolSet(TSVConn sslp, TSNextProtocolSet ps) +TSReturnCode +TSVConnProtocolEnable(TSVConn connp, const char *protocol_name) { - NetVConnection *vc = reinterpret_cast(sslp); - SSLNetVConnection *ssl_vc = dynamic_cast(vc); - if (ssl_vc) { - ssl_vc->registerNextProtocolSet(reinterpret_cast(ps)); + TSReturnCode retval = TS_ERROR; + int protocol_idx = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{protocol_name}); + auto net_vc = reinterpret_cast(connp); + auto alpn_vc = dynamic_cast(net_vc); + if (alpn_vc) { + alpn_vc->enableProtocol(protocol_idx); + retval = TS_SUCCESS; } + return retval; } -TSNextProtocolSet -TSUnregisterProtocol(TSNextProtocolSet protoset, const char *protocol) +TSReturnCode +TSVConnProtocolDisable(TSVConn connp, const char *protocol_name) { - SSLNextProtocolSet *snps = reinterpret_cast(protoset); - if (snps) { - snps->unregisterEndpoint(protocol, nullptr); - return reinterpret_cast(snps); + TSReturnCode retval = TS_ERROR; + int protocol_idx = globalSessionProtocolNameRegistry.toIndexConst(std::string_view{protocol_name}); + auto net_vc = reinterpret_cast(connp); + auto alpn_vc = dynamic_cast(net_vc); + if (alpn_vc) { + alpn_vc->disableProtocol(protocol_idx); + retval = TS_SUCCESS; } - return nullptr; + return retval; } TSAcceptor @@ -9385,15 +9390,6 @@ TSAcceptorCount() return naVec.size(); } -// clones the protoset associated with netAccept -TSNextProtocolSet -TSGetcloneProtoSet(TSAcceptor tna) -{ - NetAccept *na = reinterpret_cast(tna); - // clone protoset - return (na && na->snpa) ? reinterpret_cast(na->snpa->cloneProtoSet()) : nullptr; -} - tsapi int TSVConnIsSsl(TSVConn sslp) { diff --git a/src/traffic_server/traffic_server.cc b/src/traffic_server/traffic_server.cc index 6d50bbb7da6..2c82fd5fdce 100644 --- a/src/traffic_server/traffic_server.cc +++ b/src/traffic_server/traffic_server.cc @@ -1958,7 +1958,6 @@ main(int /* argc ATS_UNUSED */, const char **argv) start_HttpProxyServer(); // PORTS_READY_HOOK called from in here } } - SNIConfig::cloneProtoSet(); // Plugins can register their own configuration names so now after they've done that // check for unexpected names. This is very late because remap plugins must be allowed to // fire up as well. diff --git a/tests/gold_tests/h2/h2disable.test.py b/tests/gold_tests/h2/h2disable.test.py index b784665e7d4..f9347b669bd 100644 --- a/tests/gold_tests/h2/h2disable.test.py +++ b/tests/gold_tests/h2/h2disable.test.py @@ -53,15 +53,16 @@ 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir), 'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir), '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.url_remap.pristine_host_hdr': 1 + 'proxy.config.url_remap.pristine_host_hdr': 1, + 'proxy.config.accept_threads': 1 }) ts.Disk.sni_yaml.AddLines([ 'sni:', '- fqdn: bar.com', - ' disable_h2: true', + ' http2: off', '- fqdn: bob.*.com', - ' disable_h2: true', + ' http2: off', ]) tr = Test.AddTestRun("Negotiate-h2") diff --git a/tests/gold_tests/h2/h2disable_no_accept_threads.test.py b/tests/gold_tests/h2/h2disable_no_accept_threads.test.py new file mode 100644 index 00000000000..bbf5dd012bc --- /dev/null +++ b/tests/gold_tests/h2/h2disable_no_accept_threads.test.py @@ -0,0 +1,98 @@ +''' +''' +# 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 disabling H2 on a per domain basis +''' + +# need Curl +Test.SkipUnless( + Condition.HasCurlFeature('http2') +) + +# Define default ATS +ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True) +server = Test.MakeOriginServer("server") + +request_header = {"headers": "GET / HTTP/1.1\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +server.addResponse("sessionlog.json", request_header, response_header) + +# add ssl materials like key, certificates for the server +ts.addSSLfile("ssl/server.pem") +ts.addSSLfile("ssl/server.key") + +ts.Disk.remap_config.AddLine( + 'map / http://127.0.0.1:{0}'.format(server.Variables.Port)) + +ts.Disk.ssl_multicert_config.AddLine( + 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key' +) + +# Case 1, global config policy=permissive properties=signature +# override for foo.com policy=enforced properties=all +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), + '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.url_remap.pristine_host_hdr': 1, + 'proxy.config.accept_threads': 0 +}) + +ts.Disk.sni_yaml.AddLines([ + 'sni:', + '- fqdn: bar.com', + ' http2: off', + '- fqdn: bob.*.com', + ' http2: off', +]) + +tr = Test.AddTestRun("Negotiate-h2") +tr.Processes.Default.Command = "curl -v -k --resolve 'foo.com:{0}:127.0.0.1' https://foo.com:{0}".format(ts.Variables.ssl_port) +tr.ReturnCode = 0 +tr.Processes.Default.StartBefore(server) +tr.Processes.Default.StartBefore(Test.Processes.ts) +tr.StillRunningAfter = server +tr.StillRunningAfter = ts +tr.Processes.Default.TimeOut = 5 +tr.Processes.Default.Streams.All = Testers.ExcludesExpression("Could Not Connect", "Curl attempt should have succeeded") +tr.Processes.Default.Streams.All += Testers.ContainsExpression("Using HTTP2", "Curl should negotiate HTTP2") +tr.TimeOut = 5 + +tr2 = Test.AddTestRun("Do not negotiate h2") +tr2.Processes.Default.Command = "curl -v -k --resolve 'bar.com:{0}:127.0.0.1' https://bar.com:{0}".format(ts.Variables.ssl_port) +tr2.ReturnCode = 0 +tr2.StillRunningAfter = server +tr2.Processes.Default.TimeOut = 5 +tr2.StillRunningAfter = ts +tr2.Processes.Default.Streams.All = Testers.ExcludesExpression("Could Not Connect", "Curl attempt should have succeeded") +tr2.Processes.Default.Streams.All += Testers.ExcludesExpression("Using HTTP2", "Curl should not negotiate HTTP2") +tr2.TimeOut = 5 + +tr2 = Test.AddTestRun("Do not negotiate h2") +tr2.Processes.Default.Command = "curl -v -k --resolve 'bob.foo.com:{0}:127.0.0.1' https://bob.foo.com:{0}".format(ts.Variables.ssl_port) +tr2.ReturnCode = 0 +tr2.StillRunningAfter = server +tr2.Processes.Default.TimeOut = 5 +tr2.StillRunningAfter = ts +tr2.Processes.Default.Streams.All = Testers.ExcludesExpression("Could Not Connect", "Curl attempt should have succeeded") +tr2.Processes.Default.Streams.All += Testers.ExcludesExpression("Using HTTP2", "Curl should not negotiate HTTP2") +tr2.TimeOut = 5 diff --git a/tests/gold_tests/h2/h2enable.test.py b/tests/gold_tests/h2/h2enable.test.py new file mode 100644 index 00000000000..0357e93168b --- /dev/null +++ b/tests/gold_tests/h2/h2enable.test.py @@ -0,0 +1,98 @@ +''' +''' +# 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 enabling H2 on a per domain basis +''' + +# need Curl +Test.SkipUnless( + Condition.HasCurlFeature('http2') +) + +# Define default ATS +ts = Test.MakeATSProcess("ts", select_ports=False, enable_tls=True) +server = Test.MakeOriginServer("server") + +request_header = {"headers": "GET / HTTP/1.1\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +server.addResponse("sessionlog.json", request_header, response_header) + +# add ssl materials like key, certificates for the server +ts.addSSLfile("ssl/server.pem") +ts.addSSLfile("ssl/server.key") + +ts.Disk.remap_config.AddLine( + 'map / http://127.0.0.1:{0}'.format(server.Variables.Port)) + +ts.Disk.ssl_multicert_config.AddLine( + 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key' +) + +# Set up port 4444 with HTTP1 only, no HTTP/2 +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), + '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.url_remap.pristine_host_hdr': 1, + 'proxy.config.accept_threads': 1, + 'proxy.config.http.server_ports': '{0}:ssl:proto=http {1}'.format(ts.Variables.ssl_port, ts.Variables.port) +}) + +ts.Disk.sni_yaml.AddLines([ + 'sni:', + '- fqdn: bar.com', + ' http2: on', + '- fqdn: bob.*.com', + ' http2: on', +]) + +tr = Test.AddTestRun("Do-not-Negotiate-h2") +tr.Processes.Default.Command = "curl -v -k --resolve 'foo.com:{0}:127.0.0.1' https://foo.com:{0}".format(ts.Variables.ssl_port) +tr.ReturnCode = 0 +tr.Processes.Default.StartBefore(server, ready=When.PortOpen(server.Variables.Port)) +tr.Processes.Default.StartBefore(Test.Processes.ts, ready=When.PortOpen(ts.Variables.ssl_port)) +tr.StillRunningAfter = server +tr.StillRunningAfter = ts +tr.Processes.Default.TimeOut = 5 +tr.Processes.Default.Streams.All = Testers.ExcludesExpression("Could Not Connect", "Curl attempt should have succeeded") +tr.Processes.Default.Streams.All += Testers.ExcludesExpression("Using HTTP2", "Curl should negotiate HTTP2") +tr.TimeOut = 5 + +tr2 = Test.AddTestRun("Do negotiate h2") +tr2.Processes.Default.Command = "curl -v -k --resolve 'bar.com:{0}:127.0.0.1' https://bar.com:{0}".format(ts.Variables.ssl_port) +tr2.ReturnCode = 0 +tr2.StillRunningAfter = server +tr2.Processes.Default.TimeOut = 5 +tr2.StillRunningAfter = ts +tr2.Processes.Default.Streams.All = Testers.ExcludesExpression("Could Not Connect", "Curl attempt should have succeeded") +tr2.Processes.Default.Streams.All += Testers.ContainsExpression("Using HTTP2", "Curl should not negotiate HTTP2") +tr2.TimeOut = 5 + +tr2 = Test.AddTestRun("Do negotiate h2") +tr2.Processes.Default.Command = "curl -v -k --resolve 'bob.foo.com:{0}:127.0.0.1' https://bob.foo.com:{0}".format(ts.Variables.ssl_port) +tr2.ReturnCode = 0 +tr2.StillRunningAfter = server +tr2.Processes.Default.TimeOut = 5 +tr2.StillRunningAfter = ts +tr2.Processes.Default.Streams.All = Testers.ExcludesExpression("Could Not Connect", "Curl attempt should have succeeded") +tr2.Processes.Default.Streams.All += Testers.ContainsExpression("Using HTTP2", "Curl should not negotiate HTTP2") +tr2.TimeOut = 5 diff --git a/tests/gold_tests/h2/h2enable_no_accept_threads.test.py b/tests/gold_tests/h2/h2enable_no_accept_threads.test.py new file mode 100644 index 00000000000..0149e54fad5 --- /dev/null +++ b/tests/gold_tests/h2/h2enable_no_accept_threads.test.py @@ -0,0 +1,98 @@ +''' +''' +# 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 enabling H2 on a per domain basis +''' + +# need Curl +Test.SkipUnless( + Condition.HasCurlFeature('http2') +) + +# Define default ATS +ts = Test.MakeATSProcess("ts", select_ports=False, enable_tls=True) +server = Test.MakeOriginServer("server") + +request_header = {"headers": "GET / HTTP/1.1\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +server.addResponse("sessionlog.json", request_header, response_header) + +# add ssl materials like key, certificates for the server +ts.addSSLfile("ssl/server.pem") +ts.addSSLfile("ssl/server.key") + +ts.Disk.remap_config.AddLine( + 'map / http://127.0.0.1:{0}'.format(server.Variables.Port)) + +ts.Disk.ssl_multicert_config.AddLine( + 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key' +) + +# Set up port 4444 with HTTP1 only, no HTTP/2 +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), + '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.url_remap.pristine_host_hdr': 1, + 'proxy.config.http.server_ports': '{0}:ssl:proto=http {1}'.format(ts.Variables.ssl_port, ts.Variables.port), + 'proxy.config.accept_threads': 0 +}) + +ts.Disk.sni_yaml.AddLines([ + 'sni:', + '- fqdn: bar.com', + ' http2: on', + '- fqdn: bob.*.com', + ' http2: on', +]) + +tr = Test.AddTestRun("Do-not-Negotiate-h2") +tr.Processes.Default.Command = "curl -v -k --resolve 'foo.com:{0}:127.0.0.1' https://foo.com:{0}".format(ts.Variables.ssl_port) +tr.ReturnCode = 0 +tr.Processes.Default.StartBefore(server, ready=When.PortOpen(server.Variables.Port)) +tr.Processes.Default.StartBefore(Test.Processes.ts, ready=When.PortOpen(ts.Variables.ssl_port)) +tr.StillRunningAfter = server +tr.StillRunningAfter = ts +tr.Processes.Default.TimeOut = 5 +tr.Processes.Default.Streams.All = Testers.ExcludesExpression("Could Not Connect", "Curl attempt should have succeeded") +tr.Processes.Default.Streams.All += Testers.ExcludesExpression("Using HTTP2", "Curl should negotiate HTTP2") +tr.TimeOut = 5 + +tr2 = Test.AddTestRun("Do negotiate h2") +tr2.Processes.Default.Command = "curl -v -k --resolve 'bar.com:{0}:127.0.0.1' https://bar.com:{0}".format(ts.Variables.ssl_port) +tr2.ReturnCode = 0 +tr2.StillRunningAfter = server +tr2.Processes.Default.TimeOut = 5 +tr2.StillRunningAfter = ts +tr2.Processes.Default.Streams.All = Testers.ExcludesExpression("Could Not Connect", "Curl attempt should have succeeded") +tr2.Processes.Default.Streams.All += Testers.ContainsExpression("Using HTTP2", "Curl should not negotiate HTTP2") +tr2.TimeOut = 5 + +tr2 = Test.AddTestRun("Do negotiate h2") +tr2.Processes.Default.Command = "curl -v -k --resolve 'bob.foo.com:{0}:127.0.0.1' https://bob.foo.com:{0}".format(ts.Variables.ssl_port) +tr2.ReturnCode = 0 +tr2.StillRunningAfter = server +tr2.Processes.Default.TimeOut = 5 +tr2.StillRunningAfter = ts +tr2.Processes.Default.Streams.All = Testers.ExcludesExpression("Could Not Connect", "Curl attempt should have succeeded") +tr2.Processes.Default.Streams.All += Testers.ContainsExpression("Using HTTP2", "Curl should not negotiate HTTP2") +tr2.TimeOut = 5