Integración con Vault HashiCorp
En la Onesait Platform existe la funcionalidad de poder encriptar y desencriptar atributos de una Entidad de forma automática a partir de una clave maestra almacenada en la Plataforma. Pues desde la versión 4.3.0-Quest hemos integrado Vault HashiCorp para que el encriptado y desencriptado de los datos de las entidades se delegue en ella, de esta forma es el Vault el que gestiona la clave de cifrado de los datos:
Para ver un ejemplo, fijémonos en esta Entidad que tiene los atributos «file» y «stages» marcados como encriptables:
A la hora de consultar los datos, lo veremos en plano, ya que por debajo se está utilizando el Vault para (des)encriptar:
Sin embargo, en reposo, esta información estará encriptada:
Qué es HashiCorp Vault
El Vault de HashiCorp es una popular herramienta utilizada para gestionar de forma segura la información sensible en las aplicaciones actuales.
Como puedes ver en la siguiente imagen, HashiCorp Vault viene con varios componentes plugeables llamados «secrets engines» y «authentication methods» que le permiten integrarse con sistemas externos. La finalidad de estos componentes es gestionar y proteger los secretos en infraestructuras dinámicas (por ejemplo, credenciales de bases de datos, contraseñas o claves API).
¿Qué resuelve un Vault?
Antes de ver en detalle el Vault, debemos comprender el problema que intenta resolver: la gestión de información sensible.
La mayoría de las aplicaciones necesitan acceder a datos confidenciales para funcionar. Por ejemplo, una aplicación puede tener un nombre de usuario/contraseña configurado en algún lugar para conectarse a su base de datos o necesitar claves API para integrarse con otros proveedores de servicios, como pasarelas de pago, logística, etc.
Las credenciales de la base de datos y las claves API son dos ejemplos de información sensible que necesitamos almacenar y poner a disposición de nuestras aplicaciones de forma segura.
Una solución sencilla es almacenar esas credenciales en un archivo de configuración y leerlas en el momento del arranque, aunque el problema con este enfoque es obvio: el que tenga acceso a este archivo comparte los mismos privilegios de base de datos que tiene nuestra aplicación.
Podemos también cifrar estos archivos, haciendo las cosas un poco más difíciles, aunque como nuestra aplicación sabrá descifrarlos sólo se conseguirá una falsa sensación de seguridad.
Las aplicaciones actuales y los entornos Cloud añaden complejidad adicional: los servicios distribuidos, las múltiples bases de datos, los sistemas de mensajería, etc., tienen la información sensible repartida por todas partes, lo que aumenta el riesgo de una brecha de seguridad.
Aquí es donde encaja un Vault.
HashiCorp Vault aborda el problema de la gestión de información sensible (secreto en la jerga de Vault). «Gestionar» en este contexto significa que Vault controla todos los aspectos de una información sensible: su generación, almacenamiento, uso y, por último pero no menos importante, su revocación.
HashiCorp ofrece dos versiones de Vault: la versión open-source y una versión de pago, que incluye soporte técnico en diferentes SLA y características adicionales, como soporte HSM (Hardware Security Module).
Arquitectura de HashiCorp Vault
La arquitectura de Vault es sencilla. Sus principales componentes son:
- Un backend de persistencia: para el almacenamiento de todos los secretos.
- Un servidor de APIs: que gestiona las solicitudes de los clientes y realiza operaciones con los secretos.
- Una serie de motores de secretos (secret engines): uno para cada tipo de secreto soportado.
Al delegar toda la gestión de secretos en Vault, podemos mitigar algunos problemas de seguridad:
- Nuestras aplicaciones ya no tienen que almacenarlos: basta con solicitarlos a Vault cuando sea necesario y desecharlos.
- Podemos utilizar secretos de corta duración, limitando así la «ventana de oportunidad» en la que un atacante puede utilizar un secreto robado.
- Vault cifra todos los datos con una clave de cifrado antes de escribirlos en el almacén. Esta clave de cifrado está encriptada por otra clave: la clave maestra, que sólo se utiliza al inicio.
Un punto clave en la implementación de Vault es que no almacena la clave maestra en el servidor. Esto significa que ni siquiera Vault puede acceder a los datos guardados tras el arranque. En este punto, se dice que una instancia de Vault está en un estado «sellado» (sealed). Una vez desprecintado (unsealed), Vault estará listo para aceptar peticiones a las APIs.
Autenticación
Para acceder a los secretos del Vault, el cliente debe autenticarse mediante uno de los métodos admitidos. El método más sencillo utiliza tokens (cadenas que se envían en cada solicitud de API utilizando una cabecera HTTP especial).
Cuando se instala por primera vez, Vault genera automáticamente un «token root». Este token es el equivalente al superusuario root en los sistemas Linux, por lo que su uso debe limitarse al mínimo. Como mejor práctica, deberíamos usar este token de root sólo para crear otros tokens con menos privilegios y luego revocarlo (esto no es un problema, sin embargo, ya que más tarde podemos generar otro token raíz utilizando claves unseal).
Vault también soporta otros mecanismos de autenticación como LDAP, JWT o Certificados TLS. Todos estos mecanismos se basan en el mecanismo básico de token: una vez que Vault valida nuestro cliente, proporcionará un token que podremos utilizar para acceder a otras APIs.
Los tokens tienen algunas propiedades asociadas. Las principales son:
- Un conjunto de políticas asociadas.
- Time-to-live.
- Si se puede renovar.
- Recuento máximo de uso.
- A menos que se indique lo contrario, los tokens creados por Vault formarán una relación padre-hijo. Un token hijo puede tener como máximo el mismo nivel de privilegios que su padre, aunque lo normal es crear un token hijo con políticas restrictivas.
- Otro punto clave sobre esta relación: cuando invalidamos un token, también se invalidan todos los tokens hijos y sus descendientes.
Políticas asociadas
Las políticas definen exactamente a qué secretos puede acceder un cliente y qué operaciones puede realizar con ellos. Veamos cómo es una política simple:
path "secret/accounting" {
capabilities = [ "read" ]
}
Aquí hemos utilizado la sintaxis HCL (Lenguaje de Configuración de HashiCorp) para definir nuestra política. Vault también soporta JSON para este propósito.
Las políticas en Vault son «denegadas por defecto». Un token asociado a esta política de ejemplo tendrá acceso a los secretos almacenados en secret/accounting y nada más. En el momento de la creación, se puede asociar un token a varias políticas. Esto es muy útil porque nos permite crear y probar políticas más pequeñas y luego aplicarlas según sea necesario.
Si actualizamos una política determinada, todos los tokens asociados se verán afectados inmediatamente.
Las políticas descritas hasta ahora también se denominan Políticas ACL. Vault también soporta dos tipos de políticas adicionales: Políticas EGP y RGP, sólo disponibles en las versiones de pago.
Cuando están disponibles, nos permiten tener en cuenta en nuestras políticas atributos adicionales como la hora del día, múltiples factores de autenticación, origen de la red del cliente, etc. Por ejemplo, puedes definir una política que permita el acceso a un determinado secreto sólo en horario laboral.
Tipos de secretos
Vault admite diferentes tipos de secreto diferentes que responden a distintos casos de uso:
- Clave-valor: simples pares estáticos de clave-valor.
- Credenciales generadas dinámicamente: generadas por Vault a petición de un cliente.
- Claves criptográficas: utilizadas para realizar funciones criptográficas con los datos del cliente.
Cada tipo de secreto está definido por los siguientes atributos:
- Un punto de montaje: que define su prefijo de API REST.
- Un conjunto de operaciones expuestas: a través de la API correspondiente.
- Un conjunto de parámetros de configuración.
Se puede acceder a una instancia secreta determinada a través de una ruta, de forma similar a un árbol de directorios en un sistema de archivos. El primer componente de esta ruta corresponde al punto de montaje donde se encuentran todos los secretos de este tipo.
Por ejemplo, la cadena secret/my-application corresponde a la ruta en la que podemos encontrar pares clave-valor para my-application.
Secretos Clave-Valor
Los secretos clave-valor son pares disponibles bajo una ruta determinada.
Por ejemplo, podemos almacenar el par «foo=bar» en la ruta /secret/my-application y más tarde, utilizamos la misma ruta para recuperar el mismo par o pares: se pueden almacenar varios pares en la misma ruta.
Vault admite tres tipos de secretos clave-valor:
- Pares de claves no versionados: en los que las actualizaciones sustituyen a los valores existentes.
- Pares de claves versionados: que conservan hasta un número configurable de versiones antiguas.
- Cubbyhole: un tipo especial de pares de claves no versionados cuyos valores se circunscriben a un token de acceso determinado.
Los secretos clave-valor son estáticos por naturaleza, por lo que no existe el concepto de caducidad asociada a ellos. En estos casos, las actualizaciones de credenciales son un proceso semimanual, que suele requerir que alguien adquiera nuevas credenciales y utilice la línea de comandos de Vault o su interfaz de usuario para introducir los nuevos valores.
El caso principal de uso para este tipo de secreto es almacenar credenciales para acceder a sistemas externos, como claves API.
Secretos generados dinámicamente
Los secretos dinámicos son generados sobre la marcha por Vault cuando lo solicita una aplicación.
Vault admite varios tipos de secretos dinámicos, incluidos los siguientes:
- Credenciales de base de datos.
- Pares de claves SSH.
- Certificados X.509
- Credenciales de AWS.
- Cuentas de servicios de Google Cloud.
- Cuentas de Active Directory.
Todas ellas siguen el mismo patrón de uso. En primer lugar, configuramos el motor de secretos con los detalles necesarios para conectarse al servicio asociado. Después, definimos uno o más roles, que describen la creación del secreto en sí.
Tomemos como ejemplo el motor de secretos de la base de datos:
- Primero, debemos configurar Vault con todos los detalles de conexión a la base de datos de usuarios, incluyendo las credenciales de un usuario preexistente con privilegios de administrador para crear nuevos usuarios.
- Luego, creamos uno o más roles (roles de Vault, no roles de base de datos) que contengan las sentencias SQL reales utilizadas para crear un nuevo usuario. Éstas suelen incluir no sólo la sentencia de creación del usuario, sino también todas las sentencias de concesión necesarias para acceder a los objetos del esquema (tablas, vistas, etc.).
- Cuando un cliente accede a la API correspondiente, Vault creará un nuevo usuario temporal en la base de datos utilizando las sentencias proporcionadas y devolverá sus credenciales. El cliente puede entonces utilizar esas credenciales para acceder a la base de datos durante el periodo definido por el atributo time-to-live del rol solicitado.
- Una vez que una credencial alcanza su tiempo de caducidad, Vault revocará automáticamente cualquier privilegio asociado a este usuario. Un cliente también puede solicitar a Vault que renueve esas credenciales. El proceso de renovación sólo tendrá lugar si lo admite el controlador de base de datos específico y lo permite la política asociada.
Claves criptográficas
Este motor maneja funciones criptográficas como cifrado, descifrado, firma, etc. Todas estas operaciones utilizan claves criptográficas generadas y almacenadas internamente por Vault. A menos que se le indique explícitamente, Vault nunca expondrá una clave criptográfica determinada.
La API asociada permite a los clientes enviar a Vault datos en texto plano y recibir una versión cifrada de los mismos. Lo contrario también es posible: podemos enviar datos cifrados y recibir de vuelta el texto original.
Actualmente, sólo existe un motor de este tipo: el motor Transit. Este motor admite los tipos de claves más populares, como RSA y ECDSA, y también admite el cifrado convergente. Cuando se utiliza este modo, un valor de texto plano dado siempre da como resultado el mismo texto cifrado.
Por ejemplo, podemos utilizar este modo para cifrar números de tarjetas de crédito en una tabla de registro de transacciones. Con el cifrado convergente, cada vez que insertemos una nueva transacción, el valor cifrado de la tarjeta de crédito sería el mismo, lo que permitiría utilizar consultas SQL normales para informes, búsquedas, etc.
Imagen de cabecera: Vault HashiCorp.