Merge remote-tracking branch 'origin/master' into topic/johanna/tls12-decryption

This commit is contained in:
Johanna Amann 2021-10-13 10:49:29 +01:00
commit b8b6ac744e
1531 changed files with 109968 additions and 46436 deletions

View file

@ -1 +1,4 @@
@load ./main
@load ./certificate-event-cache
@load ./log-ocsp

View file

@ -0,0 +1,112 @@
##! This script sets up the certificate event cache handling of Zeek.
##!
##! The Zeek core provided a method to skip certificate processing for known certificates.
##! For more details about this functionality, see :zeek:see:`x509_set_certificate_cache`.
##!
##! This script uses this feature to lower the amount of processing that has to be performed
##! by Zeek by caching all certificate events for common certificates. For these certificates,
##! the parsing of certificate information in the core is disabled. Instead, the cached events
##! and data structures from the previous certificates are used.
@load ./main
module X509;
export {
## How often do you have to encounter a certificate before
## caching the events for it. Set to 0 to disable caching of certificates.
option caching_required_encounters : count = 10;
## The timespan over which caching_required_encounters has to be reached
option caching_required_encounters_interval : interval = 62 secs;
## After a certificate has not been encountered for this time, it
## may be evicted from the certificate event cache.
option certificate_cache_minimum_eviction_interval : interval = 62 secs;
## Maximum size of the certificate event cache
option certificate_cache_max_entries : count = 10000;
## This hook performs event-replays in case a certificate that already
## is in the cache is encountered.
##
## It is possible to change this behavior/skip sending the events by
## installing a higher priority hook instead.
global x509_certificate_cache_replay: hook(f: fa_file, e: X509::Info, sha256: string);
}
# Table tracking potential certificates to cache - indexed by the SHA256 of the
# raw on-the-wire representation (DER).
global certificates_encountered: table[string] of count &create_expire=caching_required_encounters_interval;
# Table caching the output of the X509 analyzer for commonly seen certificates.
# This is indexed by SHA256 and contains the Info record of the first certificate
# encountered. We use this info record to re-play the events.
global certificate_cache: table[string] of X509::Info &read_expire=certificate_cache_minimum_eviction_interval;
event zeek_init() &priority=5
{
x509_set_certificate_cache(certificate_cache);
x509_set_certificate_cache_hit_callback(x509_certificate_cache_replay);
}
hook x509_certificate_cache_replay(f: fa_file, e: X509::Info, sha256: string)
{
# we encountered a cached cert. The X509 analyzer will skip it. Let's raise all the events that it typically
# raises by ourselfes.
# first - let's checked if it already has an x509 record. That would mean that someone raised the file_hash event
# several times for the certificate - in which case we bail out.
if ( f$info?$x509 )
return;
event x509_certificate(f, e$handle, e$certificate);
for ( i in e$extensions_cache )
{
local ext = e$extensions_cache[i];
if ( ext is X509::Extension )
event x509_extension(f, (ext as X509::Extension));
else if ( ext is X509::BasicConstraints )
event x509_ext_basic_constraints(f, (ext as X509::BasicConstraints));
else if ( ext is X509::SubjectAlternativeName )
event x509_ext_subject_alternative_name(f, (ext as X509::SubjectAlternativeName));
else if ( ext is X509::SctInfo )
{
local s = ( ext as X509::SctInfo);
event x509_ocsp_ext_signed_certificate_timestamp(f, s$version, s$logid, s$timestamp, s$hash_alg, s$sig_alg, s$signature);
}
else
Reporter::error(fmt("Encountered unknown extension while replaying certificate with fuid %s", f$id));
}
}
event file_state_remove(f: fa_file) &priority=5
{
if ( ! f$info?$x509 )
return;
if ( f$info?$sha256 && f$info$sha256 !in certificate_cache &&
caching_required_encounters > 0 &&
f$info$sha256 in certificates_encountered &&
certificates_encountered[f$info$sha256] >= caching_required_encounters &&
|certificate_cache| < certificate_cache_max_entries )
{
delete certificates_encountered[f$info$sha256];
certificate_cache[f$info$sha256] = f$info$x509;
}
}
event file_hash(f: fa_file, kind: string, hash: string)
{
if ( ! f?$info || "X509" !in f$info$analyzers || kind != "sha256" )
return;
if ( caching_required_encounters == 0 || hash in certificate_cache )
return;
if ( hash !in certificates_encountered )
certificates_encountered[hash] = 1;
else
certificates_encountered[hash] += 1;
}

View file

@ -0,0 +1,61 @@
##! Enable logging of OCSP responses.
module OCSP;
export {
redef enum Log::ID += { LOG };
global log_policy: Log::PolicyHook;
## The record type which contains the fields of the OCSP log.
type Info: record {
## Time when the OCSP reply was encountered.
ts: time &log;
## File id of the OCSP reply.
id: string &log;
## Hash algorithm used to generate issuerNameHash and issuerKeyHash.
hashAlgorithm: string &log;
## Hash of the issuer's distingueshed name.
issuerNameHash: string &log;
## Hash of the issuer's public key.
issuerKeyHash: string &log;
## Serial number of the affected certificate.
serialNumber: string &log;
## Status of the affected certificate.
certStatus: string &log;
## Time at which the certificate was revoked.
revoketime: time &log &optional;
## Reason for which the certificate was revoked.
revokereason: string &log &optional;
## The time at which the status being shows is known to have been correct.
thisUpdate: time &log;
## The latest time at which new information about the status of the certificate will be available.
nextUpdate: time &log &optional;
};
## Event that can be handled to access the OCSP record
## as it is sent to the logging framework.
global log_ocsp: event(rec: Info);
}
event zeek_init() &priority=5
{
Log::create_stream(LOG, [$columns=Info, $ev=log_ocsp, $path="ocsp", $policy=log_policy]);
Files::register_for_mime_type(Files::ANALYZER_OCSP_REPLY, "application/ocsp-response");
}
event ocsp_response_certificate(f: fa_file, hashAlgorithm: string, issuerNameHash: string, issuerKeyHash: string, serialNumber: string, certStatus: string, revoketime: time, revokereason: string, thisUpdate: time, nextUpdate: time)
{
local wr = OCSP::Info($ts=f$info$ts, $id=f$id, $hashAlgorithm=hashAlgorithm, $issuerNameHash=issuerNameHash,
$issuerKeyHash=issuerKeyHash, $serialNumber=serialNumber, $certStatus=certStatus,
$thisUpdate=thisUpdate);
if ( revokereason != "" )
wr$revokereason = revokereason;
if ( time_to_double(revoketime) != 0 )
wr$revoketime = revoketime;
if ( time_to_double(nextUpdate) != 0 )
wr$nextUpdate = nextUpdate;
Log::write(LOG, wr);
}

View file

@ -1,6 +1,7 @@
@load base/frameworks/files
@load base/files/hash
@load base/frameworks/cluster
module X509;
@ -9,26 +10,32 @@ export {
global log_policy: Log::PolicyHook;
## How often do you have to encounter a certificate before
## caching it. Set to 0 to disable caching of certificates.
option caching_required_encounters : count = 10;
## The hash function used for certificate hashes. By default this is sha256; you can use
## any other hash function and the hashes will change in ssl.log and in x509.log.
option hash_function: function(cert: string): string = sha256_hash;
## The timespan over which caching_required_encounters has to be reached
option caching_required_encounters_interval : interval = 62 secs;
## This option specifies if X.509 certificates are logged in file.log. Typically, there
## is not much value to having the entry in files.log - especially since, by default, the
## file ID is not present in the X509 log.
option log_x509_in_files_log: bool = F;
## After a certificate has not been encountered for this time, it
## may be evicted from the certificate cache.
option certificate_cache_minimum_eviction_interval : interval = 62 secs;
## Maximum size of the certificate cache
option certificate_cache_max_entries : count = 10000;
## Type that is used to decide which certificates are duplicates for logging purposes.
## When adding entries to this, also change the create_deduplication_index to update them.
type LogCertHash: record {
## Certificate fingerprint
fingerprint: string;
## Indicates if this certificate was a end-host certificate, or sent as part of a chain
host_cert: bool;
## Indicates if this certificate was sent from the client
client_cert: bool;
};
## The record type which contains the fields of the X.509 log.
type Info: record {
## Current timestamp.
ts: time &log;
## File id of this certificate.
id: string &log;
## Fingerprint of the certificate - uses chosen algorithm.
fingerprint: string &log;
## Basic information about the certificate.
certificate: X509::Certificate &log;
## The opaque wrapping the certificate. Mainly used
@ -44,8 +51,17 @@ export {
## This is used for caching certificates that are commonly
## encountered and should not be relied on in user scripts.
extensions_cache: vector of any &default=vector();
## Indicates if this certificate was a end-host certificate, or sent as part of a chain
host_cert: bool &log &default=F;
## Indicates if this certificate was sent from the client
client_cert: bool &log &default=F;
## Record that is used to deduplicate log entries.
deduplication_index: LogCertHash &optional;
};
## Hook that is used to create the index value used for log deduplication.
global create_deduplication_index: hook(c: X509::Info);
## This record is used to store information about the SCTs that are
## encountered in Certificates.
type SctInfo: record {
@ -66,25 +82,32 @@ export {
signature: string;
};
## This hook performs event-replays in case a certificate that already
## is in the cache is encountered.
## By default, x509 certificates are deduplicated. This configuration option configures
## the maximum time after which certificates are re-logged. Note - depending on other configuration
## options, this setting might only apply on a per-worker basis and you still might see certificates
## logged several times.
##
## It is possible to change this behavior/skip sending the events by
## installing a higher priority hook instead.
global x509_certificate_cache_replay: hook(f: fa_file, e: X509::Info, sha256: string);
## To disable deduplication completely, set this to 0secs.
option relog_known_certificates_after = 1day;
## The set that stores information about certificates that already have been logged and should
## not be logged again.
global known_log_certs: set[LogCertHash] &create_expire=relog_known_certificates_after;
## Maximum size of the known_log_certs table
option known_log_certs_maximum_size = 1000000;
## Use broker stores to deduplicate certificates across the whole cluster. This will cause log-deduplication
## to work cluster wide, but come at a slightly higher cost of memory and inter-node-communication.
##
## This setting is ignored if Zeek is run in standalone mode.
global known_log_certs_use_broker: bool = T;
## Event for accessing logged records.
global log_x509: event(rec: Info);
}
# Table tracking potential certificates to cache - indexed by the SHA256 of the
# raw on-the-wire representation (DER).
global certificates_encountered: table[string] of count &create_expire=caching_required_encounters_interval;
# Table caching the output of the X509 analyzer for commonly seen certificates.
# This is indexed by SHA256 and contains the Info record of the first certificate
# encountered. We use this info record to re-play the events.
global certificate_cache: table[string] of X509::Info &read_expire=certificate_cache_minimum_eviction_interval;
global known_log_certs_with_broker: set[LogCertHash] &create_expire=relog_known_certificates_after &backend=Broker::MEMORY;
redef record Files::Info += {
## Information about X509 certificates. This is used to keep
@ -114,49 +137,41 @@ event zeek_init() &priority=5
Files::register_for_mime_type(Files::ANALYZER_SHA1, "application/x-x509-ca-cert");
Files::register_for_mime_type(Files::ANALYZER_SHA1, "application/pkix-cert");
# SHA256 is used by us to determine which certificates to cache.
# Please note that SHA256 caching is required to be enabled for the certificate event
# caching that is set up in certificate-event-cache.zeek to work.
Files::register_for_mime_type(Files::ANALYZER_SHA256, "application/x-x509-user-cert");
Files::register_for_mime_type(Files::ANALYZER_SHA256, "application/x-x509-ca-cert");
Files::register_for_mime_type(Files::ANALYZER_SHA256, "application/pkix-cert");
x509_set_certificate_cache(certificate_cache);
x509_set_certificate_cache_hit_callback(x509_certificate_cache_replay);
@if ( Cluster::is_enabled() )
if ( known_log_certs_use_broker )
known_log_certs = known_log_certs_with_broker;
@endif
}
hook x509_certificate_cache_replay(f: fa_file, e: X509::Info, sha256: string)
hook Files::log_policy(rec: Files::Info, id: Log::ID, filter: Log::Filter) &priority=5
{
# we encountered a cached cert. The X509 analyzer will skip it. Let's raise all the events that it typically
# raises by ourselfes.
if ( ( log_x509_in_files_log == F ) && ( "X509" in rec$analyzers ) )
break;
}
# first - let's checked if it already has an x509 record. That would mean that someone raised the file_hash event
# several times for the certificate - in which case we bail out.
if ( f$info?$x509 )
hook create_deduplication_index(i: X509::Info)
{
if ( i?$deduplication_index || relog_known_certificates_after == 0secs )
return;
event x509_certificate(f, e$handle, e$certificate);
for ( i in e$extensions_cache )
{
local ext = e$extensions_cache[i];
if ( ext is X509::Extension )
event x509_extension(f, (ext as X509::Extension));
else if ( ext is X509::BasicConstraints )
event x509_ext_basic_constraints(f, (ext as X509::BasicConstraints));
else if ( ext is X509::SubjectAlternativeName )
event x509_ext_subject_alternative_name(f, (ext as X509::SubjectAlternativeName));
else if ( ext is X509::SctInfo )
{
local s = ( ext as X509::SctInfo);
event x509_ocsp_ext_signed_certificate_timestamp(f, s$version, s$logid, s$timestamp, s$hash_alg, s$sig_alg, s$signature);
}
else
Reporter::error(fmt("Encountered unknown extension while replaying certificate with fuid %s", f$id));
}
i$deduplication_index = LogCertHash($fingerprint=i$fingerprint, $host_cert=i$host_cert, $client_cert=i$client_cert);
}
event x509_certificate(f: fa_file, cert_ref: opaque of x509, cert: X509::Certificate) &priority=5
{
f$info$x509 = [$ts=f$info$ts, $id=f$id, $certificate=cert, $handle=cert_ref];
local der_cert = x509_get_certificate_string(cert_ref);
local fp = hash_function(der_cert);
f$info$x509 = [$ts=f$info$ts, $fingerprint=fp, $certificate=cert, $handle=cert_ref];
if ( f$info$mime_type == "application/x-x509-user-cert" )
f$info$x509$host_cert = T;
if ( f$is_orig )
f$info$x509$client_cert = T;
}
event x509_extension(f: fa_file, ext: X509::Extension) &priority=5
@ -197,30 +212,17 @@ event file_state_remove(f: fa_file) &priority=5
if ( ! f$info?$x509 )
return;
Log::write(LOG, f$info$x509);
if ( ! f$info$x509?$deduplication_index )
hook create_deduplication_index(f$info$x509);
if ( f$info?$sha256 && f$info$sha256 !in certificate_cache &&
caching_required_encounters > 0 &&
f$info$sha256 in certificates_encountered &&
certificates_encountered[f$info$sha256] >= caching_required_encounters &&
|certificate_cache| < certificate_cache_max_entries )
if ( f$info$x509?$deduplication_index )
{
delete certificates_encountered[f$info$sha256];
certificate_cache[f$info$sha256] = f$info$x509;
if ( f$info$x509$deduplication_index in known_log_certs )
return;
else if ( |known_log_certs| < known_log_certs_maximum_size )
add known_log_certs[f$info$x509$deduplication_index];
}
Log::write(LOG, f$info$x509);
}
event file_hash(f: fa_file, kind: string, hash: string)
{
if ( ! f?$info || "X509" !in f$info$analyzers || kind != "sha256" )
return;
if ( caching_required_encounters == 0 || hash in certificate_cache )
return;
if ( hash !in certificates_encountered )
certificates_encountered[hash] = 1;
else
certificates_encountered[hash] += 1;
}

View file

@ -124,7 +124,6 @@ export {
## A set of analyzers to disable by default at startup. The default set
## contains legacy analyzers that are no longer supported.
global disabled_analyzers: set[Analyzer::Tag] = {
ANALYZER_STEPPINGSTONE,
ANALYZER_TCPSTATS,
} &redef;
}

View file

@ -31,7 +31,7 @@ export {
## authenticated.
const disable_ssl = F &redef;
## Path to a file containing concatenated trusted certificates
## Path to a file containing concatenated trusted certificates
## in PEM format. If set, Zeek will require valid certificates for
## all peers.
const ssl_cafile = "" &redef;
@ -122,6 +122,37 @@ export {
## done reading the pcap.
option peer_counts_as_iosource = T;
## Port for Broker's metric exporter. Setting this to a valid TCP port causes
## Broker to make metrics available to Prometheus scrapers via HTTP. Zeek
## overrides any value provided in zeek_init or earlier at startup if the
## environment variable BROKER_METRICS_PORT is defined.
const metrics_port = 0/unknown &redef;
## Frequency for publishing scraped metrics to the target topic. Zeek
## overrides any value provided in zeek_init or earlier at startup if the
## environment variable BROKER_METRICS_EXPORT_INTERVAL is defined.
option metrics_export_interval = 1 sec;
## Target topic for the metrics. Setting a non-empty string starts the
## periodic publishing of local metrics. Zeek overrides any value provided in
## zeek_init or earlier at startup if the environment variable
## BROKER_METRICS_EXPORT_TOPIC is defined.
option metrics_export_topic = "";
## ID for the metrics exporter. When setting a target topic for the
## exporter, Broker sets this option to the suffix of the new topic *unless*
## the ID is a non-empty string. Since setting a topic starts the periodic
## publishing of events, we recommend setting the ID always first or avoid
## setting it at all if the topic suffix serves as a good-enough ID. Zeek
## overrides any value provided in zeek_init or earlier at startup if the
## environment variable BROKER_METRICS_ENDPOINT_NAME is defined.
option metrics_export_endpoint_name = "";
## Selects prefixes from the local metrics. Only metrics with prefixes
## listed in this variable are included when publishing local metrics.
## Setting an empty vector selects *all* metrics.
option metrics_export_prefixes: vector of string = vector();
## The default topic prefix where logs will be published. The log's stream
## id is appended when writing to a particular stream.
const default_log_topic_prefix = "zeek/logs/" &redef;
@ -255,6 +286,7 @@ export {
global listen: function(a: string &default = default_listen_address,
p: port &default = default_port,
retry: interval &default = default_listen_retry): port;
## Initiate a remote connection.
##
## a: an address to connect to, e.g. "localhost" or "127.0.0.1".
@ -385,9 +417,53 @@ event Broker::log_flush() &priority=10
schedule Broker::log_batch_interval { Broker::log_flush() };
}
function update_metrics_export_interval(id: string, val: interval): interval
{
Broker::__set_metrics_export_interval(val);
return val;
}
function update_metrics_export_topic(id: string, val: string): string
{
Broker::__set_metrics_export_topic(val);
return val;
}
function update_metrics_export_endpoint_name(id: string, val: string): string
{
Broker::__set_metrics_export_endpoint_name(val);
return val;
}
function update_metrics_export_prefixes(id: string, filter: vector of string): vector of string
{
Broker::__set_metrics_export_prefixes(filter);
return filter;
}
event zeek_init()
{
schedule Broker::log_batch_interval { Broker::log_flush() };
# interval
update_metrics_export_interval("Broker::metrics_export_interval",
Broker::metrics_export_interval);
Option::set_change_handler("Broker::metrics_export_interval",
update_metrics_export_interval);
# topic
update_metrics_export_topic("Broker::metrics_export_topic",
Broker::metrics_export_topic);
Option::set_change_handler("Broker::metrics_export_topic",
update_metrics_export_topic);
# endpoint name
update_metrics_export_endpoint_name("Broker::metrics_export_endpoint_name",
Broker::metrics_export_endpoint_name);
Option::set_change_handler("Broker::metrics_export_endpoint_name",
update_metrics_export_endpoint_name);
# prefixes
update_metrics_export_prefixes("Broker::metrics_export_prefixes",
Broker::metrics_export_prefixes);
Option::set_change_handler("Broker::metrics_export_prefixes",
update_metrics_export_prefixes);
}
event retry_listen(a: string, p: port, retry: interval)

View file

@ -298,10 +298,25 @@ export {
config: table[string] of string &default=table();
};
## A hook type to implement filtering policy. Hook handlers can
## veto the logging of a record or alter it prior to logging.
## You can pass arbitrary state into the hook via the
## filter argument and its config member.
## A hook type to implement filtering policy. Hook handlers run
## on each log record. They can implement arbitrary per-record
## processing, alter the log record, or veto the writing of the
## given record by breaking from the hook handler.
##
## rec: An instance of the stream's ``columns`` type with its
## fields set to the values to be logged.
##
## id: The ID associated with the logging stream the filter
## belongs to.
type StreamPolicyHook: hook(rec: any, id: ID);
## A hook type to implement filtering policy at log filter
## granularity. Like :zeek:see:`Log::StreamPolicyHook`, these can
## implement added functionality, alter it prior to logging, or
## veto the write. These hooks run at log filter granularity,
## so get a :zeek:see:`Log::Filter` instance as additional
## argument. You can pass additional state into the hook via the
## the filter$config table.
##
## rec: An instance of the stream's ``columns`` type with its
## fields set to the values to be logged.
@ -309,8 +324,8 @@ export {
## id: The ID associated with the logging stream the filter
## belongs to.
##
## filter: The :zeek:type:`Log::Filter` instance that controls
## the fate of the given log record.
## filter: The :zeek:type:`Log::Filter` instance that steers
## the output of the given log record.
type PolicyHook: hook(rec: any, id: ID, filter: Filter);
# To allow Filters to have a policy hook that refers to
@ -551,6 +566,13 @@ export {
## This table is not meant to be modified by users! Only use it for
## examining which streams are active.
global active_streams: table[ID] of Stream = table();
## The global log policy hook. The framework invokes this hook for any
## log write, prior to iterating over the stream's associated filters.
## As with filter-specific hooks, breaking from the hook vetoes writing
## of the given log record. Note that filter-level policy hooks still get
## invoked after the global hook vetos, but they cannot "un-veto" the write.
global log_stream_policy: Log::StreamPolicyHook;
}
global all_streams: table[ID] of Stream = table();
@ -627,7 +649,7 @@ function run_rotation_postprocessor_cmd(info: RotationInfo, npath: string) : boo
# The date format is hard-coded here to provide a standardized
# script interface.
system(fmt("%s %s %s %s %s %d %s",
pp_cmd, npath, info$path,
pp_cmd, safe_shell_quote(npath), safe_shell_quote(info$path),
strftime("%y-%m-%d_%H.%M.%S", info$open),
strftime("%y-%m-%d_%H.%M.%S", info$close),
info$terminating, writer));

View file

@ -66,7 +66,7 @@ function scp_postprocessor(info: Log::RotationInfo): bool
command += fmt("scp %s %s@%s:%s;", info$fname, d$user, d$host, dst);
}
command += fmt("/bin/rm %s", info$fname);
command += fmt("/bin/rm %s", safe_shell_quote(info$fname));
system(command);
return T;
}

View file

@ -69,7 +69,7 @@ function sftp_postprocessor(info: Log::RotationInfo): bool
d$host_port, d$user, d$host);
}
command += fmt("/bin/rm %s", info$fname);
command += fmt("/bin/rm %s", safe_shell_quote(info$fname));
system(command);
return T;
}

View file

@ -38,7 +38,7 @@ export {
## written into logs.
##
## This option is also available as a per-filter ``$config`` option.
const enable_utf_8 = F &redef;
const enable_utf_8 = T &redef;
## Define the gzip level to compress the logs. If 0, then no gzip
## compression is performed. Enabling compression also changes
@ -54,6 +54,11 @@ export {
## This option is also available as a per-filter ``$config`` option.
const gzip_file_extension = "gz" &redef;
## Define the default logging directory. If empty, logs are written
## to the current working directory.
##
const logdir = "" &redef;
## Format of timestamps when writing out JSON. By default, the JSON
## formatter will use double values for timestamps which represent the
## number of seconds from the UNIX epoch.

View file

@ -18,18 +18,15 @@ export {
};
}
hook notice(n: Notice::Info) &priority=-5
hook notice(n: Notice::Info)
{
if ( |Site::local_admins| > 0 &&
ACTION_EMAIL_ADMIN in n$actions )
{
local email = "";
if ( n?$src && |Site::get_emails(n$src)| > 0 )
email = fmt("%s, %s", email, Site::get_emails(n$src));
add n$email_dest[Site::get_emails(n$src)];
if ( n?$dst && |Site::get_emails(n$dst)| > 0 )
email = fmt("%s, %s", email, Site::get_emails(n$dst));
if ( email != "" )
email_notice_to(n, email, T);
add n$email_dest[Site::get_emails(n$dst)];
}
}

View file

@ -11,14 +11,14 @@ export {
## variable.
ACTION_PAGE
};
## Email address to send notices with the :zeek:enum:`Notice::ACTION_PAGE`
## action.
option mail_page_dest = "";
}
hook notice(n: Notice::Info) &priority=-5
hook notice(n: Notice::Info)
{
if ( ACTION_PAGE in n$actions )
email_notice_to(n, mail_page_dest, F);
add n$email_dest[mail_page_dest];
}

View file

@ -136,6 +136,9 @@ export {
## The actions which have been applied to this notice.
actions: ActionSet &log &default=ActionSet();
## The email address(es) where to send this notice
email_dest: set[string] &log &default=set();
## By adding chunks of text into this element, other scripts
## can expand on notices that are being emailed. The normal
## way to add text is to extend the vector by handling the
@ -199,11 +202,12 @@ export {
##
## Note that this is overridden by the ZeekControl SendMail option.
option sendmail = "/usr/sbin/sendmail";
## Email address to send notices with the
## The default email address to send notices with the
## :zeek:enum:`Notice::ACTION_EMAIL` action or to send bulk alarm logs
## on rotation with :zeek:enum:`Notice::ACTION_ALARM`.
##
## Note that this is overridden by the ZeekControl MailTo option.
## Note that this is overridden by the ZeekControl MailTo option or by
## the `email_dest` field in the :zeek:see:`Notice::Info` record.
const mail_dest = "" &redef;
## Address that emails will be from.
@ -380,12 +384,13 @@ function log_mailing_postprocessor(info: Log::RotationInfo): bool
{
local headers = email_headers(fmt("Log Contents: %s", info$fname),
mail_dest);
local tmpfilename = fmt("%s.mailheaders.tmp", info$fname);
local tmpfilename = safe_shell_quote(fmt("%s.mailheaders.tmp", info$fname));
local tmpfile = open(tmpfilename);
write_file(tmpfile, headers);
close(tmpfile);
system(fmt("/bin/cat %s %s | %s -t -oi && /bin/rm %s %s",
tmpfilename, info$fname, sendmail, tmpfilename, info$fname));
tmpfilename, safe_shell_quote(info$fname), sendmail,
tmpfilename, safe_shell_quote(info$fname)));
}
return T;
}
@ -510,10 +515,17 @@ hook Notice::policy(n: Notice::Info) &priority=10
add n$actions[ACTION_LOG];
}
hook Notice::notice(n: Notice::Info) &priority=-5
hook Notice::notice(n: Notice::Info)
{
if ( ACTION_EMAIL in n$actions )
email_notice_to(n, mail_dest, T);
add n$email_dest[mail_dest];
}
hook Notice::notice(n: Notice::Info) &priority=-5
{
for ( dest in n$email_dest )
email_notice_to(n, dest, T);
if ( ACTION_LOG in n$actions )
Log::write(Notice::LOG, n);
if ( ACTION_ALARM in n$actions )

View file

@ -238,6 +238,18 @@ function parse(unparsed_version: string): Description
return [$version=v, $unparsed_version=unparsed_version, $name=alternate_names[software_name]];
}
global parse_cache: table[string] of Description &read_expire=65secs;
# Call parse, but cache results in the parse_cache table
function parse_with_cache(unparsed_version: string): Description
{
if (unparsed_version in parse_cache)
return parse_cache[unparsed_version];
local res = parse(unparsed_version);
parse_cache[unparsed_version] = res;
return res;
}
function parse_mozilla(unparsed_version: string): Description
{
@ -464,8 +476,25 @@ function software_fmt(i: Info): string
return fmt("%s %s", i$name, software_fmt_version(i$version));
}
# Parse unparsed_version if needed before raising register event
# This is used to maintain the behavior of the exported Software::register
# event that expects a pre-parsed 'name' field.
event Software::new(info: Info)
{
if ( ! info?$version )
{
local sw = parse_with_cache(info$unparsed_version);
info$unparsed_version = sw$unparsed_version;
info$name = sw$name;
info$version = sw$version;
}
event Software::register(info);
}
event Software::register(info: Info)
{
local ts: SoftwareSet;
if ( info$host in tracked )
@ -514,19 +543,10 @@ function found(id: conn_id, info: Info): bool
return F;
}
if ( ! info?$version )
{
local sw = parse(info$unparsed_version);
info$unparsed_version = sw$unparsed_version;
info$name = sw$name;
info$version = sw$version;
}
@if ( Cluster::is_enabled() )
Cluster::publish_hrw(Cluster::proxy_pool, info$host, Software::register,
info);
Cluster::publish_hrw(Cluster::proxy_pool, info$host, Software::new, info);
@else
event Software::register(info);
event Software::new(info);
@endif
return T;

View file

@ -42,8 +42,13 @@ export {
stdout_file: string &optional;
## The filename/path to which the node's stderr will be redirected.
stderr_file: string &optional;
## Whether to start the node in bare mode. When left out, the node
## inherits the bare-mode status the supervisor itself runs with.
bare_mode: bool &optional;
## Additional script filenames/paths that the node should load.
scripts: vector of string &default = vector();
## Environment variables to define in the supervised node.
env: table[string] of string &default=table();
## A cpu/core number to which the node will try to pin itself.
cpu_affinity: int &optional;
## The Cluster Layout definition. Each node in the Cluster Framework

View file

@ -4,6 +4,7 @@
##! That is, it may change in various incompatible ways without warning or
##! deprecation until the stable 4.0.0 release.
@load base/frameworks/broker
@load ./api
module SupervisorControl;
@ -15,6 +16,10 @@ export {
## for their topic names.
const topic_prefix = "zeek/supervisor" &redef;
## When enabled, the Supervisor will listen on the configured Broker
## :zeek:see:`Broker::default_listen_address`.
const enable_listen = F &redef;
## Send a request to a remote Supervisor process to create a node.
##
## reqid: an arbitrary string that will be directly echoed in the response

View file

@ -3,7 +3,6 @@
@load ./api
@load ./control
@load base/frameworks/broker
function Supervisor::status(node: string): Supervisor::Status
{
@ -42,6 +41,14 @@ function Supervisor::node(): Supervisor::NodeConfig
event zeek_init() &priority=10
{
if ( Supervisor::is_supervisor() && SupervisorControl::enable_listen )
{
# This may fail, possibly with scheduled retries. Any failures
# already get logged by the listen() implementation, so we don't
# report additionally.
Broker::listen();
}
Broker::subscribe(SupervisorControl::topic_prefix);
}

View file

@ -93,7 +93,7 @@ export {
const ayiya_ports = { 5072/udp };
const teredo_ports = { 3544/udp };
const gtpv1_ports = { 2152/udp, 2123/udp };
redef likely_server_ports += { ayiya_ports, teredo_ports, gtpv1_ports, vxlan_ports };
redef likely_server_ports += { ayiya_ports, teredo_ports, gtpv1_ports, vxlan_ports, geneve_ports };
event zeek_init() &priority=5
{
@ -103,6 +103,7 @@ event zeek_init() &priority=5
Analyzer::register_for_ports(Analyzer::ANALYZER_TEREDO, teredo_ports);
Analyzer::register_for_ports(Analyzer::ANALYZER_GTPV1, gtpv1_ports);
Analyzer::register_for_ports(Analyzer::ANALYZER_VXLAN, vxlan_ports);
Analyzer::register_for_ports(Analyzer::ANALYZER_GENEVE, geneve_ports);
}
function register_all(ecv: EncapsulatingConnVector)

View file

@ -25,6 +25,13 @@ type string_any_table: table[string] of any;
## directly and then remove this alias.
type string_set: set[string];
## A set of subnets.
##
## .. todo:: We need this type definition only for declaring builtin functions
## via ``bifcl``. We should extend ``bifcl`` to understand composite types
## directly and then remove this alias.
type subnet_set: set[subnet];
## A set of addresses.
##
## .. todo:: We need this type definition only for declaring builtin functions
@ -582,6 +589,8 @@ type SYN_packet: record {
win_scale: int; ##< The window scale option if present, or -1 if not.
MSS: count; ##< The maximum segment size if present, or 0 if not.
SACK_OK: bool; ##< True if the *SACK* option is present.
TSval: count &optional; ##< The TCP TS value if present.
TSecr: count &optional; ##< The TCP TS echo reply if present.
};
## Packet capture statistics. All counts are cumulative.
@ -635,7 +644,7 @@ type ProcStats: record {
real_time: interval; ##< Elapsed real time since Zeek started running.
user_time: interval; ##< User CPU seconds.
system_time: interval; ##< System CPU seconds.
mem: count; ##< Maximum memory consumed, in KB.
mem: count; ##< Maximum memory consumed, in bytes.
minor_faults: count; ##< Page faults not requiring actual I/O.
major_faults: count; ##< Page faults requiring actual I/O.
num_swap: count; ##< Times swapped out.
@ -1933,6 +1942,7 @@ type gtp_delete_pdp_ctx_response_elements: record {
@load base/frameworks/supervisor/api
@load base/bif/supervisor.bif
@load base/bif/packet_analysis.bif
@load base/bif/CPP-load.bif
## Internal function.
function add_interface(iold: string, inew: string): string
@ -2788,14 +2798,22 @@ export {
## .. zeek:see:: smb1_nt_create_andx_response smb2_create_response
type SMB::MACTimes: record {
## The time when data was last written to the file.
modified : time &log;
modified : time &log;
## Same as `modified` but in SMB's original `FILETIME` integer format.
modified_raw: count;
## The time when the file was last accessed.
accessed : time &log;
accessed : time &log;
## Same as `accessed` but in SMB's original `FILETIME` integer format.
accessed_raw: count;
## The time the file was created.
created : time &log;
created : time &log;
## Same as `created` but in SMB's original `FILETIME` integer format.
created_raw : count;
## The time when the file was last modified.
changed : time &log;
} &log;
changed : time &log;
## Same as `changed` but in SMB's original `FILETIME` integer format.
changed_raw : count;
};
## A set of file names used as named pipes over SMB. This
## only comes into play as a heuristic to identify named
@ -4117,15 +4135,6 @@ type PE::SectionHeader: record {
}
module GLOBAL;
## Internal to the stepping stone detector.
const stp_delta: interval &redef;
## Internal to the stepping stone detector.
const stp_idle_min: interval &redef;
## Internal to the stepping stone detector.
global stp_skip_src: set[addr] &redef;
## Description of a signature match.
##
## .. zeek:see:: signature_match
@ -4890,9 +4899,21 @@ const dpd_reassemble_first_packets = T &redef;
## connections will be able to analyze the session.
##
## .. zeek:see:: dpd_reassemble_first_packets dpd_match_only_beginning
## dpd_ignore_ports
## dpd_ignore_ports dpd_max_packets
const dpd_buffer_size = 1024 &redef;
## Maximum number of per-connection packets that will be buffered for dynamic
## protocol detection. For each connection, Zeek buffers up to this amount
## of packets in memory so that complete protocol analysis can start even after
## the initial packets have already passed through (i.e., when a DPD signature
## matches only later). However, once the buffer is full, data is deleted and lost
## to analyzers that are activated afterwards. Then only analyzers that can deal
## with partial connections will be able to analyze the session.
##
## .. zeek:see:: dpd_reassemble_first_packets dpd_match_only_beginning
## dpd_ignore_ports dpd_buffer_size
const dpd_max_packets = 100 &redef;
## If true, stops signature matching if :zeek:see:`dpd_buffer_size` has been
## reached.
##
@ -5029,6 +5050,12 @@ export {
## if you customize this, you may still want to manually ensure that
## :zeek:see:`likely_server_ports` also gets populated accordingly.
const vxlan_ports: set[port] = { 4789/udp } &redef;
## The set of UDP ports used for Geneve traffic. Traffic using this
## UDP destination port will attempt to be decapsulated. Note that if
## if you customize this, you may still want to manually ensure that
## :zeek:see:`likely_server_ports` also gets populated accordingly.
const geneve_ports: set[port] = { 6081/udp } &redef;
} # end export
module Reporter;

View file

@ -0,0 +1,5 @@
##! This script loads functionality needed by the supervisor. Zeek only sources
##! this when the supervisor is active (-j). Like init-default.zeek, this isn't
##! loaded in bare mode.
@load base/frameworks/supervisor

View file

@ -15,3 +15,6 @@
@load base/packet-protocols/gre
@load base/packet-protocols/iptunnel
@load base/packet-protocols/vntag
@load base/packet-protocols/udp
@load base/packet-protocols/tcp
@load base/packet-protocols/icmp

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,5 @@
module PacketAnalyzer::ICMP;
#event zeek_init() &priority=20
# {
# }

View file

@ -1,8 +1,32 @@
module PacketAnalyzer::IP;
const IPPROTO_TCP : count = 6;
const IPPROTO_UDP : count = 17;
const IPPROTO_ICMP : count = 1;
const IPPROTO_ICMP6 : count = 58;
const IPPROTO_IPIP : count = 4;
const IPPROTO_IPV6 : count = 41;
const IPPROTO_GRE : count = 47;
function analyzer_option_change_ignore_checksums_nets(ID: string, new_value: set[subnet], location: string) : set[subnet]
{
if ( ID == "ignore_checksums_nets" )
PacketAnalyzer::__set_ignore_checksums_nets(new_value);
return new_value;
}
event zeek_init() &priority=20
{
PacketAnalyzer::register_packet_analyzer(PacketAnalyzer::ANALYZER_IP, 4, PacketAnalyzer::ANALYZER_IPTUNNEL);
PacketAnalyzer::register_packet_analyzer(PacketAnalyzer::ANALYZER_IP, 41, PacketAnalyzer::ANALYZER_IPTUNNEL);
PacketAnalyzer::register_packet_analyzer(PacketAnalyzer::ANALYZER_IP, 47, PacketAnalyzer::ANALYZER_GRE);
PacketAnalyzer::register_packet_analyzer(PacketAnalyzer::ANALYZER_IP, IPPROTO_IPIP, PacketAnalyzer::ANALYZER_IPTUNNEL);
PacketAnalyzer::register_packet_analyzer(PacketAnalyzer::ANALYZER_IP, IPPROTO_IPV6, PacketAnalyzer::ANALYZER_IPTUNNEL);
PacketAnalyzer::register_packet_analyzer(PacketAnalyzer::ANALYZER_IP, IPPROTO_GRE, PacketAnalyzer::ANALYZER_GRE);
PacketAnalyzer::register_packet_analyzer(PacketAnalyzer::ANALYZER_IP, IPPROTO_TCP, PacketAnalyzer::ANALYZER_TCP);
PacketAnalyzer::register_packet_analyzer(PacketAnalyzer::ANALYZER_IP, IPPROTO_UDP, PacketAnalyzer::ANALYZER_UDP);
PacketAnalyzer::register_packet_analyzer(PacketAnalyzer::ANALYZER_IP, IPPROTO_ICMP, PacketAnalyzer::ANALYZER_ICMP);
PacketAnalyzer::register_packet_analyzer(PacketAnalyzer::ANALYZER_IP, IPPROTO_ICMP6, PacketAnalyzer::ANALYZER_ICMP);
Option::set_change_handler("ignore_checksums_nets", analyzer_option_change_ignore_checksums_nets, 5);
}

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,5 @@
module PacketAnalyzer::TCP;
#event zeek_init() &priority=20
# {
# }

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,5 @@
module PacketAnalyzer::UDP;
#event zeek_init() &priority=20
# {
# }

View file

@ -239,10 +239,7 @@ function determine_service(c: connection): string
function set_conn(c: connection, eoc: bool)
{
if ( ! c?$conn )
{
local tmp: Info &is_assigned;
c$conn = tmp;
}
c$conn = Info();
c$conn$ts=c$start_time;
c$conn$uid=c$uid;

View file

@ -2197,6 +2197,27 @@ export {
["5422fd3a-d4b8-4cef-a12e-e87d4ca22e90",0x08] = "GetCAPropertyInfo",
["5422fd3a-d4b8-4cef-a12e-e87d4ca22e90",0x09] = "Ping2",
# IDispatch - MSDN Ref: OLE Automation Protocol [ms-oaut]
["00020400-0000-0000-c000-000000000046",0x03] = "GetTypeInfoCount",
["00020400-0000-0000-c000-000000000046",0x04] = "GetTypeInfo",
["00020400-0000-0000-c000-000000000046",0x05] = "GetIDsOfNames",
["00020400-0000-0000-c000-000000000046",0x06] = "Invoke",
# ITypeInfo - MSDN Ref: OLE Automation Protocol [ms-oaut]
["00020401-0000-0000-c000-000000000046",0x03] = "GetTypeAttr",
["00020401-0000-0000-c000-000000000046",0x04] = "GetTypeComp",
["00020401-0000-0000-c000-000000000046",0x05] = "GetFuncDesc",
["00020401-0000-0000-c000-000000000046",0x06] = "GetVarDesc",
["00020401-0000-0000-c000-000000000046",0x07] = "GetNames",
["00020401-0000-0000-c000-000000000046",0x08] = "GetRefTypeOfImplType",
["00020401-0000-0000-c000-000000000046",0x09] = "GetImplTypeFlags",
["00020401-0000-0000-c000-000000000046",0x0C] = "GetDocumentation",
["00020401-0000-0000-c000-000000000046",0x0D] = "GetDllEntry",
["00020401-0000-0000-c000-000000000046",0x0E] = "GetRefTypeInfo",
["00020401-0000-0000-c000-000000000046",0x10] = "CreateInstance",
["00020401-0000-0000-c000-000000000046",0x11] = "GetMops",
["00020401-0000-0000-c000-000000000046",0x12] = "GetContainingTypeLib",
# IDMNotify - MSDN Ref: Disk Mgmt Remote Protocol [ms-dmrp]
["d2d79df7-3400-11d0-b40b-00aa005ff586",0x00] = "ObjectsChanged",

View file

@ -63,15 +63,22 @@ export {
[50] = "NSEC3",
[51] = "NSEC3PARAM",
[52] = "TLSA",
[53] = "SMIMEA",
[55] = "HIP",
[59] = "CDS",
[60] = "CDNSKEY",
[61] = "OPENPGPKEY",
[62] = "CSYNC",
[63] = "ZONEMD",
[64] = "SVCB",
[65] = "HTTPS",
[99] = "SPF",
[100] = "UINFO",
[101] = "UID",
[102] = "GID",
[103] = "UNSPEC",
[108] = "EUI48",
[109] = "EUI64",
[249] = "TKEY",
[250] = "TSIG",
[251] = "IXFR",
@ -83,6 +90,7 @@ export {
[257] = "CAA",
[32768] = "TA",
[32769] = "DLV",
[65521] = "INTEGRITY", # google: https://docs.google.com/document/d/14eCqVyT_3MSj7ydqNFl1Yl0yg1fs6g24qmYUUdi5V-k/edit
} &default = function(n: count): string { return fmt("query-%d", n); };
## Errors used for non-TSIG/EDNS types.

View file

@ -434,7 +434,11 @@ event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qcla
# worked into the query/response in some fashion.
if ( c$id$resp_p == 137/udp )
{
query = decode_netbios_name(query);
local decoded_query = decode_netbios_name(query);
if ( |decoded_query| != 0 )
query = decoded_query;
if ( c$dns$qtype_name == "SRV" )
{
# The SRV RFC used the ID used for NetBios Status RRs.

View file

@ -107,7 +107,8 @@ event ssl_established(c: connection) &priority=5
function data_channel_initial_criteria(c: connection): bool
{
return ( c?$ssl && c$ssl?$client_subject && c$ssl?$subject &&
return ( c?$ssl && c$ssl?$cert_chain && c$ssl?$client_cert_chain &&
|c$ssl$cert_chain| > 0 && |c$ssl$client_cert_chain| > 0 &&
c$ssl?$cipher && /WITH_NULL/ in c$ssl$cipher );
}

View file

@ -24,8 +24,9 @@ function get_file_handle(c: connection, is_orig: bool): string
local path_name = current_file?$path ? current_file$path : "";
local file_name = current_file?$name ? current_file$name : "";
# Include last_mod time if available because if a file has been modified it
# should be considered a new file.
local last_mod = cat(current_file?$times ? current_file$times$modified : double_to_time(0.0));
# should be considered a new file. We use the raw version here to avoid
# getting differences when double precision varies by architecture.
local last_mod = cat(current_file?$times ? current_file$times$modified_raw : 0);
# TODO: This is doing hexdump to avoid problems due to file analysis handling
# using CheckString which is not immune to encapsulated null bytes.
# This needs to be fixed lower in the file analysis code later.

View file

@ -109,7 +109,7 @@ event zeek_init() &priority=5
function find_address_in_smtp_header(header: string): string
{
local ips = extract_ip_addresses(header);
local ips = extract_ip_addresses(header, T);
# If there are more than one IP address found, return the second.
if ( |ips| > 1 )
return ips[1];

View file

@ -20,8 +20,12 @@ export {
uid: string &log;
## The connection's 4-tuple of endpoint addresses/ports.
id: conn_id &log;
## SSH major version (1 or 2)
version: count &log;
## SSH major version (1, 2, or unset). The version can be unset if the
## client and server version strings are unset, malformed or incompatible
## so no common version can be extracted. If no version can be extracted
## even though both client and server versions are set a weird
## will be generated.
version: count &log &optional;
## Authentication result (T=success, F=failure, unset=unknown)
auth_success: bool &log &optional;
## The number of authentication attemps we observed. There's always
@ -155,65 +159,82 @@ function set_session(c: connection)
}
}
function set_version(c: connection, version: string)
{
if ( c$ssh?$server && c$ssh?$client && |c$ssh$client| > 4 && |c$ssh$server| > 4 )
{
if ( c$ssh$client[4] == "1" && c$ssh$server[4] == "2" )
{
# SSH199 vs SSH2 -> 2
if ( ( |c$ssh$client| > 7 ) && ( c$ssh$client[6] == "9" ) && ( c$ssh$client[7] == "9" ) )
c$ssh$version = 2;
# SSH1 vs SSH2 -> Undefined
else
c$ssh$version = 0;
}
else if ( c$ssh$client[4] == "2" && c$ssh$server[4] == "1" )
{
# SSH2 vs SSH199 -> 2
if ( ( |c$ssh$server| > 7 ) && ( c$ssh$server[6] == "9" ) && ( c$ssh$server[7] == "9" ) )
c$ssh$version = 2;
else
# SSH2 vs SSH1 -> Undefined
c$ssh$version = 0;
}
else if ( c$ssh$client[4] == "1" && c$ssh$server[4] == "1" )
{
# SSH1 vs SSH199 -> 1
if ( ( |c$ssh$server| > 7 ) && ( c$ssh$server[6] == "9" ) && ( c$ssh$server[7] == "9" ) )
{
# SSH199 vs SSH199
if (( |c$ssh$client| > 7 ) && ( c$ssh$client[6] == "9" ) && ( c$ssh$client[7] == "9" ))
c$ssh$version = 2;
else
c$ssh$version = 1;
}
else
{
# SSH1 vs SSH1 -> 1
c$ssh$version = 1;
}
}
# SSH2 vs SSH2
else if (c$ssh$client[4] == "2" && c$ssh$server[4] == "2" )
{
c$ssh$version = 2;
}
}
}
function set_version(c: connection)
{
# We always either set the version field to a concrete value, or unset it.
delete c$ssh$version;
# If either the client or server string is unset we cannot compute a
# version and return early. We do not raise a weird in this case as we
# might arrive here while having only seen one side of the handshake.
const has_server = c$ssh?$server && |c$ssh$server| > 0;
const has_client = c$ssh?$client && |c$ssh$client| > 0;
if ( ! ( has_server && has_client ) )
return;
if ( |c$ssh$client| > 4 && |c$ssh$server| > 4 )
{
if ( c$ssh$client[4] == "1" && c$ssh$server[4] == "2" )
{
# SSH199 vs SSH2 -> 2
if ( ( |c$ssh$client| > 7 ) && ( c$ssh$client[6] == "9" ) && ( c$ssh$client[7] == "9" ) )
c$ssh$version = 2;
# SSH1 vs SSH2 -> Undefined
else
Reporter::conn_weird("SSH_version_mismatch", c, fmt("%s vs %s", c$ssh$server, c$ssh$client));
return;
}
else if ( c$ssh$client[4] == "2" && c$ssh$server[4] == "1" )
{
# SSH2 vs SSH199 -> 2
if ( ( |c$ssh$server| > 7 ) && ( c$ssh$server[6] == "9" ) && ( c$ssh$server[7] == "9" ) )
c$ssh$version = 2;
else
# SSH2 vs SSH1 -> Undefined
Reporter::conn_weird("SSH_version_mismatch", c, fmt("%s vs %s", c$ssh$server, c$ssh$client));
return;
}
else if ( c$ssh$client[4] == "1" && c$ssh$server[4] == "1" )
{
# SSH1 vs SSH199 -> 1
if ( ( |c$ssh$server| > 7 ) && ( c$ssh$server[6] == "9" ) && ( c$ssh$server[7] == "9" ) )
{
# SSH199 vs SSH199
if (( |c$ssh$client| > 7 ) && ( c$ssh$client[6] == "9" ) && ( c$ssh$client[7] == "9" ))
c$ssh$version = 2;
else
c$ssh$version = 1;
}
else
{
# SSH1 vs SSH1 -> 1
c$ssh$version = 1;
}
}
# SSH2 vs SSH2
else if (c$ssh$client[4] == "2" && c$ssh$server[4] == "2" )
{
c$ssh$version = 2;
}
return;
}
Reporter::conn_weird("SSH_cannot_determine_version", c, fmt("%s vs %s", c$ssh$server, c$ssh$client));
}
event ssh_server_version(c: connection, version: string)
{
set_session(c);
c$ssh$server = version;
set_version(c, version);
set_version(c);
}
event ssh_client_version(c: connection, version: string)
{
set_session(c);
c$ssh$client = version;
set_version(c, version);
set_version(c);
}
event ssh_auth_attempted(c: connection, authenticated: bool) &priority=5

View file

@ -1,22 +1,15 @@
#
# Do not edit this file. This file is automatically generated by gen-ct-list.pl
# File generated at Wed Dec 9 21:42:12 2020
# File generated from https://www.gstatic.com/ct/log_list/all_logs_list.json
# File generated at Wed Jun 30 13:58:18 2021
# File generated from https://www.gstatic.com/ct/log_list/log_list.json
#
@load base/protocols/ssl
module SSL;
redef ct_logs += {
["\xfa\xd4\xc9\x7c\xc4\x9e\xe2\xf8\xac\x85\xc5\xea\x5c\xea\x09\xd0\x22\x0d\xbb\xf4\xe4\x9c\x6b\x50\x66\x2f\xf8\x68\xf8\x6b\x8c\x28"] = CTInfo($description="Google 'Argon2017' log", $operator="Google", $url="ct.googleapis.com/logs/argon2017/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x54\x6d\x7c\x89\xdd\xea\x9d\xf0\xba\x5f\xf4\x6d\x60\x7a\x37\x4f\x02\x25\xbf\x1c\xf6\x6f\x85\xae\xaf\x15\xdf\x69\x6e\xed\xdb\xa9\x9a\x29\x97\xf2\x99\x76\x1e\xe6\x33\x46\x1e\x27\xf4\xbe\x70\xdd\x59\xd7\xba\xcf\xfe\xd0\x72\x8e\xb0\x57\x0f\x9d\x37\x89\x62\xa3"),
["\xa4\x50\x12\x69\x05\x5a\x15\x54\x5e\x62\x11\xab\x37\xbc\x10\x3f\x62\xae\x55\x76\xa4\x5e\x4b\x17\x14\x45\x3e\x1b\x22\x10\x6a\x25"] = CTInfo($description="Google 'Argon2018' log", $operator="Google", $url="ct.googleapis.com/logs/argon2018/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd2\x00\x55\x05\xad\xd5\x47\xb4\x19\xbb\xcd\x95\xfb\x29\xd7\x58\x3d\x78\x24\xcd\xce\x46\x9d\xfb\x32\xd4\x71\x4e\x60\x02\x25\x5e\x59\x3e\xd7\xd4\x03\xb8\x6d\x43\x68\x68\x7e\xe8\xa0\x65\x0b\x3e\x6e\x71\x59\x92\x37\xbe\xa9\xe8\xf1\xa3\x2b\xe4\xd9\x0d\x55\x68"),
["\x63\xf2\xdb\xcd\xe8\x3b\xcc\x2c\xcf\x0b\x72\x84\x27\x57\x6b\x33\xa4\x8d\x61\x77\x8f\xbd\x75\xa6\x38\xb1\xc7\x68\x54\x4b\xd8\x8d"] = CTInfo($description="Google 'Argon2019' log", $operator="Google", $url="ct.googleapis.com/logs/argon2019/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x23\x73\x10\x9b\xe1\xf3\x5e\xf6\x98\x6b\x69\x95\x96\x10\x78\xce\x49\xdb\xb4\x04\xfc\x71\x2c\x5a\x92\x60\x68\x25\xc0\x4a\x1a\xa1\xb0\x61\x2d\x1b\x87\x14\xa9\xba\xf0\x01\x33\x59\x1d\x05\x30\xe9\x42\x15\xe7\x55\xd7\x2a\xf8\xb4\xa2\xba\x45\xc9\x46\x91\x87\x56"),
["\xb2\x1e\x05\xcc\x8b\xa2\xcd\x8a\x20\x4e\x87\x66\xf9\x2b\xb9\x8a\x25\x20\x67\x6b\xda\xfa\x70\xe7\xb2\x49\x53\x2d\xef\x8b\x90\x5e"] = CTInfo($description="Google 'Argon2020' log", $operator="Google", $url="ct.googleapis.com/logs/argon2020/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xe9\x3c\x76\xa7\x5c\x8a\x63\x8d\x35\xe4\xdc\x88\x62\xf7\x6b\x93\x7e\x9e\xb3\x4b\x80\x73\x5c\xc0\xe0\xf4\x3e\x4c\x64\x58\xfb\x76\x63\x51\x32\x18\x63\xd5\xb2\xbb\xed\xea\xff\x5e\x3b\x24\x6e\x2f\x35\x52\x8b\xb4\x35\x9a\xad\x9c\x15\xa8\x69\x20\xea\x50\x18\xcc"),
["\xf6\x5c\x94\x2f\xd1\x77\x30\x22\x14\x54\x18\x08\x30\x94\x56\x8e\xe3\x4d\x13\x19\x33\xbf\xdf\x0c\x2f\x20\x0b\xcc\x4e\xf1\x64\xe3"] = CTInfo($description="Google 'Argon2021' log", $operator="Google", $url="ct.googleapis.com/logs/argon2021/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x4d\xe0\x66\x64\xea\xf3\x64\xaa\x38\xc5\x89\x2d\xc7\xd8\x08\xd9\xc8\x44\x71\xed\xdc\xc3\xfb\x5b\xaf\x9c\x64\xa1\x09\x66\x84\x1d\x7c\x68\xa7\xec\xc4\x3f\x8c\x9c\x82\xe0\x18\xd9\x74\x14\xe9\xb4\x79\x81\xa2\x94\x55\x62\xf3\x9c\x0b\x44\x83\xa1\x2b\xc9\x71\x2b"),
["\x29\x79\xbe\xf0\x9e\x39\x39\x21\xf0\x56\x73\x9f\x63\xa5\x77\xe5\xbe\x57\x7d\x9c\x60\x0a\xf8\xf9\x4d\x5d\x26\x5c\x25\x5d\xc7\x84"] = CTInfo($description="Google 'Argon2022' log", $operator="Google", $url="ct.googleapis.com/logs/argon2022/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x78\x83\xdc\xe9\xf1\xa6\xb8\x18\x3a\x00\x99\x2f\xff\x3e\xcd\x15\xc9\x26\x1e\xf7\xff\x3a\xa9\xa3\x72\x16\x49\xeb\x09\xb6\xa8\xdd\xb4\xd2\x47\x91\x0e\x0d\xf9\xd9\xd5\xa9\x8b\xb0\x87\x9d\x25\x79\xd4\x1a\x50\x60\x08\xf5\x09\x06\x39\x26\xe4\x40\xc2\xba\xc3\xc2"),
["\xe8\x3e\xd0\xda\x3e\xf5\x06\x35\x32\xe7\x57\x28\xbc\x89\x6b\xc9\x03\xd3\xcb\xd1\x11\x6b\xec\xeb\x69\xe1\x77\x7d\x6d\x06\xbd\x6e"] = CTInfo($description="Google 'Argon2023' log", $operator="Google", $url="ct.googleapis.com/logs/argon2023/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd0\x90\x8f\x64\x52\x4e\x42\xac\x84\xb6\x2e\x4c\xf2\x3d\x77\x00\xb3\x77\x08\x05\x47\xaa\x45\x4c\xe3\x2c\x8e\x70\xa5\x82\xbb\x6c\xb2\x7b\x9c\x98\x7a\xa0\xe9\x11\x76\x28\x00\xb2\x20\xb4\xcd\xd3\x98\x7b\x4d\x96\x27\xe6\xb7\xee\x22\x6a\xd1\xb0\x2e\x91\x77\x78"),
["\xb1\x0c\xd5\x59\xa6\xd6\x78\x46\x81\x1f\x7d\xf9\xa5\x15\x32\x73\x9a\xc4\x8d\x70\x3b\xea\x03\x23\xda\x5d\x38\x75\x5b\xc0\xad\x4e"] = CTInfo($description="Google 'Xenon2018' log", $operator="Google", $url="ct.googleapis.com/logs/xenon2018/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd6\xcc\x89\xbf\x04\x1d\xae\xfd\x1a\xf1\xd3\x36\x54\x09\xca\xfd\x29\x87\x24\xdc\x3f\xf8\x5c\x42\xf8\x29\xdc\x15\xc9\xd7\x31\x7d\xa3\x34\x7f\x57\xb3\xb4\x39\xe8\x58\x95\xde\xd6\x86\xe4\x6d\x82\xa2\x43\x2c\x8b\x38\x69\x72\x6e\xf1\x5f\x3b\x91\xdc\x81\xe6\x6a"),
["\x08\x41\x14\x98\x00\x71\x53\x2c\x16\x19\x04\x60\xbc\xfc\x47\xfd\xc2\x65\x3a\xfa\x29\x2c\x72\xb3\x7f\xf8\x63\xae\x29\xcc\xc9\xf0"] = CTInfo($description="Google 'Xenon2019' log", $operator="Google", $url="ct.googleapis.com/logs/xenon2019/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xfd\x7c\x83\xc2\xac\xd7\x2f\xd8\xb6\x19\x38\xcc\x62\x4a\x9a\x13\x28\x91\x2f\x40\xf2\xf6\xc1\xea\xfc\x14\xde\x6c\x57\x6c\x85\xbb\xc2\x69\x75\xc4\x87\xa9\xa3\x50\xad\x18\xcb\xe0\x2c\x0d\xc2\x38\x32\x9c\xc5\xd6\x5e\xca\x75\xe7\x76\x30\x3e\x73\x8b\x8d\xb4\x80"),
["\x07\xb7\x5c\x1b\xe5\x7d\x68\xff\xf1\xb0\xc6\x1d\x23\x15\xc7\xba\xe6\x57\x7c\x57\x94\xb7\x6a\xee\xbc\x61\x3a\x1a\x69\xd3\xa2\x1c"] = CTInfo($description="Google 'Xenon2020' log", $operator="Google", $url="ct.googleapis.com/logs/xenon2020/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x65\x4e\xf9\x56\xa8\xf2\xcd\x24\xe0\x15\x92\x80\x9d\x68\x35\x41\xe6\x1f\x14\x52\x11\x65\x33\x0a\xee\xad\xe4\x59\x66\x6c\x98\x78\x50\x76\xb0\x58\x9c\x74\x59\xdc\xe0\x38\x91\x47\x94\xc7\x42\x4d\xfb\x15\xfe\x75\x28\x2d\xd6\xbb\xaa\x52\x18\x65\xee\x33\xaf\x9b"),
["\x7d\x3e\xf2\xf8\x8f\xff\x88\x55\x68\x24\xc2\xc0\xca\x9e\x52\x89\x79\x2b\xc5\x0e\x78\x09\x7f\x2e\x6a\x97\x68\x99\x7e\x22\xf0\xd7"] = CTInfo($description="Google 'Xenon2021' log", $operator="Google", $url="ct.googleapis.com/logs/xenon2021/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x47\xed\x4c\x22\x7b\xbc\x43\x7f\x41\xc0\x36\x79\x46\x9f\x53\xc1\x78\x70\x9b\x79\x2d\xbe\x02\x73\xa6\x4f\xeb\xed\xd0\xe0\x1a\x4e\xd9\x69\xc3\x26\xde\x58\xdf\x72\x82\x2f\x3f\x51\x3b\xd9\x3f\x23\xbf\x22\xd3\xd5\xbb\xdd\x6a\x9c\xa5\x6a\x36\x5c\x4c\x7e\x96\xe6"),
["\x46\xa5\x55\xeb\x75\xfa\x91\x20\x30\xb5\xa2\x89\x69\xf4\xf3\x7d\x11\x2c\x41\x74\xbe\xfd\x49\xb8\x85\xab\xf2\xfc\x70\xfe\x6d\x47"] = CTInfo($description="Google 'Xenon2022' log", $operator="Google", $url="ct.googleapis.com/logs/xenon2022/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xf9\x64\xbd\x15\x2c\x40\x62\x50\x95\x13\x38\x3c\xc7\x21\xb0\x3a\xb9\x8f\xa2\x7a\x15\xd7\x89\xd6\x8e\x31\x13\x00\x87\x59\xbc\xbb\xee\x90\xfc\xc8\x58\x13\x0a\xbf\xab\x43\x36\x54\x23\xa4\x81\xcd\xad\x47\x14\xb7\x58\xa0\x44\xfa\x6a\xa0\xa0\xd7\xc3\x63\x1e\x2b"),
["\xad\xf7\xbe\xfa\x7c\xff\x10\xc8\x8b\x9d\x3d\x9c\x1e\x3e\x18\x6a\xb4\x67\x29\x5d\xcf\xb1\x0c\x24\xca\x85\x86\x34\xeb\xdc\x82\x8a"] = CTInfo($description="Google 'Xenon2023' log", $operator="Google", $url="ct.googleapis.com/logs/xenon2023/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x72\x16\x3e\x0b\xef\xef\xce\x3e\x60\xdd\x95\xcb\x63\x7a\xb9\xa9\x8d\x4a\x6f\x6c\xdc\x61\x80\xa6\x45\x5e\x2f\x83\xac\x94\xf3\x85\x88\xd0\xa5\x74\xd0\x7b\x8e\xff\xc5\xee\x42\xa2\xf0\x2d\x93\xe3\xc2\xd0\xb2\x99\xe2\xe1\x42\xe9\xd2\xc6\x00\x27\x69\x74\xae\xce"),
@ -25,87 +18,32 @@ redef ct_logs += {
["\xa4\xb9\x09\x90\xb4\x18\x58\x14\x87\xbb\x13\xa2\xcc\x67\x70\x0a\x3c\x35\x98\x04\xf9\x1b\xdf\xb8\xe3\x77\xcd\x0e\xc8\x0d\xdc\x10"] = CTInfo($description="Google 'Pilot' log", $operator="Google", $url="ct.googleapis.com/pilot/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x7d\xa8\x4b\x12\x29\x80\xa3\x3d\xad\xd3\x5a\x77\xb8\xcc\xe2\x88\xb3\xa5\xfd\xf1\xd3\x0c\xcd\x18\x0c\xe8\x41\x46\xe8\x81\x01\x1b\x15\xe1\x4b\xf1\x1b\x62\xdd\x36\x0a\x08\x18\xba\xed\x0b\x35\x84\xd0\x9e\x40\x3c\x2d\x9e\x9b\x82\x65\xbd\x1f\x04\x10\x41\x4c\xa0"),
["\xee\x4b\xbd\xb7\x75\xce\x60\xba\xe1\x42\x69\x1f\xab\xe1\x9e\x66\xa3\x0f\x7e\x5f\xb0\x72\xd8\x83\x00\xc4\x7b\x89\x7a\xa8\xfd\xcb"] = CTInfo($description="Google 'Rocketeer' log", $operator="Google", $url="ct.googleapis.com/rocketeer/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x20\x5b\x18\xc8\x3c\xc1\x8b\xb3\x31\x08\x00\xbf\xa0\x90\x57\x2b\xb7\x47\x8c\x6f\xb5\x68\xb0\x8e\x90\x78\xe9\xa0\x73\xea\x4f\x28\x21\x2e\x9c\xc0\xf4\x16\x1b\xaa\xf9\xd5\xd7\xa9\x80\xc3\x4e\x2f\x52\x3c\x98\x01\x25\x46\x24\x25\x28\x23\x77\x2d\x05\xc2\x40\x7a"),
["\xbb\xd9\xdf\xbc\x1f\x8a\x71\xb5\x93\x94\x23\x97\xaa\x92\x7b\x47\x38\x57\x95\x0a\xab\x52\xe8\x1a\x90\x96\x64\x36\x8e\x1e\xd1\x85"] = CTInfo($description="Google 'Skydiver' log", $operator="Google", $url="ct.googleapis.com/skydiver/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x12\x6c\x86\x0e\xf6\x17\xb1\x12\x6c\x37\x25\xd2\xad\x87\x3d\x0e\x31\xec\x21\xad\xb1\xcd\xbe\x14\x47\xb6\x71\x56\x85\x7a\x9a\xb7\x3d\x89\x90\x7b\xc6\x32\x3a\xf8\xda\xce\x8b\x01\xfe\x3f\xfc\x71\x91\x19\x8e\x14\x6e\x89\x7a\x5d\xb4\xab\x7e\xe1\x4e\x1e\x7c\xac"),
["\xa8\x99\xd8\x78\x0c\x92\x90\xaa\xf4\x62\xf3\x18\x80\xcc\xfb\xd5\x24\x51\xe9\x70\xd0\xfb\xf5\x91\xef\x75\xb0\xd9\x9b\x64\x56\x81"] = CTInfo($description="Google 'Submariner' log", $operator="Google", $url="ct.googleapis.com/submariner/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x39\xf8\x9f\x20\x62\xd4\x57\x55\x68\xa2\xef\x49\x2d\xf0\x39\x2d\x9a\xde\x44\xb4\x94\x30\xe0\x9e\x7a\x27\x3c\xab\x70\xf0\xd1\xfa\x51\x90\x63\x16\x57\x41\xad\xab\x6d\x1f\x80\x74\x30\x79\x02\x5e\x2d\x59\x84\x07\x24\x23\xf6\x9f\x35\xb8\x85\xb8\x42\x45\xa4\x4f"),
["\x1d\x02\x4b\x8e\xb1\x49\x8b\x34\x4d\xfd\x87\xea\x3e\xfc\x09\x96\xf7\x50\x6f\x23\x5d\x1d\x49\x70\x61\xa4\x77\x3c\x43\x9c\x25\xfb"] = CTInfo($description="Google 'Daedalus' log", $operator="Google", $url="ct.googleapis.com/daedalus/", $maximum_merge_delay=604800, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x6e\x0c\x1c\xba\xee\x2b\x6a\x41\x85\x60\x1d\x7b\x7e\xab\x08\x2c\xfc\x0c\x0a\xa5\x08\xb3\x3e\xd5\x70\x24\xd1\x6d\x1d\x2d\xb6\xb7\xf3\x8b\x36\xdc\x23\x4d\x95\x63\x12\xbb\xe4\x86\x8d\xcc\xe9\xd1\xee\xa1\x40\xa2\xdf\x0b\xa3\x06\x0a\x30\xca\x8d\xac\xa4\x29\x56"),
["\xb0\xcc\x83\xe5\xa5\xf9\x7d\x6b\xaf\x7c\x09\xcc\x28\x49\x04\x87\x2a\xc7\xe8\x8b\x13\x2c\x63\x50\xb7\xc6\xfd\x26\xe1\x6c\x6c\x77"] = CTInfo($description="Google 'Testtube' log", $operator="Google", $url="ct.googleapis.com/testtube/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xc3\xc8\xbc\x4b\xba\xa2\x18\x4b\x3d\x35\x7b\xf4\x64\x91\x61\xea\xeb\x8e\x99\x1d\x90\xed\xd3\xe9\xaf\x39\x3d\x5c\xd3\x46\x91\x45\xe3\xce\xac\x76\x48\x3b\xd1\x7e\x2c\x0a\x63\x00\x65\x8d\xf5\xae\x8e\x8c\xc7\x11\x25\x4f\x43\x2c\x9d\x19\xa1\xe1\x91\xa4\xb3\xfe"),
["\xc3\xbf\x03\xa7\xe1\xca\x88\x41\xc6\x07\xba\xe3\xff\x42\x70\xfc\xa5\xec\x45\xb1\x86\xeb\xbe\x4e\x2c\xf3\xfc\x77\x86\x30\xf5\xf6"] = CTInfo($description="Google 'Crucible' log", $operator="Google", $url="ct.googleapis.com/logs/crucible/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x28\x04\xe5\xd8\x1d\xd2\x01\xbc\x72\xcc\x63\x9f\x35\x10\x7e\x03\x2b\x4d\x4c\x6b\xdd\x17\xf1\x45\x63\xa1\xf3\x59\xbf\xbf\x1c\xf1\x38\x94\x9d\xfb\xbf\x1d\xa7\x12\x6f\x7d\x29\x85\x32\xf5\x2a\x0d\xcc\x5e\x55\xc9\x3c\x02\x40\x6e\x67\x2f\xf7\x39\x43\xbe\xf2\x41"),
["\x52\xeb\x4b\x22\x5e\xc8\x96\x97\x48\x50\x67\x5f\x23\xe4\x3b\xc1\xd0\x21\xe3\x21\x4c\xe5\x2e\xcd\x5f\xa8\x7c\x20\x3c\xdf\xca\x03"] = CTInfo($description="Google 'Solera2018' log", $operator="Google", $url="ct.googleapis.com/logs/solera2018/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x12\xe1\x6a\x9f\x97\x32\xd6\x70\x02\x01\x19\x56\x21\x48\xde\x25\xa4\x43\x2a\x5d\x26\x71\xff\x60\xbc\x56\x57\xa4\xf8\x47\xb3\x2c\xa4\x8b\x35\xef\x50\xb1\x79\x19\x93\x46\x7e\xe7\x16\x23\x25\x1c\x70\x14\x5f\x99\x82\x59\x4d\x3a\xd7\xab\x0d\x26\x56\xdb\x3b\x84"),
["\x0b\x76\x0e\x9a\x8b\x9a\x68\x2f\x88\x98\x5b\x15\xe9\x47\x50\x1a\x56\x44\x6b\xba\x88\x30\x78\x5c\x38\x42\x99\x43\x86\x45\x0c\x00"] = CTInfo($description="Google 'Solera2019' log", $operator="Google", $url="ct.googleapis.com/logs/solera2019/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x25\x4c\x06\x8a\x75\xd4\x59\x53\x5a\x06\x22\xb6\x56\x5f\xeb\x77\x23\x24\xc4\xa6\x96\x24\x74\x7c\x76\x3f\x72\x0f\x90\x25\x64\x4b\x44\x6d\xfb\xc0\x31\x04\x3c\xa3\xb0\xd0\xc9\x75\x66\xed\x35\xfe\x78\x00\x3d\xc0\xbd\x95\xb6\xde\x83\xa6\x83\x04\x2f\x4a\xbf\xaf"),
["\x1f\xc7\x2c\xe5\xa1\xb7\x99\xf4\x00\xc3\x59\xbf\xf9\x6c\xa3\x91\x35\x48\xe8\x64\x42\x20\x61\x09\x52\xe9\xba\x17\x74\xf7\xba\xc7"] = CTInfo($description="Google 'Solera2020' log", $operator="Google", $url="ct.googleapis.com/logs/solera2020/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x88\xa7\xd6\xb6\xea\x16\x08\xf3\x04\xcd\x22\xb2\x4a\x33\x23\x5e\x9a\x37\xf1\x63\x9d\x66\xbe\x98\xab\x45\x98\x6b\x62\x50\x3a\xfd\x6e\x54\xcc\x64\xa8\x7c\xb0\x7f\xd1\xb3\xd6\x41\x9e\x44\xba\xbf\x47\x3d\xed\x55\x5b\xd7\x82\xa6\xf8\x8d\x0d\x54\xa8\xa5\x4a\x9f"),
["\xa3\xc9\x98\x45\xe8\x0a\xb7\xce\x00\x15\x7b\x37\x42\xdf\x02\x07\xdd\x27\x2b\x2b\x60\x2e\xcf\x98\xee\x2c\x12\xdb\x9c\x5a\xe7\xe7"] = CTInfo($description="Google 'Solera2021' log", $operator="Google", $url="ct.googleapis.com/logs/solera2021/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd6\x09\x70\xc6\xa5\xec\xc3\x65\x6a\x94\x06\xc7\x49\xe5\x9b\x4e\xd8\x4c\x18\xd2\x2e\x00\x25\x67\xf0\x98\xff\x8e\xb9\xd8\xda\x23\x76\xb9\x55\x2b\x10\x4c\x0b\x8f\x95\x54\x09\xbf\x96\x17\x65\xd8\xe3\x8f\xa6\x7d\x45\x58\x63\xb4\x7f\x77\xc4\xc8\x67\x8a\x25\x9f"),
["\x69\x7a\xaf\xca\x1a\x6b\x53\x6f\xae\x21\x20\x50\x46\xde\xba\xd7\xe0\xea\xea\x13\xd2\x43\x2e\x6e\x9d\x8f\xb3\x79\xf2\xb9\xaa\xf3"] = CTInfo($description="Google 'Solera2022' log", $operator="Google", $url="ct.googleapis.com/logs/solera2022/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x15\x68\xfa\x51\x00\xf1\xcc\x75\xa6\x83\x34\x2d\x41\x0e\xd1\x10\x30\xb4\x9f\x19\xd4\xf6\x6a\x4e\x98\x0d\x25\xbf\x9b\x6b\x03\x4b\x7b\x21\x1c\xd2\x92\x1e\x03\x3b\x39\xcf\x7b\xe9\xe4\xc6\x66\x82\xf2\x24\xb5\x71\xaa\x42\xb4\xa8\xf2\x62\x15\x0d\x46\xbb\xd6\xa8"),
["\xf9\x7e\x97\xb8\xd3\x3e\xf7\xa1\x59\x02\xa5\x3a\x19\xe1\x79\x90\xe5\xdc\x40\x6a\x03\x18\x25\xba\xad\x93\xe9\x8f\x9b\x9c\x69\xcb"] = CTInfo($description="Google 'Solera2023' log", $operator="Google", $url="ct.googleapis.com/logs/solera2023/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x2b\xd6\x39\xe8\x83\xfa\x0c\x64\x32\xd9\xdf\x66\xa1\x81\x89\x0a\x16\x4f\xa2\x4b\x57\xa1\x8c\x1a\x1b\x43\x01\x37\xfe\x17\xe4\xc4\x85\x98\x16\x98\x25\xf3\x66\xde\x60\xb0\xcc\xb5\x5e\x7e\x48\xe1\xdb\x0c\xfc\x43\xaa\xb6\x4b\xbe\x61\x4b\xf3\x9e\x1c\x68\x99\x52"),
["\x1f\xbc\x36\xe0\x02\xed\xe9\x7f\x40\x19\x9e\x86\xb3\x57\x3b\x8a\x42\x17\xd8\x01\x87\x74\x6a\xd0\xda\x03\xa0\x60\x54\xd2\x0d\xf4"] = CTInfo($description="Cloudflare 'Nimbus2017' Log", $operator="Cloudflare", $url="ct.cloudflare.com/logs/nimbus2017/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd7\x9c\xa9\x07\x8d\x22\x41\xee\x93\xa0\x52\x41\xda\xf4\x80\xf0\x25\xbc\xeb\xfa\xf3\x3c\xd2\x7e\x91\xd8\x3f\x2c\xda\x51\xbd\xc8\xee\x2a\x72\xe3\xff\x18\x56\xe4\x3a\x22\x0f\x22\x3c\xc6\xd5\x30\xb3\x9b\x68\x2e\xab\x56\xc2\x41\x5f\xd6\x64\x57\x14\xb1\x5a\xaf"),
["\xdb\x74\xaf\xee\xcb\x29\xec\xb1\xfe\xca\x3e\x71\x6d\x2c\xe5\xb9\xaa\xbb\x36\xf7\x84\x71\x83\xc7\x5d\x9d\x4f\x37\xb6\x1f\xbf\x64"] = CTInfo($description="Cloudflare 'Nimbus2018' Log", $operator="Cloudflare", $url="ct.cloudflare.com/logs/nimbus2018/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x02\xc5\x69\x5a\xfa\xc7\xdc\xa7\xb4\x55\x16\x8c\x83\xd6\x50\xa1\x08\xdb\xe6\x0f\xf1\x87\x5c\xf7\x0c\x36\xba\x22\xec\x58\xe4\x3c\x8f\xb2\x4e\x9b\xae\x5b\xeb\x50\xd5\xd9\xce\x82\x20\xd0\x37\x2f\x16\x20\x27\xda\x47\x7a\xc6\x6b\xb8\x39\xb9\x39\x5c\x0f\xe7\x46"),
["\x74\x7e\xda\x83\x31\xad\x33\x10\x91\x21\x9c\xce\x25\x4f\x42\x70\xc2\xbf\xfd\x5e\x42\x20\x08\xc6\x37\x35\x79\xe6\x10\x7b\xcc\x56"] = CTInfo($description="Cloudflare 'Nimbus2019' Log", $operator="Cloudflare", $url="ct.cloudflare.com/logs/nimbus2019/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x91\x91\xf3\xd6\xfe\x6b\xf1\xaf\x4b\x99\x74\x8c\x7a\x06\x19\x02\x0e\x14\x5b\xe5\x20\xe7\xa1\xad\x35\xf2\x53\x0c\xd1\x59\xba\xe6\xc4\x25\x88\x16\x7f\x81\x5c\x0b\x90\xfe\x66\x46\x30\xb6\xd5\xd3\x0d\x2a\x38\x3a\x46\xa7\x1b\xd6\xf7\x00\x8e\x2c\xc0\x84\x36\xf2"),
["\x5e\xa7\x73\xf9\xdf\x56\xc0\xe7\xb5\x36\x48\x7d\xd0\x49\xe0\x32\x7a\x91\x9a\x0c\x84\xa1\x12\x12\x84\x18\x75\x96\x81\x71\x45\x58"] = CTInfo($description="Cloudflare 'Nimbus2020' Log", $operator="Cloudflare", $url="ct.cloudflare.com/logs/nimbus2020/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd3\x51\x00\x87\x1e\x28\xd3\x33\xd0\xad\x74\xdc\x62\x38\x02\xb7\x83\x15\x16\xc4\xf4\x3f\x08\xf3\x6f\x54\x70\xac\xcd\x25\x85\x60\xe5\xc4\x06\x0f\x3f\xaf\xe0\xc8\xc0\x97\x36\x43\xa7\xff\xb2\x85\xb2\x32\xfb\xaf\x09\x3b\xf2\xd1\xcc\xa5\x8f\x2b\x5e\x7f\x00\x62"),
["\x44\x94\x65\x2e\xb0\xee\xce\xaf\xc4\x40\x07\xd8\xa8\xfe\x28\xc0\xda\xe6\x82\xbe\xd8\xcb\x31\xb5\x3f\xd3\x33\x96\xb5\xb6\x81\xa8"] = CTInfo($description="Cloudflare 'Nimbus2021' Log", $operator="Cloudflare", $url="ct.cloudflare.com/logs/nimbus2021/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xc6\x9a\x27\xee\x2a\x6c\xa9\xe8\x48\x79\x4d\x5b\x9a\x9a\x20\xf5\x31\x68\xe0\xf9\x3c\xfb\xda\x0d\xf0\xe6\x07\x97\x54\x36\x24\x65\x57\x9e\x45\x45\x9e\xeb\xaf\x3e\x04\xa8\xd8\x4a\x7e\xea\xf2\xdf\x7c\xd2\xdc\x98\x46\xf1\x3a\xe7\x33\xd3\x7b\x05\x89\xe9\x9a\xb6"),
["\x41\xc8\xca\xb1\xdf\x22\x46\x4a\x10\xc6\xa1\x3a\x09\x42\x87\x5e\x4e\x31\x8b\x1b\x03\xeb\xeb\x4b\xc7\x68\xf0\x90\x62\x96\x06\xf6"] = CTInfo($description="Cloudflare 'Nimbus2022' Log", $operator="Cloudflare", $url="ct.cloudflare.com/logs/nimbus2022/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x48\xb2\x47\x4e\x50\x32\x72\x62\x4a\x0d\x0c\x48\xbf\xad\x29\x64\x6f\x20\xdf\x79\x52\x63\x16\x29\x0a\x2e\x60\xb5\xe2\x3a\x1c\xb7\xaf\x59\xb1\x55\x09\xdb\x59\xc7\xe9\xbd\x6f\xed\x0b\xaf\x05\x96\x97\xff\x3b\x9a\x43\x4d\xeb\x11\x34\x33\x8a\xe7\xac\x83\xc0\xff"),
["\x7a\x32\x8c\x54\xd8\xb7\x2d\xb6\x20\xea\x38\xe0\x52\x1e\xe9\x84\x16\x70\x32\x13\x85\x4d\x3b\xd2\x2b\xc1\x3a\x57\xa3\x52\xeb\x52"] = CTInfo($description="Cloudflare 'Nimbus2023' Log", $operator="Cloudflare", $url="ct.cloudflare.com/logs/nimbus2023/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x8b\xff\x2d\x92\x18\xcb\x46\x9d\x12\x5e\xb9\x59\x75\x3c\xcd\x91\x37\x7a\x1e\xa9\x9c\x99\x78\x83\x27\x3d\xdf\x01\xd5\x8b\x80\xe8\x63\x9a\xfe\x26\xa2\x1b\xd1\x87\x05\xee\x97\xd6\xe0\x5b\x43\x83\x81\x1c\x02\xf5\x41\x80\x80\x7f\xef\xa4\x61\xcf\xbc\x84\xb5\xa8"),
["\x56\x14\x06\x9a\x2f\xd7\xc2\xec\xd3\xf5\xe1\xbd\x44\xb2\x3e\xc7\x46\x76\xb9\xbc\x99\x11\x5c\xc0\xef\x94\x98\x55\xd6\x89\xd0\xdd"] = CTInfo($description="DigiCert Log Server", $operator="DigiCert", $url="ct1.digicert-ct.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x02\x46\xc5\xbe\x1b\xbb\x82\x40\x16\xe8\xc1\xd2\xac\x19\x69\x13\x59\xf8\xf8\x70\x85\x46\x40\xb9\x38\xb0\x23\x82\xa8\x64\x4c\x7f\xbf\xbb\x34\x9f\x4a\x5f\x28\x8a\xcf\x19\xc4\x00\xf6\x36\x06\x93\x65\xed\x4c\xf5\xa9\x21\x62\x5a\xd8\x91\xeb\x38\x24\x40\xac\xe8"),
["\x87\x75\xbf\xe7\x59\x7c\xf8\x8c\x43\x99\x5f\xbd\xf3\x6e\xff\x56\x8d\x47\x56\x36\xff\x4a\xb5\x60\xc1\xb4\xea\xff\x5e\xa0\x83\x0f"] = CTInfo($description="DigiCert Log Server 2", $operator="DigiCert", $url="ct2.digicert-ct.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xcc\x5d\x39\x2f\x66\xb8\x4c\x7f\xc1\x2e\x03\xa1\x34\xa3\xe8\x8a\x86\x02\xae\x4a\x11\xc6\xf7\x26\x6a\x37\x9b\xf0\x38\xf8\x5d\x09\x8d\x63\xe8\x31\x6b\x86\x66\xcf\x79\xb3\x25\x3c\x1e\xdf\x78\xb4\xa8\xc5\x69\xfa\xb7\xf0\x82\x79\x62\x43\xf6\xcc\xfe\x81\x66\x84"),
["\xc1\x16\x4a\xe0\xa7\x72\xd2\xd4\x39\x2d\xc8\x0a\xc1\x07\x70\xd4\xf0\xc4\x9b\xde\x99\x1a\x48\x40\xc1\xfa\x07\x51\x64\xf6\x33\x60"] = CTInfo($description="DigiCert Yeti2018 Log", $operator="DigiCert", $url="yeti2018.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x49\x89\x4a\x14\x32\xcb\x16\x60\x3d\x25\x27\x1a\x89\xa3\x67\xaa\x55\x3c\xa1\x60\xf2\xb7\x12\x18\x31\xfb\x30\x1f\x2f\x44\xb2\x0d\x1a\x89\x7f\x96\x9c\xff\xf2\x8f\x83\xb4\x56\x21\x07\xb4\xbc\x1b\x98\xe4\x1e\x49\x60\x46\x90\x8b\xbd\x60\xaf\x42\x2d\xe7\xab\xfa"),
["\xe2\x69\x4b\xae\x26\xe8\xe9\x40\x09\xe8\x86\x1b\xb6\x3b\x83\xd4\x3e\xe7\xfe\x74\x88\xfb\xa4\x8f\x28\x93\x01\x9d\xdd\xf1\xdb\xfe"] = CTInfo($description="DigiCert Yeti2019 Log", $operator="DigiCert", $url="yeti2019.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x91\x97\x7f\xa3\x0f\x17\xf8\x54\x95\x58\x05\x52\x7f\xcc\x73\x90\x5a\x21\x70\xfa\x61\xff\x1e\xa9\x4b\x52\x47\x87\xb8\x35\xc2\x70\x99\xe7\x2f\xfc\x1e\x4e\xa3\xcc\x9c\x6c\xea\xdd\xd8\x30\x05\xb3\xd8\x23\xdd\xe1\x59\x02\x77\x1c\x0a\x7b\x11\xa1\x70\x5c\x43\xf4"),
["\xf0\x95\xa4\x59\xf2\x00\xd1\x82\x40\x10\x2d\x2f\x93\x88\x8e\xad\x4b\xfe\x1d\x47\xe3\x99\xe1\xd0\x34\xa6\xb0\xa8\xaa\x8e\xb2\x73"] = CTInfo($description="DigiCert Yeti2020 Log", $operator="DigiCert", $url="yeti2020.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x51\x10\x06\xf9\x9a\x34\x69\xcd\xe7\xdf\xb8\x9f\x64\xa5\x21\x04\x51\x15\xea\x37\xdc\x0b\x31\x88\x47\x3d\xed\xb2\xaf\x02\x6b\xd0\x4f\xff\x95\xd4\x1f\x2e\x99\x8a\xab\x0f\x68\x01\x1b\x54\xcd\x2e\x23\x74\xe6\xf5\x4d\xb8\x45\x50\x47\x47\xd2\x71\x0c\x49\x4f\x9a"),
["\x5c\xdc\x43\x92\xfe\xe6\xab\x45\x44\xb1\x5e\x9a\xd4\x56\xe6\x10\x37\xfb\xd5\xfa\x47\xdc\xa1\x73\x94\xb2\x5e\xe6\xf6\xc7\x0e\xca"] = CTInfo($description="DigiCert Yeti2021 Log", $operator="DigiCert", $url="yeti2021.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xe8\x9e\x04\x6d\xca\x48\x02\x5d\x7e\x02\x44\x91\xb1\xb8\x68\x63\x9a\x11\x4e\x3d\xd5\xa0\x57\xda\x7f\x50\xe5\x42\x47\xe2\xed\x16\xde\xfc\x77\x23\x7d\x5b\x6b\xc0\xdf\x23\x68\x2f\xad\x40\x31\xa3\x17\xe7\x6a\xbc\xa8\x56\x24\x04\x3a\x43\xa7\xaf\xea\xaf\x4c\x7b"),
["\x22\x45\x45\x07\x59\x55\x24\x56\x96\x3f\xa1\x2f\xf1\xf7\x6d\x86\xe0\x23\x26\x63\xad\xc0\x4b\x7f\x5d\xc6\x83\x5c\x6e\xe2\x0f\x02"] = CTInfo($description="DigiCert Yeti2022 Log", $operator="DigiCert", $url="yeti2022.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x9f\xf8\xd8\x1d\xde\xfb\x5b\x51\xb5\xfb\x5d\xf5\xb5\xde\x66\x11\xb0\x9d\x5f\xfd\x6f\xfc\xa8\x98\x5b\x98\x4f\x2d\xc3\x91\x3a\xfb\xfe\xc4\x0f\x0d\xc3\x60\x43\x8c\x1e\xf2\xf9\x11\xb2\xba\xd0\xf6\xbc\xa5\xd2\xb6\x9f\xf9\x5c\x87\xa2\x7d\xfc\xd4\x7d\xd6\x13\x26"),
["\x35\xcf\x19\x1b\xbf\xb1\x6c\x57\xbf\x0f\xad\x4c\x6d\x42\xcb\xbb\xb6\x27\x20\x26\x51\xea\x3f\xe1\x2a\xef\xa8\x03\xc3\x3b\xd6\x4c"] = CTInfo($description="DigiCert Yeti2023 Log", $operator="DigiCert", $url="yeti2023.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x7d\x0d\x03\xb1\xd5\x98\x8a\xdc\xf0\x15\x3b\xc6\xdc\x5e\x0d\x6e\x3f\x0d\xbf\x95\xc8\x55\x8c\xd0\xa6\x4c\x96\xb1\x4e\x27\xb9\x26\x25\x99\xcc\x2b\x02\x9e\xa6\xd3\xdd\x9f\xb1\xd5\xc4\xc3\xac\x35\x04\x07\x87\x97\x36\xaa\xad\x28\x0d\x7f\x2b\xd9\x68\x9f\x72\xd1"),
["\x6f\xf1\x41\xb5\x64\x7e\x42\x22\xf7\xef\x05\x2c\xef\xae\x7c\x21\xfd\x60\x8e\x27\xd2\xaf\x5a\x6e\x9f\x4b\x8a\x37\xd6\x63\x3e\xe5"] = CTInfo($description="DigiCert Nessie2018 Log", $operator="DigiCert", $url="nessie2018.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x56\xaa\x4b\x6b\x65\xbe\x47\x3d\x57\x0d\x93\xc1\x23\x22\x89\x3b\xe2\x8a\x14\xe6\x19\x4e\x3f\x4c\xa4\x95\xa7\x65\xe1\x54\xab\x37\x39\x6a\x2b\xce\x89\x61\x15\x86\xcf\x06\xcb\x60\x25\x1f\x78\xab\x58\xf1\x63\x21\x93\xd9\x32\xcd\xc3\xbf\xb3\x3e\xd0\xb6\xcf\xc9"),
["\xfe\x44\x61\x08\xb1\xd0\x1a\xb7\x8a\x62\xcc\xfe\xab\x6a\xb2\xb2\xba\xbf\xf3\xab\xda\xd8\x0a\x4d\x8b\x30\xdf\x2d\x00\x08\x83\x0c"] = CTInfo($description="DigiCert Nessie2019 Log", $operator="DigiCert", $url="nessie2019.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x5f\xed\x27\xb9\xd0\x8a\x22\x67\x7b\x40\x2b\x5e\x96\x13\x2b\x0d\x6d\x0e\x5e\x78\xb9\x44\x4d\x74\xb6\x28\x82\x95\x97\xac\x9a\xbc\x14\x93\x68\x87\x2c\x2a\x13\x1c\x75\x55\xfb\x28\x39\x0f\x89\xff\xaf\x10\x91\x57\x24\x61\x8a\x43\xe9\x54\x33\x8b\x30\xbc\x49\x68"),
["\xc6\x52\xa0\xec\x48\xce\xb3\xfc\xab\x17\x09\x92\xc4\x3a\x87\x41\x33\x09\xe8\x00\x65\xa2\x62\x52\x40\x1b\xa3\x36\x2a\x17\xc5\x65"] = CTInfo($description="DigiCert Nessie2020 Log", $operator="DigiCert", $url="nessie2020.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xe2\x11\xc8\xc8\xc5\x48\xad\x1f\x68\x4a\x18\x1b\x40\xc6\x04\x93\xc5\x97\xd6\x59\xa4\x7c\x52\x81\xe3\x8f\x06\x9f\xdd\xca\x6e\xc6\x67\x9f\x09\x63\x0c\x76\x3a\x31\x0a\x84\x9d\x67\xca\x1a\x03\x0e\xab\x48\x21\xdd\x02\xb8\xf1\xce\x59\x07\x75\x0a\x48\x81\x59\xe2"),
["\xee\xc0\x95\xee\x8d\x72\x64\x0f\x92\xe3\xc3\xb9\x1b\xc7\x12\xa3\x69\x6a\x09\x7b\x4b\x6a\x1a\x14\x38\xe6\x47\xb2\xcb\xed\xc5\xf9"] = CTInfo($description="DigiCert Nessie2021 Log", $operator="DigiCert", $url="nessie2021.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xf6\x8e\xc0\x8b\x0a\xdb\x18\x12\x17\xe8\xb9\xdc\xe3\xb2\x3a\x39\xf2\xcc\x75\x99\xd1\xcc\xaa\x0f\xe6\xed\x3b\xda\x70\x62\xea\xfa\x48\x38\x4a\x28\x92\xd4\xe2\xd6\x03\x70\x95\x13\xf3\x18\x2d\xb2\x48\x67\xee\x73\x5c\x4b\x0d\xe6\x80\xff\x04\x85\x1a\x0a\x58\x16"),
["\x51\xa3\xb0\xf5\xfd\x01\x79\x9c\x56\x6d\xb8\x37\x78\x8f\x0c\xa4\x7a\xcc\x1b\x27\xcb\xf7\x9e\x88\x42\x9a\x0d\xfe\xd4\x8b\x05\xe5"] = CTInfo($description="DigiCert Nessie2022 Log", $operator="DigiCert", $url="nessie2022.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x27\x24\xdd\x68\x03\x28\xcb\xfe\x63\xbe\x0e\x11\x47\x4d\x7d\x17\x68\xa1\x11\x5d\x4c\x71\xc9\x41\x28\xc7\xb6\xa2\x4b\x97\xec\xc0\xaf\xfc\x2f\x3b\xbf\xe9\xf1\xb1\xfc\xf5\x01\xff\xa9\xfb\x49\x40\x0c\x63\x24\x98\xd7\x79\x2e\xa6\x55\xab\x16\xc6\xbe\x51\xd8\x71"),
["\xb3\x73\x77\x07\xe1\x84\x50\xf8\x63\x86\xd6\x05\xa9\xdc\x11\x09\x4a\x79\x2d\xb1\x67\x0c\x0b\x87\xdc\xf0\x03\x0e\x79\x36\xa5\x9a"] = CTInfo($description="DigiCert Nessie2023 Log", $operator="DigiCert", $url="nessie2023.ct.digicert.com/log/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x11\x7b\xbc\x89\x0c\x12\x09\x14\x9f\xd8\x26\xc8\x4c\x6a\x54\xa4\x1b\x45\x56\xdf\x3e\x23\x42\x14\xd1\xdd\x42\xdf\xa2\xdf\x7b\x5f\x9f\x6f\x07\x5a\x23\x46\x79\x16\x4b\x5f\x33\x67\xc1\xa0\x8d\x5b\x5c\x17\x75\xf2\x4d\xa0\x80\xa1\x98\x1a\x07\x59\x06\x02\xca\x4e"),
["\xdd\xeb\x1d\x2b\x7a\x0d\x4f\xa6\x20\x8b\x81\xad\x81\x68\x70\x7e\x2e\x8e\x9d\x01\xd5\x5c\x88\x8d\x3d\x11\xc4\xcd\xb6\xec\xbe\xcc"] = CTInfo($description="Symantec log", $operator="DigiCert", $url="ct.ws.symantec.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x96\xea\xac\x1c\x46\x0c\x1b\x55\xdc\x0d\xfc\xb5\x94\x27\x46\x57\x42\x70\x3a\x69\x18\xe2\xbf\x3b\xc4\xdb\xab\xa0\xf4\xb6\x6c\xc0\x53\x3f\x4d\x42\x10\x33\xf0\x58\x97\x8f\x6b\xbe\x72\xf4\x2a\xec\x1c\x42\xaa\x03\x2f\x1a\x7e\x28\x35\x76\x99\x08\x3d\x21\x14\x86"),
["\xbc\x78\xe1\xdf\xc5\xf6\x3c\x68\x46\x49\x33\x4d\xa1\x0f\xa1\x5f\x09\x79\x69\x20\x09\xc0\x81\xb4\xf3\xf6\x91\x7f\x3e\xd9\xb8\xa5"] = CTInfo($description="Symantec 'Vega' log", $operator="DigiCert", $url="vega.ws.symantec.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xea\x95\x9e\x02\xff\xee\xf1\x33\x6d\x4b\x87\xbc\xcd\xfd\x19\x17\x62\xff\x94\xd3\xd0\x59\x07\x3f\x02\x2d\x1c\x90\xfe\xc8\x47\x30\x3b\xf1\xdd\x0d\xb8\x11\x0c\x5d\x1d\x86\xdd\xab\xd3\x2b\x46\x66\xfb\x6e\x65\xb7\x3b\xfd\x59\x68\xac\xdf\xa6\xf8\xce\xd2\x18\x4d"),
["\xa7\xce\x4a\x4e\x62\x07\xe0\xad\xde\xe5\xfd\xaa\x4b\x1f\x86\x76\x87\x67\xb5\xd0\x02\xa5\x5d\x47\x31\x0e\x7e\x67\x0a\x95\xea\xb2"] = CTInfo($description="Symantec Deneb", $operator="DigiCert", $url="deneb.ws.symantec.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x96\x82\x1e\xa3\xcd\x3a\x80\x84\x1e\x97\xb8\xb7\x07\x19\xae\x76\x1a\x0e\xf8\x55\x76\x9d\x12\x33\x4e\x91\x88\xe4\xd0\x48\x50\x5c\xc1\x9f\x6a\x72\xd6\x01\xf5\x14\xd6\xd0\x38\x6e\xe1\x32\xbc\x67\x0d\x37\xe8\xba\x22\x10\xd1\x72\x86\x79\x28\x96\xf9\x17\x1e\x98"),
["\x15\x97\x04\x88\xd7\xb9\x97\xa0\x5b\xeb\x52\x51\x2a\xde\xe8\xd2\xe8\xb4\xa3\x16\x52\x64\x12\x1a\x9f\xab\xfb\xd5\xf8\x5a\xd9\x3f"] = CTInfo($description="Symantec 'Sirius' log", $operator="DigiCert", $url="sirius.ws.symantec.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xa3\x02\x64\x84\x22\xbb\x25\xec\x0d\xe3\xbc\xc2\xc9\x89\x7d\xdd\x45\xd0\xee\xe6\x15\x85\x8f\xd9\xe7\x17\x1b\x13\x80\xea\xed\xb2\x85\x37\xad\x6a\xc5\xd8\x25\x9d\xfa\xf4\xb4\xf3\x6e\x16\x28\x25\x37\xea\xa3\x37\x64\xb2\xc7\x0b\xfd\x51\xe5\xc1\x05\xf4\x0e\xb5"),
["\xcd\xb5\x17\x9b\x7f\xc1\xc0\x46\xfe\xea\x31\x13\x6a\x3f\x8f\x00\x2e\x61\x82\xfa\xf8\x89\x6f\xec\xc8\xb2\xf5\xb5\xab\x60\x49\x00"] = CTInfo($description="Certly.IO log", $operator="Certly", $url="log.certly.io/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x0b\x23\xcb\x85\x62\x98\x61\x48\x04\x73\xeb\x54\x5d\xf3\xd0\x07\x8c\x2d\x19\x2d\x8c\x36\xf5\xeb\x8f\x01\x42\x0a\x7c\x98\x26\x27\xc1\xb5\xdd\x92\x93\xb0\xae\xf8\x9b\x3d\x0c\xd8\x4c\x4e\x1d\xf9\x15\xfb\x47\x68\x7b\xba\x66\xb7\x25\x9c\xd0\x4a\xc2\x66\xdb\x48"),
["\x74\x61\xb4\xa0\x9c\xfb\x3d\x41\xd7\x51\x59\x57\x5b\x2e\x76\x49\xa4\x45\xa8\xd2\x77\x09\xb0\xcc\x56\x4a\x64\x82\xb7\xeb\x41\xa3"] = CTInfo($description="Izenpe log", $operator="Izenpe", $url="ct.izenpe.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x27\x64\x39\x0c\x2d\xdc\x50\x18\xf8\x21\x00\xa2\x0e\xed\x2c\xea\x3e\x75\xba\x9f\x93\x64\x09\x00\x11\xc4\x11\x17\xab\x5c\xcf\x0f\x74\xac\xb5\x97\x90\x93\x00\x5b\xb8\xeb\xf7\x27\x3d\xd9\xb2\x0a\x81\x5f\x2f\x0d\x75\x38\x94\x37\x99\x1e\xf6\x07\x76\xe0\xee\xbe"),
["\x89\x41\x44\x9c\x70\x74\x2e\x06\xb9\xfc\x9c\xe7\xb1\x16\xba\x00\x24\xaa\x36\xd5\x9a\xf4\x4f\x02\x04\x40\x4f\x00\xf7\xea\x85\x66"] = CTInfo($description="Izenpe 'Argi' log", $operator="Izenpe", $url="ct.izenpe.eus/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd7\xc8\x0e\x23\x3e\x9e\x02\x3c\x9a\xb8\x07\x4a\x2a\x05\xff\x4a\x4b\x88\xd4\x8a\x4d\x39\xce\xf7\xc5\xf2\xb6\x37\xe9\xa3\xed\xe4\xf5\x45\x09\x0e\x67\x14\xfd\x53\x24\xd5\x3a\x94\xf2\xea\xb5\x13\xd9\x1d\x8b\x5c\xa7\xc3\xf3\x6b\xd8\x3f\x2d\x3b\x65\x72\x58\xd6"),
["\x9e\x4f\xf7\x3d\xc3\xce\x22\x0b\x69\x21\x7c\x89\x9e\x46\x80\x76\xab\xf8\xd7\x86\x36\xd5\xcc\xfc\x85\xa3\x1a\x75\x62\x8b\xa8\x8b"] = CTInfo($description="WoSign CT log #1", $operator="WoSign", $url="ct.wosign.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd7\xec\x2f\x2b\x75\x4f\x37\xbc\xa3\x43\xba\x8b\x65\x66\x3c\x7d\x6a\xe5\x0c\x2a\xa6\xc2\xe5\x26\xfe\x0c\x7d\x4e\x7c\xf0\x3a\xbc\xe2\xd3\x22\xdc\x01\xd0\x1f\x6e\x43\x9c\x5c\x6e\x83\xad\x9c\x15\xf6\xc4\x8d\x60\xb5\x1d\xbb\xa3\x62\x69\x7e\xeb\xa7\xaa\x01\x9b"),
["\x41\xb2\xdc\x2e\x89\xe6\x3c\xe4\xaf\x1b\xa7\xbb\x29\xbf\x68\xc6\xde\xe6\xf9\xf1\xcc\x04\x7e\x30\xdf\xfa\xe3\xb3\xba\x25\x92\x63"] = CTInfo($description="WoSign log", $operator="WoSign", $url="ctlog.wosign.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xcc\x11\x88\x7b\x2d\x66\xcb\xae\x8f\x4d\x30\x66\x27\x19\x25\x22\x93\x21\x46\xb4\x2f\x01\xd3\xc6\xf9\x2b\xd5\xc8\xba\x73\x9b\x06\xa2\xf0\x8a\x02\x9c\xd0\x6b\x46\x18\x30\x85\xba\xe9\x24\x8b\x0e\xd1\x5b\x70\x28\x0c\x7e\xf1\x3a\x45\x7f\x5a\xf3\x82\x42\x60\x31"),
["\x63\xd0\x00\x60\x26\xdd\xe1\x0b\xb0\x60\x1f\x45\x24\x46\x96\x5e\xe2\xb6\xea\x2c\xd4\xfb\xc9\x5a\xc8\x66\xa5\x50\xaf\x90\x75\xb7"] = CTInfo($description="WoSign log 2", $operator="WoSign", $url="ctlog2.wosign.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xa5\x8c\xe8\x35\x2e\x8e\xe5\x6a\x75\xad\x5c\x4b\x31\x61\x29\x9d\x30\x57\x8e\x02\x13\x5f\xe9\xca\xbb\x52\xa8\x43\x05\x60\xbf\x0d\x73\x57\x77\xb2\x05\xd8\x67\xf6\xf0\x33\xc9\xf9\x44\xde\xb6\x53\x73\xaa\x0c\x55\xc2\x83\x0a\x4b\xce\x5e\x1a\xc7\x17\x1d\xb3\xcd"),
["\xc9\xcf\x89\x0a\x21\x10\x9c\x66\x6c\xc1\x7a\x3e\xd0\x65\xc9\x30\xd0\xe0\x13\x5a\x9f\xeb\xa8\x5a\xf1\x42\x10\xb8\x07\x24\x21\xaa"] = CTInfo($description="GDCA CT log #1", $operator="Wang Shengnan", $url="ct.gdca.com.cn/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xad\x0f\x30\xad\x9e\x79\xa4\x38\x89\x26\x54\x86\xab\x41\x72\x90\x6f\xfb\xca\x17\xa6\xac\xee\xc6\x9f\x7d\x02\x05\xec\x41\xa8\xc7\x41\x9d\x32\x49\xad\xb0\x39\xbd\x3a\x87\x3e\x7c\xee\x68\x6c\x60\xd1\x47\x2a\x93\xae\xe1\x40\xf4\x0b\xc8\x35\x3c\x1d\x0f\x65\xd3"),
["\x92\x4a\x30\xf9\x09\x33\x6f\xf4\x35\xd6\x99\x3a\x10\xac\x75\xa2\xc6\x41\x72\x8e\x7f\xc2\xd6\x59\xae\x61\x88\xff\xad\x40\xce\x01"] = CTInfo($description="GDCA CT log #2", $operator="GDCA", $url="ctlog.gdca.com.cn/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x5b\x4a\xc7\x01\xb7\x74\x54\xba\x40\x9c\x43\x75\x94\x3f\xac\xef\xb3\x71\x56\xb8\xd3\xe2\x7b\xae\xa1\xb1\x3e\x53\xaa\x97\x33\xa1\x82\xbb\x5f\x5d\x1c\x0b\xfa\x85\x0d\xbc\xf7\xe5\xa0\xe0\x22\xf0\xa0\x89\xd9\x0a\x7f\x5f\x26\x94\xd3\x24\xe3\x99\x2e\xe4\x15\x8d"),
["\x71\x7e\xa7\x42\x09\x75\xbe\x84\xa2\x72\x35\x53\xf1\x77\x7c\x26\xdd\x51\xaf\x4e\x10\x21\x44\x09\x4d\x90\x19\xb4\x62\xfb\x66\x68"] = CTInfo($description="GDCA Log 1", $operator="GDCA", $url="log.gdca.com.cn/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xcd\x51\xff\xa9\xef\xab\x82\xbf\xcc\xa3\xea\x30\x43\x4d\x08\xe9\xa7\xa0\x0d\x28\xc7\xb6\xd8\x2a\x40\x19\xa0\xfa\x10\x5d\x4d\x75\xcb\x80\x94\xff\xfb\xc2\xdd\x4a\x08\xdf\x63\xff\x25\x38\x1b\x5f\x8a\xf9\xdd\x27\x34\x25\x4c\xd4\x3d\x2d\xed\x5c\x53\xac\x17\x35"),
["\x14\x30\x8d\x90\xcc\xd0\x30\x13\x50\x05\xc0\x1c\xa5\x26\xd8\x1e\x84\xe8\x76\x24\xe3\x9b\x62\x48\xe0\x8f\x72\x4a\xea\x3b\xb4\x2a"] = CTInfo($description="GDCA Log 2", $operator="GDCA", $url="log2.gdca.com.cn/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xc7\xac\xc2\xf3\x00\x9b\x32\xcd\xdf\xa5\x05\xa7\x48\x27\xa8\xd9\x1b\xc6\xf3\x6e\xc0\x12\xcb\x06\x90\xc6\xbe\xe5\x15\x48\xa5\x11\x74\x48\x33\xb8\x84\x5b\x27\xd8\xf8\x75\x97\xc9\x9a\xad\x20\xae\xc1\x52\xe1\xbf\x42\x48\x37\x76\xab\x4b\x36\xd4\x89\xe5\xfa\x1c"),
["\xdb\x76\xfd\xad\xac\x65\xe7\xd0\x95\x08\x88\x6e\x21\x59\xbd\x8b\x90\x35\x2f\x5f\xea\xd3\xe3\xdc\x5e\x22\xeb\x35\x0a\xcc\x7b\x98"] = CTInfo($description="Sectigo 'Dodo' CT log", $operator="Sectigo", $url="dodo.ct.comodo.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x2c\xf5\xc2\x31\xf5\x63\x43\x6a\x16\x4a\x0a\xde\xc2\xee\x1f\x21\x6e\x12\x7e\x1d\xe5\x72\x8f\x74\x0b\x02\x99\xd3\xad\x69\xbc\x02\x35\x79\xf9\x61\xe9\xcf\x00\x08\x4f\x74\xa4\xa3\x34\x9a\xe0\x43\x1c\x23\x7e\x8f\x41\xd5\xee\xc7\x1c\xa3\x82\x8a\x40\xfa\xaa\xe0"),
["\xac\x3b\x9a\xed\x7f\xa9\x67\x47\x57\x15\x9e\x6d\x7d\x57\x56\x72\xf9\xd9\x81\x00\x94\x1e\x9b\xde\xff\xec\xa1\x31\x3b\x75\x78\x2d"] = CTInfo($description="Venafi log", $operator="Venafi", $url="ctlog.api.venafi.com/", $maximum_merge_delay=86400, $key="\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xa2\x5a\x48\x1f\x17\x52\x95\x35\xcb\xa3\x5b\x3a\x1f\x53\x82\x76\x94\xa3\xff\x80\xf2\x1c\x37\x3c\xc0\xb1\xbd\xc1\x59\x8b\xab\x2d\x65\x93\xd7\xf3\xe0\x04\xd5\x9a\x6f\xbf\xd6\x23\x76\x36\x4f\x23\x99\xcb\x54\x28\xad\x8c\x15\x4b\x65\x59\x76\x41\x4a\x9c\xa6\xf7\xb3\x3b\x7e\xb1\xa5\x49\xa4\x17\x51\x6c\x80\xdc\x2a\x90\x50\x4b\x88\x24\xe9\xa5\x12\x32\x93\x04\x48\x90\x02\xfa\x5f\x0e\x30\x87\x8e\x55\x76\x05\xee\x2a\x4c\xce\xa3\x6a\x69\x09\x6e\x25\xad\x82\x76\x0f\x84\x92\xfa\x38\xd6\x86\x4e\x24\x8f\x9b\xb0\x72\xcb\x9e\xe2\x6b\x3f\xe1\x6d\xc9\x25\x75\x23\x88\xa1\x18\x58\x06\x23\x33\x78\xda\x00\xd0\x38\x91\x67\xd2\xa6\x7d\x27\x97\x67\x5a\xc1\xf3\x2f\x17\xe6\xea\xd2\x5b\xe8\x81\xcd\xfd\x92\x68\xe7\xf3\x06\xf0\xe9\x72\x84\xee\x01\xa5\xb1\xd8\x33\xda\xce\x83\xa5\xdb\xc7\xcf\xd6\x16\x7e\x90\x75\x18\xbf\x16\xdc\x32\x3b\x6d\x8d\xab\x82\x17\x1f\x89\x20\x8d\x1d\x9a\xe6\x4d\x23\x08\xdf\x78\x6f\xc6\x05\xbf\x5f\xae\x94\x97\xdb\x5f\x64\xd4\xee\x16\x8b\xa3\x84\x6c\x71\x2b\xf1\xab\x7f\x5d\x0d\x32\xee\x04\xe2\x90\xec\x41\x9f\xfb\x39\xc1\x02\x03\x01\x00\x01"),
["\x03\x01\x9d\xf3\xfd\x85\xa6\x9a\x8e\xbd\x1f\xac\xc6\xda\x9b\xa7\x3e\x46\x97\x74\xfe\x77\xf5\x79\xfc\x5a\x08\xb8\x32\x8c\x1d\x6b"] = CTInfo($description="Venafi Gen2 CT log", $operator="Venafi", $url="ctlog-gen2.api.venafi.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x8e\x27\x27\x7a\xb6\x55\x09\x74\xeb\x6c\x4b\x94\x84\x65\xbc\xe4\x15\xf1\xea\x5a\xd8\x7c\x0e\x37\xce\xba\x3f\x6c\x09\xda\xe7\x29\x96\xd3\x45\x50\x6f\xde\x1e\xb4\x1c\xd2\x83\x88\xff\x29\x2f\xce\xa9\xff\xdf\x34\xde\x75\x0f\xc0\xcc\x18\x0d\x94\x2e\xfc\x37\x01"),
["\xa5\x77\xac\x9c\xed\x75\x48\xdd\x8f\x02\x5b\x67\xa2\x41\x08\x9d\xf8\x6e\x0f\x47\x6e\xc2\x03\xc2\xec\xbe\xdb\x18\x5f\x28\x26\x38"] = CTInfo($description="CNNIC CT log", $operator="CNNIC", $url="ctserver.cnnic.cn/", $maximum_merge_delay=86400, $key="\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xbf\xb5\x08\x61\x9a\x29\x32\x04\xd3\x25\x63\xe9\xd8\x85\xe1\x86\xe0\x1f\xd6\x5e\x9a\xf7\x33\x3b\x80\x1b\xe7\xb6\x3e\x5f\x2d\xa1\x66\xf6\x95\x4a\x84\xa6\x21\x56\x79\xe8\xf7\x85\xee\x5d\xe3\x7c\x12\xc0\xe0\x89\x22\x09\x22\x3e\xba\x16\x95\x06\xbd\xa8\xb9\xb1\xa9\xb2\x7a\xd6\x61\x2e\x87\x11\xb9\x78\x40\x89\x75\xdb\x0c\xdc\x90\xe0\xa4\x79\xd6\xd5\x5e\x6e\xd1\x2a\xdb\x34\xf4\x99\x3f\x65\x89\x3b\x46\xc2\x29\x2c\x15\x07\x1c\xc9\x4b\x1a\x54\xf8\x6c\x1e\xaf\x60\x27\x62\x0a\x65\xd5\x9a\xb9\x50\x36\x16\x6e\x71\xf6\x1f\x01\xf7\x12\xa7\xfc\xbf\xf6\x21\xa3\x29\x90\x86\x2d\x77\xde\xbb\x4c\xd4\xcf\xfd\xd2\xcf\x82\x2c\x4d\xd4\xf2\xc2\x2d\xac\xa9\xbe\xea\xc3\x19\x25\x43\xb2\xe5\x9a\x6c\x0d\xc5\x1c\xa5\x8b\xf7\x3f\x30\xaf\xb9\x01\x91\xb7\x69\x12\x12\xe5\x83\x61\xfe\x34\x00\xbe\xf6\x71\x8a\xc7\xeb\x50\x92\xe8\x59\xfe\x15\x91\xeb\x96\x97\xf8\x23\x54\x3f\x2d\x8e\x07\xdf\xee\xda\xb3\x4f\xc8\x3c\x9d\x6f\xdf\x3c\x2c\x43\x57\xa1\x47\x0c\x91\x04\xf4\x75\x4d\xda\x89\x81\xa4\x14\x06\x34\xb9\x98\xc3\xda\xf1\xfd\xed\x33\x36\xd3\x16\x2d\x35\x02\x03\x01\x00\x01"),
["\x34\xbb\x6a\xd6\xc3\xdf\x9c\x03\xee\xa8\xa4\x99\xff\x78\x91\x48\x6c\x9d\x5e\x5c\xac\x92\xd0\x1f\x7b\xfd\x1b\xce\x19\xdb\x48\xef"] = CTInfo($description="StartCom log", $operator="StartCom", $url="ct.startssl.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x48\xf3\x59\xf3\xf6\x05\x18\xd3\xdb\xb2\xed\x46\x7e\xcf\xc8\x11\xb5\x57\xb1\xa8\xd6\x4c\xe6\x9f\xb7\x4a\x1a\x14\x86\x43\xa9\x48\xb0\xcb\x5a\x3f\x3c\x4a\xca\xdf\xc4\x82\x14\x55\x9a\xf8\xf7\x8e\x40\x55\xdc\xf4\xd2\xaf\xea\x75\x74\xfb\x4e\x7f\x60\x86\x2e\x51"),
["\xe0\x12\x76\x29\xe9\x04\x96\x56\x4e\x3d\x01\x47\x98\x44\x98\xaa\x48\xf8\xad\xb1\x66\x00\xeb\x79\x02\xa1\xef\x99\x09\x90\x62\x73"] = CTInfo($description="PuChuangSiDa CT log", $operator="Beijing PuChuangSiDa Technology Ltd.", $url="www.certificatetransparency.cn/ct/", $maximum_merge_delay=86400, $key="\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xac\xcf\x2f\x4b\x70\xac\xf1\x0d\x96\xbf\xe8\x0a\xfe\x44\x9d\xd4\x8c\x17\x9d\xc3\x9a\x10\x11\x84\x13\xed\x8c\xf9\x37\x6d\x83\xe4\x00\x6f\xb1\x4b\xc0\xa6\x89\xc7\x61\x8f\x9a\x34\xbb\x56\x52\xca\x03\x56\x50\xef\x24\x7f\x4b\x49\xe9\x35\x81\xdd\xf0\xe7\x17\xf5\x72\xd2\x23\xc5\xe3\x13\x7f\xd7\x8e\x78\x35\x8f\x49\xde\x98\x04\x8a\x63\xaf\xad\xa2\x39\x70\x95\x84\x68\x4b\x91\x33\xfe\x4c\xe1\x32\x17\xc2\xf2\x61\xb8\x3a\x8d\x39\x7f\xd5\x95\x82\x3e\x56\x19\x50\x45\x6f\xcb\x08\x33\x0d\xd5\x19\x42\x08\x1a\x48\x42\x10\xf1\x68\xc3\xc3\x41\x13\xcb\x0d\x1e\xdb\x02\xb7\x24\x7a\x51\x96\x6e\xbc\x08\xea\x69\xaf\x6d\xef\x92\x98\x8e\x55\xf3\x65\xe5\xe8\x9c\xbe\x1a\x47\x60\x30\x7d\x7a\x80\xad\x56\x83\x7a\x93\xc3\xae\x93\x2b\x6a\x28\x8a\xa6\x5f\x63\x19\x0c\xbe\x7c\x7b\x21\x63\x41\x38\xb7\xf7\xe8\x76\x73\x6b\x85\xcc\xbc\x72\x2b\xc1\x52\xd0\x5b\x5d\x31\x4e\x9d\x2a\xf3\x4d\x9b\x64\x14\x99\x26\xc6\x71\xf8\x7b\xf8\x44\xd5\xe3\x23\x20\xf3\x0a\xd7\x8b\x51\x3e\x72\x80\xd2\x78\x78\x35\x2d\x4a\xe7\x40\x99\x11\x95\x34\xd4\x2f\x7f\xf9\x5f\x35\x37\x02\x03\x01\x00\x01"),
["\x55\x81\xd4\xc2\x16\x90\x36\x01\x4a\xea\x0b\x9b\x57\x3c\x53\xf0\xc0\xe4\x38\x78\x70\x25\x08\x17\x2f\xa3\xaa\x1d\x07\x13\xd3\x0c"] = CTInfo($description="Sectigo 'Sabre' CT log", $operator="Sectigo", $url="sabre.ct.comodo.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xf2\x6f\xd2\x89\x0f\x3f\xc5\xf8\x87\x1e\xab\x65\xb3\xd9\xbb\x17\x23\x8c\x06\x0e\x09\x55\x96\x3d\x0a\x08\xa2\xc5\x71\xb3\xd1\xa9\x2f\x28\x3e\x83\x10\xbf\x12\xd0\x44\x66\x15\xef\x54\xe1\x98\x80\xd0\xce\x24\x6d\x3e\x67\x9a\xe9\x37\x23\xce\x52\x93\x86\xda\x80"),
["\x6f\x53\x76\xac\x31\xf0\x31\x19\xd8\x99\x00\xa4\x51\x15\xff\x77\x15\x1c\x11\xd9\x02\xc1\x00\x29\x06\x8d\xb2\x08\x9a\x37\xd9\x13"] = CTInfo($description="Sectigo 'Mammoth' CT log", $operator="Sectigo", $url="mammoth.ct.comodo.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xef\xe4\x7d\x74\x2e\x15\x15\xb6\xe9\xbb\x23\x8b\xfb\x2c\xb5\xe1\xc7\x80\x98\x47\xfb\x40\x69\x68\xfc\x49\xad\x61\x4e\x83\x47\x3c\x1a\xb7\x8d\xdf\xff\x7b\x30\xb4\xba\xff\x2f\xcb\xa0\x14\xe3\xad\xd5\x85\x3f\x44\x59\x8c\x8c\x60\x8b\xd7\xb8\xb1\xbf\xae\x8c\x67"),
["\x53\x7b\x69\xa3\x56\x43\x35\xa9\xc0\x49\x04\xe3\x95\x93\xb2\xc2\x98\xeb\x8d\x7a\x6e\x83\x02\x36\x35\xc6\x27\x24\x8c\xd6\xb4\x40"] = CTInfo($description="Nordu 'flimsy' log", $operator="NORDUnet", $url="flimsy.ct.nordu.net:8080/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xe2\xa5\xaa\xe9\xa7\xe1\x05\x48\xb4\x39\xd7\x16\x51\x88\x72\x24\xb3\x57\x4e\x41\xaa\x43\xd3\xcc\x4b\x99\x6a\xa0\x28\x24\x57\x68\x75\x66\xfa\x4d\x8c\x11\xf6\xbb\xc5\x1b\x81\xc3\x90\xc2\xa0\xe8\xeb\xac\xfa\x05\x64\x09\x1a\x89\x68\xcd\x96\x26\x34\x71\x36\x91"),
["\xaa\xe7\x0b\x7f\x3c\xb8\xd5\x66\xc8\x6c\x2f\x16\x97\x9c\x9f\x44\x5f\x69\xab\x0e\xb4\x53\x55\x89\xb2\xf7\x7a\x03\x01\x04\xf3\xcd"] = CTInfo($description="Nordu 'plausible' log", $operator="NORDUnet", $url="plausible.ct.nordu.net/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xf5\x45\x7d\xfa\x33\xb6\x30\x24\xf3\x91\xa6\xe8\x74\xed\x85\xec\xb3\x34\xdc\xc5\x01\x73\xc3\x2b\x74\x0b\x64\x71\x6e\xaf\xe8\x60\x3d\xb5\xa4\xd3\xc3\xd4\x09\xaa\x87\xe6\xd0\x16\xdd\x02\xc6\xed\x24\xbf\xee\x9f\x21\x1f\xd3\x32\x24\x46\x05\xe3\x8f\x36\x98\xa9"),
["\xcf\x55\xe2\x89\x23\x49\x7c\x34\x0d\x52\x06\xd0\x53\x53\xae\xb2\x58\x34\xb5\x2f\x1f\x8d\xc9\x52\x68\x09\xf2\x12\xef\xdd\x7c\xa6"] = CTInfo($description="SHECA CT log 1", $operator="SHECA", $url="ctlog.sheca.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x11\xa9\x60\x2b\xb4\x71\x45\x66\xe0\x2e\xde\xd5\x87\x3b\xd5\xfe\xf0\x92\x37\xf4\x68\xc6\x92\xdd\x3f\x1a\xe2\xbc\x0c\x22\xd6\x99\x63\x29\x6e\x32\x28\x14\xc0\x76\x2c\x80\xa8\x22\x51\x91\xd6\xeb\xa6\xd8\xf1\xec\xf0\x07\x7e\xb0\xfc\x76\x70\x76\x72\x7c\x91\xe9"),
["\x32\xdc\x59\xc2\xd4\xc4\x19\x68\xd5\x6e\x14\xbc\x61\xac\x8f\x0e\x45\xdb\x39\xfa\xf3\xc1\x55\xaa\x42\x52\xf5\x00\x1f\xa0\xc6\x23"] = CTInfo($description="SHECA CT log 2", $operator="SHECA", $url="ct.sheca.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xb1\x8e\x1d\x8a\xaa\x3a\xac\xce\x86\xcb\x53\x76\xe8\xa8\x9d\x59\xbe\x17\x88\x03\x07\xf2\x27\xe0\x82\xbe\xb1\xfc\x67\x3b\x46\xee\xd3\xf1\x8d\xd6\x77\xe8\xa3\xb4\xdb\x09\x5c\xa0\x09\x43\xfc\x5f\xd0\x68\x34\x23\x24\x08\xc2\x4f\xd8\xd2\xb6\x9d\xed\xd5\x8c\xdb"),
["\x96\x06\xc0\x2c\x69\x00\x33\xaa\x1d\x14\x5f\x59\xc6\xe2\x64\x8d\x05\x49\xf0\xdf\x96\xaa\xb8\xdb\x91\x5a\x70\xd8\xec\xf3\x90\xa5"] = CTInfo($description="Akamai CT Log", $operator="Akamai", $url="ct.akamai.com/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x43\x79\xeb\x49\x5c\x50\x2a\x4a\x6a\x8f\x59\x93\xbc\xc3\x42\x76\xc2\x99\xf8\x27\x81\x3c\x06\x6c\xd2\xc8\x04\x8f\x74\x7b\xb4\xb5\x21\xf2\xe3\xa8\xdc\x33\xb9\xfe\x25\xe9\x3d\x04\xfc\x3f\xb4\xae\x40\xe3\x45\x7e\x84\x92\x2a\xd8\x52\xeb\x1f\x3f\x73\x13\xd0\xc8"),
["\x39\x37\x6f\x54\x5f\x7b\x46\x07\xf5\x97\x42\xd7\x68\xcd\x5d\x24\x37\xbf\x34\x73\xb6\x53\x4a\x48\x34\xbc\xf7\x2e\x68\x1c\x83\xc9"] = CTInfo($description="Alpha CT Log", $operator="Matt Palmer", $url="alpha.ctlogs.org/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xa2\xf7\xed\x13\xe1\xd3\x5c\x02\x08\xc4\x8e\x8b\x9b\x8b\x3b\x39\x68\xc7\x92\x6a\x38\xa1\x4f\x23\xc5\xa5\x6f\x6f\xd7\x65\x81\xf8\xc1\x9b\xf4\x9f\xa9\x8b\x45\xf4\xb9\x4e\x1b\xc9\xa2\x69\x17\xa5\x78\x87\xd9\xce\x88\x6f\x41\x03\xbb\xa3\x2a\xe3\x77\x97\x8d\x78"),
["\x65\x9b\x33\x50\xf4\x3b\x12\xcc\x5e\xa5\xab\x4e\xc7\x65\xd3\xfd\xe6\xc8\x82\x43\x77\x77\x78\xe7\x20\x03\xf9\xeb\x2b\x8c\x31\x29"] = CTInfo($description="Let's Encrypt 'Oak2019' log", $operator="Let's Encrypt", $url="oak.ct.letsencrypt.org/2019/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x16\x4a\x8d\x29\x1b\x99\xf9\x9f\x08\x3a\xc9\xcd\x26\xb5\x19\xf2\x0c\x29\xf8\xa2\x86\x39\xd4\x2b\x27\xf1\xca\x85\x26\x9d\x2b\xf4\x89\xba\x80\x9c\xf7\xe7\x71\x43\xb6\xb0\xa6\x65\x88\x32\xbf\x52\x29\xc4\x1e\x1b\x9b\x91\xcc\x6d\x64\x55\x2c\x85\x37\x85\x9f\x73"),
["\xe7\x12\xf2\xb0\x37\x7e\x1a\x62\xfb\x8e\xc9\x0c\x61\x84\xf1\xea\x7b\x37\xcb\x56\x1d\x11\x26\x5b\xf3\xe0\xf3\x4b\xf2\x41\x54\x6e"] = CTInfo($description="Let's Encrypt 'Oak2020' log", $operator="Let's Encrypt", $url="oak.ct.letsencrypt.org/2020/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x7f\x36\xf8\xd9\x97\x6b\xfe\x1e\xe1\x82\xa8\x03\x0a\x8d\x6f\xac\xd2\x6a\x1a\xa6\xdc\x52\xf2\x46\x24\x41\x11\xf4\x30\xea\xa7\x5f\x56\xff\x01\x52\x07\x49\x75\xeb\xa8\x43\xf9\x4e\x5c\x01\x77\x21\xa4\x7f\x19\x90\x13\x54\xba\xf3\x87\x4b\x43\xa7\x27\x8a\x09\xad"),
["\x94\x20\xbc\x1e\x8e\xd5\x8d\x6c\x88\x73\x1f\x82\x8b\x22\x2c\x0d\xd1\xda\x4d\x5e\x6c\x4f\x94\x3d\x61\xdb\x4e\x2f\x58\x4d\xa2\xc2"] = CTInfo($description="Let's Encrypt 'Oak2021' log", $operator="Let's Encrypt", $url="oak.ct.letsencrypt.org/2021/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x2e\xc6\x33\x18\xc3\x70\xa3\xca\xc1\x22\x56\xa4\x94\x12\x1d\x98\x3d\x8e\x7e\x7e\x87\x91\xfa\xe3\x2b\x45\x24\xcf\x5b\x8e\x21\x40\xba\x2e\x6d\x23\x4c\x84\xc2\x5e\x1a\x08\x76\x3b\x3b\x26\x4c\x97\x9f\x0b\xb0\x62\x22\x58\x88\x7e\xec\x13\x56\x1e\x2a\xef\x24\xf7"),
["\xdf\xa5\x5e\xab\x68\x82\x4f\x1f\x6c\xad\xee\xb8\x5f\x4e\x3e\x5a\xea\xcd\xa2\x12\xa4\x6a\x5e\x8e\x3b\x12\xc0\x20\x44\x5c\x2a\x73"] = CTInfo($description="Let's Encrypt 'Oak2022' log", $operator="Let's Encrypt", $url="oak.ct.letsencrypt.org/2022/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x86\x3c\xb1\x0d\x52\x23\x5a\xde\x6e\xf6\xc0\x7f\xa3\x64\xbc\xad\xc1\x89\xda\x97\x59\x4c\x60\x3c\xf8\x8a\x57\x84\x8f\xed\xbc\xa0\x63\x12\x51\x84\xe6\xbd\xdd\x7b\x8c\x80\x7d\xe3\x8f\x86\xa4\xea\xa9\xcf\xa8\xee\xf3\xe0\x5e\x70\xe0\xbb\xf6\xbd\xfc\x1f\x91\x2e"),
["\xb7\x3e\xfb\x24\xdf\x9c\x4d\xba\x75\xf2\x39\xc5\xba\x58\xf4\x6c\x5d\xfc\x42\xcf\x7a\x9f\x35\xc4\x9e\x1d\x09\x81\x25\xed\xb4\x99"] = CTInfo($description="Let's Encrypt 'Oak2023' log", $operator="Let's Encrypt", $url="oak.ct.letsencrypt.org/2023/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xb3\x3d\x0e\x78\xbe\xe3\xad\x5c\x44\x5c\x9b\xbe\xa3\x84\x16\x41\x82\xca\xca\x89\x17\x1e\x23\xce\x38\xa5\x54\x2f\x7f\xd3\x34\x51\x6a\xb9\x5c\xc3\x49\xea\xfb\x91\x9d\xe0\x8a\x3c\x73\x06\x9f\x7c\x65\x38\x11\x80\xc4\x9a\x5a\x00\xa6\x67\xc3\x83\xef\x89\x85\x51"),
["\x84\x9f\x5f\x7f\x58\xd2\xbf\x7b\x54\xec\xbd\x74\x61\x1c\xea\x45\xc4\x9c\x98\xf1\xd6\x48\x1b\xc6\xf6\x9e\x8c\x17\x4f\x24\xf3\xcf"] = CTInfo($description="Let's Encrypt 'Testflume2019' log", $operator="Let's Encrypt", $url="testflume.ct.letsencrypt.org/2019/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x02\x0d\xfe\xbc\x53\x9e\xb0\x55\xb9\xd6\xb2\x84\x09\xe9\x22\xa0\x0b\x7d\x66\x8e\x74\x6a\xd4\x68\x38\x9d\x2a\x2f\x11\x7b\x0c\x81\x07\xb0\x79\xde\x5c\xb1\x29\x80\xed\x56\x32\xb7\xa5\x79\x1c\xb5\xbc\x46\xd4\x24\x3e\xd3\x5e\x81\xfd\xaf\xca\x92\x0c\x6b\xf2\x7b"),
["\xc6\x3f\x22\x18\xc3\x7d\x56\xa6\xaa\x06\xb5\x96\xda\x8e\x53\xd4\xd7\x15\x6d\x1e\x9b\xac\x8e\x44\xd2\x20\x2d\xe6\x4d\x69\xd9\xdc"] = CTInfo($description="Let's Encrypt 'Testflume2020' log", $operator="Let's Encrypt", $url="testflume.ct.letsencrypt.org/2020/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x8d\xd8\xdc\xa0\xaa\x5e\x05\x28\x47\x80\x7b\xd1\x9b\x70\x71\x0f\x9f\xa5\xf9\xe1\xd9\xb9\xdb\xf7\x2a\x60\xf9\x48\x37\x0b\x70\x8d\x35\x56\x3e\x53\x0d\x39\xb1\x6a\x74\x0a\x0a\x0a\x6f\x9b\xda\x5f\x9d\xf0\x7a\x51\x13\x10\x57\x78\x61\x53\x52\x34\xda\x34\x68\x47"),
["\x03\xed\xf1\xda\x97\x76\xb6\xf3\x8c\x34\x1e\x39\xed\x9d\x70\x7a\x75\x70\x36\x9c\xf9\x84\x4f\x32\x7f\xe9\xe1\x41\x38\x36\x1b\x60"] = CTInfo($description="Let's Encrypt 'Testflume2021' log", $operator="Let's Encrypt", $url="testflume.ct.letsencrypt.org/2021/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x74\x22\xe8\x24\xdb\x75\x41\xc3\x5a\xee\xc3\x43\xa7\xb8\x3b\xa1\x32\x7e\xa3\xf5\x08\x60\x43\x3a\x37\xf2\x19\x59\x3f\x9d\x85\xda\xad\x25\x90\xbe\x00\xe0\xc9\xff\x87\xb1\x74\xec\x06\xd3\x80\x78\x2b\xa5\xab\x37\x55\x41\xd8\x42\xca\x40\x87\x3f\xc1\x4d\x65\x0b"),
["\x23\x27\xef\xda\x35\x25\x10\xdb\xc0\x19\xef\x49\x1a\xe3\xff\x1c\xc5\xa4\x79\xbc\xe3\x78\x78\x36\x0e\xe3\x18\xcf\xfb\x64\xf8\xc8"] = CTInfo($description="Let's Encrypt 'Testflume2022' log", $operator="Let's Encrypt", $url="testflume.ct.letsencrypt.org/2022/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x8f\x2f\xeb\x5d\xc0\x01\xb9\xfd\x32\x86\xb9\xb5\xf9\x78\x23\x0e\x78\x78\x5c\xf0\xfb\xbd\xf3\x28\xc8\x93\xb2\x4f\xe2\x80\xf9\xcd\xb3\xb9\x75\x51\xf7\xcc\x90\x9a\x9f\xc1\x97\x96\x45\x74\x61\x70\x26\xe1\x5c\x56\xb0\xb0\xec\x80\xe8\xd0\x41\x8a\xb3\xb5\x02\xc3"),
["\x55\x34\xb7\xab\x5a\x6a\xc3\xa7\xcb\xeb\xa6\x54\x87\xb2\xa2\xd7\x1b\x48\xf6\x50\xfa\x17\xc5\x19\x7c\x97\xa0\xcb\x20\x76\xf3\xc6"] = CTInfo($description="Let's Encrypt 'Testflume2023' log", $operator="Let's Encrypt", $url="testflume.ct.letsencrypt.org/2023/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xf1\xa2\xe9\x9e\xe9\xaa\x78\x84\xa6\x40\x40\x77\x84\xa3\xe0\xb4\xf2\x50\x1b\x78\xcf\xd8\x87\xed\x7d\xa5\x10\xe1\x63\xd4\x8a\x13\x41\xc1\x43\x84\x93\x54\x7d\x04\xc8\x39\x44\x64\x30\x79\xb5\x92\xb1\x19\x46\x21\x18\x82\xbe\xd1\x3d\xed\x0e\x39\x56\x1d\xe6\xa8"),
["\x29\x6a\xfa\x2d\x56\x8b\xca\x0d\x2e\xa8\x44\x95\x6a\xe9\x72\x1f\xc3\x5f\xa3\x55\xec\xda\x99\x69\x3a\xaf\xd4\x58\xa7\x1a\xef\xdd"] = CTInfo($description="Let's Encrypt 'Clicky' log", $operator="Let's Encrypt", $url="clicky.ct.letsencrypt.org/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x1f\x1a\x15\x83\x77\x00\x75\x62\xb9\x9f\xf6\x06\x05\xed\x95\x89\x83\x41\x81\x97\xe7\xe0\xd4\x33\xfe\x76\xba\x3b\xc9\x49\xc2\xcd\xf1\xcf\xfe\x12\x70\xd7\xbe\xa8\x22\x5f\xb2\xa4\x67\x02\x7b\x71\xae\x1d\xac\xa8\xe9\xd1\x08\xd5\xce\xef\x33\x7a\xc3\x5f\x00\xdc"),
["\xb0\xb7\x84\xbc\x81\xc0\xdd\xc4\x75\x44\xe8\x83\xf0\x59\x85\xbb\x90\x77\xd1\x34\xd8\xab\x88\xb2\xb2\xe5\x33\x98\x0b\x8e\x50\x8b"] = CTInfo($description="Up In The Air 'Behind the Sofa' log", $operator="Up In The Air Consulting", $url="ct.filippo.io/behindthesofa/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x59\x39\xb2\xa6\x94\xc6\x32\xb9\xfe\x63\x69\x1e\x30\x3b\xa3\x5b\xd5\xb0\x43\xc9\x50\x1e\x95\xa5\x2d\xa7\x4c\x4a\x49\x8e\x8b\x8f\xb7\xf8\xcc\xe2\x5b\x97\x72\xd5\xea\x3f\xb1\x21\x48\xe8\x44\x6b\x7f\xea\xef\x22\xff\xdf\xf4\x5f\x3b\x6d\x77\x04\xb1\xaf\x90\x8f"),
["\x47\x44\x47\x7c\x75\xde\x42\x6d\x5c\x44\xef\xd4\xa9\x2c\x96\x77\x59\x7f\x65\x7a\x8f\xe0\xca\xdb\xc6\xd6\x16\xed\xa4\x97\xc4\x25"] = CTInfo($description="Qihoo 360 2020", $operator="Qihoo 360", $url="ct.browser.360.cn/2020/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xfa\x7b\x8f\xd1\x78\xc4\xdf\xaa\x3e\x1d\xf8\xd2\xf4\x82\xa7\x0c\x8b\x42\x9e\x9d\xd3\x9f\xd3\x5a\x7d\x9a\x49\xff\xc1\x87\xbc\xb9\xbe\x67\xf0\x17\x63\xb1\x6e\x37\x72\xf8\x2c\xf5\xd1\xae\x54\xf2\x74\x12\xed\xeb\x43\xaa\xce\x4b\x47\xa0\x9e\x2d\xe1\xf3\x68\x76"),
["\xc6\xd7\xed\x9e\xdb\x8e\x74\xf0\xa7\x1b\x4d\x4a\x98\x4b\xcb\xeb\xab\xbd\x28\xcc\x1f\xd7\x63\x29\xe8\x87\x26\xcd\x4c\x25\x46\x63"] = CTInfo($description="Qihoo 360 2021", $operator="Qihoo 360", $url="ct.browser.360.cn/2021/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x60\x5b\x65\xa8\xa0\xb9\xf4\x8c\xbe\x37\x5b\x16\x69\x49\x2d\x9d\x23\x69\x6e\x1a\xeb\x2f\x85\x4c\x89\xaa\x9d\x0e\xb4\x2e\xff\x78\x4e\x9f\x13\x89\x89\x71\x65\x91\x26\x10\x8c\x1a\x07\x0c\x25\x40\xe6\x20\xbd\xa6\xef\x3e\xd4\x3b\x2b\x98\xdb\x2f\xe4\x52\x0c\x0f"),
["\x66\x3c\xb0\x9c\x1f\xcd\x9b\xaa\x62\x76\x3c\xcb\x53\x4e\xec\x80\x58\x12\x28\x05\x07\xac\x69\xa4\x5f\xcd\x38\xcf\x4c\xc7\x4c\xf1"] = CTInfo($description="Qihoo 360 2022", $operator="Qihoo 360", $url="ct.browser.360.cn/2022/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x43\x85\x25\x14\xb1\x37\xbf\x10\xc7\xb6\xca\x8e\xe6\xce\xa4\x35\x44\x52\xc9\xa2\xca\xf5\xb1\xd5\xe7\xec\xc3\x5d\x34\x33\xc2\x25\x09\x75\x31\x4a\xc4\x4f\x39\x2f\x9b\xaf\x62\xfa\x1b\xbb\x26\x69\xc9\x2a\x3b\x19\x76\x32\xa2\x92\xf8\x13\x1b\x2b\x9b\x81\xc9\x30"),
["\xe2\x64\x7f\x6e\xda\x34\x05\x03\xc6\x4d\x4e\x10\xa8\x69\x68\x1f\xde\x9c\x5a\x2c\xf3\xb3\x2d\x5f\x20\x0b\x96\x36\x05\x90\x88\x23"] = CTInfo($description="Qihoo 360 2023", $operator="Qihoo 360", $url="ct.browser.360.cn/2023/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x05\x02\xc8\x1a\xd4\x07\xde\x67\xab\x93\x63\x8c\x3b\x62\xdb\x29\x4d\x93\x16\xba\x0a\xc5\xa0\xa1\xaf\x8f\x85\xc4\x1e\x54\x30\xb6\x13\xb2\x9a\x84\x24\xa7\x03\xee\x64\x57\x10\xe6\xc7\x27\xa3\xc8\xff\x7f\x54\xbc\x42\x3d\xf0\xde\x02\xc9\xff\x71\xda\xb2\xaf\xaa"),
["\xc5\xcf\xe5\x4b\x61\x51\xb4\x9b\x14\x2e\xd2\x63\xbd\xe7\x32\x93\x36\x37\x99\x79\x95\x50\xae\x44\x35\xcd\x1a\x69\x97\xc9\xc3\xc3"] = CTInfo($description="Qihoo 360 v1 2020", $operator="Qihoo 360", $url="ct.browser.360.cn/v1/2020/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x0e\x44\x19\x9f\x55\xaa\xc6\xa5\xb2\x58\x71\xbc\xea\x2d\x78\xa9\xb6\x7e\xca\x4f\xf5\xed\x0f\x12\x59\xeb\x9b\x38\x9c\x3a\x8d\x07\x4d\x25\x18\x78\xcc\x9c\x03\xde\x61\x93\x64\x84\x20\x7b\x74\xfa\x40\xe8\x33\x0a\x15\x94\x24\x0d\xf0\x63\x3b\x52\xc7\xe3\x3f\x82"),
["\x48\x14\x58\x7c\xf2\x8b\x08\xfe\x68\x3f\xd2\xbc\xd9\x45\x99\x4c\x2e\xb7\x4c\x8a\xe8\xc8\x7f\xce\x42\x9b\x7c\xd3\x1d\x51\xbd\xc4"] = CTInfo($description="Qihoo 360 v1 2021", $operator="Qihoo 360", $url="ct.browser.360.cn/v1/2021/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x07\x53\x93\xe7\x6c\xbf\xfb\x04\x94\xa9\xa1\xdf\x26\x1e\xbf\x06\xf4\xc3\xdb\xb9\xa5\x66\xbe\xaa\x65\xd7\x64\x8a\xce\xcc\x71\xc9\x0e\x69\x7c\x79\x71\x4d\xc8\xfd\xf0\x71\xf7\x4a\x77\x8f\x5b\x56\xa8\x40\x2b\x56\xe8\x8a\x98\x71\xc0\xbe\xef\x36\x97\xd5\x33\xdb"),
["\x49\x11\xb8\xd6\x14\xcf\xd3\xd9\x9f\x16\xd3\x76\x54\x5e\xe1\xb8\xcc\xfc\x51\x1f\x50\x9f\x08\x0b\xa0\xa0\x87\xd9\x1d\xfa\xee\xa9"] = CTInfo($description="Qihoo 360 v1 2022", $operator="Qihoo 360", $url="ct.browser.360.cn/v1/2022/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x02\xfa\x9c\xb5\xe1\xc7\x18\x46\x42\x13\x99\x12\xbc\x4a\x50\x20\xf0\xf8\x7d\x7b\x04\x14\x76\x2d\xd7\xf1\x15\x89\x23\x2f\xed\x00\xed\x04\x74\xe9\x2b\xde\x08\x65\x24\x9c\x64\xb3\x96\x93\x97\xe9\xbc\x62\x01\xff\xa8\x9b\x57\x34\x79\x30\x25\xbf\x35\x23\x1d\xfc"),
["\xb6\x74\x0b\x12\x00\x2e\x03\x3f\xd0\xe7\xe9\x41\xf4\xba\x3e\xe1\xbf\xc1\x49\xb5\x24\xb4\xcf\x62\x8d\x53\xef\xea\x1f\x40\x3a\x8d"] = CTInfo($description="Qihoo 360 v1 2023", $operator="Qihoo 360", $url="ct.browser.360.cn/v1/2023/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xa6\xf2\x0f\x06\x4a\x4f\x7d\xc8\x38\x6e\x86\xac\x8d\xae\xac\xae\xf0\x5f\x96\x00\xc0\xce\x74\xf2\xb4\xa8\xcd\x58\xf5\xf5\xe7\x58\xcf\xfc\x46\x91\x66\x63\x4e\x14\x16\xaf\x80\x12\x36\xa3\x19\x50\x69\xb8\x18\x1b\x5d\x58\x1d\xdc\x07\xf8\x51\xe7\xc9\x54\xda\x37"),
["\x45\x35\x94\x98\xd9\x3a\x89\xe0\x28\x03\x08\xd3\x7d\x62\x6d\xc4\x23\x75\x47\x58\xdc\xe0\x37\x00\x36\xfb\xab\x0e\xdf\x8a\x6b\xcf"] = CTInfo($description="Trust Asia Log1", $operator="TrustAsia", $url="ct.trustasia.com/log1/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x79\x47\x98\xd1\x20\xe8\x51\xd0\x12\x38\xc8\x05\xc0\xe1\x54\x4a\x46\x99\xf0\xda\x0a\x9e\x49\xe3\xb5\xb6\xa4\x98\x10\xd4\x24\x98\x4f\x56\x7f\x4b\xfd\xa7\x67\x13\x16\x41\x5e\x5e\x3a\x64\x14\xfb\xd3\x5a\x94\x8b\x17\xea\x77\xfb\x37\x06\x9b\x8c\xc9\x65\x94\x77"),
["\xa5\x95\x94\x3b\x53\x70\xbe\xe9\x06\xe0\x05\x0d\x1f\xb5\xbb\xc6\xa4\x0e\x65\xf2\x65\xae\x85\x2c\x76\x36\x3f\xad\xb2\x33\x36\xed"] = CTInfo($description="Trust Asia Log2020", $operator="TrustAsia", $url="ct.trustasia.com/log2020/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x6e\xc5\x82\xee\xe9\x27\xd9\x66\x0e\x33\x14\xc0\x72\xa2\xfc\x80\xc4\x59\x11\x04\xd9\x17\xdf\x87\xa3\x53\x01\xf5\x62\xe1\x1c\x86\x82\x1e\x91\xec\x25\x2c\x74\x0e\x32\x5d\x54\x82\xd6\xf6\x6b\x4c\xe7\x16\x6d\x5b\x2c\x0c\x58\xc4\x3d\xfd\x1d\xce\x06\x04\x04\x5d"),
["\xa8\xdc\x52\xf6\x3d\x6b\x24\x25\xe5\x31\xe3\x7c\xf4\xe4\x4a\x71\x4f\x14\x2a\x20\x80\x3b\x0d\x04\xd2\xe2\xee\x06\x64\x79\x4a\x23"] = CTInfo($description="Trust Asia CT2021", $operator="TrustAsia", $url="ct2021.trustasia.com/log2021/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x49\xd0\x1f\x0b\xe8\x75\x49\x93\x6c\x48\x04\x6c\xd7\xcf\x27\xfd\xd0\xa2\x35\x88\xc6\x4a\x09\x13\xed\xab\xcb\x61\xed\x66\x99\x72\x73\xcc\x78\x6c\x9b\x19\x80\xa2\xb1\xed\x3b\x38\x43\x90\x58\x1a\x09\x20\xab\x50\xfa\xd7\x76\xe9\xca\xf7\x8e\x9e\xc8\x87\x92\x98"),
["\x67\x8d\xb6\x5b\x3e\x74\x43\xb6\xf3\xa3\x70\xd5\xe1\x3a\xb1\xb4\x3b\xe0\xa0\xd3\x51\xf7\xca\x74\x22\x50\xc7\xc6\xfa\x51\xa8\x8a"] = CTInfo($description="Trust Asia Log2021", $operator="TrustAsia", $url="ct.trustasia.com/log2021/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x8f\x09\x73\x63\x3b\x2c\x0c\x41\xb8\x0e\x93\xe8\x39\x2e\xf7\x13\x0b\x1d\xa2\x17\x34\x33\x36\xa8\x87\x34\x66\x53\x3f\x5d\x8a\x1e\xd9\xf1\x21\xf2\xca\xf8\x8a\x9a\x74\x0b\xd4\xa2\x9f\x63\xa5\x46\x16\x7b\x74\xba\x56\xd5\xba\x9c\xc6\x5d\x26\x98\x50\x0a\x2e\xa4"),
["\xc3\x65\xf9\xb3\x65\x4f\x32\x83\xc7\x9d\xa9\x8e\x93\xd7\x41\x8f\x5b\xab\x7b\xe3\x25\x2c\x98\xe1\xd2\xf0\x4b\xb9\xeb\x42\x7d\x23"] = CTInfo($description="Trust Asia Log2022", $operator="TrustAsia", $url="ct.trustasia.com/log2022/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xbb\x52\xf2\x16\xcf\x92\x0b\xce\x79\xe6\x54\x6d\xc2\x37\x53\xa4\xf5\xf9\x3a\xa9\xb3\x05\xec\x1d\xbd\x16\xec\x30\xac\x2e\xf8\x79\x62\x35\x15\x8e\x1a\xd8\x16\x2e\xe4\x48\x6b\xf6\xc6\x13\xf1\x96\x2e\x5c\x10\xfe\x19\x1e\xa8\x18\xb8\x0f\x2d\xc3\xa4\x86\x51\x97"),
["\xe8\x7e\xa7\x66\x0b\xc2\x6c\xf6\x00\x2e\xf5\x72\x5d\x3f\xe0\xe3\x31\xb9\x39\x3b\xb9\x2f\xbf\x58\xeb\x3b\x90\x49\xda\xf5\x43\x5a"] = CTInfo($description="Trust Asia Log2023", $operator="TrustAsia", $url="ct.trustasia.com/log2023/", $maximum_merge_delay=86400, $key="\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xa4\x11\x52\xdb\x17\x41\x4e\x90\xd4\x56\x51\x12\x30\x52\xf8\x9b\x03\xcf\x4c\x9f\xf8\x2e\x38\xb5\xf1\x5a\xba\xfa\x38\xb9\xd2\x8f\x1a\x81\xda\x95\xcc\x33\xec\x21\x28\x66\xc6\x56\x3e\x60\x36\x21\x20\xd9\xd4\xac\x5d\xfa\x5c\x19\xa1\x05\x7d\xfe\x20\x23\xfc\xf5"),

View file

@ -6,27 +6,37 @@
module SSL;
export {
## Set this to true to includd the server certificate subject and
## issuer from the SSL log file. This information is still available
## in x509.log.
const log_include_server_certificate_subject_issuer = F &redef;
## Set this to true to include the client certificate subject
## and issuer in the SSL logfile. This information is rarely present
## and probably only interesting in very specific circumstances
const log_include_client_certificate_subject_issuer = F &redef;
redef record Info += {
## Chain of certificates offered by the server to validate its
## complete signing chain.
cert_chain: vector of Files::Info &optional;
## An ordered vector of all certificate file unique IDs for the
## An ordered vector of all certificate fingerprints for the
## certificates offered by the server.
cert_chain_fuids: vector of string &optional &log;
cert_chain_fps: vector of string &optional &log;
## Chain of certificates offered by the client to validate its
## complete signing chain.
client_cert_chain: vector of Files::Info &optional;
## An ordered vector of all certificate file unique IDs for the
## An ordered vector of all certificate fingerprints for the
## certificates offered by the client.
client_cert_chain_fuids: vector of string &optional &log;
client_cert_chain_fps: vector of string &optional &log;
## Subject of the X.509 certificate offered by the server.
subject: string &log &optional;
## Subject of the signer of the X.509 certificate offered by the
## Issuer of the signer of the X.509 certificate offered by the
## server.
issuer: string &log &optional;
@ -37,6 +47,11 @@ export {
## client.
client_issuer: string &log &optional;
## Set to true if the hostname sent in the SNI matches the certificate.
## Set to false if they do not match. Unset if the client did not send
## an SNI.
sni_matches_cert: bool &log &optional;
## Current number of certificates seen from either side. Used
## to create file handles.
server_depth: count &default=0;
@ -88,6 +103,25 @@ event zeek_init() &priority=5
Files::register_protocol(Analyzer::ANALYZER_DTLS,
[$get_file_handle = SSL::get_file_handle,
$describe = SSL::describe_file]);
local ssl_filter = Log::get_filter(SSL::LOG, "default");
if ( ssl_filter$name != "<not found>" )
{
if ( ! ssl_filter?$exclude )
ssl_filter$exclude = set();
if ( ! log_include_server_certificate_subject_issuer )
{
add ssl_filter$exclude["subject"];
add ssl_filter$exclude["issuer"];
}
if ( ! log_include_client_certificate_subject_issuer )
{
add ssl_filter$exclude["client_subject"];
add ssl_filter$exclude["client_issuer"];
}
Log::add_filter(SSL::LOG, ssl_filter);
}
}
event file_sniff(f: fa_file, meta: fa_metadata) &priority=5
@ -114,28 +148,39 @@ event file_sniff(f: fa_file, meta: fa_metadata) &priority=5
{
c$ssl$cert_chain = vector();
c$ssl$client_cert_chain = vector();
c$ssl$cert_chain_fuids = string_vec();
c$ssl$client_cert_chain_fuids = string_vec();
c$ssl$cert_chain_fps = string_vec();
c$ssl$client_cert_chain_fps = string_vec();
}
if ( f$is_orig )
{
c$ssl$client_cert_chain += f$info;
c$ssl$client_cert_chain_fuids += f$id;
}
else
{
c$ssl$cert_chain += f$info;
c$ssl$cert_chain_fuids += f$id;
}
}
event ssl_established(c: connection) &priority=6
hook ssl_finishing(c: connection) &priority=20
{
# update subject and issuer information
if ( c$ssl?$cert_chain)
for ( i in c$ssl$cert_chain )
if ( c$ssl$cert_chain[i]?$x509 && c$ssl$cert_chain[i]$x509?$fingerprint )
c$ssl$cert_chain_fps += c$ssl$cert_chain[i]$x509$fingerprint;
if ( c$ssl?$client_cert_chain )
for ( i in c$ssl$client_cert_chain )
if ( c$ssl$client_cert_chain[i]?$x509 && c$ssl$client_cert_chain[i]$x509?$fingerprint )
c$ssl$client_cert_chain_fps += c$ssl$client_cert_chain[i]$x509$fingerprint;
if ( c$ssl?$cert_chain && |c$ssl$cert_chain| > 0 &&
c$ssl$cert_chain[0]?$x509 )
{
if ( c$ssl?$server_name )
{
if ( x509_check_cert_hostname(c$ssl$cert_chain[0]$x509$handle, c$ssl$server_name) != "" )
c$ssl$sni_matches_cert = T;
else
c$ssl$sni_matches_cert = F;
}
c$ssl$subject = c$ssl$cert_chain[0]$x509$certificate$subject;
c$ssl$issuer = c$ssl$cert_chain[0]$x509$certificate$issuer;
}

View file

@ -68,6 +68,36 @@ export {
## Flag to indicate if this record already has been logged, to
## prevent duplicates.
logged: bool &default=F;
## SSL history showing which types of packets we received in which order.
## Letters have the following meaning with client-sent letters being capitalized:
## H hello_request
## C client_hello
## S server_hello
## V hello_verify_request
## T NewSessionTicket
## X certificate
## K server_key_exchange
## R certificate_request
## N server_hello_done
## Y certificate_verify
## G client_key_exchange
## F finished
## W certificate_url
## U certificate_status
## A supplemental_data
## Z unassigned_handshake_type
## I change_cipher_spec
## B heartbeat
## D application_data
## E end_of_early_data
## O encrypted_extensions
## P key_update
## M message_hash
## J hello_retry_request
## L alert
## Q unknown_content_type
ssl_history: string &log &default="";
};
## The default root CA bundle. By default, the mozilla-ca-list.zeek
@ -111,8 +141,8 @@ export {
## record as it is sent on to the logging framework.
global log_ssl: event(rec: Info);
# Hook that can be used to perform actions right before the log record
# is written.
## Hook that can be used to perform actions right before the log record
## is written.
global ssl_finishing: hook(c: connection);
## SSL finalization hook. Remaining SSL info may get logged when it's called.
@ -145,7 +175,8 @@ const dtls_ports = { 443/udp };
redef likely_server_ports += { ssl_ports, dtls_ports };
event zeek_init() &priority=5
# Priority needs to be higher than priority of zeek_init in ssl/files.zeek
event zeek_init() &priority=6
{
Log::create_stream(SSL::LOG, [$columns=Info, $ev=log_ssl, $path="ssl", $policy=log_policy]);
Analyzer::register_for_ports(Analyzer::ANALYZER_SSL, ssl_ports);
@ -161,6 +192,14 @@ function set_session(c: connection)
}
}
function add_to_history(c: connection, is_orig: bool, char: string)
{
if ( is_orig )
c$ssl$ssl_history = c$ssl$ssl_history+to_upper(char);
else
c$ssl$ssl_history = c$ssl$ssl_history+to_lower(char);
}
function delay_log(info: Info, token: string)
{
if ( ! info?$delay_tokens )
@ -295,6 +334,75 @@ event ssl_handshake_message(c: connection, is_orig: bool, msg_type: count, lengt
if ( is_orig && msg_type == SSL::CLIENT_KEY_EXCHANGE )
c$ssl$client_key_exchange_seen = T;
switch ( msg_type )
{
case SSL::HELLO_REQUEST:
add_to_history(c, is_orig, "h");
break;
case SSL::CLIENT_HELLO:
add_to_history(c, is_orig, "c");
break;
case SSL::SERVER_HELLO:
add_to_history(c, is_orig, "s");
break;
case SSL::HELLO_VERIFY_REQUEST:
add_to_history(c, is_orig, "v");
break;
case SSL::SESSION_TICKET:
add_to_history(c, is_orig, "t");
break;
# end of early data
case 5:
add_to_history(c, is_orig, "e");
break;
case SSL::HELLO_RETRY_REQUEST:
add_to_history(c, is_orig, "j");
break;
case SSL::ENCRYPTED_EXTENSIONS:
add_to_history(c, is_orig, "o");
break;
case SSL::CERTIFICATE:
add_to_history(c, is_orig, "x");
break;
case SSL::SERVER_KEY_EXCHANGE:
add_to_history(c, is_orig, "k");
break;
case SSL::CERTIFICATE_REQUEST:
add_to_history(c, is_orig, "r");
break;
case SSL::SERVER_HELLO_DONE:
add_to_history(c, is_orig, "n");
break;
case SSL::CERTIFICATE_VERIFY:
add_to_history(c, is_orig, "y");
break;
case SSL::CLIENT_KEY_EXCHANGE:
add_to_history(c, is_orig, "g");
break;
case SSL::FINISHED:
add_to_history(c, is_orig, "f");
break;
case SSL::CERTIFICATE_URL:
add_to_history(c, is_orig, "w");
break;
case SSL::CERTIFICATE_STATUS:
add_to_history(c, is_orig, "u");
break;
case SSL::SUPPLEMENTAL_DATA:
add_to_history(c, is_orig, "a");
break;
case SSL::KEY_UPDATE:
add_to_history(c, is_orig, "p");
break;
# message hash
case 254:
add_to_history(c, is_orig, "m");
break;
default:
add_to_history(c, is_orig, "z");
break;
}
}
# Extension event is fired _before_ the respective client or server hello.
@ -318,6 +426,7 @@ event ssl_extension(c: connection, is_orig: bool, code: count, val: string) &pri
event ssl_change_cipher_spec(c: connection, is_orig: bool) &priority=5
{
set_session(c);
add_to_history(c, is_orig, "i");
if ( is_orig && c$ssl$client_ticket_empty_session_seen && ! c$ssl$client_key_exchange_seen )
c$ssl$resumed = T;
@ -326,10 +435,17 @@ event ssl_change_cipher_spec(c: connection, is_orig: bool) &priority=5
event ssl_alert(c: connection, is_orig: bool, level: count, desc: count) &priority=5
{
set_session(c);
add_to_history(c, is_orig, "l");
c$ssl$last_alert = alert_descriptions[desc];
}
event ssl_heartbeat(c: connection, is_orig: bool, length: count, heartbeat_type: count, payload_length: count, payload: string)
{
set_session(c);
add_to_history(c, is_orig, "b");
}
event ssl_established(c: connection) &priority=7
{
c$ssl$established = T;

File diff suppressed because one or more lines are too long

View file

@ -78,6 +78,19 @@ function request2curl(r: Request, bodyfile: string, headersfile: string): string
function request(req: Request): ActiveHTTP::Response
{
local resp: Response;
resp$code = 0;
resp$msg = "";
resp$body = "";
resp$headers = table();
# Sanity-check the method parameter as it will go directly into our command line.
if ( req$method != /[A-Za-z]+/ )
{
Reporter::error(fmt("There was an illegal method specified with ActiveHTTP (\"%s\").", req$method));
return resp;
}
local tmpfile = "/tmp/zeek-activehttp-" + unique_id("");
local bodyfile = fmt("%s_body", tmpfile);
local headersfile = fmt("%s_headers", tmpfile);
@ -85,11 +98,6 @@ function request(req: Request): ActiveHTTP::Response
local cmd = request2curl(req, bodyfile, headersfile);
local stdin_data = req?$client_data ? req$client_data : "";
local resp: Response;
resp$code = 0;
resp$msg = "";
resp$body = "";
resp$headers = table();
return when ( local result = Exec::run([$cmd=cmd, $stdin=stdin_data, $read_files=set(bodyfile, headersfile)]) )
{
# If there is no response line then nothing else will work either.

View file

@ -83,8 +83,10 @@ function has_valid_octets(octets: string_vec): bool
##
## input: a string that may contain an IP address anywhere within it.
##
## check_wrapping: if true, will only return IP addresses that are wrapped in matching pairs of spaces, square brackets, curly braces, or parens. This can be used to avoid extracting strings that look like IPs from innocuous strings, such as SMTP headers.
##
## Returns: an array containing all valid IP address strings found in *input*.
function extract_ip_addresses(input: string): string_vec
function extract_ip_addresses(input: string, check_wrapping: bool &default=F): string_vec
{
local parts = split_string_all(input, ip_addr_regex);
local output: string_vec;
@ -92,7 +94,24 @@ function extract_ip_addresses(input: string): string_vec
for ( i in parts )
{
if ( i % 2 == 1 && is_valid_ip(parts[i]) )
output += parts[i];
{
if ( ! check_wrapping )
{
output += parts[i];
}
else if ( i > 0 && i < |parts| - 1 )
{
local p1 = parts[i-1];
local p3 = parts[i+1];
if ( ( |p1| == 0 && |p3| == 0 ) ||
( p1[-1] == "\[" && p3[0] == "\]" ) ||
( p1[-1] == "\(" && p3[0] == "\)" ) ||
( p1[-1] == "\{" && p3[0] == "\}" ) ||
( p1[-1] == " " && p3[0] == " " ) )
output += parts[i];
}
}
}
return output;
}

View file

@ -5,16 +5,102 @@
module Site;
export {
## Address space that is considered private and unrouted.
## By default it has RFC defined non-routable IPv4 address space.
## A list of subnets that are considered private address space.
##
## By default, it has address blocks defined by IANA as not being routable over the Internet.
##
## See the `IPv4 Special-Purpose Address Registry <https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml>`_
## and the `IPv6 Special-Purpose Address Registry <https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml>`_
option private_address_space: set[subnet] = {
# "This network", see :rfc:`791`
0.0.0.0/8,
# 0.0.0.0/8 as a 6to4 address, see :rfc:`791` and :rfc:`3056`
[2002::]/24,
# Private-Use, see :rfc:`1918`
10.0.0.0/8,
192.168.0.0/16,
172.16.0.0/12,
100.64.0.0/10, # RFC6598 Carrier Grade NAT
# 10.0.0.0/8 as a 6to4 address, see :rfc:`1918` and :rfc:`3056`
[2002:a00::]/24,
# Shared Address Space (also known as Carrier-grade NAT), see :rfc:`6598`
100.64.0.0/10,
# 100.64.0.0/10 as a 6to4 address, see :rfc:`6598` and :rfc:`3056`
[2002:6440::]/26,
# Loopback, see :rfc:`1122`
127.0.0.0/8,
[fe80::]/10,
# 127.0.0.0/8 as a 6to4 address, see :rfc:`1122` and :rfc:`3056`
[2002:7f00::]/24,
# Link Local, see :rfc:`3927`
169.254.0.0/16,
# 169.254.0.0/16 as a 6to4 address, see :rfc:`3927` and :rfc:`3056`
[2002:a9fe::]/32,
# Private-Use, see :rfc:`1918`
172.16.0.0/12,
# 172.16.0.0/12 as a 6to4 address, see :rfc:`1918` and :rfc:`3056`
[2002:ac10::]/28,
# IETF Protocol Assignments, see :rfc:`6890`
192.0.0.0/24,
# 192.0.0.0/24 as a 6to4 address, see :rfc:`6890` and :rfc:`3056`
[2002:c000::]/40,
# Documentation (TEST-NET-1), see :rfc:`5737`
192.0.2.0/24,
# 192.0.2.0/24 as a 6to4 address, see :rfc:`5737` and :rfc:`3056`
[2002:c000:200::]/40,
# Private-Use, see :rfc:`1918`
192.168.0.0/16,
# 192.168.0.0/16 as a 6to4 address, see :rfc:`1918` and :rfc:`3056`
[2002:c0a8::]/32,
# Benchmarking, see :rfc:`2544`
198.18.0.0/15,
# 198.18.0.0/15 as a 6to4 address, see :rfc:`2544` and :rfc:`3056`
[2002:c612::]/31,
# Documentation (TEST-NET-2), see :rfc:`5737`
198.51.100.0/24,
# 198.51.100.0/24 as a 6to4 address, see :rfc:`5737` and :rfc:`3056`
[2002:c633:6400::]/40,
# Documentation (TEST-NET-3), see :rfc:`5737`
203.0.113.0/24,
# 203.0.113.0/24 as a 6to4 address, see :rfc:`5737` and :rfc:`3056`
[2002:cb00:7100::]/40,
# Reserved, see :rfc:`1112`
240.0.0.0/4,
# 240.0.0.0/4 as a 6to4 address, see :rfc:`1112` and :rfc:`3056`
[2002:f000::]/20,
# Limited Broadcast, see :rfc:`919` and :rfc:`8190`
255.255.255.255/32,
# 255.255.255.255/32 as a 6to4 address, see :rfc:`8190` and :rfc:`3056`
[2002:ffff:ffff::]/48,
# Unspecified Address, see :rfc:`4291`
[::]/128,
# Loopback Address, see :rfc:`4291`
[::1]/128,
# IPv4-IPv6 Translation, see :rfc:`8215`
[64:ff9b:1::]/48,
# Discard-Only Address Block, see :rfc:`6666`
[100::]/64,
# IETF Protocol Assignments, see :rfc:`2928`
[2001::]/23,
# Benchmarking, see :rfc:`5180`
[2001:2::]/48,
# Documentation, see :rfc:`3849`
[2001:db8::]/32,
# Unique-Local, see :rfc:`4193` and :rfc:`8190`
[fc00::]/7,
# Link-Local Unicast, see :rfc:`4291`
[fe80::]/10,
};
## Networks that are considered "local". Note that ZeekControl sets

View file

@ -0,0 +1,84 @@
##! This script disables repeat certificate events for hosts for hosts for which the same
##! certificate was seen in the recent past;
##!
##! This script specifically plugs into the event caching mechanism that is set up by the
##! base X509 script certificate-event-cache.zeek. It adds another layer of tracking that
##! checks if the same certificate was seen for the server IP address before, when the same
##! SNI was used to connect. If the certificate is in the event cache and all of these conditions
##! apply, then no certificate related events will be raised.
##!
##! Please note that while this optimization can lead to a considerable reduction of load in some
##! settings, it also means that certain detection scripts that rely on the certificate events being
##! raised do no longer work - since the events will not be raised for all connections.
##!
##! Currently this script only works for X509 certificates that are sent via SSL/TLS connections.
##!
##! If you use any script that requires certificate events for each single connection,
##! you should not load this script.
@load base/protocols/ssl
@load base/files/x509
module DisableX509Events;
## Let's be a bit more generous with the number of certificates that we allow to be put into
## the cache.
redef X509::certificate_cache_max_entries = 100000;
type CacheIndex: record {
## IP address of the server the certificate was seen on.
ip: addr;
## SNI the client sent in the connection
sni: string &optional;
## sha256 of the certificate
sha256: string;
};
redef record SSL::Info += {
## Set to true to force certificate events to always be raised for this connection.
always_raise_x509_events: bool &default=F;
};
redef record X509::Info += {
## Set to true to force certificate events to always be raised for this certificate.
always_raise_x509_events: bool &default=F;
};
global certificate_replay_tracking: set[CacheIndex] &read_expire=X509::certificate_cache_minimum_eviction_interval;
hook X509::x509_certificate_cache_replay(f: fa_file, e: X509::Info, sha256: string) &priority=5
{
# Bail out if x509 is already set - or if the file tells us that we should always raise events.
if ( f$info?$x509 || e$always_raise_x509_events )
return;
local raise_events = F;
# not sure how that could happen - but let's be safe...
if ( |f$conns| == 0 )
return;
for ( c in f$conns )
{
if ( ! f$conns[c]?$ssl )
return;
local test = CacheIndex($ip=f$conns[c]$id$resp_h, $sha256=sha256);
if ( f$conns[c]$ssl?$server_name )
test$sni = f$conns[c]$ssl$server_name;
if ( test !in certificate_replay_tracking || f$conns[c]$ssl$always_raise_x509_events )
{
raise_events = T;
add certificate_replay_tracking[test];
}
}
if ( ! raise_events )
{
# We don't have to raise the events. :).
# Instead we just already set f$x509. That makes the data available to scripts that might need them - and the x509_certificate_cache_replayh
# hook in certificate-event-cache will just abort.
f$info$x509 = e;
}
}

View file

@ -1,64 +1 @@
##! Enable logging of OCSP responses.
#
# This script is in policy and not loaded by default because OCSP logging
# does not provide a lot of interesting information in most environments.
module OCSP;
export {
redef enum Log::ID += { LOG };
global log_policy: Log::PolicyHook;
## The record type which contains the fields of the OCSP log.
type Info: record {
## Time when the OCSP reply was encountered.
ts: time &log;
## File id of the OCSP reply.
id: string &log;
## Hash algorithm used to generate issuerNameHash and issuerKeyHash.
hashAlgorithm: string &log;
## Hash of the issuer's distingueshed name.
issuerNameHash: string &log;
## Hash of the issuer's public key.
issuerKeyHash: string &log;
## Serial number of the affected certificate.
serialNumber: string &log;
## Status of the affected certificate.
certStatus: string &log;
## Time at which the certificate was revoked.
revoketime: time &log &optional;
## Reason for which the certificate was revoked.
revokereason: string &log &optional;
## The time at which the status being shows is known to have been correct.
thisUpdate: time &log;
## The latest time at which new information about the status of the certificate will be available.
nextUpdate: time &log &optional;
};
## Event that can be handled to access the OCSP record
## as it is sent to the logging framework.
global log_ocsp: event(rec: Info);
}
event zeek_init() &priority=5
{
Log::create_stream(LOG, [$columns=Info, $ev=log_ocsp, $path="ocsp", $policy=log_policy]);
Files::register_for_mime_type(Files::ANALYZER_OCSP_REPLY, "application/ocsp-response");
}
event ocsp_response_certificate(f: fa_file, hashAlgorithm: string, issuerNameHash: string, issuerKeyHash: string, serialNumber: string, certStatus: string, revoketime: time, revokereason: string, thisUpdate: time, nextUpdate: time)
{
local wr = OCSP::Info($ts=f$info$ts, $id=f$id, $hashAlgorithm=hashAlgorithm, $issuerNameHash=issuerNameHash,
$issuerKeyHash=issuerKeyHash, $serialNumber=serialNumber, $certStatus=certStatus,
$thisUpdate=thisUpdate);
if ( revokereason != "" )
wr$revokereason = revokereason;
if ( time_to_double(revoketime) != 0 )
wr$revoketime = revoketime;
if ( time_to_double(nextUpdate) != 0 )
wr$nextUpdate = nextUpdate;
Log::write(LOG, wr);
}
@deprecated("Remove in v5.1. OCSP logging is now enabled by default")

View file

@ -0,0 +1,5 @@
# The entry point for the cluster agent. It only runs bootstrap logic for
# launching via the Supervisor. If we're not running the Supervisor, this does
# nothing.
@load ./boot

View file

@ -0,0 +1,33 @@
@load base/frameworks/supervisor/control
@load policy/frameworks/cluster/controller/types
module ClusterAgent::API;
export {
const version = 1;
# Agent API events
global set_configuration_request: event(reqid: string,
config: ClusterController::Types::Configuration);
global set_configuration_response: event(reqid: string,
result: ClusterController::Types::Result);
# Notification events, agent -> controller
# Report agent being available.
global notify_agent_hello: event(instance: string, host: addr,
api_version: count);
# Report node state changes.
global notify_change: event(instance: string,
n: ClusterController::Types::Node,
old: ClusterController::Types::State,
new: ClusterController::Types::State);
# Report operational error.
global notify_error: event(instance: string, msg: string, node: string &default="");
# Report informational message.
global notify_log: event(instance: string, msg: string, node: string &default="");
}

View file

@ -0,0 +1,35 @@
@load ./config
# The agent needs the supervisor to listen for node management requests. We
# need to tell it to do so, and we need to do so here, in the agent
# bootstrapping code, so the redef applies prior to the fork of the agent
# process itself.
redef SupervisorControl::enable_listen = T;
event zeek_init()
{
if ( ! Supervisor::is_supervisor() )
return;
local epi = ClusterAgent::endpoint_info();
local sn = Supervisor::NodeConfig($name=epi$id, $bare_mode=T,
$scripts=vector("policy/frameworks/cluster/agent/main.zeek"));
if ( ClusterAgent::directory != "" )
sn$directory = ClusterAgent::directory;
if ( ClusterAgent::stdout_file_suffix != "" )
sn$stdout_file = epi$id + "." + ClusterAgent::stdout_file_suffix;
if ( ClusterAgent::stderr_file_suffix != "" )
sn$stderr_file = epi$id + "." + ClusterAgent::stderr_file_suffix;
# This helps Zeek run controller and agent with a minimal set of scripts.
sn$env["ZEEK_CLUSTER_MGMT_NODE"] = "AGENT";
local res = Supervisor::create(sn);
if ( res != "" )
{
print(fmt("error: supervisor could not create agent node: %s", res));
exit(1);
}
}

View file

@ -0,0 +1,85 @@
@load policy/frameworks/cluster/controller/types
module ClusterAgent;
export {
# The name this agent uses to represent the cluster instance
# it manages. When the environment variable isn't set and there's,
# no redef, this falls back to "agent-<hostname>".
const name = getenv("ZEEK_AGENT_NAME") &redef;
# Agent stdout/stderr log files to produce in Zeek's working
# directory. If empty, no such logs will result. The actual
# log files have the agent's name (as per above) dot-prefixed.
const stdout_file_suffix = "agent.stdout" &redef;
const stderr_file_suffix = "agent.stderr" &redef;
# The address and port the agent listens on. When
# undefined, falls back to configurable default values.
const listen_address = getenv("ZEEK_AGENT_ADDR") &redef;
const default_address = Broker::default_listen_address &redef;
const listen_port = getenv("ZEEK_AGENT_PORT") &redef;
const default_port = 2151/tcp &redef;
# The agent communicates under to following topic prefix,
# suffixed with "/<name>" (see above):
const topic_prefix = "zeek/cluster-control/agent" &redef;
# The coordinates of the controller. When defined, it means
# agents peer with (connect to) the controller; otherwise the
# controller knows all agents and peers with them.
const controller: Broker::NetworkInfo = [
$address="0.0.0.0", $bound_port=0/unknown] &redef;
# Agent and controller currently log only, not via the data cluster's
# logger. (This might get added later.) For now, this means that
# if both write to the same log file, it gets garbled. The following
# lets you specify the working directory specifically for the agent.
const directory = "" &redef;
# Working directory for data cluster nodes. When relative, note
# that this will apply from the working directory of the agent,
# since it creates data cluster nodes.
const cluster_directory = "" &redef;
# The following functions return the effective network endpoint
# information for this agent, in two related forms.
global instance: function(): ClusterController::Types::Instance;
global endpoint_info: function(): Broker::EndpointInfo;
}
function instance(): ClusterController::Types::Instance
{
local epi = endpoint_info();
return ClusterController::Types::Instance($name=epi$id,
$host=to_addr(epi$network$address),
$listen_port=epi$network$bound_port);
}
function endpoint_info(): Broker::EndpointInfo
{
local epi: Broker::EndpointInfo;
local network: Broker::NetworkInfo;
if ( ClusterAgent::name != "" )
epi$id = ClusterAgent::name;
else
epi$id = fmt("agent-%s", gethostname());
if ( ClusterAgent::listen_address != "" )
network$address = ClusterAgent::listen_address;
else if ( ClusterAgent::default_address != "" )
network$address = ClusterAgent::default_address;
else
network$address = "127.0.0.1";
if ( ClusterAgent::listen_port != "" )
network$bound_port = to_port(ClusterAgent::listen_port);
else
network$bound_port = ClusterAgent::default_port;
epi$network = network;
return epi;
}

View file

@ -0,0 +1,223 @@
@load base/frameworks/broker
@load policy/frameworks/cluster/controller/config
@load policy/frameworks/cluster/controller/log
@load policy/frameworks/cluster/controller/request
@load ./api
redef ClusterController::role = ClusterController::Types::AGENT;
# The global configuration as passed to us by the controller
global global_config: ClusterController::Types::Configuration;
# A map to make other instance info accessible
global instances: table[string] of ClusterController::Types::Instance;
# A map for the nodes we run on this instance, via this agent.
global nodes: table[string] of ClusterController::Types::Node;
# The node map employed by the supervisor to describe the cluster
# topology to newly forked nodes. We refresh it when we receive
# new configurations.
global data_cluster: table[string] of Supervisor::ClusterEndpoint;
event SupervisorControl::create_response(reqid: string, result: string)
{
local req = ClusterController::Request::lookup(reqid);
if ( ClusterController::Request::is_null(req) )
return;
local name = req$supervisor_state$node;
if ( |result| > 0 )
{
local msg = fmt("failed to create node %s: %s", name, result);
ClusterController::Log::error(msg);
event ClusterAgent::API::notify_error(ClusterAgent::name, msg, name);
}
ClusterController::Request::finish(reqid);
}
event SupervisorControl::destroy_response(reqid: string, result: bool)
{
local req = ClusterController::Request::lookup(reqid);
if ( ClusterController::Request::is_null(req) )
return;
local name = req$supervisor_state$node;
if ( ! result )
{
local msg = fmt("failed to destroy node %s, %s", name, reqid);
ClusterController::Log::error(msg);
event ClusterAgent::API::notify_error(ClusterAgent::name, msg, name);
}
ClusterController::Request::finish(reqid);
}
function supervisor_create(nc: Supervisor::NodeConfig)
{
local req = ClusterController::Request::create();
req$supervisor_state = ClusterController::Request::SupervisorState($node = nc$name);
event SupervisorControl::create_request(req$id, nc);
ClusterController::Log::info(fmt("issued supervisor create for %s, %s", nc$name, req$id));
}
function supervisor_destroy(node: string)
{
local req = ClusterController::Request::create();
req$supervisor_state = ClusterController::Request::SupervisorState($node = node);
event SupervisorControl::destroy_request(req$id, node);
ClusterController::Log::info(fmt("issued supervisor destroy for %s, %s", node, req$id));
}
event ClusterAgent::API::set_configuration_request(reqid: string, config: ClusterController::Types::Configuration)
{
ClusterController::Log::info(fmt("rx ClusterAgent::API::set_configuration_request %s", reqid));
local nodename: string;
local node: ClusterController::Types::Node;
local nc: Supervisor::NodeConfig;
local msg: string;
# Adopt the global configuration provided.
# XXX this can later handle validation and persistence
# XXX should do this transactionally, only set when all else worked
global_config = config;
# Refresh the instances table:
instances = table();
for ( inst in config$instances )
instances[inst$name] = inst;
# Terminate existing nodes
for ( nodename in nodes )
supervisor_destroy(nodename);
nodes = table();
# Refresh the data cluster and nodes tables
data_cluster = table();
for ( node in config$nodes )
{
if ( node$instance == ClusterAgent::name )
nodes[node$name] = node;
local cep = Supervisor::ClusterEndpoint(
$role = node$role,
$host = instances[node$instance]$host,
$p = node$p);
if ( node?$interface )
cep$interface = node$interface;
data_cluster[node$name] = cep;
}
# Apply the new configuration via the supervisor
for ( nodename in nodes )
{
node = nodes[nodename];
nc = Supervisor::NodeConfig($name=nodename);
if ( ClusterAgent::cluster_directory != "" )
nc$directory = ClusterAgent::cluster_directory;
if ( node?$interface )
nc$interface = node$interface;
if ( node?$cpu_affinity )
nc$cpu_affinity = node$cpu_affinity;
if ( node?$scripts )
nc$scripts = node$scripts;
if ( node?$env )
nc$env = node$env;
# XXX could use options to enable per-node overrides for
# directory, stdout, stderr, others?
nc$cluster = data_cluster;
supervisor_create(nc);
}
# XXX this currently doesn not fail if any of above problems occurred,
# mainly due to the tediousness of handling the supervisor's response
# events asynchonously. The only indication of error will be
# notification events to the controller.
local res = ClusterController::Types::Result(
$reqid = reqid,
$instance = ClusterAgent::name);
ClusterController::Log::info(fmt("tx ClusterAgent::API::set_configuration_response %s", reqid));
event ClusterAgent::API::set_configuration_response(reqid, res);
}
event Broker::peer_added(peer: Broker::EndpointInfo, msg: string)
{
# This does not (cannot?) immediately verify that the new peer
# is in fact a controller, so we might send this redundantly.
# Controllers handle the hello event accordingly.
local epi = ClusterAgent::endpoint_info();
# XXX deal with unexpected peers, unless we're okay with it
event ClusterAgent::API::notify_agent_hello(epi$id,
to_addr(epi$network$address), ClusterAgent::API::version);
}
event zeek_init()
{
local epi = ClusterAgent::endpoint_info();
local agent_topic = ClusterAgent::topic_prefix + "/" + epi$id;
# The agent needs to peer with the supervisor -- this doesn't currently
# happen automatically. The address defaults to Broker's default, which
# relies on ZEEK_DEFAULT_LISTEN_ADDR and so might just be "". Broker
# internally falls back to listening on any; we pick 127.0.0.1.
local supervisor_addr = Broker::default_listen_address;
if ( supervisor_addr == "" )
supervisor_addr = "127.0.0.1";
Broker::peer(supervisor_addr, Broker::default_port, Broker::default_listen_retry);
# Agents need receive communication targeted at it, and any responses
# from the supervisor.
Broker::subscribe(agent_topic);
Broker::subscribe(SupervisorControl::topic_prefix);
# Auto-publish a bunch of events. Glob patterns or module-level
# auto-publish would be helpful here.
Broker::auto_publish(agent_topic, ClusterAgent::API::set_configuration_response);
Broker::auto_publish(agent_topic, ClusterAgent::API::notify_agent_hello);
Broker::auto_publish(agent_topic, ClusterAgent::API::notify_change);
Broker::auto_publish(agent_topic, ClusterAgent::API::notify_error);
Broker::auto_publish(agent_topic, ClusterAgent::API::notify_log);
Broker::auto_publish(SupervisorControl::topic_prefix, SupervisorControl::create_request);
Broker::auto_publish(SupervisorControl::topic_prefix, SupervisorControl::create_response);
Broker::auto_publish(SupervisorControl::topic_prefix, SupervisorControl::destroy_request);
Broker::auto_publish(SupervisorControl::topic_prefix, SupervisorControl::destroy_response);
Broker::auto_publish(SupervisorControl::topic_prefix, SupervisorControl::restart_request);
Broker::auto_publish(SupervisorControl::topic_prefix, SupervisorControl::restart_response);
Broker::auto_publish(SupervisorControl::topic_prefix, SupervisorControl::stop_request);
# Establish connectivity with the controller.
if ( ClusterAgent::controller$address != "0.0.0.0" )
{
# We connect to the controller.
Broker::peer(ClusterAgent::controller$address,
ClusterAgent::controller$bound_port,
ClusterController::connect_retry);
}
else
{
# Controller connects to us; listen for it.
Broker::listen(cat(epi$network$address), epi$network$bound_port);
}
ClusterController::Log::info("agent is live");
}

View file

@ -0,0 +1,5 @@
# The entry point for the cluster controller. It only runs bootstrap logic for
# launching via the Supervisor. If we're not running the Supervisor, this does
# nothing.
@load ./boot

View file

@ -0,0 +1,16 @@
@load ./types
module ClusterController::API;
export {
const version = 1;
global get_instances_request: event(reqid: string);
global get_instances_response: event(reqid: string,
instances: vector of ClusterController::Types::Instance);
global set_configuration_request: event(reqid: string,
config: ClusterController::Types::Configuration);
global set_configuration_response: event(reqid: string,
result: ClusterController::Types::ResultVec);
}

View file

@ -0,0 +1,29 @@
@load ./config
event zeek_init()
{
if ( ! Supervisor::is_supervisor() )
return;
local epi = ClusterController::endpoint_info();
local sn = Supervisor::NodeConfig($name=epi$id, $bare_mode=T,
$scripts=vector("policy/frameworks/cluster/controller/main.zeek"));
if ( ClusterController::directory != "" )
sn$directory = ClusterController::directory;
if ( ClusterController::stdout_file != "" )
sn$stdout_file = ClusterController::stdout_file;
if ( ClusterController::stderr_file != "" )
sn$stderr_file = ClusterController::stderr_file;
# This helps Zeek run controller and agent with a minimal set of scripts.
sn$env["ZEEK_CLUSTER_MGMT_NODE"] = "CONTROLLER";
local res = Supervisor::create(sn);
if ( res != "" )
{
print(fmt("error: supervisor could not create controller node: %s", res));
exit(1);
}
}

View file

@ -0,0 +1,85 @@
@load policy/frameworks/cluster/agent/config
module ClusterController;
export {
# The name of this controller in the cluster.
# Without the environment variable and no redef, this
# falls back to "controller-<hostname>".
const name = getenv("ZEEK_CONTROLLER_NAME") &redef;
# Controller stdout/stderr log files to produce in Zeek's
# working directory. If empty, no such logs will result.
const stdout_file = "controller.stdout" &redef;
const stderr_file = "controller.stderr" &redef;
# The address and port the controller listens on. When
# undefined, falls back to the default_address, which you can
# likewise customize.
const listen_address = getenv("ZEEK_CONTROLLER_ADDR") &redef;
const default_address = Broker::default_listen_address &redef;
const listen_port = getenv("ZEEK_CONTROLLER_PORT") &redef;
const default_port = 2150/tcp &redef;
# A more aggressive default retry interval (vs default 30s)
const connect_retry = 1sec &redef;
# The controller listens for messages on this topic:
const topic = "zeek/cluster-control/controller" &redef;
# The set of agents to interact with. When this is non-empty
# at startup, the controller contacts the agents; when it is
# empty, it waits for agents to connect. They key is a name of
# each instance. This should match the $name member of the
# instance records.
const instances: table[string] of ClusterController::Types::Instance = { } &redef;
# The role of this node in cluster management. Agent and
# controller both redef this. Used during logging.
const role = ClusterController::Types::NONE &redef;
# Agent and controller currently log only, not via the data cluster's
# logger. (This might get added later.) For now, this means that
# if both write to the same log file, it gets garbled. The following
# lets you specify the working directory specifically for the agent.
const directory = "" &redef;
# The following functions return the effective network endpoint
# information for this controller, in two related forms.
global network_info: function(): Broker::NetworkInfo;
global endpoint_info: function(): Broker::EndpointInfo;
}
function network_info(): Broker::NetworkInfo
{
local ni: Broker::NetworkInfo;
if ( ClusterController::listen_address != "" )
ni$address = ClusterController::listen_address;
else if ( ClusterController::default_address != "" )
ni$address = ClusterController::default_address;
else
ni$address = "127.0.0.1";
if ( ClusterController::listen_port != "" )
ni$bound_port = to_port(ClusterController::listen_port);
else
ni$bound_port = ClusterController::default_port;
return ni;
}
function endpoint_info(): Broker::EndpointInfo
{
local epi: Broker::EndpointInfo;
if ( ClusterController::name != "" )
epi$id = ClusterController::name;
else
epi$id = fmt("controller-%s", gethostname());
epi$network = network_info();
return epi;
}

View file

@ -0,0 +1,109 @@
@load ./config
module ClusterController::Log;
export {
## The cluster logging stream identifier.
redef enum Log::ID += { LOG };
## A default logging policy hook for the stream.
global log_policy: Log::PolicyHook;
type Level: enum {
DEBUG,
INFO,
WARNING,
ERROR,
};
## The record type which contains the column fields of the cluster log.
type Info: record {
## The time at which a cluster message was generated.
ts: time;
## The name of the node that is creating the log record.
node: string;
## Log level of this message, converted from the above Level enum
level: string;
## The role of the node, translated from ClusterController::Types::Role.
role: string;
## A message indicating information about cluster controller operation.
message: string;
} &log;
global log_level = DEBUG &redef;
global info: function(message: string);
global warning: function(message: string);
global error: function(message: string);
}
# Enum translations to strings. This avoids those enums being reported
# with full qualifications in the logs, which is too verbose.
global l2s: table[Level] of string = {
[DEBUG] = "DEBUG",
[INFO] = "INFO",
[WARNING] = "WARNING",
[ERROR] = "ERROR",
};
global r2s: table[ClusterController::Types::Role] of string = {
[ClusterController::Types::AGENT] = "AGENT",
[ClusterController::Types::CONTROLLER] = "CONTROLLER",
};
function debug(message: string)
{
if ( enum_to_int(log_level) > enum_to_int(DEBUG) )
return;
local node = Supervisor::node();
Log::write(LOG, [$ts=network_time(), $node=node$name, $level=l2s[DEBUG],
$role=r2s[ClusterController::role], $message=message]);
}
function info(message: string)
{
if ( enum_to_int(log_level) > enum_to_int(INFO) )
return;
local node = Supervisor::node();
Log::write(LOG, [$ts=network_time(), $node=node$name, $level=l2s[INFO],
$role=r2s[ClusterController::role], $message=message]);
}
function warning(message: string)
{
if ( enum_to_int(log_level) > enum_to_int(WARNING) )
return;
local node = Supervisor::node();
Log::write(LOG, [$ts=network_time(), $node=node$name, $level=l2s[WARNING],
$role=r2s[ClusterController::role], $message=message]);
}
function error(message: string)
{
if ( enum_to_int(log_level) > enum_to_int(ERROR) )
return;
local node = Supervisor::node();
Log::write(LOG, [$ts=network_time(), $node=node$name, $level=l2s[ERROR],
$role=r2s[ClusterController::role], $message=message]);
}
event zeek_init()
{
if ( ! Supervisor::is_supervised() )
return;
local node = Supervisor::node();
# Defining the stream outside of the stream creation call sidesteps
# the coverage.find-bro-logs test, which tries to inventory all logs.
# This log isn't yet ready for that level of scrutiny.
local stream = Log::Stream($columns=Info, $path=fmt("cluster-%s", node$name),
$policy=log_policy);
Log::create_stream(ClusterController::Log::LOG, stream);
}

View file

@ -0,0 +1,250 @@
@load base/frameworks/broker
@load policy/frameworks/cluster/agent/config
@load policy/frameworks/cluster/agent/api
@load ./api
@load ./log
@load ./request
redef ClusterController::role = ClusterController::Types::CONTROLLER;
event ClusterAgent::API::notify_agent_hello(instance: string, host: addr, api_version: count)
{
# See if we already know about this agent; if not, register
# it.
#
# XXX protection against rogue agents?
if ( instance in ClusterController::instances )
{
# Do nothing, unless this known agent checks in with a mismatching
# API version, in which case we kick it out.
if ( api_version != ClusterController::API::version )
{
local inst = ClusterController::instances[instance];
if ( inst?$listen_port )
{
# We peered with this instance, unpeer.
Broker::unpeer(cat(inst$host), inst$listen_port );
# XXX what to do if they connected to us?
}
delete ClusterController::instances[instance];
}
# Update the instance name in the pointed-to record, in case it
# was previously named otherwise. Not being too picky here allows
# the user some leeway in spelling out the original config.
ClusterController::instances[instance]$name = instance;
return;
}
if ( api_version != ClusterController::API::version )
{
ClusterController::Log::warning(
fmt("agent %s/%s speaks incompatible agent protocol (%s, need %s), unpeering",
instance, host, api_version, ClusterController::API::version));
}
ClusterController::instances[instance] = ClusterController::Types::Instance($name=instance, $host=host);
ClusterController::Log::info(fmt("instance %s/%s has checked in", instance, host));
}
event ClusterAgent::API::notify_change(instance: string, n: ClusterController::Types::Node,
old: ClusterController::Types::State,
new: ClusterController::Types::State)
{
# XXX TODO
}
event ClusterAgent::API::notify_error(instance: string, msg: string, node: string)
{
# XXX TODO
}
event ClusterAgent::API::notify_log(instance: string, msg: string, node: string)
{
# XXX TODO
}
event ClusterAgent::API::set_configuration_response(reqid: string, result: ClusterController::Types::Result)
{
ClusterController::Log::info(fmt("rx ClusterAgent::API::set_configuration_response %s", reqid));
# Retrieve state for the request we just got a response to
local areq = ClusterController::Request::lookup(reqid);
if ( ClusterController::Request::is_null(areq) )
return;
# Record the result and mark the request as done. This also
# marks the request as done in the parent-level request, since
# these records are stored by reference.
areq$results[0] = result; # We only have a single result here atm
areq$finished = T;
# Update the original request from the client:
local req = ClusterController::Request::lookup(areq$parent_id);
if ( ClusterController::Request::is_null(req) )
return;
# If there are any requests to the agents still unfinished,
# we're not done yet.
for ( i in req$set_configuration_state$requests )
if ( ! req$set_configuration_state$requests[i]$finished )
return;
# All set_configuration requests to instances are done, so respond
# back to client. We need to compose the result, aggregating
# the results we got from the requests to the agents. In the
# end we have one Result per instance requested in the
# original set_configuration_request.
#
# XXX we can likely generalize result aggregation in the request module.
for ( i in req$set_configuration_state$requests )
{
local r = req$set_configuration_state$requests[i];
local success = T;
local errors: string_vec;
local instance = "";
for ( j in r$results )
{
local res = r$results[j];
instance = res$instance;
if ( res$success )
next;
success = F;
errors += fmt("node %s failed: %s", res$node, res$error);
}
req$results += ClusterController::Types::Result(
$reqid = req$id,
$instance = instance,
$success = success,
$error = join_string_vec(errors, ", ")
);
ClusterController::Request::finish(r$id);
}
ClusterController::Log::info(fmt("tx ClusterController::API::set_configuration_response %s", req$id));
event ClusterController::API::set_configuration_response(req$id, req$results);
ClusterController::Request::finish(req$id);
}
event ClusterController::API::set_configuration_request(reqid: string, config: ClusterController::Types::Configuration)
{
ClusterController::Log::info(fmt("rx ClusterController::API::set_configuration_request %s", reqid));
local req = ClusterController::Request::create(reqid);
req$set_configuration_state = ClusterController::Request::SetConfigurationState();
# Compare new configuration to the current one and send updates
# to the instances as needed.
if ( config?$instances )
{
# XXX properly handle instance update: connect to new instances provided
# when they are listening, accept connections from new instances that are
# not
for ( inst in config$instances )
{
if ( inst$name !in ClusterController::instances )
{
local res = ClusterController::Types::Result($reqid=reqid, $instance=inst$name);
res$error = fmt("instance %s is unknown, skipping", inst$name);
req$results += res;
}
}
}
# XXX validate the configuration:
# - Are node instances among defined instances?
# - Are all names unique?
# - Are any node options understood?
# - Do node types with optional fields have required values?
# ...
# Transmit the configuration on to the agents. They need to be aware of
# each other's location and nodes, so the data cluster nodes can connect
# (for example, so a worker on instance 1 can connect to a logger on
# instance 2).
for ( name in ClusterController::instances )
{
local agent_topic = ClusterAgent::topic_prefix + "/" + name;
local areq = ClusterController::Request::create();
areq$parent_id = reqid;
# We track the requests sent off to each agent. As the
# responses come in, we can check them off as completed,
# and once all are, we respond back to the client.
req$set_configuration_state$requests += areq;
# XXX could also broadcast just once on the agent prefix, but
# explicit request/response pairs for each agent seems cleaner.
ClusterController::Log::info(fmt("tx ClusterAgent::API::set_configuration_request %s to %s",
areq$id, name));
Broker::publish(agent_topic, ClusterAgent::API::set_configuration_request, areq$id, config);
}
# Response event gets sent via the agents' reponse event.
}
event ClusterController::API::get_instances_request(reqid: string)
{
ClusterController::Log::info(fmt("rx ClusterController::API::set_instances_request %s", reqid));
local insts: vector of ClusterController::Types::Instance;
for ( i in ClusterController::instances )
insts += ClusterController::instances[i];
ClusterController::Log::info(fmt("tx ClusterController::API::get_instances_response %s", reqid));
event ClusterController::API::get_instances_response(reqid, insts);
}
event zeek_init()
{
# Controller always listens -- it needs to be able to respond
# to the Zeek client. This port is also used by the agents
# if they connect to the client.
local cni = ClusterController::network_info();
Broker::listen(cat(cni$address), cni$bound_port);
Broker::subscribe(ClusterAgent::topic_prefix);
Broker::subscribe(ClusterController::topic);
Broker::auto_publish(ClusterController::topic,
ClusterController::API::get_instances_response);
Broker::auto_publish(ClusterController::topic,
ClusterController::API::set_configuration_response);
if ( |ClusterController::instances| > 0 )
{
# We peer with the agents -- otherwise, the agents peer
# with (i.e., connect to) us.
for ( i in ClusterController::instances )
{
local inst = ClusterController::instances[i];
if ( ! inst?$listen_port )
{
# XXX config error -- this must be there
next;
}
Broker::peer(cat(inst$host), inst$listen_port,
ClusterController::connect_retry);
}
}
# If ClusterController::instances is empty, agents peer with
# us and we do nothing. We'll build up state as the
# notify_agent_hello() events come int.
ClusterController::Log::info("controller is live");
}

View file

@ -0,0 +1,86 @@
@load ./types
module ClusterController::Request;
export {
type Request: record {
id: string;
parent_id: string &optional;
};
# API-specific state. XXX we may be able to generalize after this
# has settled a bit more.
# State specific to the set_configuration request/response events
type SetConfigurationState: record {
requests: vector of Request &default=vector();
};
# State specific to the set_nodes request/response events
type SetNodesState: record {
requests: vector of Request &default=vector();
};
# State specific to supervisor interactions
type SupervisorState: record {
node: string;
};
# The redef is a workaround so we can use the Request type
# while it is still being defined
redef record Request += {
results: ClusterController::Types::ResultVec &default=vector();
finished: bool &default=F;
set_configuration_state: SetConfigurationState &optional;
set_nodes_state: SetNodesState &optional;
supervisor_state: SupervisorState &optional;
};
global null_req = Request($id="", $finished=T);
global create: function(reqid: string &default=unique_id("")): Request;
global lookup: function(reqid: string): Request;
global finish: function(reqid: string): bool;
global is_null: function(request: Request): bool;
}
# XXX this needs a mechanism for expiring stale requests
global requests: table[string] of Request;
function create(reqid: string): Request
{
local ret = Request($id=reqid);
requests[reqid] = ret;
return ret;
}
function lookup(reqid: string): Request
{
if ( reqid in requests )
return requests[reqid];
return null_req;
}
function finish(reqid: string): bool
{
if ( reqid !in requests )
return F;
local req = requests[reqid];
delete requests[reqid];
req$finished = T;
return T;
}
function is_null(request: Request): bool
{
if ( request$id == "" )
return T;
return F;
}

View file

@ -0,0 +1,80 @@
# Types for the Cluster Controller framework. These are used by both agent and controller.
module ClusterController::Types;
export {
## Management infrastructure node type. This intentionally does not
## include the data cluster node types (worker, logger, etc) -- those
## continue to be managed by the cluster framework.
type Role: enum {
NONE,
AGENT,
CONTROLLER,
};
## A Zeek-side option with value.
type Option: record {
name: string; # Name of option
value: string; # Value of option
};
## Configuration describing a Zeek instance running a Cluster
## Agent. Normally, there'll be one instance per cluster
## system: a single physical system.
type Instance: record {
# Unique, human-readable instance name
name: string;
# IP address of system
host: addr;
# Agent listening port. Not needed if agents connect to controller.
listen_port: port &optional;
};
## State that a Cluster Node can be in. State changes trigger an
## API notification (see notify_change()).
type State: enum {
Running, # Running and operating normally
Stopped, # Explicitly stopped
Failed, # Failed to start; and permanently halted
Crashed, # Crashed, will be restarted,
Unknown, # State not known currently (e.g., because of lost connectivity)
};
## Configuration describing a Cluster Node process.
type Node: record {
name: string; # Cluster-unique, human-readable node name
instance: string; # Name of instance where node is to run
p: port; # Port on which this node will listen
role: Supervisor::ClusterRole; # Role of the node.
state: State; # Desired, or current, run state.
scripts: vector of string &optional; # Additional Zeek scripts for node
options: set[Option] &optional; # Zeek options for node
interface: string &optional; # Interface to sniff
cpu_affinity: int &optional; # CPU/core number to pin to
env: table[string] of string &default=table(); # Custom environment vars
};
# Data structure capturing a cluster's complete configuration.
type Configuration: record {
id: string &default=unique_id(""); # Unique identifier for a particular configuration
## The instances in the cluster.
## XXX we may be able to make this optional
instances: set[Instance];
## The set of nodes in the cluster, as distributed over the instances.
nodes: set[Node];
};
# Return value for request-response API event pairs
type Result: record {
reqid: string; # Request ID of operation this result refers to
instance: string; # Name of associated instance (for context)
success: bool &default=T; # True if successful
data: any &optional; # Addl data returned for successful operation
error: string &default=""; # Descriptive error on failure
node: string &optional; # Name of associated node (for context)
};
type ResultVec: vector of Result;
}

View file

@ -26,7 +26,7 @@ export {
## The Match notice has a sub message with a URL where you can get more
## information about the file. The %s will be replaced with the SHA-1
## hash of the file.
option match_sub_url = "https://www.virustotal.com/en/search/?query=%s";
option match_sub_url = "https://www.virustotal.com/gui/search/%s";
## The malware hash registry runs each malware sample through several
## A/V engines. Team Cymru returns a percentage to indicate how

View file

@ -20,7 +20,7 @@ event ssl_established(c: connection)
if ( c$ssl$cert_chain[0]$x509?$certificate && c$ssl$cert_chain[0]$x509$certificate?$cn )
Intel::seen([$indicator=c$ssl$cert_chain[0]$x509$certificate$cn,
$indicator_type=Intel::DOMAIN,
$fuid=c$ssl$cert_chain_fuids[0],
$fuid=c$ssl$cert_chain[0]$fuid,
$conn=c,
$where=X509::IN_CERT]);
}

View file

@ -13,13 +13,14 @@ module Notice;
# reference to the original notice)
global tmp_notice_storage: table[string] of Notice::Info &create_expire=max_email_delay+10secs;
hook notice(n: Notice::Info) &priority=10
# Run after e-mail address is set, but before e-mail is sent.
hook notice(n: Notice::Info) &priority=-1
{
if ( ! n?$src && ! n?$dst )
return;
# This should only be done for notices that are being sent to email.
if ( ACTION_EMAIL !in n$actions )
if ( ! n?$email_dest )
return;
# I'm not recovering gracefully from the when statements because I want

View file

@ -24,7 +24,7 @@ event TrimTraceFile::go(first_trim: bool)
{
local info = rotate_file_by_name(trace_output_file);
if ( info$old_name != "" )
system(fmt("/bin/rm %s", info$new_name));
system(fmt("/bin/rm %s", safe_shell_quote(info$new_name)));
}
schedule trim_interval { TrimTraceFile::go(F) };

View file

@ -14,21 +14,21 @@ export {
## Indicates that a certificate's NotValidAfter date has lapsed
## and the certificate is now invalid.
Certificate_Expired,
## Indicates that a certificate is going to expire within
## Indicates that a certificate is going to expire within
## :zeek:id:`SSL::notify_when_cert_expiring_in`.
Certificate_Expires_Soon,
## Indicates that a certificate's NotValidBefore date is future
## dated.
Certificate_Not_Valid_Yet,
};
## The category of hosts you would like to be notified about which have
## certificates that are going to be expiring soon. By default, these
## notices will be suppressed by the notice framework for 1 day after
## The category of hosts you would like to be notified about which have
## certificates that are going to be expiring soon. By default, these
## notices will be suppressed by the notice framework for 1 day after
## a particular certificate has had a notice generated.
## Choices are: LOCAL_HOSTS, REMOTE_HOSTS, ALL_HOSTS, NO_HOSTS
option notify_certs_expiration = LOCAL_HOSTS;
## The time before a certificate is going to expire that you would like
## to start receiving :zeek:enum:`SSL::Certificate_Expires_Soon` notices.
option notify_when_cert_expiring_in = 30days;
@ -42,24 +42,24 @@ event ssl_established(c: connection) &priority=3
! c$ssl$cert_chain[0]?$x509 || ! c$ssl$cert_chain[0]?$sha1 )
return;
local fuid = c$ssl$cert_chain_fuids[0];
local fuid = c$ssl$cert_chain[0]$fuid;
local cert = c$ssl$cert_chain[0]$x509$certificate;
local hash = c$ssl$cert_chain[0]$sha1;
if ( cert$not_valid_before > network_time() )
NOTICE([$note=Certificate_Not_Valid_Yet,
$conn=c, $suppress_for=1day,
$msg=fmt("Certificate %s isn't valid until %T", cert$subject, cert$not_valid_before),
$identifier=cat(c$id$resp_h, c$id$resp_p, hash),
$fuid=fuid]);
else if ( cert$not_valid_after < network_time() )
NOTICE([$note=Certificate_Expired,
$conn=c, $suppress_for=1day,
$msg=fmt("Certificate %s expired at %T", cert$subject, cert$not_valid_after),
$identifier=cat(c$id$resp_h, c$id$resp_p, hash),
$fuid=fuid]);
else if ( cert$not_valid_after - notify_when_cert_expiring_in < network_time() )
NOTICE([$note=Certificate_Expires_Soon,
$msg=fmt("Certificate %s is going to expire at %T", cert$subject, cert$not_valid_after),

View file

@ -1,3 +1,5 @@
@deprecated "Remove in v5.1. Use log-certs-base64.zeek instead."
##! This script is used to extract host certificates seen on the wire to disk
##! after being converted to PEM files. The certificates will be stored in
##! a single file, one for local certificates and one for remote certificates.

View file

@ -165,14 +165,14 @@ event ssl_established(c: connection) &priority=3
{
if ( ! c$ssl?$cert_chain )
return;
if ( |c$ssl$cert_chain| < 1 )
return;
if ( ! c$ssl$cert_chain[0]?$x509 )
return;
local fuid = c$ssl$cert_chain_fuids[0];
local fuid = c$ssl$cert_chain[0]$fuid;
if ( ! c$ssl$cert_chain[0]?$sha1 )
{

View file

@ -0,0 +1,19 @@
##! This script is used to extract certificates seen on the wire to Zeek log files.
##! The certificates are base64-encoded and written to ssl.log, to the newly added cert
##! field.
@load base/protocols/ssl
@load base/files/x509
redef record X509::Info += {
## Base64 endoded X.509 certificate.
cert: string &log &optional;
};
event x509_certificate(f: fa_file, cert_ref: opaque of x509, cert: X509::Certificate) &priority=1
{
if ( ! f$info?$x509 )
return;
f$info$x509$cert = encode_base64(x509_get_certificate_string(cert_ref));
}

View file

@ -6,71 +6,8 @@
module X509;
export {
redef record Info += {
## Logging of certificate is suppressed if set to F
logcert: bool &default=T;
};
}
# We need both the Info and the fa_file record modified.
# The only instant when we have both, the connection and the
# file available without having to loop is in the file_over_new_connection
# event.
# When that event is raised, the x509 record in f$info (which is the only
# record the logging framework gets) is not yet available. So - we
# have to do this two times, sorry.
# Alternatively, we could place it info Files::Info first - but we would
# still have to copy it.
redef record fa_file += {
logcert: bool &default=T;
};
hook X509::log_policy(rec: X509::Info, id: Log::ID, filter: Log::Filter)
{
if ( ! rec$logcert )
if ( ! rec$host_cert )
break;
}
event file_sniff(f: fa_file, meta: fa_metadata) &priority=4
{
if ( ( ! f?$conns ) || ( |f$conns| != 1 ) )
return;
if ( ! f?$info || ! f$info?$mime_type )
return;
if ( ! ( f$info$mime_type == "application/x-x509-ca-cert" || f$info$mime_type == "application/x-x509-user-cert"
|| f$info$mime_type == "application/pkix-cert" ) )
return;
local c: connection &is_assigned;
for ( cid, c in f$conns )
{
if ( ! c?$ssl )
return;
}
local chain: vector of string;
if ( f$is_orig )
chain = c$ssl$client_cert_chain_fuids;
else
chain = c$ssl$cert_chain_fuids;
if ( |chain| == 0 )
{
Reporter::warning(fmt("Certificate not in chain? (fuid %s)", f$id));
return;
}
# Check if this is the host certificate
if ( f$id != chain[0] )
f$logcert=F;
}
event x509_certificate(f: fa_file, cert_ref: opaque of x509, cert: X509::Certificate) &priority=2
{
f$info$x509$logcert = f$logcert; # info record available, copy information.
}

View file

@ -1,3 +1,5 @@
@deprecated("Remove in v5.1. Please switch to other more modern approaches like SCT validation (validate-sct.zeek).")
@load base/protocols/ssl
module CertNotary;

View file

@ -0,0 +1,176 @@
##! This file adds a lot of additional information to the SSL log
##! It is not loaded by default since the information significantly expands
##! the log and is probably not interesting for a majority of people.
@load base/protocols/ssl
redef record SSL::Info += {
## Numeric version of the server in the server hello
server_version: count &log &optional;
## Numeric version of the client in the client hello
client_version: count &log &optional;
## Ciphers that were offered by the client for the connection
client_ciphers: vector of count &log &optional;
## SSL Client extensions
ssl_client_exts: vector of count &log &optional;
## SSL server extensions
ssl_server_exts: vector of count &log &optional;
## Suggested ticket lifetime sent in the session ticket handshake
## by the server.
ticket_lifetime_hint: count &log &optional;
## The diffie helman parameter size, when using DH.
dh_param_size: count &log &optional;
## supported elliptic curve point formats
point_formats: vector of count &log &optional;
## The curves supported by the client.
client_curves: vector of count &log &optional;
## Application layer protocol negotiation extension sent by the client.
orig_alpn: vector of string &log &optional;
## TLS 1.3 supported versions
client_supported_versions: vector of count &log &optional;
## TLS 1.3 supported versions
server_supported_version: count &log &optional;
## TLS 1.3 Pre-shared key exchange modes
psk_key_exchange_modes: vector of count &log &optional;
## Key share groups from client hello
client_key_share_groups: vector of count &log &optional;
## Selected key share group from server hello
server_key_share_group: count &log &optional;
## Client supported compression methods
client_comp_methods: vector of count &log &optional;
## Server chosen compression method
comp_method: count &optional;
## Client supported signature algorithms
sigalgs: vector of count &log &optional;
## Client supported hash algorithms
hashalgs: vector of count &log &optional;
};
event ssl_client_hello(c: connection, version: count, record_version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec, comp_methods: index_vec)
{
if ( ! c?$ssl )
return;
c$ssl$client_ciphers = ciphers;
c$ssl$client_version = version;
c$ssl$client_comp_methods = comp_methods;
}
event ssl_server_hello(c: connection, version: count, record_version: count, possible_ts: time, server_random: string, session_id: string, cipher: count, comp_method: count)
{
if ( ! c?$ssl )
return;
c$ssl$server_version = version;
c$ssl$comp_method = comp_method;
}
event ssl_session_ticket_handshake(c: connection, ticket_lifetime_hint: count, ticket: string)
{
if ( ! c?$ssl )
return;
c$ssl$ticket_lifetime_hint = ticket_lifetime_hint;
}
event ssl_extension(c: connection, is_orig: bool, code: count, val: string)
{
if ( ! c?$ssl )
return;
if ( is_orig )
{
if ( ! c$ssl?$ssl_client_exts )
c$ssl$ssl_client_exts = vector();
c$ssl$ssl_client_exts[|c$ssl$ssl_client_exts|] = code;
}
else
{
if ( ! c$ssl?$ssl_server_exts )
c$ssl$ssl_server_exts = vector();
c$ssl$ssl_server_exts[|c$ssl$ssl_server_exts|] = code;
}
}
event ssl_extension_ec_point_formats(c: connection, is_orig: bool, point_formats: index_vec)
{
if ( ! c?$ssl || ! is_orig )
return;
c$ssl$point_formats = point_formats;
}
event ssl_extension_elliptic_curves(c: connection, is_orig: bool, curves: index_vec)
{
if ( ! c?$ssl || ! is_orig )
return;
c$ssl$client_curves = curves;
}
event ssl_extension_application_layer_protocol_negotiation(c: connection, is_orig: bool, names: string_vec)
{
if ( ! c?$ssl )
return;
if ( is_orig )
c$ssl$orig_alpn = names;
}
event ssl_dh_server_params(c: connection, p: string, q: string, Ys: string)
{
if ( ! c?$ssl )
return;
local key_length = |Ys| * 8; # key length in bits
c$ssl$dh_param_size = key_length;
}
event ssl_extension_supported_versions(c: connection, is_orig: bool, versions: index_vec)
{
if ( ! c?$ssl )
return;
if ( is_orig )
c$ssl$client_supported_versions = versions;
else
c$ssl$server_supported_version = versions[0];
}
event ssl_extension_psk_key_exchange_modes(c: connection, is_orig: bool, modes: index_vec)
{
if ( ! c?$ssl || ! is_orig )
return;
c$ssl$psk_key_exchange_modes = modes;
}
event ssl_extension_key_share(c: connection, is_orig: bool, curves: index_vec)
{
if ( ! c?$ssl )
return;
if ( is_orig )
c$ssl$client_key_share_groups = curves;
else
c$ssl$server_key_share_group = curves[0];
}
event ssl_extension_signature_algorithm(c: connection, is_orig: bool, signature_algorithms: signature_and_hashalgorithm_vec)
{
if ( ! c?$ssl || ! is_orig )
return;
local sigalgs: index_vec = vector();
local hashalgs: index_vec = vector();
for ( i in signature_algorithms )
{
local rec = signature_algorithms[i];
sigalgs[|sigalgs|] = rec$SignatureAlgorithm;
hashalgs[|hashalgs|] = rec$HashAlgorithm;
}
c$ssl$sigalgs = sigalgs;
c$ssl$hashalgs = hashalgs;
}

View file

@ -26,7 +26,7 @@ export {
}
# MD5 hash values for recently validated chains along with the OCSP validation
# SHA256 hash values for recently validated chains along with the OCSP validation
# status are kept in this table to avoid constant validation every time the same
# certificate chain is seen.
global recently_ocsp_validated: table[string] of string = table() &read_expire=5mins;
@ -49,7 +49,11 @@ event ssl_established(c: connection) &priority=3
chain[i] = c$ssl$cert_chain[i]$x509$handle;
}
local reply_id = cat(md5_hash(c$ssl$ocsp_response), join_string_vec(c$ssl$cert_chain_fuids, "."));
local chain_fuids = "";
for ( i in c$ssl$cert_chain )
chain_fuids += cat(c$ssl$cert_chain[i]$fuid, ",");
local reply_id = cat(sha256_hash(c$ssl$ocsp_response), chain_fuids);
if ( reply_id in recently_ocsp_validated )
{

View file

@ -55,7 +55,7 @@ event ssl_established(c: connection) &priority=3
! c$ssl$cert_chain[0]?$x509 )
return;
local fuid = c$ssl$cert_chain_fuids[0];
local fuid = c$ssl$cert_chain[0]$fuid;
local cert = c$ssl$cert_chain[0]$x509$certificate;
local hash = c$ssl$cert_chain[0]$sha1;

View file

@ -67,10 +67,6 @@ redef digest_salt = "Please change this value.";
# This script prevents the logging of SSL CA certificates in x509.log
@load protocols/ssl/log-hostcerts-only
# Uncomment the following line to check each SSL certificate hash against the ICSI
# certificate notary service; see http://notary.icsi.berkeley.edu .
# @load protocols/ssl/notary
# If you have GeoIP support built in, do some geographic detections and
# logging for SSH traffic.
@load protocols/ssh/geo-data

View file

@ -11,6 +11,19 @@
# @load frameworks/control/controllee.zeek
# @load frameworks/control/controller.zeek
@load frameworks/cluster/agent/__load__.zeek
@load frameworks/cluster/agent/api.zeek
@load frameworks/cluster/agent/boot.zeek
@load frameworks/cluster/agent/config.zeek
# @load frameworks/cluster/agent/main.zeek
@load frameworks/cluster/controller/__load__.zeek
@load frameworks/cluster/controller/api.zeek
@load frameworks/cluster/controller/boot.zeek
@load frameworks/cluster/controller/config.zeek
@load frameworks/cluster/controller/log.zeek
# @load frameworks/cluster/controller/main.zeek
@load frameworks/cluster/controller/request.zeek
@load frameworks/cluster/controller/types.zeek
@load frameworks/dpd/detect-protocols.zeek
@load frameworks/dpd/packet-segment-logging.zeek
@load frameworks/intel/do_notice.zeek
@ -41,6 +54,7 @@
@load frameworks/notice/extend-email/hostnames.zeek
@load files/unified2/__load__.zeek
@load files/unified2/main.zeek
@load files/x509/disable-certificate-events-known-certs.zeek
@load files/x509/log-ocsp.zeek
@load frameworks/packet-filter/shunt.zeek
@load frameworks/software/version-changes.zeek
@ -103,9 +117,11 @@
@load protocols/ssh/software.zeek
#@load protocols/ssl/decryption.zeek
@load protocols/ssl/expiring-certs.zeek
@load protocols/ssl/extract-certs-pem.zeek
# @load protocols/ssl/extract-certs-pem.zeek
@load protocols/ssl/heartbleed.zeek
@load protocols/ssl/known-certs.zeek
@load protocols/ssl/log-certs-base64.zeek
@load protocols/ssl/ssl-log-ext.zeek
@load protocols/ssl/log-hostcerts-only.zeek
#@load protocols/ssl/notary.zeek
@load protocols/ssl/validate-certs.zeek

View file

@ -5,9 +5,12 @@
@load protocols/ssl/notary.zeek
@load frameworks/control/controllee.zeek
@load frameworks/control/controller.zeek
@load frameworks/cluster/agent/main.zeek
@load frameworks/cluster/controller/main.zeek
@load frameworks/files/extract-all-files.zeek
@load policy/misc/dump-events.zeek
@load policy/protocols/conn/speculative-service.zeek
@load policy/protocols/ssl/extract-certs-pem.zeek
@load ./example.zeek