This is an automated email from the ASF dual-hosted git repository. paziz 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 b61b82c SNI based IP_ALLOW b61b82c is described below commit b61b82cdfb1ad1cd9c8e03bc3363396aea034123 Author: Persia Aziz <per...@yahoo-inc.com> AuthorDate: Thu Apr 5 17:43:42 2018 -0500 SNI based IP_ALLOW --- configs/ssl_server_name.yaml.default | 4 ++- iocore/net/P_SNIActionPerformer.h | 61 +++++++++++++++++++++++++++++++++--- iocore/net/SNIActionPerformer.cc | 8 +++-- iocore/net/SSLSNIConfig.cc | 2 ++ iocore/net/SSLUtils.cc | 6 +++- iocore/net/YamlSNIConfig.cc | 10 ++++-- iocore/net/YamlSNIConfig.h | 2 ++ 7 files changed, 83 insertions(+), 10 deletions(-) diff --git a/configs/ssl_server_name.yaml.default b/configs/ssl_server_name.yaml.default index ed2c956..60bb6ea 100644 --- a/configs/ssl_server_name.yaml.default +++ b/configs/ssl_server_name.yaml.default @@ -13,6 +13,8 @@ # 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 +# ip_allow - lists or range of client IP addresses, subnets that are allowed for this connection. This accepts CIDR format +# for subnet specification. # # Example: # @@ -23,4 +25,4 @@ # verify_client: MODERATE # - fqdn: two.com # tunnel_route: two.com -# +# ip_allow = '10.0.0.1-10.0.0.255' diff --git a/iocore/net/P_SNIActionPerformer.h b/iocore/net/P_SNIActionPerformer.h index f876a70..c40091e 100644 --- a/iocore/net/P_SNIActionPerformer.h +++ b/iocore/net/P_SNIActionPerformer.h @@ -35,6 +35,7 @@ //#include"P_UnixNetProcessor.h" #include <vector> #include "P_SSLNextProtocolAccept.h" +#include "ts/ink_inet.h" extern Map<int, SSLNextProtocolSet *> snpsMap; // enum of all the actions @@ -50,7 +51,7 @@ enum PropertyActions { TS_VERIFY_SERVER = 200, TS_CLIENT_CERT }; class ActionItem { public: - virtual void SNIAction(Continuation *cont) = 0; + virtual int SNIAction(Continuation *cont) = 0; virtual ~ActionItem(){}; }; @@ -60,7 +61,7 @@ public: DisableH2() {} ~DisableH2() override {} - void + int SNIAction(Continuation *cont) override { auto ssl_vc = reinterpret_cast<SSLNetVConnection *>(cont); @@ -69,6 +70,7 @@ public: auto nps = snpsMap.get(accept_obj->id); ssl_vc->registerNextProtocolSet(reinterpret_cast<SSLNextProtocolSet *>(nps)); } + return SSL_TLSEXT_ERR_OK; } }; @@ -80,12 +82,63 @@ public: VerifyClient(const char *param) : mode(atoi(param)) {} VerifyClient(uint8_t param) : mode(param) {} ~VerifyClient() override {} - void + int 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); + return SSL_TLSEXT_ERR_OK; + } +}; + +class SNI_IpAllow : public ActionItem +{ + IpMap ip_map; + +public: + SNI_IpAllow(std::string const &ip_allow_list, cchar *servername) + { + // the server identified by item.fqdn requires ATS to do IP filtering + if (ip_allow_list.length()) { + IpAddr addr1; + IpAddr addr2; + // check format first + // check if the input is a comma separated list of IPs + ts::TextView content(ip_allow_list); + while (!content.empty()) { + ts::TextView list{content.take_prefix_at(',')}; + if (0 != ats_ip_range_parse(list, addr1, addr2)) { + Debug("ssl_sni", "%.*s is not a valid format", static_cast<int>(list.size()), list.data()); + break; + } else { + Debug("ssl_sni", "%.*s added to the ip_allow list %s", static_cast<int>(list.size()), list.data(), servername); + ip_map.fill(IpEndpoint().assign(addr1), IpEndpoint().assign(addr2), reinterpret_cast<void *>(1)); + } + } + } + } // end function SNI_IpAllow + + int + SNIAction(Continuation *cont) override + { + // i.e, ip filtering is not required + if (ip_map.getCount() == 0) { + return SSL_TLSEXT_ERR_OK; + } + + auto ssl_vc = reinterpret_cast<SSLNetVConnection *>(cont); + auto ip = ssl_vc->get_remote_endpoint(); + + // check the allowed ips + if (ip_map.contains(ip)) { + return SSL_TLSEXT_ERR_OK; + } else { + char buff[256]; + ats_ip_ntop(&ip.sa, buff, sizeof(buff)); + Debug("ssl_sni", "%s is not allowed. Denying connection", buff); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } } }; @@ -93,5 +146,5 @@ class SNIActionPerformer { public: SNIActionPerformer() = default; - static void PerformAction(Continuation *cont, cchar *servername); + static int PerformAction(Continuation *cont, cchar *servername); }; diff --git a/iocore/net/SNIActionPerformer.cc b/iocore/net/SNIActionPerformer.cc index 710a7b5..0561096 100644 --- a/iocore/net/SNIActionPerformer.cc +++ b/iocore/net/SNIActionPerformer.cc @@ -37,7 +37,7 @@ extern Map<int, SSLNextProtocolSet *> snpsMap; -void +int SNIActionPerformer::PerformAction(Continuation *cont, cchar *servername) { SNIConfig::scoped_config params; @@ -47,8 +47,12 @@ SNIActionPerformer::PerformAction(Continuation *cont, cchar *servername) } else { for (auto it : *actionvec) { if (it) { - it->SNIAction(cont); + auto ret = it->SNIAction(cont); + if (ret != SSL_TLSEXT_ERR_OK) { + return ret; + } } } } + return SSL_TLSEXT_ERR_OK; } diff --git a/iocore/net/SSLSNIConfig.cc b/iocore/net/SSLSNIConfig.cc index a735444..71ff860 100644 --- a/iocore/net/SSLSNIConfig.cc +++ b/iocore/net/SSLSNIConfig.cc @@ -82,6 +82,8 @@ SNIConfigParams::loadSNIConfig() TunnelMap.emplace(item.fqdn.data(), item.tunnel_destination); } + auto ai3 = new SNI_IpAllow(item.ip_allow, servername); + aiVec->push_back(ai3); // set the next hop properties SSLConfig::scoped_config params; auto clientCTX = params->getCTX(servername); diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc index 54b56a0..e383c26 100644 --- a/iocore/net/SSLUtils.cc +++ b/iocore/net/SSLUtils.cc @@ -448,12 +448,16 @@ extern SNIActionPerformer sni_action_performer; static int ssl_servername_only_callback(SSL *ssl, int * /* ad */, void * /*arg*/) { + int ret = SSL_TLSEXT_ERR_OK; SSLNetVConnection *netvc = SSLNetVCAccess(ssl); const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); Debug("ssl", "Requested servername is %s", servername); if (servername != nullptr) { - sni_action_performer.PerformAction(netvc, servername); + ret = sni_action_performer.PerformAction(netvc, servername); } + if (ret != SSL_TLSEXT_ERR_OK) + return SSL_TLSEXT_ERR_ALERT_FATAL; + netvc->callHooks(TS_EVENT_SSL_SERVERNAME); return SSL_TLSEXT_ERR_OK; } diff --git a/iocore/net/YamlSNIConfig.cc b/iocore/net/YamlSNIConfig.cc index 5ab7ad2..5d20ccd 100644 --- a/iocore/net/YamlSNIConfig.cc +++ b/iocore/net/YamlSNIConfig.cc @@ -52,8 +52,8 @@ YamlSNIConfig::loader(const char *cfgFilename) TsEnumDescriptor LEVEL_DESCRIPTOR = {{{"NONE", 0}, {"MODERATE", 1}, {"STRICT", 2}}}; -std::set<std::string> valid_sni_config_keys = {TS_fqdn, TS_disable_H2, TS_verify_client, - TS_tunnel_route, TS_verify_origin_server, TS_client_cert}; +std::set<std::string> valid_sni_config_keys = { + TS_fqdn, TS_disable_H2, TS_verify_client, TS_tunnel_route, TS_verify_origin_server, TS_client_cert, TS_ip_allow}; namespace YAML { @@ -70,6 +70,8 @@ template <> struct convert<YamlSNIConfig::Item> { if (node[TS_fqdn]) { item.fqdn = node[TS_fqdn].as<std::string>(); + } else { + return false; // servername must be present } if (node[TS_disable_H2]) { item.fqdn = node[TS_disable_H2].as<bool>(); @@ -101,6 +103,10 @@ template <> struct convert<YamlSNIConfig::Item> { if (node[TS_client_cert]) { item.client_cert = node[TS_client_cert].as<std::string>(); } + + if (node[TS_ip_allow]) { + item.ip_allow = node[TS_ip_allow].as<std::string>(); + } return true; } }; diff --git a/iocore/net/YamlSNIConfig.h b/iocore/net/YamlSNIConfig.h index e3fcd11..cb57d5b 100644 --- a/iocore/net/YamlSNIConfig.h +++ b/iocore/net/YamlSNIConfig.h @@ -32,6 +32,7 @@ constexpr char TS_verify_client[] = "verify_client"; constexpr char TS_tunnel_route[] = "tunnel_route"; constexpr char TS_verify_origin_server[] = "verify_origin_server"; constexpr char TS_client_cert[] = "client_cert"; +constexpr char TS_ip_allow[] = "ip_allow"; const int start = 0; struct YamlSNIConfig { @@ -54,6 +55,7 @@ struct YamlSNIConfig { std::string tunnel_destination; uint8_t verify_origin_server = 0; std::string client_cert; + std::string ip_allow; }; ts::Errata loader(const char *cfgFilename);