Sichere Bereitstellung des ElasticSearch-Servers

    Nach einem erfolgreichen Übergang von der MongoDB-Volltextsuche zu ElasticSearch konnten wir mehrere neue Dienste starten, die auf Elastic, einer Erweiterung für den Browser, ausgeführt werden, und ich war im Allgemeinen äußerst zufrieden mit der Migration.

    In einem Faß Honig befand sich jedoch eine Fliege in der Salbe - etwa einen Monat nach der Konfiguration und dem erfolgreichen Betrieb rief LogEntries / NewRelic einstimmig, dass der Suchserver nicht reagiere. Nachdem ich mich beim Digital Ocean Deshardboard angemeldet hatte, sah ich einen Supportbrief, in dem stand, dass der Server aufgrund des großen ausgehenden UDP-Verkehrs angehalten wurde, was höchstwahrscheinlich darauf hinwies, dass der Server kompromittiert war.

    DigitalOcean hat einen Link zu Anleitungen bereitgestelltwas ist in diesem Fall zu tun. Das Interessanteste war jedoch in den Kommentaren, dass fast jeder, der in letzter Zeit unter Angriffen litt, einen ElasticSeach-Cluster mit einem offenen 9200-Port hatte. Angreifer nutzten Java- und ES-Schwachstellen, erlangten Zugriff auf den Server und machten ihn zu einem integralen Bestandteil eines Bot-Netzwerks.

    Ich musste den Server von Grund auf wiederherstellen, aber diesmal werde ich nicht so naiv sein, der Server wird zuverlässig geschützt. Ich werde mein Setup mit Node.js, Dokku / Docker , SSL beschreiben.

    Warum so?


    Trotz der vollen Leistungsfähigkeit von ElasticSearch bietet es keine internen Schutz- und Autorisierungsmöglichkeiten. Sie müssen alles selbst erledigen. Es gibt einen guten Artikel zu diesem Thema.

    Angreifer nutzen (höchstwahrscheinlich) die Schwachstelle dynamischer elastischer Skripte aus. Wenn sie (wie in meinem Fall) nicht verwendet werden, wird empfohlen, sie zu deaktivieren.

    Und schließlich ist der offene 9200-Port wie ein Lockvogel, den Sie schließen müssen.

    Was wird der Plan sein?


    Mein Plan war es, ein „sauberes“ Digital Ocean-Droplet zu erzeugen, Elastic Search im Docker-Container zu implementieren (auch wenn die Instanz gefährdet ist, alles, was getan werden muss, den Container neu zu starten), 9200/9300 für den Zugriff von außen zu schließen und den gesamten Datenverkehr zum Elastic zu versorgen über den Proxy-Server von Node.j mit einem einfachen Berechtigungsmodell über das "gemeinsame Geheimnis".

    Hebe das neue Tröpfchen auf


    DigitalOcean liefert ein vorgefertigtes Image mit Dokku / Docker an Bord von Ubuntu 14, daher ist es sinnvoll, es sofort auszuwählen. Wie üblich dauert es einige zehn Sekunden, ein neues Auto anzuheben, und wir sind bereit zu arbeiten.

    Bild

    Erweitern Sie ElasticSearch im Container


    Als erstes benötigen wir ein Docker-Image mit ElasticSearch. Trotz der Tatsache, dass es mehrere Plug-Ins für Dokku gibt, habe ich mich dazu entschlossen, diese selbst zu installieren, sodass mir die Konfiguration einfacher erschien.

    Das Bild für Elastic ist bereits fertig und es gibt gute Anweisungen für seine Verwendung.

    $ docker pull docker pull dockerfile/elasticsearch
    

    Sobald das Image geladen ist, müssen wir ein Volume vorbereiten, das sich außerhalb des ausgeführten Containers befindet (selbst wenn der Container angehalten und neu gestartet wird, werden die Daten im Host-Dateisystem gespeichert).

    $ cd /
    $ mkdir elastic
    

    In diesem Ordner erstellen wir eine Konfigurationsdatei, elasticsearch.yml. In meinem Fall ist es sehr einfach, ich habe einen Cluster von einem Computer, so dass alle Standardeinstellungen mich befriedigen. Wie oben erwähnt, ist es jedoch erforderlich, dynamische Skripte zu deaktivieren.

    $ nano elasticsearch.yml
    

    Welches wird nur aus einer Zeile bestehen,

    script.disable_dynamic: true
    

    Danach können Sie den Server starten. Ich habe ein einfaches Skript erstellt, für die Zeit der Konfiguration und des Debuggens müssen Sie möglicherweise mehrmals neu starten.

    docker run --name elastic -d -p 127.0.0.1:9200:9200 -p 127.0.0.1:9300:9300 -v /elastic:/data dockerfile/elasticsearch /elasticsearch/bin/elasticsearch -Des.config=/data/elasticsearch.yml
    

    Achten Sie darauf -p 127.0.0.1:9200:9200, hier "binden" wir 9200nur mit localhost. Ich habe mehrere Stunden damit verbracht, iptables9200/9300 Ports zu konfigurieren und zu schließen, ohne Erfolg. Dank der Hilfe von @darkproger und @kkdoo hat alles so funktioniert, wie es sollte.

    -v /elastic:/dataordnet das Containervolumen /datalokal zu /elastic.

    Proxy Node.js Server


    Jetzt müssen Sie den Proxy-Server Node.js starten, der den Datenverkehr von / zu localhost : 9200 sicher nach außen leitet . Ich habe ein kleines Projekt basierend auf http-proxy namens elastic-proxy erstellt , es ist sehr einfach und kann durchaus in anderen Projekten wiederverwendet werden.

    $ git clone https://github.com/likeastore/elastic-proxy
    $ cd elastic-proxy
    

    Der Servercode selbst,

    var http = require('http');
    var httpProxy = require('http-proxy');
    var url = require('url');
    var config = require('./config');
    var logger = require('./source/utils/logger');
    var port = process.env.PORT || 3010;
    var proxy = httpProxy.createProxyServer();
    var parseAccessToken = function (req) {
      var request = url.parse(req.url, true).query;
      var referer = url.parse(req.headers.referer || '', true).query;
      return request.access_token || referer.access_token;
    };
    var server = http.createServer(function (req, res) {
      var accessToken = parseAccessToken(req);
      logger.info('request: ' + req.url + ' accessToken: ' + accessToken + ' referer: ' + req.headers.referer);
      if (!accessToken || accessToken !== config.accessToken) {
          res.statusCode = 401;
          return res.end('Missing access_token query parameter');
      }
      proxy.web(req, res, {target: config.target});
    });
    server.listen(port, function () {
      logger.info('Likeastore Elastic-Proxy started at: ' + port);
    });
    

    Es ersetzt alle Anforderungen und "überspringt" nur diejenigen, die access_token als Anforderungsparameter angeben. access_token wird auf dem Server über eine Umgebungsvariable konfiguriertPROXY_ACCESS_TOKEN .

    Da der Server bereits für Dokku konfiguriert ist, müssen lediglich die Quellen gepusht werden, und Dokku stellt einen neuen Dienst bereit.

    $ git push master production
    

    Nach der Bereitstellung gehen wir zum Server und konfigurieren das Zugriffstoken.

    $ dokku config proxy set PROXY_ACCESS_TOKEN="your_secret_value"
    

    Ich mag auch , dass alles läuft durch die SSL, Dokku mit dieser sehr einfach zu machen, kopieren server.crtund server.keyin /home/dokku/proxy/tls.

    Wir starten den Proxy neu, um die neuesten Änderungen zu übernehmen. Stellen Sie sicher, dass alles in Ordnung ist, indem Sie auf den Link https://search.likeastore.com klicken. Wenn alles in Ordnung ist, wird Folgendes ausgegeben:

    Missing access_token query parameter
    

    Proxy- und ElasticSeach-Container binden


    Wir müssen zwei Container miteinander verbinden, den ersten mit Node.js-Proxys und den zweiten mit ElasticSearch. Ich mochte das Dokku-Link- Plugin, das genau das tut, was es braucht. Installiere es,

    $ cd /var/lib/dokku/plugins
    $ git clone https://github.com/rlaneve/dokku-link
    

    Und nach der Installation verbinden wir den Proxy mit dem elastischen,

    $ dokku link proxy elastic
    

    Danach muss der Proxy erneut gestartet werden. Wenn alles in proxy.yourserver.com?access_token=your_secret_value,Ordnung ist, erhalten wir durch Klicken auf den Link eine Antwort von ElasticSearch.

    {
      status: 200,
      name: "Tundra",
      version: {
          number: "1.2.1",
          build_hash: "6c95b759f9e7ef0f8e17f77d850da43ce8a4b364",
          build_timestamp: "2014-06-03T15:02:52Z",
          build_snapshot: false,
          lucene_version: "4.8"
      },
      tagline: "You Know, for Search"
    }
    

    Wir passen den Kunden an


    Der Client muss weiterhin so konfiguriert werden, dass er für alle Anforderungen an den Server access_token übergibt. Für eine Node.js-Anwendung sieht es so aus:

    var client = elasticsearch.Client({
      host: {
          protocol: 'https',
          host: 'search.likeastore.com',
          port: 443,
          query: {
              access_token: process.env.ELASTIC_ACCESS_TOKEN
          }
      },
      requestTimeout: 5000
    });
    

    Jetzt können Sie die Anwendung neu starten, sicherstellen, dass alles so funktioniert, wie es sollte ... und ausatmen.

    Nachwort


    Dieses Setup funktionierte (und funktioniert jetzt) ​​für Likeastore perfekt. Mit der Zeit sah ich jedoch einen gewissen Aufwand für diesen Ansatz. Höchstwahrscheinlich können Sie den Proxy-Server loswerden und nginx c basic-authorizationmit upstreameinem Docker-Container konfigurieren , auch mit SSL-Unterstützung.

    Außerdem wird Elastic wahrscheinlich gute Ideen in einem privaten Netzwerk haben, und alle Anfragen dazu sollten über die Anwendungs-API erfolgen. Dies ist unter Entwicklungsgesichtspunkten möglicherweise nicht sehr praktisch, unter Sicherheitsgesichtspunkten jedoch zuverlässiger.

    Bedrohung. Dies ist eine Nacherzählung meines Beitrags aus einem persönlichen Blog in russischer Sprache .

    Jetzt auch beliebt: