diff --git a/doc/admin-guide/plugins/traffic_dump.en.rst b/doc/admin-guide/plugins/traffic_dump.en.rst index eece21518cd..76bedf3bbc7 100644 --- a/doc/admin-guide/plugins/traffic_dump.en.rst +++ b/doc/admin-guide/plugins/traffic_dump.en.rst @@ -50,6 +50,10 @@ Plugin Configuration (`optional`) - a comma seperatated list of HTTP case-insensitive field names whose values are considered sensitive information. Traffic Dump will not dump the incoming field values for any of these fields but will instead dump a generic value for them of the same length as the original. If this option is not used, a default list of "Cookie,Set-Cookie" is used. Providing this option overwrites that default list with whatever values the user provides. Pass a quoted empty string as the argument to specify that no fields are sensitive, + .. option:: --sni-filter + + (`optional`) - an SNI with which to filter sessions. Only HTTPS sessions with the provided SNI will be dumped. The sample option will apply a sampling rate to these filtered sessions. Thus, with a sample value of 2, 1/2 of all sessions with the specified SNI will be dumped. + ``traffic_ctl`` * ``traffic_ctl plugin msg traffic_dump.sample N`` - changes the sampling ratio N as mentioned above. * ``traffic_ctl plugin msg traffic_dump.reset`` - resets the disk usage counter. diff --git a/plugins/experimental/traffic_dump/traffic_dump.cc b/plugins/experimental/traffic_dump/traffic_dump.cc index cfe5b3673f9..34ca15d8e74 100644 --- a/plugins/experimental/traffic_dump/traffic_dump.cc +++ b/plugins/experimental/traffic_dump/traffic_dump.cc @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -49,6 +50,8 @@ namespace { const char *PLUGIN_NAME = "traffic_dump"; const std::string closing = "]}]}"; +uint64_t session_counter = 0; + std::string defaut_sensitive_field_value; // A case-insensitive comparitor used for comparing HTTP field names. @@ -87,6 +90,7 @@ std::unordered_set default_s std::unordered_set sensitive_fields; ts::file::path log_path{"dump"}; // default log directory +std::string sni_filter; // The SNI requested for filtering against. int s_arg_idx = 0; // Session Arg Index to pass on session data std::atomic sample_pool_size(1000); // Sampling ratio std::atomic max_disk_usage(10000000); //< Max disk space for logs (approximate) @@ -597,9 +601,29 @@ global_ssn_handler(TSCont contp, TSEvent event, void *edata) return TS_SUCCESS; } case TS_EVENT_HTTP_SSN_START: { - // Grab session id to do sampling + // Grab session id for logging against a global value rather than the local + // session_counter. int64_t id = TSHttpSsnIdGet(ssnp); - if (id % sample_pool_size != 0) { + + // If the user has asked for SNI filtering, filter on that first because + // any sampling will apply just to that subset of connections that match + // that SNI. + if (!sni_filter.empty()) { + TSVConn ssn_vc = TSHttpSsnClientVConnGet(ssnp); + TSSslConnection ssl_conn = TSVConnSslConnectionGet(ssn_vc); + SSL *ssl_obj = (SSL *)ssl_conn; + if (ssl_obj == nullptr) { + TSDebug(PLUGIN_NAME, "global_ssn_handler(): Ignore non-HTTPS session %" PRId64 "...", id); + break; + } + const std::string sni = SSL_get_servername(ssl_obj, TLSEXT_NAMETYPE_host_name); + if (sni != sni_filter) { + TSDebug(PLUGIN_NAME, "global_ssn_handler(): Ignore HTTPS session with non-filtered SNI: %s", sni.c_str()); + break; + } + } + const auto this_session_count = session_counter++; + if (this_session_count % sample_pool_size != 0) { TSDebug(PLUGIN_NAME, "global_ssn_handler(): Ignore session %" PRId64 "...", id); break; } else if (disk_usage >= max_disk_usage) { @@ -632,10 +656,10 @@ global_ssn_handler(TSCont contp, TSEvent event, void *edata) std::string beginning = R"({"meta":{"version":"1.0"},"sessions":[{"protocol":[)" + result + "]" + R"(,"connection-time":)" + std::to_string(start.count()) + R"(,"transactions":[)"; - // Grab session id and use its hex string as fname + // Use the session count's hex string as the filename. std::stringstream stream; - stream << std::setw(16) << std::setfill('0') << std::hex << id; - std::string session_id = stream.str(); + stream << std::setw(16) << std::setfill('0') << std::hex << this_session_count; + std::string session_hex_name = stream.str(); // Use client ip as sub directory name char client_str[INET6_ADDRSTRLEN]; @@ -653,7 +677,7 @@ global_ssn_handler(TSCont contp, TSEvent event, void *edata) TSMutexLock(ssnData->disk_io_mutex); if (ssnData->log_fd < 0) { ts::file::path log_p = log_path / ts::file::path(std::string(client_str, 3)); - ts::file::path log_f = log_p / ts::file::path(session_id); + ts::file::path log_f = log_p / ts::file::path(session_hex_name); // Create subdir if not existing std::error_code ec; @@ -682,7 +706,7 @@ global_ssn_handler(TSCont contp, TSEvent event, void *edata) break; } case TS_EVENT_HTTP_SSN_CLOSE: { - // Write session and log file closing + // Write session and close the log file. int64_t id = TSHttpSsnIdGet(ssnp); TSDebug(PLUGIN_NAME, "global_ssn_handler(): Closing session %" PRId64 "...", id); // Retrieve SsnData @@ -721,12 +745,11 @@ TSPluginInit(int argc, const char *argv[]) bool sensitive_fields_were_specified = false; /// Commandline options - static const struct option longopts[] = {{"logdir", required_argument, nullptr, 'l'}, - {"sample", required_argument, nullptr, 's'}, - {"limit", required_argument, nullptr, 'm'}, - {"sensitive-fields", required_argument, nullptr, 'f'}, - {nullptr, no_argument, nullptr, 0}}; - int opt = 0; + static const struct option longopts[] = { + {"logdir", required_argument, nullptr, 'l'}, {"sample", required_argument, nullptr, 's'}, + {"limit", required_argument, nullptr, 'm'}, {"sensitive-fields", required_argument, nullptr, 'f'}, + {"sni-filter", required_argument, nullptr, 'n'}, {nullptr, no_argument, nullptr, 0}}; + int opt = 0; while (opt >= 0) { opt = getopt_long(argc, const_cast(argv), "l:", longopts, nullptr); switch (opt) { @@ -751,6 +774,12 @@ TSPluginInit(int argc, const char *argv[]) } break; } + case 'n': { + // --sni-filter is used to filter sessions based upon an SNI. + sni_filter = std::string(optarg); + TSDebug(PLUGIN_NAME, "Filtering to only dump connections with SNI: %s", sni_filter.c_str()); + break; + } case 'l': { log_path = ts::file::path{optarg}; break; diff --git a/tests/gold_tests/pluginTest/traffic_dump/gold/200_sni_bob.gold b/tests/gold_tests/pluginTest/traffic_dump/gold/200_sni_bob.gold new file mode 100644 index 00000000000..1fb2a8ffe61 --- /dev/null +++ b/tests/gold_tests/pluginTest/traffic_dump/gold/200_sni_bob.gold @@ -0,0 +1,11 @@ +`` +* Hostname bob was found in DNS cache +`` +> GET / HTTP/2 +> Host: bob +`` +< HTTP/2 200 +< content-length: 0 +`` +< server: ATS/10.0.0 +`` diff --git a/tests/gold_tests/pluginTest/traffic_dump/gold/200_sni_dave.gold b/tests/gold_tests/pluginTest/traffic_dump/gold/200_sni_dave.gold new file mode 100644 index 00000000000..55211bc34f8 --- /dev/null +++ b/tests/gold_tests/pluginTest/traffic_dump/gold/200_sni_dave.gold @@ -0,0 +1,12 @@ +`` +* Hostname dave was found in DNS cache +`` +> GET / HTTP/2 +> Host: dave +`` +< HTTP/2 200 +< content-length: 0 +`` +< server: ATS/10.0.0 +`` + diff --git a/tests/gold_tests/pluginTest/traffic_dump/ssl/server.key b/tests/gold_tests/pluginTest/traffic_dump/ssl/server.key new file mode 100644 index 00000000000..9cdfc36aa5f --- /dev/null +++ b/tests/gold_tests/pluginTest/traffic_dump/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/traffic_dump/ssl/server.pem b/tests/gold_tests/pluginTest/traffic_dump/ssl/server.pem new file mode 100644 index 00000000000..2b56cc83ea2 --- /dev/null +++ b/tests/gold_tests/pluginTest/traffic_dump/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/traffic_dump/ssl/signed-foo.key b/tests/gold_tests/pluginTest/traffic_dump/ssl/signed-foo.key new file mode 100644 index 00000000000..e2c7066f679 --- /dev/null +++ b/tests/gold_tests/pluginTest/traffic_dump/ssl/signed-foo.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJheOsr0o4C0QV +elo8viYl/IjWvkj4Y6YojnO/E9T5HSfWxkRikRT8X7yqs9ClrjNcYoPNNQP5vgl1 +qAIeixUrbZZklcZ3kHF/MXdzuYV//uT3YfWrsOTdfbcu6adHtBr/Qc0w9GSbBpp4 +2ZALxzjZ2hZZq51XmKvcz55/gm/9YvlGErzvsPzkejdH6U1GKeLqGaZOqkV7N2J4 +zKmJyiPzwrmR/8y7k+9jDHQX+A2wa5gaiAMGdIM8aTV+XsNNAvespvXyX7UhwxDs +w0er0GLv4ssYCru5hF+tUcjeOCHxSnCoEC45b2zDZHATLLjr7AIJ52TcSo1kTaTi +rAOAiHRLAgMBAAECggEAB7fXBnAYOZlE3EW5WwY1U9MeMotLJCg83uTFzhWmXHwf +YHxrdhL0aM4J3cfRP+cyFGG5hox3QINkvVrX6e+NugISdnu+BCpGDocIeigq0sIi +Zs8bp524xjrgXy2XuIlPV2NfxnY1vDI+jE5Y0/qnVMCjhn+qIQa53lUdTujh/SRR +3U7di+QMK4mdGwRnInos++ENy33A+2LqtUK8i0ERkzPFa1yMQEE4DOFPzZcW+jhK +arvzBwPIn37PZmL5oyiQB1YiGPGt4XNfPBwACTMYM8LlYBfEBHG77k3bMtUf0WqE +GctoT5SIe5+YbyrWkpfHgoKPxggH3I3TrFnVvqrKQQKBgQDmcV1YbuNEQLeif521 +iGqMgPQYmnpO6k27RsZrM9ikhIgm9bVJsOqnaYzQFeSfJ3eNLXYUL6IF8g46xddw +fDBtrEjDAA9OUkNRcizbeKF+GJRMtX11d4ZNbnG1wyMZYkArZGfraZBLHPEF1pya +2iFdVfokQCBpLmX7BMQEPePyuwKBgQDf33H9njf9oO0l9GfuWDvSoaV8GwqV9x55 +sFjggQYD/xqwEprrzr524X5Y2ZiTUpBu+kqqM8GYfm3bzBKkZU1rnjwxADUwBw8U +L2U/Z7Id3om8tAdzHOSI9d7mxWA8uTsScMm0IFv2l/XBQo1+AAJSD03pcsabr4Lf +SuJGmoFTsQKBgQDVzPASEC+DL5gwh75Gop5YZXwTJ5+6f+BGlM+avquNV/kKTIU6 +LY5IbMFcfjNzBicBMOCQsfDdG0rgdJYBovc7idCoOvH4dJJIimnb5fvPBfbxhKE1 +zwMn7ARL4xQ5hNKMb8eKvpJFXkCwbgE2GpNCCXbfEy/+5jFvx2gll1ZZ6QKBgG3J +OzJ/w796irHBQLKOzI+HvAq3jCJs9KICjCNUwql1EhZkmVqooZjVDkvuMbeVlsUF +s1XyWa852RAf7Mh38VakW6pACtVJsOhaMdG9PYkOWAeVVc3qzlwoDy6mfoJo6AIs +E45lDBRLAzbKN28h/AFYBgJEygcRNCHirEKphGCRAoGAEhcaxbmMo2fHBYuvOR1Q +ZAIq1EPvysDROUBHhdTJqN1wHsuJsmVJxX42+YHcZdjtgeCdjU3HMoyCnTaRxDee +K3VeB4PobN1WpQwFklFoqcvAhW6eicdZXme7ktK120NPQsXrmjgN6Lfg3PNjosn0 +tqSxQhQ4DrSf60fxx0/M/rw= +-----END PRIVATE KEY----- diff --git a/tests/gold_tests/pluginTest/traffic_dump/ssl/signed-foo.pem b/tests/gold_tests/pluginTest/traffic_dump/ssl/signed-foo.pem new file mode 100644 index 00000000000..6f6aecf53d7 --- /dev/null +++ b/tests/gold_tests/pluginTest/traffic_dump/ssl/signed-foo.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCzCCAnQCCQC81MtBCwmQtzANBgkqhkiG9w0BAQsFADCBnTELMAkGA1UEBhMC +VVMxCzAJBgNVBAgTAklMMRIwEAYDVQQHEwlDaGFtcGFpZ24xDjAMBgNVBAoTBVlh +aG9vMQ0wCwYDVQQLEwRFZGdlMSgwJgYDVQQDEx9qdWljZXByb2R1Y2UuY29ycC5u +ZTEueWFob28uY29tMSQwIgYJKoZIhvcNAQkBFhVwZXJzaWEuYXppekB5YWhvby5j +b20wHhcNMTgxMDE1MTU1NjMzWhcNMjgxMDEyMTU1NjMzWjByMQswCQYDVQQGEwJV +UzELMAkGA1UECAwCSUwxEjAQBgNVBAcMCUNoYW1wYWlnbjEQMA4GA1UECgwHRXhh +bXBsZTEQMA4GA1UEAwwHZm9vLmNvbTEeMBwGCSqGSIb3DQEJARYPYm9iQGV4YW1w +bGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyYXjrK9KOAtE +FXpaPL4mJfyI1r5I+GOmKI5zvxPU+R0n1sZEYpEU/F+8qrPQpa4zXGKDzTUD+b4J +dagCHosVK22WZJXGd5BxfzF3c7mFf/7k92H1q7Dk3X23LumnR7Qa/0HNMPRkmwaa +eNmQC8c42doWWaudV5ir3M+ef4Jv/WL5RhK877D85Ho3R+lNRini6hmmTqpFezdi +eMypicoj88K5kf/Mu5PvYwx0F/gNsGuYGogDBnSDPGk1fl7DTQL3rKb18l+1IcMQ +7MNHq9Bi7+LLGAq7uYRfrVHI3jgh8UpwqBAuOW9sw2RwEyy46+wCCedk3EqNZE2k +4qwDgIh0SwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBALr2gm+KgveEcTXwURM0wxJC +m0yOR8w6MX8fxHKaekhJH1U84G64Ub0gbn2beOdLBQkG+4czLiOOOgyeukPaJJ81 +od2ooE7DrGUPGnbHYxW/70EtVF5nQEctcqpKNF/d04mVKrqI90919MJSxJ5KedHK +2H11+gUPwDWy/mAwJzEJ +-----END CERTIFICATE----- diff --git a/tests/gold_tests/pluginTest/traffic_dump/ssl/signer.pem b/tests/gold_tests/pluginTest/traffic_dump/ssl/signer.pem new file mode 100644 index 00000000000..111cd079718 --- /dev/null +++ b/tests/gold_tests/pluginTest/traffic_dump/ssl/signer.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICszCCAhwCCQD4jSkztmlO1TANBgkqhkiG9w0BAQsFADCBnTELMAkGA1UEBhMC +VVMxCzAJBgNVBAgTAklMMRIwEAYDVQQHEwlDaGFtcGFpZ24xDjAMBgNVBAoTBVlh +aG9vMQ0wCwYDVQQLEwRFZGdlMSgwJgYDVQQDEx9qdWljZXByb2R1Y2UuY29ycC5u +ZTEueWFob28uY29tMSQwIgYJKoZIhvcNAQkBFhVwZXJzaWEuYXppekB5YWhvby5j +b20wHhcNMTcwODI4MDM0NDQ1WhcNMjcwODI2MDM0NDQ1WjCBnTELMAkGA1UEBhMC +VVMxCzAJBgNVBAgTAklMMRIwEAYDVQQHEwlDaGFtcGFpZ24xDjAMBgNVBAoTBVlh +aG9vMQ0wCwYDVQQLEwRFZGdlMSgwJgYDVQQDEx9qdWljZXByb2R1Y2UuY29ycC5u +ZTEueWFob28uY29tMSQwIgYJKoZIhvcNAQkBFhVwZXJzaWEuYXppekB5YWhvby5j +b20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANYwc6JQX45GZmMDEjwxYT11 +uVvuBBInfpYJeU8WIXHrKcX5LUSRcBikiKnlfSnMNRohsu6TElQACc60wQ7Q8KDE +lBSsS1FaHzCIl1t1AkXRmz/1H65JSBvrV/6Z1NC+Gp58EbH7Gul8ByC1xaJm5ID1 +Dd++kOPlY5ZI9ZcFS7HLAgMBAAEwDQYJKoZIhvcNAQELBQADgYEATX7975NdhIbJ +glda+sXI9a86GgOpiuKO+vKubRJQZA+UlPf2vHEONjC2+7Y1aZvZYaKYL74vxGky +zkgp6ANSPl45lqD632x0e1Z7vzW5TkqK1JB2/xH2WgDcQZmP0FuQHzVNs4GjghDr +HCp1+sQDhfPB4aLmLFeyN0TkhdH1N3M= +-----END CERTIFICATE----- diff --git a/tests/gold_tests/pluginTest/traffic_dump/traffic_dump_sni_filter.test.py b/tests/gold_tests/pluginTest/traffic_dump/traffic_dump_sni_filter.test.py new file mode 100644 index 00000000000..9b020659dfd --- /dev/null +++ b/tests/gold_tests/pluginTest/traffic_dump/traffic_dump_sni_filter.test.py @@ -0,0 +1,144 @@ +""" +Verify traffic_dump functionality. +""" +# 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 = ''' +Verify traffic_dump functionality. +''' + +Test.SkipUnless( + Condition.PluginExists('traffic_dump.so'), +) + +# Configure the origin server. +server = Test.MakeOriginServer("server") + +request_header = {"headers": "GET / HTTP/1.1\r\n" + "Host: www.example.com\r\nContent-Length: 0\r\n\r\n", + "timestamp": "1469733493.993", "body": ""} +response_header = {"headers": "HTTP/1.1 200 OK" + "\r\nConnection: close\r\nContent-Length: 0" + "\r\nSet-Cookie: classified_not_for_logging\r\n\r\n", + "timestamp": "1469733493.993", "body": ""} +server.addResponse("sessionfile.log", request_header, response_header) + +# Define ATS and configure +ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True) +replay_dir = os.path.join(ts.RunDirectory, "ts", "log") + +ts.addSSLfile("ssl/server.pem") +ts.addSSLfile("ssl/server.key") +ts.addSSLfile("ssl/signer.pem") + +ts.Disk.records_config.update({ + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'traffic_dump', + + '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.client.verify.server': 0, + 'proxy.config.url_remap.pristine_host_hdr': 1, + 'proxy.config.ssl.CA.cert.filename': '{0}/signer.pem'.format(ts.Variables.SSLDir), + 'proxy.config.exec_thread.autoconfig.scale': 1.0, + 'proxy.config.http.host_sni_policy': 2, + '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://127.0.0.1:{0}'.format(server.Variables.Port) +) + +ts.Disk.sni_yaml.AddLines([ + 'sni:', + '- fqdn: boblite', + ' verify_client: STRICT', + ' host_sni_policy: PERMISSIVE', + '- fqdn: bob', + ' verify_client: STRICT', +]) + +# Configure traffic_dump to filter to only dump with connections with SNI bob. +sni_filter = "bob" +ts.Disk.plugin_config.AddLine( + 'traffic_dump.so --logdir {0} --sample 1 --limit 1000000000 ' + '--sni-filter "{1}"'.format(replay_dir, sni_filter) +) + +# Set up trafficserver expectations. +ts.Streams.stderr += Testers.ContainsExpression( + "Filtering to only dump connections with SNI: {}".format(sni_filter), + "Verify filtering for the expected SNI.") + +ts.Streams.stderr += Testers.ContainsExpression( + "Ignore HTTPS session with non-filtered SNI: dave", + "Verify that the non-desired SNI session was filtered out.") + +# Set up the json replay file expectations. +replay_file_session_1 = os.path.join(replay_dir, "127", "0000000000000000") +ts.Disk.File(replay_file_session_1, exists=True) + +# The second session should be filtered out because it doesn't have the +# expected SNI (note exists is set to False). +replay_file_session_2 = os.path.join(replay_dir, "127", "0000000000000001") +ts.Disk.File(replay_file_session_2, exists=False) + +# +# Test 1: Verify dumping a session with the desired SNI and not dumping +# the session with the other SNI. +# + +# Execute the first transaction. +tr = Test.AddTestRun("Verify dumping of a session with the filtered SNI") +tr.Setup.Copy("ssl/signed-foo.pem") +tr.Setup.Copy("ssl/signed-foo.key") +tr.Processes.Default.StartBefore(server, ready=When.PortOpen(server.Variables.Port)) +tr.Processes.Default.StartBefore(Test.Processes.ts) +tr.Processes.Default.Command = \ + ('curl --tls-max 1.2 -k -H"Host: bob" --resolve "bob:{0}:127.0.0.1" ' + '--cert ./signed-foo.pem --key ./signed-foo.key --verbose https://bob:{0}'.format(ts.Variables.ssl_port)) +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Streams.stderr = "gold/200_sni_bob.gold" +tr.StillRunningAfter = server +tr.StillRunningAfter = ts + +# Execute the second transaction. +tr = Test.AddTestRun("Verify that a session of a different SNI is not dumped.") +tr.Processes.Default.Command = \ + ('curl --tls-max 1.2 -k -H"Host: dave" --resolve "dave:{0}:127.0.0.1" ' + '--cert ./signed-foo.pem --key ./signed-foo.key --verbose https://dave:{0}'.format(ts.Variables.ssl_port)) +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Streams.stderr = "gold/200_sni_dave.gold" +tr.StillRunningAfter = server +tr.StillRunningAfter = ts + +# Verify the properties of the replay file for the dumped transaction. +tr = Test.AddTestRun("Verify the json content of the first session") +verify_replay = "verify_replay.py" +tr.Setup.CopyAs(verify_replay, Test.RunDirectory) +tr.Processes.Default.Command = "python3 {0} {1} {2}".format( + verify_replay, + os.path.join(Test.Variables.AtsTestToolsDir, 'lib', 'replay_schema.json'), + replay_file_session_1) +tr.Processes.Default.ReturnCode = 0 +tr.StillRunningAfter = server +tr.StillRunningAfter = ts