You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by am...@apache.org on 2017/11/14 01:35:25 UTC
[trafficserver] branch master updated: YTSATS-1222: SNI based
Configuration for traffic server
This is an automated email from the ASF dual-hosted git repository.
amc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 22e6bf6 YTSATS-1222: SNI based Configuration for traffic server
22e6bf6 is described below
commit 22e6bf6f0e0db3bf57af6d9e33a4e210eb818640
Author: Persia Aziz <pe...@yahoo-inc.com>
AuthorDate: Tue May 2 17:05:23 2017 -0500
YTSATS-1222: SNI based Configuration for traffic server
---
Makefile.am | 2 +-
cmd/traffic_layout/traffic_layout.cc | 1 +
cmd/traffic_manager/AddConfigFilesHere.cc | 1 +
cmd/traffic_manager/traffic_manager.cc | 2 +
iocore/net/LuaSNIConfig.cc | 127 ++++++++++++
iocore/net/LuaSNIConfig.h | 114 +++++++++++
iocore/net/Makefile.am | 13 ++
iocore/net/P_SNIActionPerformer.h | 100 +++++++++
iocore/net/P_SSLConfig.h | 7 +-
iocore/net/P_SSLNetVConnection.h | 8 +
iocore/net/P_SSLSNI.h | 86 ++++++++
iocore/net/P_SSLUtils.h | 63 ++++++
iocore/net/SNIActionPerformer.cc | 53 +++++
iocore/net/SSLCertLookup.cc | 19 --
iocore/net/SSLConfig.cc | 14 +-
iocore/net/SSLNetProcessor.cc | 4 +-
iocore/net/SSLNetVConnection.cc | 91 ++++++---
iocore/net/SSLSNIConfig.cc | 224 +++++++++++++++++++++
iocore/net/SSLUtils.cc | 39 +++-
lib/ts/Vec.h | 5 +-
lib/tsconfig/Makefile.am | 4 +-
lib/tsconfig/TsConfigLua.cc | 53 +++++
lib/tsconfig/TsConfigLua.h | 184 +++++++++++++++++
mgmt/RecordsConfig.cc | 4 +
mgmt/utils/MgmtUtils.h | 2 +
proxy/Main.cc | 4 +-
proxy/Makefile.am | 1 +
proxy/config/Makefile.am | 1 +
proxy/config/ssl_server_name.config.default | 36 ++++
proxy/http/HttpSM.cc | 70 ++++++-
proxy/http/HttpTransact.cc | 44 ----
proxy/http/remap/RemapConfig.cc | 2 +-
proxy/http/remap/RemapProcessor.cc | 1 -
.../gold_tests/autest-site/min_cfg/ssl_SNI.config | 0
.../gold_tests/autest-site/trafficserver.test.ext | 4 +
35 files changed, 1273 insertions(+), 110 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index b3f6b69..323f427 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -29,7 +29,7 @@ export CCACHE_BASEDIR
# proxy/api/ts has to be built first, since so much of libraries and "core
# depends on the generates ts/ts.h include file.
-SUBDIRS = proxy/api/ts iocore lib proxy/hdrs proxy/shared mgmt proxy cmd plugins tools example rc
+SUBDIRS = proxy/api/ts lib iocore proxy/hdrs proxy/shared mgmt proxy cmd plugins tools example rc
if BUILD_DOCS
SUBDIRS += doc
endif
diff --git a/cmd/traffic_layout/traffic_layout.cc b/cmd/traffic_layout/traffic_layout.cc
index 34cd685..c0d2ef2 100644
--- a/cmd/traffic_layout/traffic_layout.cc
+++ b/cmd/traffic_layout/traffic_layout.cc
@@ -172,6 +172,7 @@ produce_layout(bool json)
print_var("remap.config", RecConfigReadConfigPath("proxy.config.url_remap.filename"), json);
print_var("plugin.config", RecConfigReadConfigPath(nullptr, "plugin.config"), json);
print_var("ssl_multicert.config", RecConfigReadConfigPath("proxy.config.ssl.server.multicert.filename"), json);
+ print_var("ssl_server_name.config", RecConfigReadConfigPath("proxy.config.ssl.servername.filename"), json);
print_var("storage.config", RecConfigReadConfigPath("proxy.config.cache.storage_filename"), json);
print_var("hosting.config", RecConfigReadConfigPath("proxy.config.cache.hosting_filename"), json);
print_var("volume.config", RecConfigReadConfigPath("proxy.config.cache.volume_filename"), json);
diff --git a/cmd/traffic_manager/AddConfigFilesHere.cc b/cmd/traffic_manager/AddConfigFilesHere.cc
index f620204..62caa38 100644
--- a/cmd/traffic_manager/AddConfigFilesHere.cc
+++ b/cmd/traffic_manager/AddConfigFilesHere.cc
@@ -77,5 +77,6 @@ initializeRegistry()
configFiles->addFile("splitdns.config", false);
configFiles->addFile("ssl_multicert.config", false);
configFiles->addFile("metrics.config", false);
+ configFiles->addFile(SSL_SERVER_NAME_CONFIG, false);
configFiles->registerCallback(testcall);
}
diff --git a/cmd/traffic_manager/traffic_manager.cc b/cmd/traffic_manager/traffic_manager.cc
index 2d286ba..941c51b 100644
--- a/cmd/traffic_manager/traffic_manager.cc
+++ b/cmd/traffic_manager/traffic_manager.cc
@@ -962,6 +962,8 @@ fileUpdated(char *fname, bool incVersion)
lmgmt->signalFileChange("proxy.config.http.congestion_control.filename");
} else if (strcmp(fname, "proxy.config.ssl.server.ticket_key.filename") == 0) {
lmgmt->signalFileChange("proxy.config.ssl.server.ticket_key.filename");
+ } else if (strcmp(fname, SSL_SERVER_NAME_CONFIG) == 0) {
+ lmgmt->signalFileChange("proxy.config.ssl.servername.filename");
} else {
mgmt_log("[fileUpdated] Unknown config file updated '%s'\n", fname);
}
diff --git a/iocore/net/LuaSNIConfig.cc b/iocore/net/LuaSNIConfig.cc
new file mode 100644
index 0000000..13d5e37
--- /dev/null
+++ b/iocore/net/LuaSNIConfig.cc
@@ -0,0 +1,127 @@
+/** @file
+
+ @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 "LuaSNIConfig.h"
+#include <cstring>
+#include "ts/Diags.h"
+#include "P_SNIActionPerformer.h"
+#include "tsconfig/Errata.h"
+#include "tsconfig/TsConfigLua.h"
+
+TsConfigDescriptor LuaSNIConfig::desc = {TsConfigDescriptor::Type::ARRAY, "Array", "Item vector", "Vector"};
+TsConfigArrayDescriptor LuaSNIConfig::DESCRIPTOR(LuaSNIConfig::desc);
+TsConfigDescriptor LuaSNIConfig::Item::FQDN_DESCRIPTOR = {TsConfigDescriptor::Type::STRING, "String", TS_fqdn,
+ "Fully Qualified Domain Name"};
+TsConfigDescriptor LuaSNIConfig::Item::DISABLE_h2_DESCRIPTOR = {TsConfigDescriptor::Type::BOOL, "Boolean", TS_disable_H2,
+ "Disable H2"};
+TsConfigEnumDescriptor LuaSNIConfig::Item::LEVEL_DESCRIPTOR = {TsConfigDescriptor::Type::ENUM,
+ "enum",
+ "Level",
+ "Level for client verification",
+ {{"NONE", 0}, {"MODERATE", 1}, {"STRICT", 2}}};
+TsConfigDescriptor LuaSNIConfig::Item::TUNNEL_DEST_DESCRIPTOR = {TsConfigDescriptor::Type::STRING, "String", TS_tunnel_route,
+ "tunnel route destination"};
+TsConfigDescriptor LuaSNIConfig::Item::CLIENT_CERT_DESCRIPTOR = {TsConfigDescriptor::Type::STRING, "String", TS_client_cert,
+ "Client certificate to present to the next hop server"};
+TsConfigDescriptor LuaSNIConfig::Item::VERIFY_NEXT_SERVER_DESCRIPTOR = {TsConfigDescriptor::Type::INT, "Int",
+ TS_verify_origin_server, "Next hop verification level"};
+
+ts::Errata
+LuaSNIConfig::loader(lua_State *L)
+{
+ ts::Errata zret;
+ // char buff[256];
+ // int error;
+
+ lua_getfield(L, LUA_GLOBALSINDEX, "server_config");
+ int l_type = lua_type(L, -1);
+
+ switch (l_type) {
+ case LUA_TTABLE: // this has to be a multidimensional table
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0) {
+ l_type = lua_type(L, -1);
+ if (l_type == LUA_TTABLE) { // the item should be table
+ // new Item
+ LuaSNIConfig::Item item;
+ item.loader(L);
+ items.push_back(item);
+ } else {
+ zret.push(ts::Errata::Message(0, 0, "Invalid Entry at SNI config"));
+ }
+ lua_pop(L, 1);
+ }
+ break;
+ case LUA_TSTRING:
+ Debug("ssl", "string value %s", lua_tostring(L, -1));
+ break;
+ default:
+ zret.push(ts::Errata::Message(0, 0, "Invalid Lua SNI Config"));
+ Debug("ssl", "Please check your SNI config");
+ break;
+ }
+
+ return zret;
+}
+
+ts::Errata
+LuaSNIConfig::Item::loader(lua_State *L)
+{
+ ts::Errata zret;
+ //-1 will contain the subarray now (since it is a value in the main table))
+ lua_pushnil(L);
+ while (lua_next(L, -2)) {
+ if (lua_type(L, -2) != LUA_TSTRING) {
+ Debug("ssl", "string keys expected for entries in %s", SSL_SERVER_NAME_CONFIG);
+ }
+ const char *name = lua_tostring(L, -2);
+ if (!strncmp(name, TS_fqdn, strlen(TS_fqdn))) {
+ FQDN_CONFIG.loader(L);
+ } else if (!strncmp(name, TS_disable_H2, strlen(TS_disable_H2))) {
+ DISABLEH2_CONFIG.loader(L);
+ } else if (!strncmp(name, TS_verify_client, strlen(TS_verify_client))) {
+ VERIFYCLIENT_CONFIG.loader(L);
+ } else if (!strncmp(name, TS_verify_origin_server, strlen(TS_verify_origin_server))) {
+ VERIFY_NEXT_SERVER_CONFIG.loader(L);
+ } else if (!strncmp(name, TS_client_cert, strlen(TS_client_cert))) {
+ CLIENT_CERT_CONFIG.loader(L);
+ } else if (!strncmp(name, TS_tunnel_route, strlen(TS_tunnel_route))) {
+ TUNNEL_DEST_CONFIG.loader(L);
+ } else {
+ zret.push(ts::Errata::Message(0, 0, "Invalid Entry at SNI config"));
+ }
+ lua_pop(L, 1);
+ }
+ return zret;
+}
+
+ts::Errata
+LuaSNIConfig::registerEnum(lua_State *L)
+{
+ ts::Errata zret;
+ lua_newtable(L);
+ lua_setglobal(L, "LevelTable");
+ int i = start;
+ LUA_ENUM(L, "NONE", i++);
+ LUA_ENUM(L, "MODERATE", i++);
+ LUA_ENUM(L, "STRICT", i++);
+ return zret;
+}
diff --git a/iocore/net/LuaSNIConfig.h b/iocore/net/LuaSNIConfig.h
new file mode 100644
index 0000000..06330e5
--- /dev/null
+++ b/iocore/net/LuaSNIConfig.h
@@ -0,0 +1,114 @@
+/** @file
+
+ @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.
+*/
+/*
+ * File: LuaSNIConfig.h
+ * Author: persia
+ *
+ * Created on September 21, 2017, 4:25 PM
+ */
+
+#ifndef LUASNICONFIG_H
+#define LUASNICONFIG_H
+
+#include "tsconfig/TsConfigLua.h"
+
+#include <vector>
+
+using ts::Errata;
+#define LUA_ENUM(L, name, val) \
+ lua_pushlstring(L, #name, sizeof(#name) - 1); \
+ lua_pushnumber(L, val); \
+ lua_settable(L, -3);
+
+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";
+
+const int start = 0;
+struct LuaSNIConfig : public TsConfigBase {
+ using self = LuaSNIConfig;
+ enum class Action {
+ disable_h2 = start,
+ verify_client,
+ tunnel_route, // blind tunnel action
+ verify_origin_server, // this applies to server side vc only
+ client_cert
+
+ };
+ enum class Level { NONE = 0, MODERATE, STRICT };
+ static TsConfigDescriptor desc;
+ static TsConfigArrayDescriptor DESCRIPTOR;
+
+ LuaSNIConfig() : TsConfigBase(this->DESCRIPTOR) { self::Item::Initialize(); }
+
+ struct Item : public TsConfigBase {
+ Item()
+ : TsConfigBase(DESCRIPTOR),
+ FQDN_CONFIG(FQDN_DESCRIPTOR, fqdn),
+ DISABLEH2_CONFIG(DISABLE_h2_DESCRIPTOR, disable_h2),
+ VERIFYCLIENT_CONFIG(LEVEL_DESCRIPTOR, (int &)verify_client_level),
+ TUNNEL_DEST_CONFIG(TUNNEL_DEST_DESCRIPTOR, tunnel_destination),
+ CLIENT_CERT_CONFIG(CLIENT_CERT_DESCRIPTOR, client_cert),
+ VERIFY_NEXT_SERVER_CONFIG(VERIFY_NEXT_SERVER_DESCRIPTOR, (int &)verify_origin_server)
+ {
+ }
+ ts::Errata loader(lua_State *s) override;
+ static void
+ Initialize()
+ {
+ // ACTION_DESCRIPTOR.values = {
+ // {TS_disable_H2, 0}, {TS_verify_client, 1}, {TS_tunnel_route, 2}, {TS_verify_origin_server, 3}, {TS_client_cert, 4}};
+ //
+ // ACTION_DESCRIPTOR.keys = {
+ // {0, TS_disable_H2}, {1, TS_verify_client}, {2, TS_tunnel_route}, {3, TS_verify_origin_server}, {4, TS_client_cert}};
+ }
+
+ std::string fqdn;
+ bool disable_h2 = false;
+ uint8_t verify_client_level = 0;
+ std::string tunnel_destination;
+ uint8_t verify_origin_server = 0;
+ std::string client_cert;
+
+ // These need to be initialized statically.
+ static TsConfigObjectDescriptor OBJ_DESCRIPTOR;
+ static TsConfigDescriptor FQDN_DESCRIPTOR;
+ TsConfigString FQDN_CONFIG;
+ static TsConfigDescriptor DISABLE_h2_DESCRIPTOR;
+ TsConfigBool DISABLEH2_CONFIG;
+ static TsConfigEnumDescriptor LEVEL_DESCRIPTOR;
+ TsConfigEnum<self::Level> VERIFYCLIENT_CONFIG;
+ static TsConfigDescriptor TUNNEL_DEST_DESCRIPTOR;
+ TsConfigString TUNNEL_DEST_CONFIG;
+ static TsConfigDescriptor CLIENT_CERT_DESCRIPTOR;
+ TsConfigString CLIENT_CERT_CONFIG;
+ static TsConfigDescriptor VERIFY_NEXT_SERVER_DESCRIPTOR;
+ TsConfigInt VERIFY_NEXT_SERVER_CONFIG;
+ ~Item() {}
+ };
+ std::vector<self::Item> items;
+ ts::Errata loader(lua_State *s) override;
+ ts::Errata registerEnum(lua_State *L);
+};
+#endif /* LUASNICONFIG_H */
diff --git a/iocore/net/Makefile.am b/iocore/net/Makefile.am
index 1f28b06..aaa7e94 100644
--- a/iocore/net/Makefile.am
+++ b/iocore/net/Makefile.am
@@ -50,6 +50,7 @@ test_certlookup_LDADD = \
test_UDPNet_CPPFLAGS = \
$(AM_CPPFLAGS) \
+ $(LUAJIT_CPPFLAGS) \
$(iocore_include_dirs) \
-I$(abs_top_srcdir)/proxy \
-I$(abs_top_srcdir)/proxy/hdrs \
@@ -70,6 +71,8 @@ test_UDPNet_LDADD = \
$(top_builddir)/lib/records/librecords_p.a \
$(top_builddir)/lib/ts/libtsutil.la \
$(top_builddir)/proxy/ParentSelectionStrategy.o \
+ $(top_builddir)/lib/tsconfig/libtsconfig.la \
+ $(top_builddir)/lib/luajit/src/libluajit.a \
@LIBTCL@ @HWLOC_LIBS@ @OPENSSL_LIBS@
test_UDPNet_SOURCES = \
@@ -89,8 +92,11 @@ libinknet_a_SOURCES = \
Inline.cc \
I_SessionAccept.h \
SessionAccept.cc \
+ LuaSNIConfig.h \
+ LuaSNIConfig.cc \
Net.cc \
NetVConnection.cc \
+ P_SNIActionPerformer.h \
P_CompletionUtil.h \
P_Connection.h \
P_InkBulkIO.h \
@@ -106,6 +112,7 @@ libinknet_a_SOURCES = \
P_SSLNetVConnection.h \
P_SSLNextProtocolAccept.h \
P_SSLNextProtocolSet.h \
+ P_SSLSNI.h \
P_SSLUtils.h \
P_SSLClientUtils.h \
P_OCSPStapling.h \
@@ -122,6 +129,7 @@ libinknet_a_SOURCES = \
P_UnixPollDescriptor.h \
P_UnixUDPConnection.h \
Socks.cc \
+ SNIActionPerformer.cc \
SSLCertLookup.cc \
SSLSessionCache.cc \
SSLConfig.cc \
@@ -131,6 +139,7 @@ libinknet_a_SOURCES = \
SSLNetVConnection.cc \
SSLNextProtocolAccept.cc \
SSLNextProtocolSet.cc \
+ SSLSNIConfig.cc \
SSLUtils.cc \
SSLClientUtils.cc \
OCSPStapling.cc \
@@ -157,6 +166,10 @@ if BUILD_TESTS
P_NetVCTest.h
endif
+libinknet_a_LIBADD = \
+ $(top_builddir)/lib/luajit/src/libluajit.a \
+ $(top_builddir)/lib/tsconfig/libtsconfig.la
+
include $(top_srcdir)/build/tidy.mk
tidy-local: $(DIST_SOURCES)
diff --git a/iocore/net/P_SNIActionPerformer.h b/iocore/net/P_SNIActionPerformer.h
new file mode 100644
index 0000000..803e09a
--- /dev/null
+++ b/iocore/net/P_SNIActionPerformer.h
@@ -0,0 +1,100 @@
+/** @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++ -*- ******************************
+ P_ActionProcessor.h
+ Created On : 05/02/2017
+
+ Description:
+ SNI based Configuration in ATS
+ ****************************************************************************/
+#ifndef __P_ACTIONPROCESSOR_H__
+#define __P_ACTIONPROCESSOR_H__
+
+#include "I_EventSystem.h"
+#include "ts/Map.h"
+//#include"P_UnixNetProcessor.h"
+#include <vector>
+#include "P_SSLNextProtocolAccept.h"
+
+extern Map<int, SSLNextProtocolSet *> snpsMap;
+// enum of all the actions
+enum AllActions {
+ TS_DISABLE_H2 = 0,
+ TS_VERIFY_CLIENT, // this applies to server side vc only
+ TS_TUNNEL_ROUTE, // blind tunnel action
+};
+
+/** action for setting next hop properties should be listed in the following enum*/
+enum PropertyActions { TS_VERIFY_SERVER = 200, TS_CLIENT_CERT };
+
+class ActionItem
+{
+public:
+ virtual void SNIAction(Continuation *cont) = 0;
+ virtual ~ActionItem(){};
+};
+
+class DisableH2 : public ActionItem
+{
+public:
+ DisableH2() {}
+ ~DisableH2() override {}
+
+ void
+ SNIAction(Continuation *cont) override
+ {
+ auto ssl_vc = reinterpret_cast<SSLNetVConnection *>(cont);
+ auto accept_obj = ssl_vc->accept_object;
+ if (accept_obj && accept_obj->snpa && ssl_vc) {
+ auto nps = snpsMap.get(accept_obj->id);
+ ssl_vc->registerNextProtocolSet(reinterpret_cast<SSLNextProtocolSet *>(nps));
+ }
+ }
+};
+
+class VerifyClient : public ActionItem
+{
+ uint8_t mode;
+
+public:
+ VerifyClient(const char *param) : mode(atoi(param)) {}
+ VerifyClient(uint8_t param) : mode(param) {}
+ ~VerifyClient() override {}
+ void
+ SNIAction(Continuation *cont) override
+ {
+ auto ssl_vc = reinterpret_cast<SSLNetVConnection *>(cont);
+ Debug("ssl_sni", "action verify param %d", this->mode);
+ setClientCertLevel(ssl_vc->ssl, this->mode);
+ }
+};
+
+class SNIActionPerformer
+{
+public:
+ SNIActionPerformer() = default;
+ static void PerformAction(Continuation *cont, cchar *servername);
+};
+
+#endif
diff --git a/iocore/net/P_SSLConfig.h b/iocore/net/P_SSLConfig.h
index f15df36..c16af27 100644
--- a/iocore/net/P_SSLConfig.h
+++ b/iocore/net/P_SSLConfig.h
@@ -84,7 +84,7 @@ struct SSLConfigParams : public ConfigInfo {
char *clientKeyPath;
char *clientCACertFilename;
char *clientCACertPath;
- int clientVerify;
+ int8_t clientVerify;
int client_verify_depth;
long ssl_ctx_options;
long ssl_client_ctx_protocols;
@@ -101,6 +101,7 @@ struct SSLConfigParams : public ConfigInfo {
static size_t session_cache_number_buckets;
static size_t session_cache_max_bucket_size;
static bool session_cache_skip_on_lock_contention;
+ static bool sni_map_enable;
// TS-3435 Wiretracing for SSL Connections
static int ssl_wire_trace_enabled;
@@ -120,10 +121,10 @@ struct SSLConfigParams : public ConfigInfo {
SSL_CTX *getCTX(cchar *client_cert) const;
void deleteKey(cchar *key) const;
void freeCTXmap() const;
- void printCTXmap();
+ void printCTXmap() const;
bool InsertCTX(cchar *client_cert, SSL_CTX *cctx) const;
SSL_CTX *getClientSSL_CTX(void) const;
- SSL_CTX *getNewCTX(char *client_cert) const;
+ SSL_CTX *getNewCTX(cchar *client_cert) const;
void initialize();
void cleanup();
diff --git a/iocore/net/P_SSLNetVConnection.h b/iocore/net/P_SSLNetVConnection.h
index f885184..805b231 100644
--- a/iocore/net/P_SSLNetVConnection.h
+++ b/iocore/net/P_SSLNetVConnection.h
@@ -167,6 +167,12 @@ public:
return transparentPassThrough;
}
+ bool
+ GetSNIMapping()
+ {
+ return SNIMapping;
+ }
+
void
setTransparentPassThrough(bool val)
{
@@ -296,6 +302,7 @@ public:
ink_hrtime sslHandshakeEndTime = 0;
ink_hrtime sslLastWriteTime = 0;
int64_t sslTotalBytesSent = 0;
+ char *serverName = nullptr;
/// Set by asynchronous hooks to request a specific operation.
SslVConnOp hookOpRequested = SSL_HOOK_OP_DEFAULT;
@@ -335,6 +342,7 @@ private:
Continuation *npnEndpoint = nullptr;
SessionAccept *sessionAcceptPtr = nullptr;
bool sslTrace = false;
+ bool SNIMapping = false;
};
typedef int (SSLNetVConnection::*SSLNetVConnHandler)(int, void *);
diff --git a/iocore/net/P_SSLSNI.h b/iocore/net/P_SSLSNI.h
new file mode 100644
index 0000000..cbc2825
--- /dev/null
+++ b/iocore/net/P_SSLSNI.h
@@ -0,0 +1,86 @@
+/** @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++ -*- ******************************
+ P_SSLSNI.h
+ Created On : 05/02/2017
+
+ Description:
+ SNI based Configuration in ATS
+ ****************************************************************************/
+#ifndef __P_SSL_SNI_H__
+#define __P_SSL_SNI_H__
+
+#include "ProxyConfig.h"
+#include "ts/Map.h"
+#include "P_SNIActionPerformer.h"
+#include "ts/MatcherUtils.h"
+#include "openssl/ossl_typ.h"
+#include <vector>
+#include <unordered_map>
+#include <strings.h>
+#include "LuaSNIConfig.h"
+
+// Properties for the next hop server
+struct NextHopProperty {
+ const char *name = nullptr; // name of the server
+ int8_t verifyLevel = 0; // whether to verify the next hop
+ SSL_CTX *ctx = nullptr; // ctx generated off the certificate to present to this server
+ NextHopProperty();
+};
+
+typedef std::vector<ActionItem *> actionVector;
+typedef HashMap<cchar *, StringHashFns, actionVector *> SNIMap;
+typedef HashMap<cchar *, StringHashFns, NextHopProperty *> NextHopPropertyTable;
+
+struct SNIConfigParams : public ConfigInfo {
+ char *sni_filename = nullptr;
+ SNIMap sni_action_map;
+ SNIMap wild_sni_action_map;
+ NextHopPropertyTable next_hop_table;
+ NextHopPropertyTable wild_next_hop_table;
+ LuaSNIConfig L_sni;
+ NextHopProperty *getPropertyConfig(cchar *servername) const;
+ SNIConfigParams();
+ virtual ~SNIConfigParams();
+ void cleanup();
+ int Initialize();
+ void loadSNIConfig();
+ actionVector *get(cchar *servername) const;
+ void printSNImap() const;
+};
+
+struct SNIConfig {
+ static void startup();
+ 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<SNIConfig, SNIConfigParams> scoped_config;
+
+private:
+ static int configid;
+};
+
+#endif
\ No newline at end of file
diff --git a/iocore/net/P_SSLUtils.h b/iocore/net/P_SSLUtils.h
index a3d530d..62c40ff 100644
--- a/iocore/net/P_SSLUtils.h
+++ b/iocore/net/P_SSLUtils.h
@@ -171,6 +171,8 @@ void SSLNetVCDetach(SSL *ssl);
// Return the SSLNetVConnection (if any) attached to this SSL session.
SSLNetVConnection *SSLNetVCAccess(const SSL *ssl);
+void setClientCertLevel(SSL *ssl, uint8_t certLevel);
+
namespace ssl
{
namespace detail
@@ -215,6 +217,67 @@ namespace detail
/* namespace ssl */ } /* namespace detail */
}
+struct ats_wildcard_matcher {
+ ats_wildcard_matcher()
+ {
+ if (regex.compile("^\\*\\.[^\\*.]+") != 0) {
+ Fatal("failed to compile TLS wildcard matching regex");
+ }
+ }
+
+ ~ats_wildcard_matcher() {}
+ bool
+ match(const char *hostname) const
+ {
+ return regex.match(hostname) != -1;
+ }
+
+private:
+ DFA regex;
+};
+
+class TunnelHashMap
+{
+public:
+ struct HostStruct {
+ cchar *hostname;
+ int len;
+ int port;
+ HostStruct(cchar *name, int hostnamelen, int port_)
+ {
+ hostname = name;
+ len = hostnamelen;
+ port = port_;
+ }
+ };
+
+ typedef HashMap<cchar *, StringHashFns, const HostStruct *> Tunnel_hashMap;
+ Tunnel_hashMap TunnelhMap;
+
+ void
+ emplace(cchar *key, std::string &hostname)
+ {
+ ts::string_view addr, port;
+ if (ats_ip_parse(ts::string_view(hostname), &addr, &port) == 0) {
+ auto *hs = new HostStruct(ats_strdup(addr.data()), addr.length(), atoi(port.data()));
+ TunnelhMap.put(ats_strdup(key), hs);
+ }
+ }
+
+ void
+ emplace(cchar *key, cchar *name, int hostnamelen, int port_)
+ {
+ auto *hs = new HostStruct(ats_strdup(name), hostnamelen, port_);
+ TunnelhMap.put(ats_strdup(key), hs);
+ }
+
+ const HostStruct *
+ find(cchar *key)
+ {
+ return TunnelhMap.get(key);
+ }
+};
+
typedef ats_scoped_resource<ssl::detail::SCOPED_X509_TRAITS> scoped_X509;
typedef ats_scoped_resource<ssl::detail::SCOPED_BIO_TRAITS> scoped_BIO;
diff --git a/iocore/net/SNIActionPerformer.cc b/iocore/net/SNIActionPerformer.cc
new file mode 100644
index 0000000..b14e9c9
--- /dev/null
+++ b/iocore/net/SNIActionPerformer.cc
@@ -0,0 +1,53 @@
+/** @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 "ts/ink_memory.h"
+#include "P_SSLSNI.h"
+#include "P_Net.h"
+#include "P_SSLNextProtocolAccept.h"
+#include "P_SSLUtils.h"
+
+extern Map<int, SSLNextProtocolSet *> snpsMap;
+
+void
+SNIActionPerformer::PerformAction(Continuation *cont, cchar *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)
+ it->SNIAction(cont);
+ }
+ }
+}
diff --git a/iocore/net/SSLCertLookup.cc b/iocore/net/SSLCertLookup.cc
index 5d2f20a..6d39a2d 100644
--- a/iocore/net/SSLCertLookup.cc
+++ b/iocore/net/SSLCertLookup.cc
@@ -304,25 +304,6 @@ SSLCertLookup::get(unsigned i) const
return ssl_storage->get(i);
}
-struct ats_wildcard_matcher {
- ats_wildcard_matcher()
- {
- if (regex.compile(R"(^\*\.[^\*.]+)") != 0) {
- Fatal("failed to compile TLS wildcard matching regex");
- }
- }
-
- ~ats_wildcard_matcher() {}
- bool
- match(const char *hostname) const
- {
- return regex.match(hostname) != -1;
- }
-
-private:
- DFA regex;
-};
-
static void
make_to_lower_case(const char *name, char *lower_case_name, int buf_len)
{
diff --git a/iocore/net/SSLConfig.cc b/iocore/net/SSLConfig.cc
index b18d302..a0da4fc 100644
--- a/iocore/net/SSLConfig.cc
+++ b/iocore/net/SSLConfig.cc
@@ -56,6 +56,7 @@ bool SSLConfigParams::session_cache_skip_on_lock_contention = false;
size_t SSLConfigParams::session_cache_max_bucket_size = 100;
init_ssl_ctx_func SSLConfigParams::init_ssl_ctx_cb = nullptr;
load_ssl_file_func SSLConfigParams::load_ssl_file_cb = nullptr;
+bool SSLConfigParams::sni_map_enable = false;
// TS-3534 Wiretracing for SSL Connections
int SSLConfigParams::ssl_wire_trace_enabled = 0;
@@ -298,6 +299,7 @@ SSLConfigParams::initialize()
// ++++++++++++++++++++++++ Client part ++++++++++++++++++++
client_verify_depth = 7;
+ REC_EstablishStaticConfigByte(clientVerify, "proxy.config.ssl.client.verify.server");
ssl_client_cert_filename = nullptr;
ssl_client_cert_path = nullptr;
@@ -321,6 +323,9 @@ SSLConfigParams::initialize()
ats_free(clientCACertRelativePath);
ats_free(ssl_client_ca_cert_filename);
+ // Enable/disable sni mapping
+ REC_ReadConfigInteger(sni_map_enable, "proxy.config.ssl.sni.map.enable");
+
REC_ReadConfigInt32(ssl_allow_client_renegotiation, "proxy.config.ssl.allow_client_renegotiation");
// SSL Wire Trace configurations
@@ -379,13 +384,12 @@ SSLConfigParams::InsertCTX(cchar *client_cert, SSL_CTX *cctx) const
}
void
-SSLConfigParams::printCTXmap()
+SSLConfigParams::printCTXmap() const
{
Vec<cchar *> keys;
ctx_map.get_keys(keys);
- for (size_t i = 0; i < keys.length(); i++) {
- Debug("ssl", "Client certificates in the map %s", keys.get(i));
- }
+ for (size_t i = 0; i < keys.length(); i++)
+ Debug("ssl", "Client certificates in the map %s: %p", keys.get(i), ctx_map.get(keys.get(i)));
}
void
SSLConfigParams::freeCTXmap() const
@@ -404,7 +408,7 @@ SSLConfigParams::freeCTXmap() const
}
// creates a new context attaching the provided certificate
SSL_CTX *
-SSLConfigParams::getNewCTX(char *client_cert) const
+SSLConfigParams::getNewCTX(cchar *client_cert) const
{
SSL_CTX *nclient_ctx = nullptr;
nclient_ctx = SSLInitClientContext(this);
diff --git a/iocore/net/SSLNetProcessor.cc b/iocore/net/SSLNetProcessor.cc
index c519691..b592581 100644
--- a/iocore/net/SSLNetProcessor.cc
+++ b/iocore/net/SSLNetProcessor.cc
@@ -26,6 +26,7 @@
#include "I_RecHttp.h"
#include "P_SSLUtils.h"
#include "P_OCSPStapling.h"
+#include "P_SSLSNI.h"
//
// Global Data
@@ -33,7 +34,7 @@
SSLNetProcessor ssl_NetProcessor;
NetProcessor &sslNetProcessor = ssl_NetProcessor;
-
+SNIActionPerformer sni_action_performer;
#ifdef HAVE_OPENSSL_OCSP_STAPLING
struct OCSPContinuation : public Continuation {
int
@@ -59,6 +60,7 @@ SSLNetProcessor::start(int, size_t stacksize)
// This initialization order matters ...
SSLInitializeLibrary();
SSLConfig::startup();
+ SNIConfig::startup();
if (!SSLCertificateConfig::startup()) {
return -1;
diff --git a/iocore/net/SSLNetVConnection.cc b/iocore/net/SSLNetVConnection.cc
index fbb48c8..a0cf7b0 100644
--- a/iocore/net/SSLNetVConnection.cc
+++ b/iocore/net/SSLNetVConnection.cc
@@ -31,9 +31,12 @@
#include "BIO_fastopen.h"
#include "Log.h"
#include "P_SSLClientUtils.h"
+#include "P_SSLSNI.h"
+#include "HttpTunnel.h"
#include <climits>
#include <string>
+#include <stdbool.h>
#if !TS_USE_SET_RBIO
// Defined in SSLInternal.c, should probably make a separate include
@@ -934,12 +937,19 @@ SSLNetVConnection::sslStartHandShake(int event, int &err)
// Escape if this is marked to be a tunnel.
// No data has been read at this point, so we can go
// directly into blind tunnel mode
- if (cc && SSLCertContext::OPT_TUNNEL == cc->opt && this->is_transparent) {
- this->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
- sslHandShakeComplete = true;
- SSL_free(this->ssl);
- this->ssl = nullptr;
- return EVENT_DONE;
+
+ if (cc && SSLCertContext::OPT_TUNNEL == cc->opt) {
+ if (this->is_transparent) {
+ this->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
+ sslHandShakeComplete = 1;
+ SSL_free(this->ssl);
+ this->ssl = NULL;
+ return EVENT_DONE;
+ } else {
+ SSLConfig::scoped_config params;
+ this->SNIMapping = params->sni_map_enable;
+ hookOpRequested = SSL_HOOK_OP_TUNNEL;
+ }
}
// Attach the default SSL_CTX to this SSL session. The default context is never going to be able
@@ -965,40 +975,46 @@ SSLNetVConnection::sslStartHandShake(int event, int &err)
return sslServerHandShakeEvent(err);
case SSL_EVENT_CLIENT:
+
if (this->ssl == nullptr) {
+ // Making the check here instead of later, so we only
+ // do this setting immediately after we create the SSL object
+ SNIConfig::scoped_config sniParam;
+ int8_t clientVerify = 0;
+ cchar *serverKey = this->options.sni_servername;
+ if (!serverKey) {
+ char buff[INET6_ADDRSTRLEN];
+ ats_ip_ntop(this->get_remote_addr(), buff, INET6_ADDRSTRLEN);
+ serverKey = buff;
+ }
+ auto nps = sniParam->getPropertyConfig(serverKey);
SSL_CTX *clientCTX = nullptr;
- if (this->options.clientCertificate) {
- const char *certfile = (const char *)this->options.clientCertificate;
- if (certfile != nullptr) {
- clientCTX = params->getCTX(certfile);
- if (clientCTX != nullptr) {
- Debug("ssl", "context for %s is found at %p", this->options.clientCertificate.get(), (void *)clientCTX);
- } else {
- Debug("ssl", "failed to find context for %s", this->options.clientCertificate.get());
- }
- }
+
+ if (nps) {
+ clientCTX = nps->ctx;
+ clientVerify = nps->verifyLevel;
} else {
- clientCTX = params->client_ctx;
+ clientCTX = params->client_ctx;
+ clientVerify = params->clientVerify;
}
-
- if (this->options.clientVerificationFlag && params->clientCACertFilename != nullptr && params->clientCACertPath != nullptr) {
+ if (!clientCTX) {
+ SSLErrorVC(this, "failed to create SSL client session");
+ return EVENT_ERROR;
+ }
+ if (clientVerify && params->clientCACertFilename != nullptr && params->clientCACertPath != nullptr) {
if (!SSL_CTX_load_verify_locations(clientCTX, params->clientCACertFilename, params->clientCACertPath)) {
SSLError("invalid client CA Certificate file (%s) or CA Certificate path (%s)", params->clientCACertFilename,
params->clientCACertPath);
return EVENT_ERROR;
}
}
- this->ssl = make_ssl_connection(clientCTX, this);
- if (this->ssl != nullptr) {
- uint8_t clientVerify = this->options.clientVerificationFlag;
- int verifyValue = clientVerify & 1 ? SSL_VERIFY_PEER : SSL_VERIFY_NONE;
- SSL_set_verify(this->ssl, verifyValue, verify_callback);
- }
+ this->ssl = make_ssl_connection(clientCTX, this);
if (this->ssl == nullptr) {
SSLErrorVC(this, "failed to create SSL client session");
return EVENT_ERROR;
}
+ SSL_set_verify(this->ssl, clientVerify ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, verify_callback);
#if TS_USE_TLS_SNI
if (this->options.sni_servername) {
@@ -1050,7 +1066,8 @@ SSLNetVConnection::sslServerHandShakeEvent(int &err)
// Again no data has been exchanged, so we can go directly
// without data replay.
// Note we can't arrive here if a hook is active.
- if (SSL_HOOK_OP_TUNNEL == hookOpRequested) {
+
+ if (SSL_HOOK_OP_TUNNEL == hookOpRequested && !SNIMapping) {
this->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
SSL_free(this->ssl);
this->ssl = nullptr;
@@ -1465,6 +1482,8 @@ SSLNetVConnection::sslContextSet(void *ctx)
return zret;
}
+extern TunnelHashMap TunnelMap; // stores the name of the servers to tunnel to
+
bool
SSLNetVConnection::callHooks(TSEvent eventId)
{
@@ -1531,6 +1550,26 @@ SSLNetVConnection::callHooks(TSEvent eventId)
Debug("ssl", "callHooks iterated to curHook=%p", curHook);
+ this->serverName = const_cast<char *>(SSL_get_servername(this->ssl, TLSEXT_NAMETYPE_host_name));
+ if (this->serverName) {
+ auto *hs = TunnelMap.find(this->serverName);
+ if (hs != nullptr) {
+ this->SNIMapping = true;
+ this->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
+ return EVENT_DONE;
+ }
+ }
+
+ if (SSL_HOOK_OP_TUNNEL == hookOpRequested && SNIMapping) {
+ this->attributes = HttpProxyPort::TRANSPORT_BLIND_TUNNEL;
+ // Don't mark the handshake as complete yet,
+ // Will be checking for that flag not being set after
+ // we get out of this callback, and then will shuffle
+ // over the buffered handshake packets to the O.S.
+ // sslHandShakeComplete = 1;
+ return EVENT_DONE;
+ }
+
bool reenabled = true;
if (curHook != nullptr) {
curHook->invoke(eventId, this);
diff --git a/iocore/net/SSLSNIConfig.cc b/iocore/net/SSLSNIConfig.cc
new file mode 100644
index 0000000..3b812de
--- /dev/null
+++ b/iocore/net/SSLSNIConfig.cc
@@ -0,0 +1,224 @@
+/** @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++ -*- ******************************
+ SSLSNIConfig.cc
+ Created On : 05/02/2017
+
+ Description:
+ SNI based Configuration in ATS
+ ****************************************************************************/
+
+#include "P_SSLSNI.h"
+#include "ts/Diags.h"
+#include "ts/SimpleTokenizer.h"
+#include "P_SSLConfig.h"
+#include "ts/ink_memory.h"
+
+#define SNI_NAME_TAG "dest_host"
+#define SNI_ACTION_TAG "action"
+#define SNI_PARAM_TAG "param"
+
+static ConfigUpdateHandler<SNIConfig> *sniConfigUpdate;
+struct NetAccept;
+extern std::vector<NetAccept *> naVec;
+Map<int, SSLNextProtocolSet *> snpsMap;
+extern TunnelHashMap TunnelMap;
+NextHopProperty::NextHopProperty()
+{
+}
+
+NextHopProperty *
+SNIConfigParams::getPropertyConfig(cchar *servername) const
+{
+ NextHopProperty *nps = nullptr;
+ nps = next_hop_table.get(servername);
+ if (!nps)
+ nps = wild_next_hop_table.get(servername);
+ return nps;
+}
+
+void
+SNIConfigParams::loadSNIConfig()
+{
+ for (auto item : L_sni.items) {
+ actionVector *aiVec = new actionVector();
+ Debug("ssl", "name: %s", item.fqdn.data());
+ cchar *servername = item.fqdn.data();
+ ats_wildcard_matcher w_Matcher;
+ auto wildcard = w_Matcher.match(servername);
+
+ // set SNI based actions to be called in the ssl_servername_only callback
+ auto ai1 = new DisableH2();
+ aiVec->push_back(ai1);
+ auto ai2 = new VerifyClient(item.verify_client_level);
+ aiVec->push_back(ai2);
+ sni_action_map.put(ats_strdup(servername), aiVec);
+
+ if (item.tunnel_destination.length()) {
+ TunnelMap.emplace(item.fqdn.data(), item.tunnel_destination);
+ }
+
+ // set the next hop properties
+ SSLConfig::scoped_config params;
+ auto clientCTX = params->getCTX(servername);
+ cchar *certFile = item.client_cert.data();
+ if (!clientCTX && certFile) {
+ clientCTX = params->getNewCTX(certFile);
+ params->InsertCTX(certFile, clientCTX);
+ }
+ NextHopProperty *nps = new NextHopProperty();
+ nps->name = ats_strdup(servername);
+ nps->verifyLevel = item.verify_origin_server;
+ nps->ctx = clientCTX;
+ if (wildcard)
+ wild_next_hop_table.put(nps->name, nps);
+ else
+ next_hop_table.put(nps->name, nps);
+ } // end for
+}
+
+int SNIConfig::configid = 0;
+/*definition of member functions of SNIConfigParams*/
+SNIConfigParams::SNIConfigParams()
+{
+}
+
+actionVector *
+SNIConfigParams::get(cchar *servername) const
+{
+ auto actionVec = sni_action_map.get(servername);
+ if (!actionVec)
+ actionVec = wild_sni_action_map.get(servername);
+ return actionVec;
+}
+
+void
+SNIConfigParams::printSNImap() const
+{
+ Vec<cchar *> keys;
+ sni_action_map.get_keys(keys);
+ for (size_t i = 0; i < keys.length(); i++) {
+ Debug("ssl", "Domain name in the map %s: # of registered action items %lu", (char *)keys.get(i),
+ sni_action_map.get(keys.get(i))->size());
+ }
+}
+
+int
+SNIConfigParams::Initialize()
+{
+ sni_filename = ats_stringdup(RecConfigReadConfigPath("proxy.config.ssl.servername.filename"));
+ lua_State *L = lua_open(); /* opens Lua */
+ luaL_openlibs(L);
+ luaL_loadfile(L, sni_filename);
+ lua_pcall(L, 0, 0, 0);
+ L_sni.loader(L);
+ loadSNIConfig();
+ return 0;
+}
+
+void
+SNIConfigParams::cleanup()
+{
+ Vec<cchar *> keys;
+ sni_action_map.get_keys(keys);
+ for (int i = keys.length() - 1; i >= 0; i--) {
+ auto actionVec = sni_action_map.get(keys.get(i));
+ for (auto &ai : *actionVec)
+ delete ai;
+
+ actionVec->clear();
+ }
+ keys.free_and_clear();
+
+ wild_sni_action_map.get_keys(keys);
+ for (int i = keys.length() - 1; i >= 0; i--) {
+ auto actionVec = wild_sni_action_map.get(keys.get(i));
+ for (auto &ai : *actionVec)
+ delete ai;
+
+ actionVec->clear();
+ }
+ keys.free_and_clear();
+
+ next_hop_table.get_keys(keys);
+ for (int i = 0; i < static_cast<int>(keys.length()); i++) {
+ auto *nps = next_hop_table.get(keys.get(i));
+ delete (nps);
+ }
+ keys.free_and_clear();
+
+ wild_next_hop_table.get_keys(keys);
+ for (int i = 0; static_cast<int>(keys.length()); i++) {
+ auto *nps = wild_next_hop_table.get(keys.get(i));
+ delete (nps);
+ }
+ keys.free_and_clear();
+}
+
+SNIConfigParams::~SNIConfigParams()
+{
+ cleanup();
+}
+
+/*definition of member functions of SNIConfig*/
+void
+SNIConfig::startup()
+{
+ sniConfigUpdate = new ConfigUpdateHandler<SNIConfig>();
+ sniConfigUpdate->attach("proxy.config.ssl.servername.filename");
+ reconfigure();
+}
+
+void
+SNIConfig::cloneProtoSet()
+{
+ for (auto na : naVec) {
+ if (na->snpa) {
+ auto snps = na->snpa->cloneProtoSet();
+ snps->unregisterEndpoint(TS_ALPN_PROTOCOL_HTTP_2_0, nullptr);
+ snpsMap.put(na->id, snps);
+ }
+ }
+}
+
+void
+SNIConfig::reconfigure()
+{
+ SNIConfigParams *params = new SNIConfigParams;
+
+ params->Initialize();
+ configid = configProcessor.set(configid, params);
+}
+
+SNIConfigParams *
+SNIConfig::acquire()
+{
+ return (SNIConfigParams *)configProcessor.get(configid);
+}
+
+void
+SNIConfig::release(SNIConfigParams *params)
+{
+ configProcessor.release(configid, params);
+}
diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc
index dffe016..37452e2 100644
--- a/iocore/net/SSLUtils.cc
+++ b/iocore/net/SSLUtils.cc
@@ -42,6 +42,7 @@
#include <unistd.h>
#include <termios.h>
#include <vector>
+#include "P_SNIActionPerformer.h"
#if HAVE_OPENSSL_EVP_H
#include <openssl/evp.h>
@@ -68,6 +69,7 @@
#define SSL_ACTION_TUNNEL_TAG "tunnel"
#define SSL_SESSION_TICKET_ENABLED "ssl_ticket_enabled"
#define SSL_KEY_DIALOG "ssl_key_dialog"
+#define SSL_SERVERNAME "dest_fqdn"
#define SSL_CERT_SEPARATE_DELIM ','
// openssl version must be 0.9.4 or greater
@@ -83,6 +85,7 @@
#endif
#endif
+TunnelHashMap TunnelMap; // stores the name of the servers to tunnel to
/*
* struct ssl_user_config: gather user provided settings from ssl_multicert.config in to this single struct
* ssl_ticket_enabled - session ticket enabled
@@ -94,6 +97,7 @@
* ssl_key_name - Private key
* ticket_key_name - session key file. [key_name (16Byte) + HMAC_secret (16Byte) + AES_key (16Byte)]
* ssl_key_dialog - Private key dialog
+ * servername - Destination server
*/
struct ssl_user_config {
ssl_user_config() : opt(SSLCertContext::OPT_NONE)
@@ -108,6 +112,7 @@ struct ssl_user_config {
ats_scoped_str ca;
ats_scoped_str key;
ats_scoped_str dialog;
+ ats_scoped_str servername;
SSLCertContext::Option opt;
};
@@ -407,10 +412,16 @@ ssl_cert_callback(SSL *ssl, void * /*arg*/)
/*
* Cannot stop this callback. Always reeneabled
*/
+extern SNIActionPerformer sni_action_performer;
static int
ssl_servername_only_callback(SSL *ssl, int * /* ad */, void * /*arg*/)
{
- SSLNetVConnection *netvc = SSLNetVCAccess(ssl);
+ SSLNetVConnection *netvc = reinterpret_cast<SSLNetVConnection *>(SSL_get_app_data(ssl));
+ const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ Debug("ssl", "Requested servername is %s", servername);
+ if (servername != nullptr) {
+ sni_action_performer.PerformAction(netvc, servername);
+ }
netvc->callHooks(TS_EVENT_SSL_SERVERNAME);
return SSL_TLSEXT_ERR_OK;
}
@@ -1472,6 +1483,23 @@ ssl_set_handshake_callbacks(SSL_CTX *ctx)
#endif
}
+void
+setClientCertLevel(SSL *ssl, uint8_t certLevel)
+{
+ SSLConfig::scoped_config params;
+ int server_verify_client = SSL_VERIFY_NONE;
+
+ if (certLevel == 2) {
+ server_verify_client = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE;
+ } else if (certLevel == 1) {
+ server_verify_client = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
+ }
+
+ Debug("ssl", "setting cert level to %d", server_verify_client);
+ SSL_set_verify(ssl, server_verify_client, nullptr);
+ SSL_set_verify_depth(ssl, params->verify_depth); // might want to make configurable at some point.
+}
+
SSL_CTX *
SSLInitServerContext(const SSLConfigParams *params, const ssl_user_config *sslMultCertSettings, std::vector<X509 *> &certList)
{
@@ -1946,6 +1974,11 @@ ssl_extract_certificate(const matcher_line *line_info, ssl_user_config &sslMultC
if (strcasecmp(label, SSL_KEY_DIALOG) == 0) {
sslMultCertSettings.dialog = ats_strdup(value);
}
+
+ if (strcasecmp(label, SSL_SERVERNAME) == 0) {
+ sslMultCertSettings.servername = ats_strdup(value);
+ }
+
if (strcasecmp(label, SSL_ACTION_TAG) == 0) {
if (strcasecmp(SSL_ACTION_TUNNEL_TAG, value) == 0) {
sslMultCertSettings.opt = SSLCertContext::OPT_TUNNEL;
@@ -1980,6 +2013,8 @@ SSLParseCertificateConfiguration(const SSLConfigParams *params, SSLCertLookup *l
Note("loading SSL certificate configuration from %s", params->configFilePath);
+ // TunnelMap.clear();
+
if (params->configFilePath) {
file_buf = readIntoBuffer(params->configFilePath, __func__, nullptr);
}
@@ -2009,7 +2044,7 @@ SSLParseCertificateConfiguration(const SSLConfigParams *params, SSLCertLookup *l
const char *errPtr;
errPtr = parseConfigLine(line, &line_info, &sslCertTags);
-
+ Debug("ssl", "currently parsing %s", line);
if (errPtr != nullptr) {
RecSignalWarning(REC_SIGNAL_CONFIG_ERROR, "%s: discarding %s entry at line %d: %s", __func__, params->configFilePath,
line_num, errPtr);
diff --git a/lib/ts/Vec.h b/lib/ts/Vec.h
index f599ead..d3f6ac5 100644
--- a/lib/ts/Vec.h
+++ b/lib/ts/Vec.h
@@ -853,9 +853,8 @@ template <class C, class A, int S>
inline void
Vec<C, A, S>::free_and_clear()
{
- for (int x = 0; x < n; x++)
- if (v[x])
- A::free(v[x]);
+ for (size_t x = 0; x < (n); x++)
+ A::free((void *)v[x]);
clear();
}
diff --git a/lib/tsconfig/Makefile.am b/lib/tsconfig/Makefile.am
index 0e7584d..5d4216e 100644
--- a/lib/tsconfig/Makefile.am
+++ b/lib/tsconfig/Makefile.am
@@ -40,6 +40,8 @@ libtsconfig_la_SOURCES = \
TsBuilder.h \
TsConfigGrammar.hpp \
TsConfigLexer.h \
+ TsConfigLua.cc \
+ TsConfigLua.h \
TsConfigParseEvents.h \
TsConfigTypes.h \
TsErrataUtil.cc \
@@ -62,7 +64,7 @@ endif
test_tsconfig_SOURCES = test-tsconfig.cc
-test_tsconfig_LDADD = libtsconfig.la ../ts/libtsutil.la
+test_tsconfig_LDADD = libtsconfig.la ../ts/libtsutil.la $(top_builddir)/lib/luajit/src/libluajit.a
# Strip to just the enum in the ts::config namespace so we can use
# it more easily in C++.
diff --git a/lib/tsconfig/TsConfigLua.cc b/lib/tsconfig/TsConfigLua.cc
new file mode 100644
index 0000000..46a2f54
--- /dev/null
+++ b/lib/tsconfig/TsConfigLua.cc
@@ -0,0 +1,53 @@
+/** @file
+
+ Implementation of the handler for parsing events.
+
+ @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 "TsConfigLua.h"
+
+ts::Errata TsConfigInt::loader(lua_State* s)
+{
+ ts::Errata zret;
+ ref = lua_tonumber(s,-1);
+ return zret;
+}
+
+ts::Errata TsConfigString::loader(lua_State* s)
+{
+ ts::Errata zret;
+ ref = lua_tostring(s,-1);
+ return zret;
+
+}
+
+ts::Errata TsConfigBool::loader(lua_State* s)
+{
+ ts::Errata zret;
+ ref = lua_toboolean(s,-1);
+ return zret;
+}
+
+//template <>
+//ts::Errata TsConfigEnum<Level>::loader(lua_State* s)
+//{
+// ts::Errata zret;
+// return zret;
+//}
diff --git a/lib/tsconfig/TsConfigLua.h b/lib/tsconfig/TsConfigLua.h
new file mode 100644
index 0000000..db79f99
--- /dev/null
+++ b/lib/tsconfig/TsConfigLua.h
@@ -0,0 +1,184 @@
+/** @file
+
+ @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.
+ */
+
+/*
+ * File: TSConfigLua.h
+ * Author: persia
+ *
+ * Created on September 21, 2017, 4:04 PM
+ */
+
+
+#ifndef TSCONFIGLUA_H
+#define TSCONFIGLUA_H
+
+
+#include "tsconfig/Errata.h"
+#include <unordered_map>
+#include "luajit/src/lua.hpp"
+#include <iostream>
+#include <ts/string_view.h>
+#include <ts/HashFNV.h>
+
+/// Hash functor for @c string_view
+inline size_t TsLuaConfigSVHash(ts::string_view const& sv)
+{
+ ATSHash64FNV1a h;
+ h.update(sv.data(), sv.size());
+ return h.get();
+}
+
+/** Static schema data for a configuration value.
+
+ This is a base class for data about a configuration value. This is intended to be a singleton
+ static instance that contains schema data that is the same for all instances of the
+ configuration value.
+*/
+struct TsConfigDescriptor {
+ /// Type of the configuration value.
+ enum class Type {
+ ARRAY, ///< A homogenous array of nested values.
+ OBJECT, ///< A set of fields, each a name / value pair.
+ INT, ///< Integer value.
+ FLOAT, ///< Floating point value.
+ STRING, ///< String.
+ BOOL,
+ ENUM ///< Enumeration (specialized).
+ };
+/* TsConfigDescriptor() : type_name(nullptr),name(nullptr),description(nullptr) {}
+ TsConfigDescriptor(Type typ,std::initializer_list<std::string> str_list): type(typ)
+ {
+ for (auto str :str_list) {
+ std::cout << str << std::endl;
+ }
+ }
+ * */
+ Type type; ///< Value type.
+ ts::string_view type_name; ///< Literal type name used in the schema.
+ ts::string_view name; ///< Name of the configuration value.
+ ts::string_view description; ///< Description of the value.
+};
+
+/** Configuration item instance data.
+
+ This is an abstract base class for data about an instance of the value in a configuration
+ struct. Actual instances will be a subclass for a supported configuration item type. This holds
+ data that is per instance and therefore must be dynamically constructed as part of the
+ configuration struct construction. The related description classes in contrast are data that is
+ schema based and therefore can be static and shared among instances of the configuration struct.
+*/
+class TsConfigBase {
+public:
+ /// Source of the value in the config struct.
+ enum class Source {
+ NONE, ///< No source, the value is default constructed.
+ SCHEMA, ///< Value set in schema.
+ CONFIG ///< Value set in configuration file.
+ };
+ /// Constructor - need the static descriptor.
+ TsConfigBase(TsConfigDescriptor const& d) : descriptor(d) {}
+ TsConfigDescriptor const& descriptor; ///< Static schema data.
+ Source source = Source::NONE; ///< Where the instance data came from.
+ ~TsConfigBase()
+ {}
+ /// Load the instance data from the Lua stack.
+ virtual ts::Errata loader(lua_State* s) = 0;
+};
+
+class TsConfigInt : public TsConfigBase {
+public:
+ TsConfigInt(TsConfigDescriptor const& d, int& i):TsConfigBase(d),ref(i){}
+ ts::Errata loader(lua_State* s) override;
+private:
+ int & ref;
+};
+
+class TsConfigBool : public TsConfigBase {
+public:
+ TsConfigBool(TsConfigDescriptor const& d, bool& i):TsConfigBase(d), ref(i) {}
+ ts::Errata loader(lua_State* s) override;
+private:
+ bool &ref;
+
+};
+
+class TsConfigString : public TsConfigBase {
+public:
+ TsConfigString(TsConfigDescriptor const& d, std::string& str) : TsConfigBase(d), ref(str) {}
+// TsConfigString& operator= (const TsConfigString& other)
+// {
+// ref = other.ref;
+// return *this;
+// }
+ ts::Errata loader(lua_State* s) override;
+private:
+ std::string& ref;
+};
+
+
+
+class TsConfigArrayDescriptor : public TsConfigDescriptor {
+public:
+ TsConfigArrayDescriptor(TsConfigDescriptor const& d) : item(d) {}
+ const TsConfigDescriptor& item;
+};
+
+class TsConfigEnumDescriptor : public TsConfigDescriptor {
+ using self_type = TsConfigEnumDescriptor;
+ using super_type = TsConfigDescriptor;
+public:
+ struct Pair { ts::string_view key; int value; };
+ TsConfigEnumDescriptor(Type t, ts::string_view t_name, ts::string_view n, ts::string_view d, std::initializer_list<Pair> pairs)
+ : super_type{t, t_name, n, d}, values{pairs.size(), &TsLuaConfigSVHash}, keys{pairs.size()}
+ {
+ for ( auto& p : pairs ) {
+ values[p.key] = p.value;
+ keys[p.value] = p.key;
+ }
+ }
+ std::unordered_map<ts::string_view, int, size_t(*)(ts::string_view const&) > values;
+ std::unordered_map<int, ts::string_view> keys;
+ int get(ts::string_view key)
+ {
+ return values[key];
+ }
+};
+
+class TsConfigObjectDescriptor : public TsConfigDescriptor {
+ std::unordered_map<std::string, TsConfigDescriptor const*> fields;
+};
+
+template < typename E >
+class TsConfigEnum : public TsConfigBase {
+public:
+ TsConfigEnum(TsConfigEnumDescriptor const& d, int& i) : TsConfigBase(d),edescriptor(d), ref(i) {}
+ TsConfigEnumDescriptor edescriptor;
+ int& ref;
+ ts::Errata loader(lua_State* L) override
+ {
+ ts::Errata zret;
+ std::string key(lua_tostring(L,-1));
+ ref = edescriptor.get(ts::string_view(key));
+ return zret;
+ }
+};
+
+#endif /* TSCONFIGLUA_H */
diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc
index 17b0672..fdc5b8a 100644
--- a/mgmt/RecordsConfig.cc
+++ b/mgmt/RecordsConfig.cc
@@ -1146,6 +1146,8 @@ static const RecordElement RecordsConfig[] =
{RECT_CONFIG, "proxy.config.ssl.server.multicert.filename", RECD_STRING, "ssl_multicert.config", RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
,
{RECT_CONFIG, "proxy.config.ssl.server.multicert.exit_on_load_fail", RECD_INT, "1", RECU_RESTART_TS, RR_NULL, RECC_NULL, "[0-1]", RECA_NULL}
+,
+ {RECT_CONFIG, "proxy.config.ssl.servername.filename", RECD_STRING, "ssl_server_name.config", RECU_RESTART_TS, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
,
{RECT_CONFIG, "proxy.config.ssl.server.ticket_key.filename", RECD_STRING, nullptr, RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
,
@@ -1193,6 +1195,8 @@ static const RecordElement RecordsConfig[] =
,
{RECT_CONFIG, "proxy.config.ssl.handshake_timeout_in", RECD_INT, "0", RECU_RESTART_TS, RR_NULL, RECC_INT, "[0-65535]", RECA_NULL}
,
+ {RECT_CONFIG, "proxy.config.ssl.sni.map.enable", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
+ ,
{RECT_CONFIG, "proxy.config.ssl.wire_trace_enabled", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-2]", RECA_NULL}
,
{RECT_CONFIG, "proxy.config.ssl.wire_trace_addr", RECD_STRING, nullptr , RECU_DYNAMIC, RR_NULL, RECC_IP, R"([0-255]\.[0-255]\.[0-255]\.[0-255])", RECA_NULL}
diff --git a/mgmt/utils/MgmtUtils.h b/mgmt/utils/MgmtUtils.h
index 03acab6..633438a 100644
--- a/mgmt/utils/MgmtUtils.h
+++ b/mgmt/utils/MgmtUtils.h
@@ -39,6 +39,8 @@
#include "P_RecCore.h"
+constexpr const char SSL_SERVER_NAME_CONFIG[] = "ssl_server_name.config";
+
int mgmt_readline(int fd, char *buf, int maxlen);
int mgmt_writeline(int fd, const char *data, int nbytes);
diff --git a/proxy/Main.cc b/proxy/Main.cc
index 48eaf64..5348726 100644
--- a/proxy/Main.cc
+++ b/proxy/Main.cc
@@ -92,6 +92,8 @@ extern "C" int plock(int);
#include "I_Tasks.h"
#include "InkAPIInternal.h"
#include "HTTP2.h"
+#include "ts/ink_config.h"
+#include "P_SSLSNI.h"
#include <ts/ink_cap.h>
@@ -1904,7 +1906,7 @@ 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/proxy/Makefile.am b/proxy/Makefile.am
index e031613..1df35af 100644
--- a/proxy/Makefile.am
+++ b/proxy/Makefile.am
@@ -206,6 +206,7 @@ traffic_server_LDADD += \
$(top_builddir)/iocore/eventsystem/libinkevent.a \
$(top_builddir)/lib/records/librecords_p.a \
$(top_builddir)/iocore/eventsystem/libinkevent.a \
+ $(top_builddir)/lib/tsconfig/libtsconfig.la \
@HWLOC_LIBS@ \
@LIBPCRE@ \
@LIBTCL@ \
diff --git a/proxy/config/Makefile.am b/proxy/config/Makefile.am
index c84c355..1206018 100644
--- a/proxy/config/Makefile.am
+++ b/proxy/config/Makefile.am
@@ -40,6 +40,7 @@ dist_sysconf_DATA = \
socks.config.default \
splitdns.config.default \
ssl_multicert.config.default \
+ ssl_server_name.config.default \
volume.config.default
install-exec-hook:
diff --git a/proxy/config/ssl_server_name.config.default b/proxy/config/ssl_server_name.config.default
new file mode 100644
index 0000000..2db9bbf
--- /dev/null
+++ b/proxy/config/ssl_server_name.config.default
@@ -0,0 +1,36 @@
+--[[
+ssl_server_name.config.default
+This configuration file
+ - sets the SSL actions to be performed based on the servername provided during SSL handhshake phase (SNI extension)
+ - sets the SSL properties required to make SSL connection with the next hop or origin server.
+
+Lua based Configuration file
+ Format :
+ Actions available:
+ disable_h2 - removes H2 from the protocol list advertised by ATS; parameter required = None
+ verify_client - sets the verification flag for verifying the client certificate; parameters = Integer [0-2]
+ verify_origin_server - sets the verification flag for verifying the server certificate; parameters = Integer [0-2]
+ client_cert - sets the client certificate to present to the server specified in dest_host; parameters = certificate file .
+ The location of the certificate file is relative to proxy.config.ssl.server.cert.path directory.
+ tunnel_route - sets the e2e tunnel route
+ Example:
+
+
+params for client verification
+ NONE, MODERATE, STRICT
+
+server_config = {
+ {
+ fqdn='one.com',
+ disable_h2 = <true|false>,
+ verify_origin_server = <NONE|MODERATE|STRICT>,
+ client_cert = "somepem.pem",
+ verify_client = <NONE|MODERATE|STRICT>
+ },
+ {
+ fqdn='two.com',
+ tunnel_route = 'two.com'
+ }
+};
+
+--]]
\ No newline at end of file
diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc
index bb01166..d7c1ce8 100644
--- a/proxy/http/HttpSM.cc
+++ b/proxy/http/HttpSM.cc
@@ -42,6 +42,7 @@
#include <openssl/ossl_typ.h>
#include <openssl/ssl.h>
#include "HttpPages.h"
+#include <unordered_map>
#include "IPAllow.h"
//#include "I_Auth.h"
@@ -69,6 +70,7 @@
#define USE_NEW_EMPTY_MIOBUFFER
extern int cache_config_read_while_writer;
+extern TunnelHashMap TunnelMap; // stores the name of the servers to tunnel to
// We have a debugging list that can use to find stuck
// state machines
@@ -533,8 +535,39 @@ HttpSM::setup_client_read_request_header()
void
HttpSM::setup_blind_tunnel_port()
{
- // We gotten a connect on a port for blind tunneling so
- // call transact figure out where it is going
+ NetVConnection *netvc = ua_session->get_netvc();
+ SSLNetVConnection *ssl_vc = dynamic_cast<SSLNetVConnection *>(netvc);
+ int host_len;
+ if (ssl_vc && ssl_vc->GetSNIMapping()) {
+ if (!t_state.hdr_info.client_request.url_get()->host_get(&host_len)) {
+ // the URL object has not been created in the start of the transaction. Hence, we need to create the URL here
+ URL u;
+
+ t_state.hdr_info.client_request.create(HTTP_TYPE_REQUEST);
+ t_state.hdr_info.client_request.method_set(HTTP_METHOD_CONNECT, HTTP_LEN_CONNECT);
+ t_state.hdr_info.client_request.url_create(&u);
+ u.scheme_set(URL_SCHEME_TUNNEL, URL_LEN_TUNNEL);
+ t_state.hdr_info.client_request.url_set(&u);
+ auto *hs = TunnelMap.find(ssl_vc->serverName);
+ if (hs != nullptr) {
+ t_state.hdr_info.client_request.url_get()->host_set(hs->hostname, hs->len);
+ if (hs->port > 0) {
+ t_state.hdr_info.client_request.url_get()->port_set(hs->port);
+ } else {
+ t_state.hdr_info.client_request.url_get()->port_set(t_state.state_machine->ua_session->get_netvc()->get_local_port());
+ }
+ } else {
+ t_state.hdr_info.client_request.url_get()->host_set(ssl_vc->serverName, strlen(ssl_vc->serverName));
+ t_state.hdr_info.client_request.url_get()->port_set(t_state.state_machine->ua_session->get_netvc()->get_local_port());
+ }
+ }
+ } else {
+ char new_host[INET6_ADDRSTRLEN];
+ ats_ip_ntop(t_state.state_machine->ua_session->get_netvc()->get_local_addr(), new_host, sizeof(new_host));
+
+ t_state.hdr_info.client_request.url_get()->host_set(new_host, strlen(new_host));
+ t_state.hdr_info.client_request.url_get()->port_set(t_state.state_machine->ua_session->get_netvc()->get_local_port());
+ }
call_transact_and_set_next_state(HttpTransact::HandleBlindTunnel);
}
@@ -1335,6 +1368,39 @@ HttpSM::state_api_callout(int event, void *data)
pending_action = nullptr;
// FALLTHROUGH
case EVENT_NONE:
+ if (cur_hook_id == TS_HTTP_TXN_START_HOOK && t_state.client_info.port_attribute == HttpProxyPort::TRANSPORT_BLIND_TUNNEL) {
+ /* Creating the request object early to set the host header and port for blind tunneling here for the
+plugins required to work with sni_routing.
+*/
+ // Plugins triggered on txn_start_hook will get the host and port at that point
+ // We've received a request on a port which we blind forward
+ URL u;
+
+ t_state.hdr_info.client_request.create(HTTP_TYPE_REQUEST);
+ t_state.hdr_info.client_request.method_set(HTTP_METHOD_CONNECT, HTTP_LEN_CONNECT);
+ t_state.hdr_info.client_request.url_create(&u);
+ u.scheme_set(URL_SCHEME_TUNNEL, URL_LEN_TUNNEL);
+ t_state.hdr_info.client_request.url_set(&u);
+
+ NetVConnection *netvc = ua_session->get_netvc();
+ SSLNetVConnection *ssl_vc = dynamic_cast<SSLNetVConnection *>(netvc);
+ auto *hs = TunnelMap.find(ssl_vc->serverName);
+
+ if (ssl_vc && ssl_vc->GetSNIMapping()) {
+ if (hs != nullptr) {
+ t_state.hdr_info.client_request.url_get()->host_set(hs->hostname, hs->len);
+ if (hs->port > 0) {
+ t_state.hdr_info.client_request.url_get()->port_set(hs->port);
+ } else {
+ t_state.hdr_info.client_request.url_get()->port_set(t_state.state_machine->ua_session->get_netvc()->get_local_port());
+ }
+ } else {
+ t_state.hdr_info.client_request.url_get()->host_set(ssl_vc->serverName, strlen(ssl_vc->serverName));
+ t_state.hdr_info.client_request.url_get()->port_set(t_state.state_machine->ua_session->get_netvc()->get_local_port());
+ }
+ }
+ }
+ // FALLTHROUGH
case HTTP_API_CONTINUE:
if ((cur_hook_id >= 0) && (cur_hook_id < TS_HTTP_LAST_HOOK)) {
if (!cur_hook) {
diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc
index 1053bec..54ba25f 100644
--- a/proxy/http/HttpTransact.cc
+++ b/proxy/http/HttpTransact.cc
@@ -542,34 +542,18 @@ HttpTransact::Forbidden(State *s)
void
HttpTransact::HandleBlindTunnel(State *s)
{
- NetVConnection *vc = s->state_machine->ua_session->get_netvc();
- bool inbound_transparent_p = vc->get_is_transparent();
URL u;
// IpEndpoint dest_addr;
// ip_text_buffer new_host;
DebugTxn("http_trans", "[HttpTransact::HandleBlindTunnel]");
- // We've received a request on a port which we blind forward
- // For logging purposes we create a fake request
- s->hdr_info.client_request.create(HTTP_TYPE_REQUEST);
- s->hdr_info.client_request.method_set(HTTP_METHOD_CONNECT, HTTP_LEN_CONNECT);
- s->hdr_info.client_request.url_create(&u);
- u.scheme_set(URL_SCHEME_TUNNEL, URL_LEN_TUNNEL);
- s->hdr_info.client_request.url_set(&u);
-
// We set the version to 0.9 because once we know where we are going
// this blind ssl tunnel is indistinguishable from a "CONNECT 0.9"
// except for the need to suppression error messages
HTTPVersion ver(0, 9);
s->hdr_info.client_request.version_set(ver);
- char new_host[INET6_ADDRSTRLEN];
- ats_ip_ntop(vc->get_local_addr(), new_host, sizeof(new_host));
-
- s->hdr_info.client_request.url_get()->host_set(new_host, strlen(new_host));
- s->hdr_info.client_request.url_get()->port_set(vc->get_local_port());
-
// Initialize the state vars necessary to sending error responses
bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
@@ -579,35 +563,7 @@ HttpTransact::HandleBlindTunnel(State *s)
DebugTxn("http_trans", "[HandleBlindTunnel] destination set to %.*s:%d", host_len, host,
s->hdr_info.client_request.url_get()->port_get());
}
- // Now we need to run the url remapping engine to find the where
- // this request really goes since we were sent was bound for
- // machine we are running on
-
- bool url_remap_success = false;
- char *remap_redirect = nullptr;
-
- if (s->transparent_passthrough) {
- url_remap_success = true;
- }
- // We must have mapping or we will self loop since the this
- // request was addressed to us to begin with. Remap directs
- // are something used in the normal reverse proxy and if we
- // get them here they indicate a very bad misconfiguration!
- if (!(inbound_transparent_p || url_remap_success) || remap_redirect != nullptr) {
- // The error message we send back will be suppressed so
- // the only important thing in selecting the error is what
- // status code it gets logged as
- build_error_response(s, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Port Forwarding Error", "default");
- int host_len;
- const char *host = s->hdr_info.client_request.url_get()->host_get(&host_len);
-
- Log::error("Forwarded port error: request with destination %.*s:%d "
- "does not have a mapping",
- host_len, host, s->hdr_info.client_request.url_get()->port_get());
-
- TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
- }
// Set the mode to tunnel so that we don't lookup the cache
s->current.mode = TUNNELLING_PROXY;
diff --git a/proxy/http/remap/RemapConfig.cc b/proxy/http/remap/RemapConfig.cc
index 1f889df..b32246f 100644
--- a/proxy/http/remap/RemapConfig.cc
+++ b/proxy/http/remap/RemapConfig.cc
@@ -1357,7 +1357,7 @@ remap_parse_config_bti(const char *path, BUILD_TABLE_INFO *bti)
ip_text_buffer ipb; // buffer for address string conversion.
if (0 == getaddrinfo(fromHost_lower, nullptr, nullptr, &ai_records)) {
for (addrinfo *ai_spot = ai_records; ai_spot; ai_spot = ai_spot->ai_next) {
- if (ats_is_ip(ai_spot->ai_addr) && !ats_is_ip_any(ai_spot->ai_addr)) {
+ if (ats_is_ip(ai_spot->ai_addr) && !ats_is_ip_any(ai_spot->ai_addr) && ai_spot->ai_protocol == IPPROTO_TCP) {
url_mapping *u_mapping;
ats_ip_ntop(ai_spot->ai_addr, ipb, sizeof ipb);
diff --git a/proxy/http/remap/RemapProcessor.cc b/proxy/http/remap/RemapProcessor.cc
index 767df83..8e76989 100644
--- a/proxy/http/remap/RemapProcessor.cc
+++ b/proxy/http/remap/RemapProcessor.cc
@@ -78,7 +78,6 @@ RemapProcessor::setup_for_remap(HttpTransact::State *s)
request_host = request_header->host_get(&request_host_len);
request_port = request_header->port_get();
proxy_request = request_header->is_target_in_url() || !s->reverse_proxy;
-
// Default to empty host.
if (!request_host) {
request_host = "";
diff --git a/tests/gold_tests/autest-site/min_cfg/ssl_SNI.config b/tests/gold_tests/autest-site/min_cfg/ssl_SNI.config
new file mode 100644
index 0000000..e69de29
diff --git a/tests/gold_tests/autest-site/trafficserver.test.ext b/tests/gold_tests/autest-site/trafficserver.test.ext
index b1a4e4f..d678def 100755
--- a/tests/gold_tests/autest-site/trafficserver.test.ext
+++ b/tests/gold_tests/autest-site/trafficserver.test.ext
@@ -243,6 +243,10 @@ def MakeATSProcess(obj, name, command='traffic_server', select_ports=True):
tmpname = os.path.join(config_dir, fname)
p.Disk.File(tmpname, id=make_id(fname), typename="ats:config")
+ fname = "ssl_server_name.config"
+ tmpname = os.path.join(config_dir, fname)
+ p.Disk.File(tmpname, id=make_id(fname), typename="ats:config")
+
fname = "storage.config"
tmpname = os.path.join(config_dir, fname)
p.Disk.File(tmpname, id=make_id(fname), typename="ats:config")
--
To stop receiving notification emails like this one, please contact
['"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>'].