Authentifizierung mit X-Pack für Elasticsearch einrichten

Um eine einfach HTTP basic authentication einzurichten für Elasticsearch mit Username und Passwort muss man als erstes X-Pack installieren.

Danach sollten alle Funktionen automatisch mit Basic Auth geschützt sein. Nach der Installation steht ein Default-User bereit, um weiterarbeiten zu können:

Name: elastic
Password: changeme

Damit können dann erfolgreich Requests gemacht werden:

curl --user elastic:changeme -XGET 'localhost:9200'

Anlegen eigener User

Nun könnne eigene User hinzugefügt werden:

curl --user elastic:changeme -XPOST 'hlocalhost:9200/_xpack/security/user/SebastianViereck?pretty' -H 'Content-Type: application/json' -d'
{
  "password" : "thePassword",
  "roles" : [ "superuser"],
  "full_name" : "Sebastian Viereck",
  "email" : "%MINIFYHTML3f42078677e841cb8f24d6d157439d006%",
  "metadata" : {
    "intelligence" : 7
  },
  "enabled": true
}

Danach können sofort Requests mit dem User gemacht werden:

curl --user SebastianViereck:thePassword -XGET 'localhost:9200'

Es sollten eigene Rollen angelegt werden und benutzt werden oder wie hier die vorgefertigten Rollen (superuser) benutzt werden.

Deaktivieren des Elastic Users

Sehr wichtig: Natürlich muss der Default elastic User mit dem “changeme” Passwort wieder deaktiviert werden. In der elasticsearch.yml muss dafür der folgende Parameter eingesetzt werden:

xpack.security.authc.accept_default_password: false

Und der elasticsearch Service neugestartet werden:

 sudo service elasticsearch restart

Zur Kontrolle sollte bei dem Request eine Fehlermeldung erscheinen:

curl --user elastic:changeme -XGET 'localhost:9200'

Weitere Verbesserungen der Sicherheit

Es sollte unbedingt eine Daten-Verschlüsselung mit SSL benutzt werden.

Der IP Raum, mit dem überhaupt kommuniziert werden darf, sollte auch eingeengt werden.

ELK Stack auf Amazon EC2 installieren

Um den ELK Stack, bestehend aus:

  • Logstash
  • Elasticsearch
  • Kibana

auf Amazon AWS zum Testen auf einer einzigen Amazon EC2 Instanz zu installieren, kann man wie folgt vorgehen:

Man fährt eine EC2 Instanz hoch, die nicht zu klein ist, was den Ram anbelangt, mindestens eine m4.large mit 8GB Ram und 2 Prozessoren, da Elasticsearch schon gehobene Ansprüche an die Speicher stellt und auch Logstash sehr ressourcenhungrig ist. Als Betriebsystem habe ich Ubuntu-16 gewählt (ami-1e339e71).

Dann kann man eine Elastic-IP auf die Instanz legen, damit man die Instanzen unkompliziert austauschen kann und man trotzdem die IP weiter behält.

Security Groups weiterlesen…

Elasticsearch Completion Suggest zeigt nur Ergebnisse vom Anfang des Strings an

Wenn man ein Autocomplete mit Elasticsearch mit Realtime “Search-As-You-Type” Funktionalität bauen will, bietet Elasticsearch ein sehr schnelles Completion Suggest an.

Das Problem ist, dass damit nur Ergebnisse erzielt werden können, die am Anfang des Strings liegen:

Ein Suche nach “Jackson” findet nicht den Eintrag “Michael Jackson“. Oder eine Suche nach “Thiller Michael Jackson” findet nicht “Michael Jackson Thriller“.

Die Lösung ist: Es ist schlichtweg nicht möglich mit dem Completion Suggest dies zu bewerkstelligen, da der Algorithmus dies nicht unterstützt.

Um trotzdem die passende Ergebnisse zu bekommen, habe ich  die Daten für das Autocomplete über denselben Query, wie für die Suchergebnisse generiert. Dies ist zwar Performance mäßig schlechter, aber die Ergebnisse des Autocomplete und der Suchergebnisse nach dem Abschicken stimmen auf jeden Fall überein und iritieren den User nicht.

Einführung in Elasticsearch

Elasticsearch macht den Einstieg nicht einfach, es ist hilfreich die folgenden Begrifflichkeiten zu verstehen.

Der Analyser

Ein Analyser berechnet die Daten für den Index vor und speichert das Ergebnis beim Aktualisieren der Daten einmalig ab. Aus dieser Menge an Tokens kann dann die Suche Ergebnisse ermitteln.

Ein Analyzer besteht aus 3 Teilen, die in der Reihenfolge angewendet werden:

1. Character Filter
- html_strip: Entfernt HTML Tags and dekodiert HTML Entitäten wie zum Beispiel &
- mapping: Ersetzt jedes Vorkommen eines Strings durch einen anderen
- pattern_replace character: Ersetzt mit Hilfe eines Regex jeden Treffer durch einen geeigneten
2. Tokenizer

- Ein Tokenizer berechnet aus einem String Tokens, d.h. einzelne Wörter und Wortgruppen

3. Token Filter

- ein Token Filter kann Tokens herausfiltern, die z.B: sehr kurz sind oder bestimmte Stop-Wörter wie “der”, “es”, “am”

Es gibt auch schon vorgefertigte Analyzer für den Anfang.

Wenn nur ein Analyzer angegen wird, dann wird dieser sowohl für die Indexierung, als auch für den Query-String verwendet. Es lassen sich unterschiedliche verwenden mit Hilfe von analyzer und search_analyzer.

Mapping weiterlesen…

Projekt: Elasticsearch für XT-Commerce Shop Suche

Das letzte Projekt war sehr spannend, es handelte sich um eine Erweiterung des PHP Shop-Systems namens XT-Commerce bzw. des Derivats SEO-Commerce um eine Suche aktuellem Standards für Zeedee Berlin.

Elasticsearch wurde auf einer eigene Amatzon MWS EC2 Instanz gehostet mit 1GB Ram und 1 CPU (sehr kostengünstig).

Die folgende Funktionalität kann ganz einfach wieder deaktiviert werden an zentraler Stelle, wenn es Probleme mit Elasticsearch gibt und die alte MySQL Suche tritt wieder in Kraft.

1. Autocomplete / Suggest Funktion beim Befüllen der Suche

Beim Eintippen des Suchwortes werden schon Vorschläge gegeben im Millisekundenbereich. Dadurch kann der Kunde viel Zeit sparen und bei der Rechtschreibung wird auch geholfen. suggest_zeedee

weiterlesen…

Elasticsearch 5 Cluster mit Docker Beispiel

Mit der docker-compose.yml kann man sich schnell eine Container-basierte Umgebung mit Docker bauen. Anpassbar sind die Memory-Werte für die Java-VM (512MB) und für das Docker Image (1GB).

Um einen Cluster mit 2 Nodes zu betreiben, kann man die elasticsearch2 auskommentieren.

version: '2'
services:
  elasticsearch1:
    image: docker.elastic.co/elasticsearch/elasticsearch:5.4.1
    container_name: elasticsearch1
    environment:
      - cluster.name=docker-cluster
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    mem_limit: 1g
    volumes:
      - esdata1:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
    networks:
      - esnet
#  elasticsearch2:
#    image: docker.elastic.co/elasticsearch/elasticsearch:5.4.1
#    environment:
#      - cluster.name=docker-cluster
#      - bootstrap.memory_lock=true
#      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
#      - "discovery.zen.ping.unicast.hosts=elasticsearch1"
#    ulimits:
#      memlock:
#        soft: -1
#        hard: -1
#    mem_limit: 1g
#    volumes:
#      - esdata2:/usr/share/elasticsearch/data
#    networks:
#      - esnet

volumes:
  esdata1:
    driver: local
#  esdata2:
#    driver: local

networks:
  esnet:

Elasticsearch Server außerhalb von localhost verfübar machen

Um eine Elasticsearch Instanz nach außen über HTTP verfübar zu machen, muss man folgendes tun:

1. Den Server nach außen verfübar machen, bei Amazon AWS z.B. durch das konfigurieren einer Security Group

2. Die elasticsearch.yml ändern ( /etc/elasticsearch/elasticsearch.yml):

# ---------------------------------- Network -----------------------------------
#
# Set the bind address to a specific IP (IPv4 or IPv6):
#
network.host: 172.44.11.222

3. Den Elasticsearch Service neustarten:

sudo service elasticsearch restart

4. Danach kann man den Status abfragen von außen um zu schauen, ob alles funktioniert hat:

curl -XGET '172.44.11.222:9200/_cluster/health?pretty'

Response:

{
  "cluster_name" : "elasticsearch",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 1,
  "number_of_data_nodes" : 1,
  "active_primary_shards" : 0,
  "active_shards" : 0,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 100.0
}

Elasticsearch Subquery Scoring Optimization

If you want to build a search query in Easticsearch where you can give documents a bonus score depending on how often a property can be found in other documents

- you need is a Subquery which is not supported by Elasticsearch, you need another better solution.

An Example for a Subquery is the problem:

Imagine a CD online shop. You want to score CDs ( = documents) higher which

1. match a term query AND

2. and which artist has many other CDs in your shop database

You would need a field, which aggragts the artist count in your mapping (artistCount for Example) and on query time and boost the artistCount field with the score:

{
    "query" : {
        "custom_score" : {
            "query" : {
                "match_all" : {}
            },
            "script" : "_score + (1 * doc.artistCount.integerValue)"
        }
    }
}

It would be also a good idea, to have a second index, which holds the artistCount information, because on every update of the inventory of your shop, the artsitCount needs to be recalculated.