You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by bc...@apache.org on 2018/12/13 22:40:57 UTC

[trafficserver] branch master updated: Make sslheaders plugin better conform to documentation.

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

bcall 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 7f63d16  Make sslheaders plugin better conform to documentation.
7f63d16 is described below

commit 7f63d1630a11cd324b8ebdd606d896a5b475c279
Author: Walter Karas <wk...@oath.com>
AuthorDate: Mon Dec 10 11:36:59 2018 -0600

    Make sslheaders plugin better conform to documentation.
---
 plugins/experimental/sslheaders/expand.cc          |  2 +-
 plugins/experimental/sslheaders/sslheaders.cc      | 66 +++++++++++++---
 tests/gold_tests/pluginTest/sslheaders/observer.py | 31 ++++++++
 .../pluginTest/sslheaders/ssl/server.key           | 28 +++++++
 .../pluginTest/sslheaders/ssl/server.pem           | 21 +++++
 .../pluginTest/sslheaders/sslheaders.gold          |  1 +
 .../pluginTest/sslheaders/sslheaders.test.py       | 91 ++++++++++++++++++++++
 7 files changed, 228 insertions(+), 12 deletions(-)

diff --git a/plugins/experimental/sslheaders/expand.cc b/plugins/experimental/sslheaders/expand.cc
index 5d99853..647bdbc 100644
--- a/plugins/experimental/sslheaders/expand.cc
+++ b/plugins/experimental/sslheaders/expand.cc
@@ -113,7 +113,7 @@ static const std::array<x509_expansion, SSL_HEADERS_FIELD_MAX> expansions = {{
   x509_expand_serial,      // SSL_HEADERS_FIELD_SERIAL
   x509_expand_signature,   // SSL_HEADERS_FIELD_SIGNATURE
   x509_expand_notbefore,   // SSL_HEADERS_FIELD_NOTBEFORE
-  x509_expand_notafter,    // SSL_HEADERS_FIELD_NOTBEFORE
+  x509_expand_notafter,    // SSL_HEADERS_FIELD_NOTAFTER
 }};
 
 bool
diff --git a/plugins/experimental/sslheaders/sslheaders.cc b/plugins/experimental/sslheaders/sslheaders.cc
index 6c6838d..657a9cd 100644
--- a/plugins/experimental/sslheaders/sslheaders.cc
+++ b/plugins/experimental/sslheaders/sslheaders.cc
@@ -116,6 +116,49 @@ SslHdrSetHeader(TSMBuffer mbuf, TSMLoc mhdr, const std::string &name, BIO *value
   }
 }
 
+namespace
+{
+template <bool IsClient> class WrapX509
+{
+public:
+  WrapX509(SSL *ssl) : _ssl(ssl), _x509(_nonNullInvalidValue()) {}
+
+  X509 *
+  get()
+  {
+    if (_x509 == _nonNullInvalidValue()) {
+      _set();
+    }
+
+    return _x509;
+  }
+
+  ~WrapX509()
+  {
+    if (IsClient && (_x509 != _nonNullInvalidValue()) && (_x509 != nullptr)) {
+      X509_free(_x509);
+    }
+  }
+
+private:
+  SSL *_ssl;
+  X509 *_x509;
+
+  // The address of this object can not be a valid X509 structure address.
+  X509 *
+  _nonNullInvalidValue() const
+  {
+    return reinterpret_cast<X509 *>(const_cast<WrapX509 *>(this));
+  }
+
+  void
+  _set()
+  {
+    _x509 = (IsClient ? SSL_get_peer_certificate : SSL_get_certificate)(_ssl);
+  }
+};
+} // end anonymous namespace
+
 // Process SSL header expansions. If this is not an SSL connection, then we need to delete the SSL headers
 // so that malicious clients cannot inject bogus information. Otherwise, we populate the header with the
 // expanded value. If the value expands to something empty, we nuke the header.
@@ -127,22 +170,28 @@ SslHdrExpand(SSL *ssl, const SslHdrInstance::expansion_list &expansions, TSMBuff
       SslHdrRemoveHeader(mbuf, mhdr, expansion.name);
     }
   } else {
+    WrapX509<true> clientX509(ssl);
+    WrapX509<false> serverX509(ssl);
     X509 *x509;
+
     BIO *exp = BIO_new(BIO_s_mem());
 
     for (const auto &expansion : expansions) {
       switch (expansion.scope) {
       case SSL_HEADERS_SCOPE_CLIENT:
-        x509 = SSL_get_peer_certificate(ssl);
+        x509 = clientX509.get();
+        if (x509 == nullptr) {
+          SslHdrRemoveHeader(mbuf, mhdr, expansion.name);
+          continue;
+        }
         break;
       case SSL_HEADERS_SCOPE_SERVER:
-        x509 = SSL_get_certificate(ssl);
+        x509 = serverX509.get();
+        if (x509 == nullptr) {
+          continue;
+        }
         break;
       default:
-        x509 = nullptr;
-      }
-
-      if (x509 == nullptr) {
         continue;
       }
 
@@ -152,11 +201,6 @@ SslHdrExpand(SSL *ssl, const SslHdrInstance::expansion_list &expansions, TSMBuff
       } else {
         SslHdrRemoveHeader(mbuf, mhdr, expansion.name);
       }
-
-      // Getting the peer certificate takes a reference count, but the server certificate doesn't.
-      if (x509 && expansion.scope == SSL_HEADERS_SCOPE_CLIENT) {
-        X509_free(x509);
-      }
     }
 
     BIO_free(exp);
diff --git a/tests/gold_tests/pluginTest/sslheaders/observer.py b/tests/gold_tests/pluginTest/sslheaders/observer.py
new file mode 100644
index 0000000..673a56e
--- /dev/null
+++ b/tests/gold_tests/pluginTest/sslheaders/observer.py
@@ -0,0 +1,31 @@
+'''
+Extract ssl-* headers and store in a log file for later verification.
+'''
+#  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.
+
+log = open('sslheaders.log', 'w')
+
+def observe(headers):
+
+    for h in headers.items():
+        if h[0].lower().startswith('ssl-'):
+
+            log.write(h[0] + ": " + h[1] + "\n");
+    log.write("-\n")
+    log.flush()
+
+Hooks.register(Hooks.ReadRequestHook, observe)
diff --git a/tests/gold_tests/pluginTest/sslheaders/ssl/server.key b/tests/gold_tests/pluginTest/sslheaders/ssl/server.key
new file mode 100644
index 0000000..9cdfc36
--- /dev/null
+++ b/tests/gold_tests/pluginTest/sslheaders/ssl/server.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCZkEXSlZ+ZFKFg
+CPpcDG39e73BuK6E5uE38q2PHh4DV0xcsJnIUx51viqLPwYughxfP0crHyBdXoHV
+dW/3WX4gpiGrdiM/dvCouheo0DPaqUUJ2nZKVYh2M57oyeiuJidlKb7BGkfw3HWP
+9TV7dVyGWok/cowjopqaLHJWxg/kh2KqvUBD0CHt9Kd1XvgXVmHwE7vCv0j5owv2
+MaExTsFb16uWmVLhl1gNHI2RqCX2yLaebH1DvtbLrit1XErjtaSYeJE9clVRaqT6
+vsvLOhyB5tA9WfZqfBYr/MHDeXQfrbIf+4Cp3aTpq5grc5InIMMH0eOk6/f/4tW+
+nq1lfszZAgMBAAECggEAYvYAqRbXRRVwca0Xel5gO2yU+tSDUw5esWlow8RK3yhR
+A6KjV9+Iz6P/UsEIwMwEcLUcrgNfHgybau5Fe4dmqq+lHxQA3xNNP869FIMoB4/x
+98mbVYgNau8VRztnAWOBG8ZtMZA4MFZCRMVm8+rL96E8tXCiMwzEyPo/rP/ymfhN
+3GRunX+GhfIA79AYNbd7HMVL+cvWWUGUF5Bc5i1wXcLy4I7b9NYtv920BeCLzSFK
+BypFB7ku/vKgTcBxe4yxThxPeXPwm4WFzGYKk/Afl1j8tVXCE2U4Y3yykfC0Qk6S
+ECZbCKLO2Rxi9fclIDZBHWuKejZhdjHfjeNvZ2vLoQKBgQDJzLmkVLvWAxgl1yvF
+U7gwqj/TzYqtVowbjEvTNEnPU1j/hIVI343SVV/EvJmif/iRUop6sRYfLsUjpMsH
+CmPysNKL3UtgSYOxLs+0xLhG4OOQRpPSf/uvl9YyWY9G3AqiC7ScthkQjEhZa4c1
+eycYy0jr42kX0OL9MuIH9q0ENQKBgQDCzvGKMs8r5E/Qd3YB9VYB60dx+6G83AHZ
+YqIelykObhCdxL9n4K+p4VKKLvgTcCOLYYIkBSWRJWR+ue3s3ey9+XWd2/q4Xvfh
+TCjAuO2ibMV+y5ClNlW0fQ/doIVWSDbjO2tZW1jh7YWZ4CtuVrsEisv1sk3KltMB
+MguhpTUylQKBgG6TfrncMFzxrx61C+gBmvEXqQffHfkjbnx94OKnSTaQ3jiNHhez
+X9v8KhD8o1bWtpay2uyl8pA9qYqBdzqxZ9kJKSW4qd/mCIJjOy87iBpWint5IPD8
+biZmldlbF9ZlJnJq5ZnlclCN/er5r8oPZHoCkj+nieOh8294nUBt25ptAoGAMnPA
+EIeaKgbmONpHgLhWPwb9KOL/f1cHT5KA5CVH58nPmdyTqcaCGCAX7Vu+ueIIApgN
+SWDf2thxT3S9zuOm5YiO0oRfSZKm5f2AbHE4ciFzgKQd4PvSdH0TN9XT0oW/WVhR
+NAI5YcHPIQvyk4/4vXNo4Uf9Z6NqIFwisQmFXoUCgYBK/ZI/HsFsvnR5MV0tFdGM
+AdNe6bsQRSZkowoaPxuWbklE4Hn6QvwEmQg3ST2O+vCQV1f1yI6YiWYoptOYscJc
+MSs/HxhhaaO5ZsiuPUO6WEPzpNb2CxuIGDDtl83VtUQyjxCmOb6pqqjwzFmZ2bsw
+JNMaBCzokrJTxknvauCuSQ==
+-----END PRIVATE KEY-----
diff --git a/tests/gold_tests/pluginTest/sslheaders/ssl/server.pem b/tests/gold_tests/pluginTest/sslheaders/ssl/server.pem
new file mode 100644
index 0000000..2b56cc8
--- /dev/null
+++ b/tests/gold_tests/pluginTest/sslheaders/ssl/server.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDZDCCAkygAwIBAgIJANod1+h9CtCaMA0GCSqGSIb3DQEBCwUAMEcxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJJTDEPMA0GA1UECgwGQXBhY2hlMRowGAYDVQQDDBFy
+YW5kb20uc2VydmVyLmNvbTAeFw0xODExMTkxNzEwMTlaFw0yODExMTYxNzEwMTla
+MEcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJJTDEPMA0GA1UECgwGQXBhY2hlMRow
+GAYDVQQDDBFyYW5kb20uc2VydmVyLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAJmQRdKVn5kUoWAI+lwMbf17vcG4roTm4TfyrY8eHgNXTFywmchT
+HnW+Kos/Bi6CHF8/RysfIF1egdV1b/dZfiCmIat2Iz928Ki6F6jQM9qpRQnadkpV
+iHYznujJ6K4mJ2UpvsEaR/DcdY/1NXt1XIZaiT9yjCOimposclbGD+SHYqq9QEPQ
+Ie30p3Ve+BdWYfATu8K/SPmjC/YxoTFOwVvXq5aZUuGXWA0cjZGoJfbItp5sfUO+
+1suuK3VcSuO1pJh4kT1yVVFqpPq+y8s6HIHm0D1Z9mp8Fiv8wcN5dB+tsh/7gKnd
+pOmrmCtzkicgwwfR46Tr9//i1b6erWV+zNkCAwEAAaNTMFEwHQYDVR0OBBYEFI2y
+qm0+UAChDAnLrAINeFOuyUlhMB8GA1UdIwQYMBaAFI2yqm0+UAChDAnLrAINeFOu
+yUlhMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAA3ZNFbqxcOX
+szS5A4EXCepyBJBFejEYy0CsvwQX/ai/pMrw5jqVeF0GAOTpBCVLddyY+ZV1arD2
+Pqi7Qwot9OxEZOzbCBiuMJGotruKgnWFQDHzJ9HA7KDQs270uNESAOG/xW9os9zN
+MXApzqfBSR5EIQU5L3RtaiPzoKdQenGQUOj86s0Kon7snDSUzaA2VcfstMWgGvXP
+JHtaVusULm0gry32cEap5G5UK+gII6DfLWgFwFGhHHmTz3mKjyGiJQ+09XBtu4lb
+ENE+HGRBBA49dUKSr3kwErO4HyHnS0YrsTDnbYURCsGUDma12oijX2sCos6Q4zn8
+3svaouRrucw=
+-----END CERTIFICATE-----
diff --git a/tests/gold_tests/pluginTest/sslheaders/sslheaders.gold b/tests/gold_tests/pluginTest/sslheaders/sslheaders.gold
new file mode 100644
index 0000000..39cdd0d
--- /dev/null
+++ b/tests/gold_tests/pluginTest/sslheaders/sslheaders.gold
@@ -0,0 +1 @@
+-
diff --git a/tests/gold_tests/pluginTest/sslheaders/sslheaders.test.py b/tests/gold_tests/pluginTest/sslheaders/sslheaders.test.py
new file mode 100644
index 0000000..26c3a43
--- /dev/null
+++ b/tests/gold_tests/pluginTest/sslheaders/sslheaders.test.py
@@ -0,0 +1,91 @@
+'''
+Test the sslheaders plugin.
+'''
+#  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.
+
+Test.Summary = '''
+Test sslheaders plugin.
+'''
+
+Test.SkipUnless(
+    Condition.HasATSFeature('TS_USE_TLS_ALPN'),
+    Condition.HasCurlFeature('http2'),
+)
+
+Test.Disk.File('sslheaders.log').Content = 'sslheaders.gold'
+
+server = Test.MakeOriginServer("server", options={'--load': Test.TestDirectory + '/observer.py'})
+
+request_header = {
+    "headers": "GET / HTTP/1.1\r\nHost: doesnotmatter\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)
+
+ts = Test.MakeATSProcess("ts", select_ports=False)
+
+ts.addSSLfile("ssl/server.pem")
+ts.addSSLfile("ssl/server.key")
+# ts.addSSLfile("ssl/signer.pem")
+
+ts.Variables.ssl_port = 4443
+
+ts.Disk.records_config.update({
+    'proxy.config.diags.debug.enabled': 0,
+    'proxy.config.diags.debug.tags': 'http',
+    'proxy.config.http.cache.http': 0,  # Make sure each request is forwarded to the origin server.
+    'proxy.config.proxy_name': 'Poxy_Proxy',  # This will be the server name.
+    '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.http.server_ports': (
+        'ipv4:{0} ipv4:{1}:proto=http2;http:ssl ipv6:{0} ipv6:{1}:proto=http2;http:ssl'
+        .format(ts.Variables.port, ts.Variables.ssl_port)),
+#    'proxy.config.ssl.client.verify.server':  0,
+#    '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.ssl.client.certification_level': 2,
+#    'proxy.config.ssl.CA.cert.filename': '{0}/signer.pem'.format(ts.Variables.SSLDir),
+#    'proxy.config.ssl.TLSv1_3': 0
+})
+
+ts.Disk.ssl_multicert_config.AddLine(
+    'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
+)
+
+ts.Disk.remap_config.AddLine(
+    'map http://bar.com http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+ts.Disk.remap_config.AddLine(
+    'map https://bar.com http://127.0.0.1:{0}'.format(server.Variables.Port)
+)
+
+ts.Disk.ssl_server_name_yaml.AddLines([
+    '- fqdn: "*bar.com"',
+    '  verify_client: STRICT',
+])
+
+ts.Disk.plugin_config.AddLine(
+    'sslheaders.so SSL-Client-ID=client.subject'
+)
+
+tr = Test.AddTestRun()
+tr.Processes.Default.StartBefore(server)
+tr.Processes.Default.StartBefore(Test.Processes.ts, ready=When.PortOpen(ts.Variables.ssl_port))
+tr.Processes.Default.Command = (
+    'curl -H "SSL-Client-ID: My Fake Client ID" --verbose --ipv4 --insecure --header "Host: bar.com"' +
+    ' https://localhost:{}'.format(ts.Variables.ssl_port)
+)
+tr.Processes.Default.ReturnCode = 0