You are on page 1of 14

Seguridad en Elasticsearch con

el plugin ReadonlyREST

1. Introducción

Cuando estamos trabajando con Elasticsearch nos puede resultar útil securizar las
peticiones realizadas a los diferentes índices que tengamos creados y tener algún
control sobre el acceso a los mismos.

Elastic dispone de la extensión de pago X-Pack que proporciona funciones de


seguridad, alertas, monitoreo, informes y gráficos en un paquete fácil de instalar.

Aunque puede resultar conveniente el pago de esta licencia, puede haber proyectos
que no requieran tantas funciones y en los que resulte más viable el uso de alguna
tecnología open source como el plugin para Elasticsearch ReadonlyREST.

Concretamente, lo que nos ofrece ReadonlyREST en su versión open source es:

 Cifrado: acceso a la API de Elasticsearch a través de HTTPS.

 Autenticación: solicitud de credenciales de acceso.

 Autorización: declarar grupos de usuarios, permisos y acceso parcial a


índices.

 Control de acceso: lógica de acceso compleja a través de listas de control


de acceso (ACL).

 Registro de auditoría: trazas de las solicitudes de acceso se pueden


guardar en ficheros o en índices.
Flujo de una solicitud de búsqueda. Fuente:
https://github.com/beshu-tech/readonlyrest-docs/blob/master/elasticsearch.md

Vamos a verlo en acción.

2. Caso de prueba

Para nuestro caso, vamos a suponer que tenemos que controlar el acceso a
Elasticsearch para distintos usuarios que se autenticarán con su contraseña de
LDAP. Para ello, vamos a montar un entorno con docker que contenga OpenLDAP,
Elasticsearch y el plugin ReadonlyREST adecuado a nuestra versión de Elastic.
Para hacer las peticiones HTTP utilizaremos Postman.

3.1. Instalación

Lo primero, es descargar el código de aquí:


https://github.com/abarriosautentia/example-readonlyrest-elasticsearch

Una vez que tenemos el código descargado, hay que descomprimir el fichero (si os
bajásteis un ZIP) y ejecutar en un terminal (dentro de la carpeta docker):

Shell

1docker-compose up -d

Si todo ha ido bien, al final de nuestra consola veremos algo así:

Shell

1Creating readonlyrest-ldap ... done


2Creating readonlyrest-prepare-ldap ... done
3Creating readonlyrest-elasticsearch ... done

3.2. Explicación
Vamos a pasar a explicar un poco lo que acabamos de hacer repasando el
fichero docker-compose.yml que tenemos en la carpeta docker:

LDAP

Hemos montado un LDAP cuya raíz es dc=adictosaltrabajo,dc=com en el que


hemos creado los usuarios ‘usuario1’, ‘usuario2’ y ‘usuario3’ que pertenecen a los
grupos ‘grupo1’, ‘grupo2’ y ‘grupo3’, respectivamente. La contraseña es la misma
que el nombre de usuario.
Para el usuario admin la contraseña es ‘autentia’.

Nuestro LDAP tendrá las siguientes entradas:

Shell

1 # adictosaltrabajo.com
2 dn: dc=adictosaltrabajo,dc=com
3 objectClass: top
4 objectClass: dcObject
5 objectClass: organization
6 o: autentia
7 dc: adictosaltrabajo
8
9 # admin, adictosaltrabajo.com
10 dn: cn=admin,dc=adictosaltrabajo,dc=com
11 objectClass: simpleSecurityObject
12 objectClass: organizationalRole
13 cn: admin
14 description: LDAP administrator
15 userPassword::
16e1NTSEF9SU9tUHI1OTNDMG1NdmV2bmNoRkhEUWQ5clk0U3Y3eWc=
17
18 # Usuarios, adictosaltrabajo.com
19 dn: ou=Usuarios,dc=adictosaltrabajo,dc=com
20 objectClass: organizationalUnit
21 ou: Usuarios
22
23 # usuario1, Usuarios, adictosaltrabajo.com
24 dn: uid=usuario1,ou=Usuarios,dc=adictosaltrabajo,dc=com
25 objectClass: inetOrgPerson
26 uid: usuario1
27 sn: Usuario1
28 cn: Mi Usuario1
29 userPassword::
30e1NTSEF9OEc4SkhrTTM2aDVkYUNTRUZNSGVTZ3BoM3lVcUpZRXo=
31
32 # usuario2, Usuarios, adictosaltrabajo.com
33 dn: uid=usuario2,ou=Usuarios,dc=adictosaltrabajo,dc=com
34 objectClass: inetOrgPerson
35 uid: usuario2
36 sn: Usuario2
37 cn: Mi Usuario2
38 userPassword::
39e1NTSEF9RzBKK3JXLzN2Ny83VXJ5MWhVdTdqS0dIUjJqbU9VaVo=
40
41 # usuario3, Usuarios, adictosaltrabajo.com
42 dn: uid=usuario3,ou=Usuarios,dc=adictosaltrabajo,dc=com
43 objectClass: inetOrgPerson
44 uid: usuario3
45 sn: Usuario3
46 cn: Mi Usuario3
47 userPassword::
48e1NTSEF9MjRObm42aXhleTJOUlZSM1IrblV2WDl4bDJVM2tiRUg=
49
50 # Grupos, adictosaltrabajo.com
51 dn: ou=Grupos,dc=adictosaltrabajo,dc=com
52 objectClass: organizationalUnit
53 ou: Grupos
54 description: generic groups branch
55
56 # grupo1, Grupos, adictosaltrabajo.com
57 dn: cn=grupo1,ou=Grupos,dc=adictosaltrabajo,dc=com
58 objectClass: groupOfUniqueNames
59 cn: grupo1
60 uniqueMember: uid=usuario1,ou=Usuarios,dc=adictosaltrabajo,dc=com
61
62 # grupo2, Grupos, adictosaltrabajo.com
63 dn: cn=grupo2,ou=Grupos,dc=adictosaltrabajo,dc=com
64 objectClass: groupOfUniqueNames
65 cn: grupo2
66 uniqueMember: uid=usuario2,ou=Usuarios,dc=adictosaltrabajo,dc=com
67
68 # grupo3, Grupos, adictosaltrabajo.com
dn: cn=grupo3,ou=Grupos,dc=adictosaltrabajo,dc=com
objectClass: groupOfUniqueNames
cn: grupo3
uniqueMember: uid=usuario3,ou=Usuarios,dc=adictosaltrabajo,dc=com
Elasticsearch

Tenemos montado un Elasticsearch (versión 6.5.4) con el plugin ReadonlyREST


instalado (versión 1.16.33_es6.5.4), en el que hemos deshabilitado las
características de seguridad de Elasticsearch (para evitar conflictos con el plugin) y
hemos habilitado SSL para disponer de conexión HTTPS. Para ello, hemos tenido
que crear un almacen de claves de prueba (keystore.jks).

ReadonlyREST

En el fichero readonlyrest.yml está la parte más interesante ya que es donde hemos


configurado el control de acceso a nuestros índices y el acceso a LDAP.

Lo primero que hemos hecho es activar la propiedad audit_collector para que los
errores de acceso se guarden en un índice de Elasticsearch. El nombre por defecto
del índice es readonlyrest_audit-YYYY-MM-DD.

Habilitamos el cifrado SSL para poder utilizar el protocolo HTTPS y configuramos


el keystore de pruebas que tenemos generado con un certificado autofirmado. Esto
es sólo para pruebas, la generación de certificados para producción debe estar
validada por una Autoridad certificada (CA). Podéis leer más información en este
tutorial.

Las reglas para el control de acceso (access_control_rules) se dividen en bloques


que contienen un listado de reglas. Estos bloques se aplican en orden secuencial, es
decir, se va comprobando cada bloque de reglas de arriba a abajo hasta que se
cumple un bloque completo de reglas. Si no se encuentra ningún bloque en el que se
cumpla todo el listado de reglas, la petición se rechaza.

En nuestro caso, hemos creado 4 bloques de reglas:


1. Los usuarios del grupo1, que se autentiquen correctamente contra LDAP,
pueden crear índices e introducir datos (con bulk) en los índices que
comiencen con el nombre «my_index».

2. Los usuarios del grupo2, que se autentiquen correctamente contra LDAP,


pueden consultar datos de los índices que comiencen con el nombre
«my_index».

3. Los usuarios del grupo3, que se autentiquen correctamente contra LDAP,


pueden gestionar el cluster de Elasticsearch.

4. Cualquier usuario, que se autentique correctamente contra LDAP, puede


consultar los índices de auditoría (readonlyrest_audit-*).

Shell

1 access_control_rules:
2 - name: 'Create and Bulk index access to all indices my_index* from
3users belong to grupo1'
4 ldap_authentication: 'my_ldap'
5 ldap_authorization:
6 name: 'my_ldap' # ldap name from 'ldaps' section
7 groups: ['grupo1'] # group within
8'ou=Grupos,dc=adictosaltrabajo,dc=com'
9 indices: ['my_index*']
10 actions: ['indices:admin/create', 'indices:data/write/bulk']
11
12 - name: 'Read access to all indices my_index* from users belong
13to grupo2'
14 ldap_authentication: 'my_ldap'
15 ldap_authorization:
16 name: 'my_ldap' # ldap name from 'ldaps' section
17 groups: ['grupo2'] # group within
18'ou=Grupos,dc=adictosaltrabajo,dc=com'
19 indices: ['my_index*']
20 actions: ['indices:data/read/*']
21
22 - name: 'Cluster access from users belong to grupo3'
23 ldap_authentication: 'my_ldap'
24 ldap_authorization:
25 name: 'my_ldap' # ldap name from 'ldaps' section
26 groups: ['grupo3'] # group within
27'ou=Grupos,dc=adictosaltrabajo,dc=com'
28 actions: ['cluster:*']

- name: 'Read access to audit logs indices readonlyrest_audit-*'


ldap_authentication: 'my_ldap'
indices: ['readonlyrest_audit-*']
actions: ['indices:data/read/*']

3.3. Probando el acceso

Abrimos Postman y vamos a comprobar que nuestras reglas de acceso se cumplen


lanzando varias peticiones.

Antes de comenzar, debemos deshabilitar la verificación de certificados SSL para


que no nos dé problemas el certificado autofirmado que estamos usando para
nuestras pruebas. Lo hacemos desde:

Shell

1Preferences -> General -> SSL certificate verification -> OFF


Para todas las peticiones, en la pestaña Auth seleccionaremos Basic Auth, donde
introduciremos los usuarios de pruebas:

y en la pestaña Headers añadiremos Content-Type -> application/json:

Vamos con nuestra primera prueba donde crearemos el índice ‘my_index1’ con
el usuario1, que tiene permisos para crear un índice con este nombre. Para ello
lanzamos el siguiente PUT:

Shell

1https://localhost:9200/my_index1

y en la respuesta recibimos el ACK correctamente:


Ahora vamos a insertar documentos en el índice con este mismo usuario. Lanzamos
el siguiente PUT:

Shell

1https://localhost:9200/_bulk

y en la respuesta recibimos la confirmación con el código 201 (Created):

Para consultar los datos del índice, debemos utilizar el usuario2 que tiene permisos
de lectura sobre este índice. Lanzamos el siguiente GET:

Shell

1https://localhost:9200/my_index1/_search

y recibimos la respuesta con el documento que acabamos de crear:


Si lo intentamos con el usuario1, que no tiene permiso de lectura, o metemos mal el
usuario o la contraseña, nos devolverá un error 401 (Unauthorized):

Ahora vamos a consultar información del cluster con el usuario3. Lanzamos el


siguiente GET:

Shell

1https://localhost:9200/_cluster/state

y recibimos correctamente la información del cluster:


Por último, con cualquiera de los usuarios que tenemos podemos consultar los logs
que se han cargado en el índice readonlyrest_audit-YYYY-MM-DD. Lanzamos el
siguiente GET:

Shell

1https://localhost:9200/readonlyrest_audit-*/_search

y vemos la auditoría que se ha guardado:

Podemos ver que se ha almacenado un documento en el índice para cada petición


realizada, con mucha información que podemos explotar. Podemos destacar el
campo acl_historydonde se guardan los bloques de reglas que se han procesado para
esa petición y el resultado de evaluar (en orden) cada una de ellas.

4. Conclusiones

En este tutorial hemos visto que tenemos alternativas open source para proteger el
acceso a nuestro índices, más allá de la herramienta oficial X-Pack y sin necesidad
de configurar ningún proxy.

Por medio del plugin ReadonlyREST para Elasticsearch hemos sido capaces de
securizar nuestro Elasticsearch conectándolo a un LDAP de manera sencilla, pero
ofrece más opciones como autenticación interna básica, delegar en un proxy,
autenticación externa básica, JWT. También existen versiones de pago de
ReadonlyREST que ofrecen más características.

La posibilidad de explotar los logs de acceso es un complemento muy útil a la hora


de conocer cómo se están aplicando las reglas que hemos definido.

Espero que os haya resultado útil.

5. Referencias

 https://github.com/sscarduzio/elasticsearch-readonlyrest-plugin

 https://github.com/beshu-tech/readonlyrest-docs/blob/master/elasticsearch
.md

 https://readonlyrest.com/

 https://www.elastic.co/

You might also like