Статьи

Как обезопасить Elasticsearch и Kibana

Вступление

Elasticsearch (ES) — это поисковая система, основанная на Lucene. Он предоставляет распределенную полнотекстовую поисковую систему с поддержкой нескольких арендаторов с веб-интерфейсом HTTP и JSON-документами без схемы.

Kibana — это плагин для визуализации данных с открытым исходным кодом для Elasticsearch. Он предоставляет возможности визуализации поверх содержимого, проиндексированного в кластере Elasticsearch. Пользователи могут создавать линейчатые, линейные и точечные графики или круговые диаграммы и карты поверх больших объемов данных.

Эти два продукта широко используются на рынке сегодня для анализа данных. Однако безопасность — это один из аспектов, который изначально не был встроен в продукт. Поскольку сегодня данные являются жизненно важным для любой организации, становится необходимым обеспечить безопасность Elasticsearch и Kibana. В этом сообщении мы рассмотрим один из способов, с помощью которого для них могут быть реализованы аутентификация, авторизация и шифрование.

Предположения

Учебное пособие предполагает следующее:

  1. MapR Sandbox работает
  2. Elasticsearch и Kibana были установлены и работают

Варианты, доступные для обеспечения безопасности Elasticsearch и Kibana

Самые популярные варианты обеспечения безопасности Elasticsearch и Kibana сравниваются в таблице ниже.

Shield — это плагин безопасности, разработанный той же компанией, которая разработала Elasticsearch. Это позволяет вам легко защитить эти данные с помощью имени пользователя и пароля, упрощая вашу архитектуру. Расширенные функции безопасности, такие как шифрование, управление доступом на основе ролей, IP-фильтрация и аудит, также доступны по мере необходимости.

NGINX — это веб-сервер с открытым исходным кодом. Он может выступать в качестве прокси-сервера и, помимо прочего, может выполнять балансировку нагрузки. В сочетании с LUA и внешними сценариями его можно использовать для защиты Elasticsearch и Kibana. Мы будем использовать этот подход в этом уроке.

Searchguard — это альтернатива Shield с открытым исходным кодом. Он обеспечивает практически все те же функции, что и Shield, за исключением некоторых функций, таких как аутентификация LDAP. Однако эти функции доступны в платном варианте.

ЩИТ NGINX SEARCHGUARD
Плагин безопасности для Elasticsearch и Kibana от Elasticsearch. NGINX (произносится как «движок х») — это веб-сервер. Он может выступать в качестве обратного прокси-сервера, балансировщика нагрузки и кэша HTTP. Search Guard — это плагин Elasticsearch, который предлагает шифрование, аутентификацию и авторизацию.
Имеет встроенную поддержку для:

  1. Управление сессиями
  2. Зашифрованные сообщения
  3. Управление доступом на основе ролей
  4. Ведение аудита
Управление доступом на основе ролей реализовано с помощью модуля LUA. Аутентификация LDAP может быть реализована с использованием внешних программ. В бесплатной версии нет поддержки аутентификации на основе LDAP или ведения журнала аудита.
$ 1600 / год / кластер Свободно.
NGINX Plus — платная версия — $ 1900 / экземпляр
Свободно. Коммерческая версия также доступна.

Установка NGINX

NGINX — это веб-сервер с открытым исходным кодом, ориентированный на высокую производительность, параллелизм и низкий объем занимаемой памяти.

NGINX с самого начала был разработан с учетом роли прокси и поддерживает множество связанных директив и опций конфигурации. Мы будем использовать NGINX для настройки аутентификации и авторизации на основе LDAP.

OpenResty ™ — это полноценная веб-платформа, объединяющая стандартное ядро ​​NGINX, LuaJIT, множество тщательно написанных библиотек Lua, множество высококачественных сторонних модулей NGINX и большинство их внешних зависимостей.

Используя преимущества различных хорошо разработанных функций NGINX, OpenResty эффективно превращает сервер NGINX в мощный сервер веб-приложений.

меры

  1. Загрузите последнюю версию OpenResty здесь:
    http://openresty.org/en/download.html
  2. Создайте и установите пакет, используя следующие команды:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    tar xvf openresty-.tar.gz
     
    cd openresty-
     
    ./configure --prefix=/usr/local/openresty --with-luajit 
    --with-http_auth_request_module
     
    gmake
     
    gmake install
     
    export PATH=/usr/local/openresty/bin:/usr/local/openresty/nginx/sbin:$PATH

Аутентификация

В этом руководстве мы рассмотрим следующие два метода аутентификации:

  1. Базовая HTTP-аутентификация
  2. Аутентификация LDAP

Базовая HTTP-аутентификация

Шаг 1 — Установка инструментов Apache

Вам потребуется команда htpassword для настройки пароля, который ограничит доступ к Elasticsearch и Kibana. Эта команда является частью пакета apache2-utils, поэтому первым шагом является установка этого пакета.

sudo apt-get install apache2-utils

Шаг 2. Настройка учетных данных для базовой аутентификации HTTP

На этом этапе вы создадите пароль для пользователя, которому должен быть разрешен доступ к Elasticsearch и Kibana. Этот пароль и соответствующее имя пользователя будут сохранены в указанном вами файле.

Пароль будет зашифрован, а имя файла может быть любым. Здесь мы используем файл /opt/elk/.espasswd и имя пользователя vikash .

Чтобы создать пароль, выполните следующую команду. Вам нужно будет пройти аутентификацию, затем указать и подтвердить пароль.

sudo htpasswd -c /opt/elk/.espasswd vikash

Вы можете проверить содержимое вновь созданного файла, чтобы увидеть имя пользователя и хешированный пароль.

cat /opt/elk/.espasswd

обеспечение-упруго-поиска и kibana-1

Шаг 3 — Обновление конфигурации NGINX

Теперь, когда вы создали учетные данные базовой аутентификации HTTP, следующим шагом является обновление конфигурации NGINX для Elasticsearch и Kibana, чтобы использовать ее.

Базовая аутентификация HTTP становится возможной благодаря директивам auth_basic и auth_basic_user_file .

Значением auth_basic является любая строка, которая будет отображаться в приглашении для аутентификации. Значение auth_basic_user_file — это путь к файлу паролей, который был создан на шаге 2. Обе директивы должны быть добавлены в раздел location.

Проверьте, запущены ли какие-либо процессы NGINX, и уничтожьте их:

1
2
3
4
5
6
7
cd /usr/local/openresty/nginx/
sbin/nginx -s stop
 
        (Or)
 
      ps –ef | grep nginx
kill -9 <pid1> <pid2> …. <pidn>

Запустите сервер NGINX с этим файлом конфигурации, как показано ниже:

1
2
cd /usr/local/openresty/nginx
sbin/nginx -p $PWD -c conf/nginx_basic_http_authentication.conf

Содержание файла конфигурации приведено ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
worker_processes  1;
 
error_log /usr/local/openresty/nginx/logs/lua.log debug;
 
events {
  worker_connections 1024;
}
 
http {
  upstream elasticsearch {
    server 127.0.0.1:9201;
    keepalive 15;
  }
 
  upstream kibana {
    server 127.0.0.1:5701;
    keepalive 15;
  }
 
  server {
    listen 8881;
 
    location / {
      auth_basic           "Protected Elasticsearch";<
      auth_basic_user_file /opt/elk/.espasswd;
 
 
      proxy_pass http://elasticsearch;
      proxy_redirect off;
      proxy_buffering off;
 
      proxy_http_version 1.1;
      proxy_set_header Connection "Keep-Alive";
      proxy_set_header Proxy-Connection "Keep-Alive";
    }
 
  }
 
  server {
    listen 8882;
 
    location / {
      auth_basic           "Protected Kibana";
      auth_basic_user_file /opt/elk/.espasswd;
 
 
      proxy_pass http://kibana;
      proxy_redirect off;
      proxy_buffering off;
 
      proxy_http_version 1.1;
      proxy_set_header Connection "Keep-Alive";
      proxy_set_header Proxy-Connection "Keep-Alive";
    }
 
  }
 
}

Ниже представлены скриншоты, когда пользователь пытается получить доступ к Elasticsearch.
Примечание: NGINX настроен на прослушивание порта 8881 для соединений с Elasticsearch и порта 8882 для соединений с Kibana в этом примере.

Снимки экрана, показывающие, что «evil_user» не имеет доступа к Elasticsearch

обеспечение-упруго-поиска и kibana-2

Поскольку пользователь не присутствует в файле паролей, он / она был снова перенаправлен на страницу входа.

обеспечение-упруго-поиска и kibana-3

Снимок экрана, показывающий, что пользователь ‘vikash’ имеет доступ к Elasticsearch

обеспечение-упруго-поиска и kibana-4

обеспечение-упруго-поиска и kibana-5

Аутентификация LDAP

Шаг 1. Если сервер LDAP еще не запущен, установите и настройте его. Для целей этого примера, пожалуйста, следуйте инструкциям на https://github.com/osixia/docker-openldap для настройки сервера LDAP.

Поскольку сервер LDAP работает в контейнере Docker, следующие команды будут полезны для перезапуска и выполнения административных задач с ним:

docker ps –a — выводит список всех запущенных процессов Docker

docker exec -it <Docker PID> bash — открывает оболочку bash на компьютере Docker

Шаг 2: В этом примере мы будем запускать внутренний сервер, написанный на Python, который обслуживает страницу входа в систему, и демон аутентификации LDAP, написанный на Python. Файлы кода Python доступны в этом репозитории GitHub:

https://github.com/nginxinc/nginx-ldap-auth

Шаг 3: На хосте, где должен запускаться демон ldap-auth, установите следующее дополнительное программное обеспечение. Мы рекомендуем использовать версии, распространяемые вместе с операционной системой, вместо загрузки программного обеспечения из репозитория с открытым исходным кодом.

  • Версия Python 2. Версия 3 не поддерживается.
  • Модуль Python LDAP, python-ldap (По проекту ОС python-ldap.org).

Шаг 4: Ниже представлен файл конфигурации NGINX Plus. Важные директивы обсуждаются здесь.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
Filename - /usr/local/openresty/nginx/conf/conf/nginx-ldap-auth.conf
 
error_log logs/error.log debug;
 
error_log /usr/local/openresty/nginx/logs/lua.log notice;
 
events { }
 
http {
    proxy_cache_path cache/  keys_zone=auth_cache:10m;
 
    # The back-end daemon listens on port 9000 as implemented
    # in backend-sample-app.py.
    # Change the IP address if the daemon is not running on the
    # same host as NGINX/NGINX Plus.
    upstream backend {
        server 127.0.0.1:9000;
    }
 
    upstream elasticsearch {
        server 127.0.0.1:9200;
    }
 
    upstream kibana4 {
        server 127.0.0.1:5601;
    }
 
    # NGINX/NGINX Plus listen on port 8081 for requests that require
    # authentication. Change the port number as appropriate.
     
 
    server {
        listen 8881;
 
        # Protected application
        location / {
 
            auth_request /auth-proxy;
 
            # redirect 401 and 403 to login form
            error_page 401 403 =200 /login;
 
            auth_request_set $user $upstream_http_LDAPUser;
  
        access_by_lua_file '/usr/local/openresty/nginx/authorize_es_ldap.lua';
  
        proxy_pass http://elasticsearch/;
        }
 
 
 
        location /login {
            proxy_pass http://backend/login;
        # Login service returns a redirect to the original URI
            # and sets the cookie for the ldap-auth daemon
            proxy_set_header X-Target $request_uri;
        }
 
        location = /auth-proxy {
            internal;
 
            # The ldap-auth daemon listens on port 8888, as set
            # in nginx-ldap-auth-daemon.py.
            # Change the IP address if the daemon is not running on
            # the same host as NGINX/NGINX Plus.
            proxy_pass http://127.0.0.1:8888;
 
            proxy_pass_request_body off;
            proxy_set_header X-Target 'http://localhost:9200/';
        #proxy_set_header Content-Length "";
            proxy_cache auth_cache;
            proxy_cache_valid 200 403 10m;
 
            # The following directive adds the cookie to the cache key
            proxy_cache_key "$http_authorization$cookie_nginxauth";
 
            # As implemented in nginx-ldap-auth-daemon.py, the ldap-auth daemon
            # communicates with an OpenLDAP server, passing in the following
            # parameters to specify which user account to authenticate. To
            # eliminate the need to modify the Python code, this file contains
            # 'proxy_set_header' directives that set the values of the
            # parameters. Set or change them as instructed in the comments.
            #
            #    Parameter      Proxy header
            #    -----------    ----------------
            #    basedn         X-Ldap-BaseDN
            #    binddn         X-Ldap-BindDN
            #    bindpasswd     X-Ldap-BindPass
            #    cookiename     X-CookieName
            #    realm          X-Ldap-Realm
            #    template       X-Ldap-Template
            #    url            X-Ldap-URL
 
            # (Required) Set the URL and port for connecting to the LDAP server,
            # by replacing 'example.com' and '636'.
            proxy_set_header X-Ldap-URL      "ldap://172.17.0.1:389";
 
            # (Required) Set the Base DN, by replacing the value enclosed in
            # double quotes.
            proxy_set_header X-Ldap-BaseDN   "dc=example,dc=org";
 
            # (Required) Set the Bind DN, by replacing the value enclosed in
            # double quotes.
            proxy_set_header X-Ldap-BindDN   "cn=admin,dc=example,dc=org";
 
            # (Required) Set the Bind password, by replacing 'secret'.
            proxy_set_header X-Ldap-BindPass "admin";
 
            # (Required) The following directives set the cookie name and pass
            # it, respectively. They are required for cookie-based
            # authentication. Comment them out if using HTTP basic
            # authentication.
            proxy_set_header X-CookieName "nginxauth";
            proxy_set_header Cookie nginxauth=$cookie_nginxauth;
 
            # (Required if using Microsoft Active Directory as the LDAP server)
            # Set the LDAP template by uncommenting the following directive.
            #proxy_set_header X-Ldap-Template "(SAMAccountName=%(username)s)";
 
            # (Optional if using OpenLDAP as the LDAP server) Set the LDAP
            # template by uncommenting the following directive and replacing
            # '(cn=%(username)s)' which is the default set in
            # nginx-ldap-auth-daemon.py.
            #proxy_set_header X-Ldap-Template "(cn=%(username)s)";
 
            # (Optional) Set the realm name, by uncommenting the following
            # directive and replacing 'Restricted' which is the default set
            # in nginx-ldap-auth-daemon.py.
            #proxy_set_header X-Ldap-Realm    "Restricted";
             
    }
    }
 
 
 
    server {
        listen 8882;
 
        # Protected application
        location / {
 
            auth_request /auth-proxy;
 
            # redirect 401 and 403 to login form
            error_page 401 403 =200 /login;
 
            auth_request_set $user $upstream_http_LDAPUser;
  
        access_by_lua_file '/usr/local/openresty/nginx/authorize_kibana4_ldap.lua';
  
        proxy_pass http://kibana4/;
        }
 
 
 
        location /login {
            proxy_pass http://backend/login;
        # Login service returns a redirect to the original URI
            # and sets the cookie for the ldap-auth daemon
            proxy_set_header X-Target $request_uri;
        }
 
        location = /auth-proxy {
            internal;
 
            # The ldap-auth daemon listens on port 8888, as set
            # in nginx-ldap-auth-daemon.py.
            # Change the IP address if the daemon is not running on
            # the same host as NGINX/NGINX Plus.
            proxy_pass http://127.0.0.1:8888;
 
            proxy_pass_request_body off;
            proxy_set_header X-Target 'http://localhost:5601/';
        #proxy_set_header Content-Length "";
            proxy_cache auth_cache;
            proxy_cache_valid 200 403 10m;
 
            # The following directive adds the cookie to the cache key
            proxy_cache_key "$http_authorization$cookie_nginxauth";
 
            # As implemented in nginx-ldap-auth-daemon.py, the ldap-auth daemon
            # communicates with an OpenLDAP server, passing in the following
            # parameters to specify which user account to authenticate. To
            # eliminate the need to modify the Python code, this file contains
            # 'proxy_set_header' directives that set the values of the
            # parameters. Set or change them as instructed in the comments.
            #
            #    Parameter      Proxy header
            #    -----------    ----------------
            #    basedn         X-Ldap-BaseDN
            #    binddn         X-Ldap-BindDN
            #    bindpasswd     X-Ldap-BindPass
            #    cookiename     X-CookieName
            #    realm          X-Ldap-Realm
            #    template       X-Ldap-Template
            #    url            X-Ldap-URL
 
            # (Required) Set the URL and port for connecting to the LDAP server,
            # by replacing 'example.com' and '636'.
            proxy_set_header X-Ldap-URL      "ldap://172.17.0.1:389";
 
            # (Required) Set the Base DN, by replacing the value enclosed in
            # double quotes.
            proxy_set_header X-Ldap-BaseDN   "dc=example,dc=org";
 
            # (Required) Set the Bind DN, by replacing the value enclosed in
            # double quotes.
            proxy_set_header X-Ldap-BindDN   "cn=admin,dc=example,dc=org";
 
            # (Required) Set the Bind password, by replacing 'secret'.
            proxy_set_header X-Ldap-BindPass "admin";
 
            # (Required) The following directives set the cookie name and pass
            # it, respectively. They are required for cookie-based
            # authentication. Comment them out if using HTTP basic
            # authentication.
            proxy_set_header X-CookieName "nginxauth";
            proxy_set_header Cookie nginxauth=$cookie_nginxauth;
 
            # (Required if using Microsoft Active Directory as the LDAP server)
            # Set the LDAP template by uncommenting the following directive.
            #proxy_set_header X-Ldap-Template "(SAMAccountName=%(username)s)";
 
            # (Optional if using OpenLDAP as the LDAP server) Set the LDAP
            # template by uncommenting the following directive and replacing
            # '(cn=%(username)s)' which is the default set in
            # nginx-ldap-auth-daemon.py.
            #proxy_set_header X-Ldap-Template "(cn=%(username)s)";
 
            # (Optional) Set the realm name, by uncommenting the following
            # directive and replacing 'Restricted' which is the default set
            # in nginx-ldap-auth-daemon.py.
            #proxy_set_header X-Ldap-Realm    "Restricted";
             
    }
    }
 
}

Настройки LDAP

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# URL and port for connecting to the LDAP server
# Use “ldaps://< IP Address of the LDAP Server >:636” if you are using secure LDAP
proxy_set_header X-Ldap-URL "ldap://< IP Address of the LDAP Server > ";
 
# Base DN
proxy_set_header X-Ldap-BaseDN "cn=admin,dc=example,dc=org";
 
# Bind DN
proxy_set_header X-Ldap-BindDN "cn=admin,dc=example,dc=org";
 
# Bind password
proxy_set_header X-Ldap-BindPass "admin";
 
 
IP Address for Backend Daemon
 
If the backend daemon is not running on the same host as NGINX Plus, change the IP address for it in the upstream configuration block:
 
upstream backend {
    server 127.0.0.1:9000;
 }
 
IP Address for ldap-auth Daemon
 
If the ldap-auth daemon is not running on the same host as NGINX Plus, change the IP address in this proxy_pass directive:
 
location = /auth-proxy {
    proxy_pass http://127.0.0.1:8888;
    ...
}
 
IP Address and Port on Which NGINX Listens
 
If the client is not running on the same host as NGINX Plus, change the IP address in this listen directive (or remove the address completely to accept traffic from any client). You can also change the port on which NGINX listens from 8081 if you wish:
 
server {
    listen 127.0.0.1:8081;        
    
 }

Примечание. Elasticsearch работает на порту 9200, а NGINX прослушивает соединения с Elasticsearch на порту 8081.

Шаг 5: Запустите сервер NGINX, серверную часть и демон аутентификации LDAP:

Проверьте, запущен ли какой-либо процесс сервера NGINX, и убейте их:

1
2
3
4
5
6
7
cd /usr/local/openresty/nginx/
 sbin/nginx -s stop
 
        (Or)
 
 ps –ef | grep nginx
kill -9 <pid1> <pid2> …. <pidn>

Запустите сервер NGINX с соответствующим файлом конфигурации:

1
2
cd  /usr/local/openresty/nginx/
sbin/nginx -p $PWD -c conf/nginx-ldap-auth.conf

Запустите внутренний сервер:

1
2
cd  /usr/local/openresty/nginx-ldap-auth
python backend-sample-app.py

Запустите демон аутентификации LDAP:

1
2
cd  /usr/local/openresty/nginx-ldap-auth
python nginx-ldap-auth-daemon.py

Ниже приведены снимки экрана, на которых показаны выходные данные сервера, демона аутентификации LDAP и браузера, пытающегося получить доступ к Elasticsearch.

  1. Попытка получить доступ к Elasticsearch с недействительными учетными данными
    обеспечение-упруго-поиска и kibana-6
  2. Демон аутентификации LDAP, показывающий ошибку аутентификации (перетащите изображение в нижний угол, чтобы получить более четкое представление).
    обеспечение-упруго-поиска и kibana-7
  3. Попытка получить доступ к Elasticsearch, предоставив действительные учетные данные
    обеспечение-упруго-поиска и kibana-8
  4. Веб-браузер перенаправляется в Elasticsearch после успешной аутентификации
    обеспечение-упруго-поиска и kibana-9
  5. Аутентификация LDAP для запросов CURL
    обеспечение-упруго-поиск и kibana-10

авторизация

Мы покажем, как реализовать следующие методы авторизации:

  1. Контроль доступа на основе скриптов LUA
  2. Многоуровневая безопасность для разных экземпляров Elasticsearch / Kibana

Контроль доступа с помощью скриптов LUA

Директива access_by_lua_file в файле конфигурации NGINX используется для указания пути к файлу LUA, который контролирует доступ к определенному ресурсу в Elasticsearch.

Ниже приведен пример сценария LUA, который показывает, как разрешить только пользователю «vikash» доступ к индексу «Traffic» и ограничить пользователя «swapnil»

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
-- authorization rules
  
local restrictions = {
  all  = {
    ["^/$"]                             = { "HEAD" }
  },
  
  swapnil = {
    ["^/$"]                             = { "GET" },
    ["^/?[^/]*/?[^/]*/_search"]         = { "GET", "POST" },
    ["^/?[^/]*/?[^/]*/_msearch"]        = { "GET", "POST" },
    ["^/?[^/]*/?[^/]*/_validate/query"] = { "GET", "POST" },
    ["/_aliases"]                       = { "GET" },
    ["/_cluster.*"]                     = { "GET" }
  },
  vikash = {
    ["^/$"]                             = { "GET" },
    ["^/?[^/]*/?[^/]*/_search"]         = { "GET", "POST" },
    ["^/?[^/]*/?[^/]*/_msearch"]        = { "GET", "POST" },
    ["^/?[^/]*/traffic*"]               = { "GET", "POST", "PUT", "DELETE" },
    ["^/?[^/]*/?[^/]*/_validate/query"] = { "GET", "POST" },
    ["/_aliases"]                       = { "GET" },
    ["/_cluster.*"]                     = { "GET" }
  },
  
  admin = {
    ["^/?[^/]*/?[^/]*/_bulk"]          = { "GET", "POST" },
    ["^/?[^/]*/?[^/]*/_refresh"]       = { "GET", "POST" },
    ["^/?[^/]*/?[^/]*/?[^/]*/_create"] = { "GET", "POST" },
    ["^/?[^/]*/?[^/]*/?[^/]*/_update"] = { "GET", "POST" },
    ["^/?[^/]*/?[^/]*/?.*"]            = { "GET", "POST", "PUT", "DELETE" },
    ["^/?[^/]*/?[^/]*$"]               = { "GET", "POST", "PUT", "DELETE" },
    ["/_aliases"]                      = { "GET", "POST" }
  }
}
  
-- get authenticated user as role
local role = ngx.var.remote_user
ngx.log(ngx.DEBUG, role)
  
-- exit 403 when no matching role has been found
if restrictions[role] == nil then
  ngx.header.content_type = 'text/plain'
  ngx.log(ngx.WARN, "Unknown role ["..role.."]")
  ngx.status = 403
  ngx.say("403 Forbidden: You don\'t have access to this resource.")
  return ngx.exit(403)
end
  
-- get URL
local uri = ngx.var.uri
ngx.log(ngx.DEBUG, uri)
  
-- get method
local method = ngx.req.get_method()
ngx.log(ngx.DEBUG, method)
  
local allowed  = false
  
for path, methods in pairs(restrictions[role]) do
  
  -- path matched rules?
  local p = string.match(uri, path)
  
  local m = nil
  
  -- method matched rules?
  for _, _method in pairs(methods) do
    m = m and m or string.match(method, _method)
  end
  
  if p and m then
    allowed = true
    ngx.log(ngx.NOTICE, method.." "..uri.." matched: "..tostring(m).." "..tostring(path).." for "..role)
    break
  end
end
  
if not allowed then
  ngx.header.content_type = 'text/plain'
  ngx.log(ngx.WARN, "Role ["..role.."] not allowed to access the resource ["..method.." "..uri.."]")
  ngx.status = 403
  ngx.say("403 Forbidden: You don\'t have access to this resource.")
  return ngx.exit(403)
end
1
2
3
4
5
6
7
cd /usr/local/openresty/nginx/
 sbin/nginx -s stop
 
        (Or)
 
 ps –ef | grep nginx
kill -9 <pid1> <pid2> …. <pidn>

Запустите службу NGINX с этими конфигурациями:

1
2
cd /usr/local/openresty/nginx/
sbin/nginx -p $PWD -c conf/nginx_authorize_by_lua.conf

Снимки экрана, на которых показан отказ в доступе к индексу «трафика» для пользователя «swapnil»

URL: http: // localhost: 8881
обеспечение-упруго-поиск и kibana-11

обеспечение эластичность-поиск и kibana-12

Снимки экрана, на которых пользователь swapnil может получить доступ к другим ресурсам, кроме «трафика»
обеспечение-упруго-поиск и kibana-13

Снимок экрана, показывающий, что пользователь ‘vikash’ имеет доступ к индексу «трафика»
обеспечение-упруго-поиск и kibana-14
обеспечение эластичность-поиск и kibana-15

Многоуровневая безопасность (MLS)

Проблема с вышеуказанным подходом состоит в том, что мы не можем повторить этот процесс для Кибаны. Kibana получает все свои данные от Elasticsearch и переписывает все URL-адреса внутри, поэтому мы больше не знаем, что такое URL-адреса, и, следовательно, не можем писать для них правила.

В подобных ситуациях лучше использовать MLS-подходы. Соответственно, у нас будут три разных экземпляра Elasticsearch и Kibana, каждый из которых соответствует разным уровням очистки, и, таким образом, мы решаем проблему авторизации. Аутентификация для этих экземпляров может быть либо базовой HTTP-аутентификацией, либо на основе LDAP.

Шаг 1. Настройте несколько экземпляров Elasticsearch (ES) и предоставьте один общий URL-адрес конечным пользователям. В этом случае было установлено http: // localhost: 8081 .

Для этого эксперимента были созданы три экземпляра Elasticsearch, каждый из которых прослушивал свой порт на локальной машине.

1
2
3
ES Node 1 – http://localhost:9201
ES Node 2 – http://localhost:9202
ES Node 3 – http://localhost:9203

Создайте три разных файла конфигурации Elasticsearch, по одному для каждого из этих экземпляров, и запустите их, используя следующую команду:

01
02
03
04
05
06
07
08
09
10
ES_HOME=/opt/elk/elasticsearch-1.4.4/
 
nohup $ES_HOME/bin/elasticsearch \
-Des.config=$ES_HOME/config/elasticsearch_node1.yml >>  /tmp/elasticsearch_node1.out \ 2>&1 &
 
nohup $ES_HOME/bin/elasticsearch \
-Des.config=$ES_HOME/config/elasticsearch_node2.yml >>  /tmp/elasticsearch_node2.out \ 2>&1 &
 
nohup $ES_HOME/bin/elasticsearch \
-Des.config=$ES_HOME/config/elasticsearch_node3.yml >>  /tmp/elasticsearch_node3.out \ 2>&1 &

Пример файла конфигурации для Elasticsearch приведен в разделе ресурсов. Имя файла:

1
/opt/elk/elasticsearch-1.4.4/config/elasticsearch_node1.yml

Шаг 2: Настройте несколько экземпляров Kibana, по одному для каждого уровня очистки и предоставьте один URL-адрес для конечных пользователей; в этом случае было установлено http: // localhost: 8082 .

Для этого эксперимента были созданы три экземпляра Kibana, каждый из которых слушал разные порты на локальной машине, соответствующие разным уровням разрешения.

1
2
3
Kibana Node 1 – http://localhost:5701 - Top Secret – Connects to ES Node 1
Kibana Node 2 – http://localhost:5702 - Secret – Connects to ES Node 2
Kibana Node 3 – http://localhost:5703 - Public – Connects to ES Node 3

Запустите их, используя следующую команду:

1
2
3
nohup /opt/elk/kibana_nodes/kibana_node1/bin/kibana > /tmp/kibana_node1.out 2>&1 &
nohup /opt/elk/kibana_nodes/kibana_node2/bin/kibana > /tmp/kibana_node2.out 2>&1 &
nohup /opt/elk/kibana_nodes/kibana_node3/bin/kibana > /tmp/kibana_node3.out 2>&1 &

Пример файла конфигурации для Kibana приведен в разделе ресурсов. Имя файла:

1
/opt/elk/kibana_nodes/kibana_node1/config/kibana.yml

Шаг 3: Настройте прокси-сервер NGINX для прослушивания соединений с Elasticsearch (ES) или Kibana (NGINX будет прослушивать http: // localhost: 8081 для соединений с ES и http: // localhost: 8082 для соединений с Kibana). Пожалуйста, ознакомьтесь с файлом «Установка NGINX и LDAP Authentication.docx» в zip для пошаговых инструкций по его установке.

Аутентифицируйте пользователя по базе данных LDAP и проверьте, какой уровень авторизации он / она имеет на основании информации, прочитанной из базы данных (в этом случае использовался локальный файл, содержащий список пользователей и уровней авторизации), и перенаправьте пользователя на соответствующий сервер Elasticsearch / Kibana.

Сервер LDAP — работает в контейнере Docker ( https://github.com/osixia/docker-openldap )

База данных пользовательских ролей — файл представлен в разделе ресурсов (имя файла — «user_authorization_level.dat»)

Файл конфигурации NGINX — файл представлен в разделе ресурсов (имя файла — «nginx-ldap-auth-clusters.conf»)

Python Daemon для проверки на сервере LDAP (имя файла «nginx-ldap-auth-daemon.py»)

Форма входа в бэкэнд Python (имя файла «backend-sample-app.py»)

Оба вышеуказанных файла Python можно найти по адресу: https://github.com/nginxinc/nginx-ldap-auth

Шаг 4: Запустите сервер NGINX, как указано ниже:

Проверьте, запущены ли какие-либо процессы сервера NGINX, и уничтожьте их:

1
2
3
4
5
6
7
cd /usr/local/openresty/nginx/
 sbin/nginx -s stop
 
        (Or)
 
 ps –ef | grep nginx
kill -9 <pid1> <pid2> …. <pidn>

Запустите внутренний сервер:

1
2
cd  /usr/local/openresty/nginx-ldap-auth
python backend-sample-app.py

Запустите демон аутентификации LDAP:

1
2
cd  /usr/local/openresty/nginx-ldap-auth
python nginx-ldap-auth-daemon.py

Проверьте, запущены ли какие-либо процессы сервера NGINX, и уничтожьте их:

1
2
3
4
5
6
7
cd /usr/local/openresty/nginx/
sbin/nginx -s stop
 
        (Or)
 
ps –ef | grep nginx
kill -9 <pid1> <pid2> …. <pidn>

Запустите сервер NGINX с соответствующим файлом конфигурации:

1
2
cd /usr/local/openresty/nginx/
sbin/nginx -p $PWD -c conf/nginx-ldap-auth-clusters.conf

Скриншоты:

Пользователь «admin» вошел в систему, и у него есть уровень «Совершенно секретно». Соответствующим этому сервером Kibana является http: // localhost: 5701, который подключен к серверу Elasticsearch http: // localhost: 9201, в котором есть только индекс «Шекспира».

обеспечение-упруго-поиск и kibana-16

Elasticsearch, показывающий, что на нем доступен только индекс «Шекспира»

обеспечение-упруго-поиск и kibana-17

обеспечение-упруго-поиск и kibana-18

обеспечение-упруго-поиск и kibana-19

Лог-файл, показывающий, что администратор «пользователя» имеет совершенно секретный уровень «разрешения»

обеспечение-упруго-поиск и kibana-20

Пользователь «Викаш» вошел в систему, и у него есть «Секретный» уровень допуска. Соответствующим этому сервером Kibana является http: // localhost: 5702, который подключен к серверу Elasticsearch http: // localhost: 9202, в котором есть индексы «logstash *».

обеспечение-упруго-поиск и kibana-21

Elasticsearch показывает, что на нем доступны только индексы «logstash *»

обеспечение эластичность-поиск и kibana-22

обеспечение эластичность-поиск и kibana-23

обеспечение-упруго-поиск и kibana-24

Файл журнала, показывающий, что «пользовательский» викаш имеет только секретный уровень «очистки»

обеспечение эластичность-поиск и kibana-25

Пользователь «swapnil» вошел в систему и имеет уровень «Public». Соответствующим этому сервером Kibana является http: // localhost: 5703, который подключен к серверу Elasticsearch http: // localhost: 9203, в котором есть только индекс «банк».

обеспечение эластичность-поиск и kibana-26

Elasticsearch показывает, что на нем доступен только «банковский» индекс

обеспечение эластичность-поиск и kibana-27

обеспечение эластичность-поиск и kibana-28

обеспечение эластичность-поиск и kibana-29

Файл журнала, показывающий, что swapnil пользователя имеет только публичный уровень очистки

обеспечение эластичность-поиск и kibana-30
Примечание. Все сопоставления и данные индекса были загружены с
https://www.elastic.co/guide/en/kibana/current/getting-started.html

КОДИРОВАНИЕ

Если бы вы внимательно заметили, вы бы поняли, что до сих пор мы использовали только «http». В производственных средах мы часто хотели бы использовать «https», поскольку это шифрует все данные и предотвращает кражу информации злоумышленниками. В приведенном ниже руководстве вы узнаете, как использовать протокол «https».

Шаг 1. Создайте самозаверяющий сертификат SSL.

01
02
03
04
05
06
07
08
09
10
11
12
13
cd /usr/local/openresty/nginx
 
mkdir certs
 
cd certs
 
openssl genrsa 2048 > host.key
 
openssl req -new -x509 -nodes -sha1 -days 3650 -key host.key > host.cert
 
openssl x509 -noout -fingerprint -text < host.cert > host.info
 
cat host.cert host.key > host.pem

Шаг 2: Добавьте информацию, связанную с сертификатом, в файл конфигурации NGINX.

1
2
3
4
5
6
7
ssl on;
  ssl_certificate /usr/local/openresty/nginx/certs/host.cert;
  ssl_certificate_key /usr/local/openresty/nginx/certs/host.key;
  ssl_session_timeout 5m;
  ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
  ssl_ciphers HIGH:!aNULL:!eNULL:!LOW:!MD5;
  ssl_prefer_server_ciphers on;

Примечание. Полный файл конфигурации NGINX приведен ниже для справки.


NGINX прослушивает порт 8080 для соединений с ES и реализует базовую HTTP-аутентификацию

Конфигурационный файл NGINX

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
worker_processes  1;
 
error_log /usr/local/openresty/nginx/logs/nginx_https.log debug;
 
events {
  worker_connections 1024;
}
 
http {
 
    upstream elasticsearch {
        server 127.0.0.1:9200;
        keepalive 15;
    }
 
    server {
        listen 8080;
        keepalive_timeout   60s;
        ssl on;
        ssl_certificate /usr/local/openresty/nginx/certs/host.cert;
        ssl_certificate_key /usr/local/openresty/nginx/certs/host.key;
        ssl_session_timeout 1m;
        ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
        ssl_ciphers HIGH:!aNULL:!eNULL:!LOW:!MD5;
        ssl_prefer_server_ciphers on;
 
        auth_basic "ElasticSearch";
        auth_basic_user_file /opt/elk/.espasswd;
 
        location / {
                proxy_pass http://elasticsearch;
                proxy_http_version 1.1;
                proxy_set_header Connection "Keep-Alive";
                proxy_set_header Proxy-Connection "Keep-Alive";
        }
    }
}

Поскольку это самозаверяющий сертификат, вы увидите «красный» значок https, зачеркнутый. Если вы используете сертификат от доверенной третьей стороны, такой как Verisign, вы увидите, что он превращается в «зеленый» значок.

В этой записи блога вы узнали об одном из способов реализации аутентификации, авторизации и шифрования для Elasticsearch и Kibana. Если у вас есть какие-либо вопросы, пожалуйста, задавайте их в разделе комментариев ниже.

Ссылка: Как обезопасить Elasticsearch и Kibana от нашего партнера JCG Чейза Хули в блоге Mapr .