eris

- Elasticsearch Recon Ingestion Scripts (ERIS) 🔎
git clone git://git.acid.vegas/-c.git
Log | Files | Refs | Archive | README | LICENSE

sniff_patch.py (3708B)

      1 #!/usr/bin/env python
      2 # Elasticsearch Recon Ingestion Scripts (ERIS) - Developed by Acidvegas (https://git.acid.vegas/eris)
      3 
      4 # Note:
      5 #   This is a patch for the elasticsearch 8.x client to fix the sniff_* options.
      6 #   This patch is only needed if you use the sniff_* options and only works with basic auth.
      7 #   Call init_elasticsearch() with normal Elasticsearch params.
      8 #
      9 # Source:
     10 #   - https://github.com/elastic/elasticsearch-py/issues/2005#issuecomment-1645641960
     11 
     12 import base64
     13 
     14 import elasticsearch._sync.client as client
     15 from elasticsearch.exceptions import SerializationError, ConnectionError
     16 
     17 
     18 def init_elasticsearch(*args, **kwargs):
     19     '''
     20     Initialize the Elasticsearch client with the sniff patch.
     21     
     22     :param args: Elasticsearch positional arguments.
     23     :param kwargs: Elasticsearch keyword arguments.
     24     '''
     25     client.default_sniff_callback = _override_sniff_callback(kwargs['basic_auth'])
     26 
     27     return client.Elasticsearch(*args, **kwargs)
     28 
     29 
     30 def _override_sniff_callback(basic_auth):
     31     '''
     32     Taken from https://github.com/elastic/elasticsearch-py/blob/8.8/elasticsearch/_sync/client/_base.py#L166
     33     Completely unmodified except for adding the auth header to the elastic request.
     34     Allows us to continue using the sniff_* options while this is broken in the library.
     35 
     36     TODO: Remove this when this issue is patched:
     37         - https://github.com/elastic/elasticsearch-py/issues/2005
     38     '''
     39     auth_str = base64.b64encode(':'.join(basic_auth).encode()).decode()
     40     sniffed_node_callback = client._base._default_sniffed_node_callback
     41 
     42     def modified_sniff_callback(transport, sniff_options):
     43         for _ in transport.node_pool.all():
     44             try:
     45                 meta, node_infos = transport.perform_request(
     46                     'GET',
     47                     '/_nodes/_all/http',
     48                     headers = {
     49                         'accept': 'application/vnd.elasticsearch+json; compatible-with=8',
     50                         'authorization': f'Basic {auth_str}' # This auth header is missing in 8.x releases of the client, and causes 401s
     51                     },
     52                     request_timeout = (
     53                         sniff_options.sniff_timeout
     54                         if not sniff_options.is_initial_sniff
     55                         else None
     56                     ),
     57                 )
     58             except (SerializationError, ConnectionError):
     59                 continue
     60 
     61             if not 200 <= meta.status <= 299:
     62                 continue
     63 
     64             node_configs = []
     65             for node_info in node_infos.get('nodes', {}).values():
     66                 address = node_info.get('http', {}).get('publish_address')
     67                 if not address or ':' not in address:
     68                     continue
     69 
     70                 if '/' in address:
     71                     # Support 7.x host/ip:port behavior where http.publish_host has been set.
     72                     fqdn, ipaddress = address.split('/', 1)
     73                     host = fqdn
     74                     _, port_str = ipaddress.rsplit(':', 1)
     75                     port = int(port_str)
     76                 else:
     77                     host, port_str = address.rsplit(':', 1)
     78                     port = int(port_str)
     79 
     80                 assert sniffed_node_callback is not None
     81                 sniffed_node = sniffed_node_callback(
     82                     node_info, meta.node.replace(host=host, port=port)
     83                 )
     84                 if sniffed_node is None:
     85                     continue
     86 
     87                 # Use the node which was able to make the request as a base.
     88                 node_configs.append(sniffed_node)
     89 
     90             if node_configs:
     91                 return node_configs
     92 
     93         return []
     94 
     95     return modified_sniff_callback