Wybór języka

Elasticsearch: certyfikaty i hasła

Gdybym dostawał jednego dolara za każdym razem, gdy słyszę “musieliśmy wyłączyć security i uwierzytelnianie w Elasticsearch, bo inaczej nie dało się z tego korzystać”, to byłbym już parę dolarów do przodu ;-)

Prawdę mówiąc, to nie bardzo rozumiem, skąd bierze się to zamieszanie. Mamy 2024 rok, jednak niektórzy z nas są wciąż zaskoczeni przez “ale jak to certyfikat i hasło???”

Pozwólcie mi w paru prostych akapitach wyjaśnić temat security w Elasticserchu.

Zmarkotniały programista patrzący na bezpieczny i elastyczny tunel

Zacznę od tego, że to nie dotyczy tylko Elasticsearcha, ale czegokolwiek, co gada po HTTPSie. To może być serwer HTTP, może być baza danych, może być dowolny inny system. Żeby mieć nie HTTP, tylko HTTPS, trzeba mieć certyfikat. Normalnie, gdy zabezpieczamy certyfikatem komunikację z naszym serwerem HTTP (żeby przy pasku adresu pojawiła się kłódka), trzeba wystąpić o certyfikat. Taki certyfikat jest wystawiany przez jakąś organizację i dotyczy jakiegoś konkretnego.serwera.com.

Ale skąd wiadomo, że ten certyfikat jest w porządku? Otóż organizacja jest certyfikowana przez kogoś innego, ten ktoś może jeszcze przez kogoś wyżej, więc robi się nam taki łańcuszek, aż dojdziemy do samej “najwyższej góry”. A certyfikat samej najwyższej góry jest zaszyty w naszej przeglądarce albo systemie operacyjnym i właśnie dlatego nie musimy za każdym razem klikać w przeglądarce “tak, ten certyfikat i cały łańcuszek jest w porzo, możesz przeglądarko gadać z tym serwerem”.

Kłopot pojawia się wtedy, gdy mamy certyfikat, który nie jest podpisany przez “kogoś z góry”, tylko nas samych. Ot, uruchamiamy sobie coś lokalnie do testów, nie ma żadnego.sensownego.adresu.com, nie chcemy płacić nikomu za certyfikat, takie tam. Jednak wciąż chcemy mieć HTTPS, żeby nikt nie mógł podsłuchać, co my do tego serwera wysyłamy i co on nam odsyła. I żeby testować tak, jak będzie na produkcji. Wówczas na scenie pojawia się nasz własny, chałupniczo zrobiony certyfikat i łańcuszek do niego (choćby z jednym oczkiem), którego nikt znany i zaszyty w naszym systemie operacyjnym nie podpisał.

Taki właśnie łańcuszek Elasticsearch domyślnie generuje sobie od wersji 8, jeśli nie podamy żadnego “oficjalnego” certyfikatu.

Przykładowo, jak uruchomimy Elasticsearch przy pomocy Dockera (i to nie korzystając z Docker Huba):

docker run --name es -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:8.14.2

to Elasticsearch wygeneruje sobie swój łańcuszek i zapisze w: /usr/share/elasticsearch/config/certs/http_ca.crt.

I teraz: skoro ten łańcuszek nie jest zatwierdzony przez nasz OS, to musimy albo powiedzieć “zignoruj” (nie polecam), albo powiedzieć naszemu oprogramowaniu klienckiemu “spoko, ja TEMU łańcuszkowi ufam”. I trzeba podać TEN łańcuszek, proste ;-)

W przypadku curl można wykorzystać do tego --cacert.

Czyli takie coś (gdy chcemy uruchomić curl wewnątrz kontenera z działającym Elasticsearchem) nie ma szansy zadziałać (bo nie zaufaliśmy łańcuszkowi):

docker exec es curl https://localhost:9200

Żeby połączyć się z Elasticsearchem, musimy zrobić tak:

docker exec es curl --cacert /usr/share/elasticsearch/config/certs/http_ca.crt https://localhost:9200

W tym momencie ktoś może krzyknąć “ale nadal nie działa, a łańcuszek potwierdzony!!!!jedenaście”

Teraz co mamy, to bezpieczne połączenie, czyli nikt nie może podsłuchać, co tym połączeniem jest wysyłane i odbierane (natomiast tak przy okazji: nadal wie, dokąd to połączenie jest, kiedy, i nawet ile mniej więcej danych jest przesyłane). Żeby technologia po drugiej stronie bezpiecznej rury nas rozpoznała, to musimy się uwierzytelnić, czyli powiedzieć “mamo, to ja, rzuć kanapkę” (i znowu, to nie jest coś, co dotyczy tylko Elasticsearcha, ale również masy innych systemów).

Sposoby na to są różne. Do testów (automatycznych) powinno wystarczyć podanie nazwy użytkownika i hasła w Basic Auth, więc możemy ustawić hasło po uruchomieniu kontenera:

docker exec -it es elasticsearch-reset-password -u elastic --interactive

(Polecam też docker exec -it es elasticsearch-reset-password -u elastic --help pod inne tryby działania).

Inną opcją jest ustawienie hasła w zmiennej środowiskowej przy uruchamianiu kontenera, np.:

docker run --name es -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e "ELASTIC_PASSWORD=changeme" docker.elastic.co/elasticsearch/elasticsearch:8.14.2

Jak już mamy i bezpieczne połączenie, i hasło ustawione, to możemy wysłać zapytanie do Elasticsearcha następująco:

docker exec es curl --cacert /usr/share/elasticsearch/config/certs/http_ca.crt --user elastic:changeme https://localhost:9200

  • No dobra, ale to jak ja mam się połączyć czymś innym, niż curl?
  • A to już zależy, czym się łączysz, jakie to ma opcje itd.

Można sobie skopiować łańcuszek i jakoś zatwierdzić w swoim kliencie:

docker cp es:/usr/share/elasticsearch/config/certs/http_ca.crt .

Można zrobić odcisk palca i zatwierdzić łańcuszek po samym odcisku palca, np.

docker exec es openssl x509 -noout -fingerprint -sha256 -inform pem -in /usr/share/elasticsearch/config/certs/http_ca.crt

Podsumowując:

  • jedna rzecz to TLS/SSL/HTTPS i bezpieczne połączenie do serwera (Elasticsearch)
  • druga rzecz to hasło albo klucz API

Dwie uwagi na koniec:

  1. warto czytać dokumentację np. jak połączyć się klientem dla Javy
  2. na produkcji, zamiast haseł to polecam korzystać z kluczy API
  3. nie zaszkodzi rzucić okiem na HTTP TLS/SSL settings

Dzięki i udanego wyszukiwania! ;-)