F.40. sepgsql — Módulo de seguridad de control de acceso obligatorio (MAC) basado en etiquetas de SELinux #

F.40.1. Visión general
F.40.2. Instalación
F.40.3. Pruebas de regresión
F.40.4. Parámetros GUC
F.40.5. Características
F.40.6. Funciones de Sepgsql
F.40.7. Limitaciones
F.40.8. Recursos externos
F.40.9. Autor

sepgsql es un módulo cargable que admite el control de acceso obligatorio (MAC) basado en etiquetas y según la política de seguridad de SELinux.

Warning

La implementación actual tiene limitaciones significativas y no aplica el control de acceso obligatorio para todas las acciones. Consulta Section F.40.7.

F.40.1. Visión general #

Este módulo se integra con SELinux para proporcionar una capa adicional de comprobación de seguridad más allá de lo que normalmente ofrece PostgreSQL. Desde la perspectiva de SELinux, este módulo permite que PostgreSQL funcione como un gestor de objetos en el espacio de usuario. Cada acceso a una tabla o función iniciado por una consulta DML se comprobará contra la política de seguridad del sistema. Esta comprobación es adicional a la habitual comprobación de permisos SQL que realiza PostgreSQL.

Las decisiones de control de acceso de SELinux se toman utilizando etiquetas de seguridad, representadas por cadenas como system_u:object_r:sepgsql_table_t:s0. Cada decisión de control de acceso implica dos etiquetas: la etiqueta del sujeto que intenta realizar la acción y la etiqueta del objeto sobre el cual se va a realizar la operación. Dado que estas etiquetas se pueden aplicar a cualquier tipo de objeto, las decisiones de control de acceso para los objetos almacenados dentro de la base de datos se pueden someter (y, con este módulo, se someten) a los mismos criterios generales que se utilizan para objetos de cualquier otro tipo, como los archivos. Este diseño está pensado para permitir que una política de seguridad centralizada proteja los activos de información independientemente de las particularidades de cómo se almacenan dichos activos.

La sentencia SECURITY LABEL permite asignar una etiqueta de seguridad a un objeto de la base de datos.

F.40.2. Instalación #

sepgsql solo se puede utilizar en Linux 2.6.28 o superior con SELinux habilitado. No está disponible en ninguna otra plataforma. También necesitarás libselinux 2.1.10 o superior y selinux-policy 3.9.13 o superior (aunque algunas distribuciones pueden portar las reglas necesarias a versiones de política más antiguas).

El comando sestatus te permite comprobar el estado de SELinux. Una visualización típica es:

$ sestatus
SELinux status:                 enabled
SELinuxfs mount:                /selinux
Current mode:                   enforcing
Mode from config file:          enforcing
Policy version:                 24
Policy from config file:        targeted

Si SELinux está deshabilitado o no está instalado, debes configurar ese producto primero antes de instalar este módulo.

Para compilar este módulo, especifica --with-selinux (cuando utilices make y autoconf) o -Dselinux={ auto | enabled | disabled } (cuando utilices meson). Asegúrate de que el RPM libselinux-devel esté instalado en el momento de la compilación.

Para usar este módulo, debes incluir sepgsql en el parámetro shared_preload_libraries en postgresql.conf. El módulo no funcionará correctamente si se carga de cualquier otra manera. Una vez cargado el módulo, debes ejecutar sepgsql.sql en cada base de datos. Esto instalará las funciones necesarias para la gestión de etiquetas de seguridad y asignará las etiquetas de seguridad iniciales.

Aquí tienes un ejemplo que muestra cómo inicializar un clúster de bases de datos nuevo con las funciones de sepgsql y las etiquetas de seguridad instaladas. Ajusta las rutas que se muestran según corresponda para tu instalación:

$ export PGDATA=/ruta/al/directorio/de/datos
$ initdb
$ vi $PGDATA/postgresql.conf
  cambia
    #shared_preload_libraries = ''                # (el cambio requiere reiniciar)
  a
    shared_preload_libraries = 'sepgsql'          # (el cambio requiere reiniciar)
$ for DBNAME in template0 template1 postgres; do
    postgres --single -F -c exit_on_error=true $DBNAME \
      < /usr/local/pgsql/share/contrib/sepgsql.sql >/dev/null
  done

Ten en cuenta que puedes ver algunas o todas las siguientes notificaciones dependiendo de las versiones particulares que tengas de libselinux y selinux-policy:

/etc/selinux/targeted/contexts/sepgsql_contexts:  line 33 has invalid object type db_blobs
/etc/selinux/targeted/contexts/sepgsql_contexts:  line 36 has invalid object type db_language
/etc/selinux/targeted/contexts/sepgsql_contexts:  line 37 has invalid object type db_language
/etc/selinux/targeted/contexts/sepgsql_contexts:  line 38 has invalid object type db_language
/etc/selinux/targeted/contexts/sepgsql_contexts:  line 39 has invalid object type db_language
/etc/selinux/targeted/contexts/sepgsql_contexts:  line 40 has invalid object type db_language

Estos mensajes son inofensivos y deben ignorarse.

Si el proceso de instalación se completa sin errores, ahora puedes iniciar el servidor normalmente.

F.40.3. Pruebas de regresión #

El conjunto de pruebas de sepgsql se ejecuta si PG_TEST_EXTRA contiene sepgsql (consulta Section 31.1.3). Este método es adecuado durante el desarrollo de PostgreSQL. Alternativamente, existe una forma de ejecutar las pruebas para comprobar si una instancia de base de datos se ha configurado correctamente para sepgsql.

Debido a la naturaleza de SELinux, la ejecución de las pruebas de regresión para sepgsql requiere varios pasos de configuración adicionales, algunos de los cuales deben realizarse como root.

Las pruebas manuales deben ejecutarse en el directorio contrib/sepgsql de un árbol de compilación configurado de PostgreSQL. Aunque requieren un árbol de compilación, las pruebas están diseñadas para ejecutarse contra un servidor instalado, es decir, son comparables a make installcheck, no a make check.

Primero, configura sepgsql en una base de datos en funcionamiento de acuerdo con las instrucciones en Section F.40.2. Ten en cuenta que el usuario actual del sistema operativo debe poder conectarse a la base de datos como superusuario sin autenticación de contraseña.

Segundo, compila e instala el paquete de políticas para la prueba de regresión. La política sepgsql-regtest es un paquete de políticas de propósito especial que proporciona un conjunto de reglas que se permitirán durante las pruebas de regresión. Debe compilarse a partir del archivo de origen de políticas sepgsql-regtest.te, lo cual se hace utilizando make con un Makefile proporcionado por SELinux. Necesitarás localizar el Makefile adecuado en tu sistema; la ruta que se muestra a continuación es solo un ejemplo. (Este Makefile suele ser suministrado por el RPM selinux-policy-devel o selinux-policy). Una vez compilado, instala este paquete de políticas utilizando el comando semodule, el cual carga los paquetes de políticas suministrados en el núcleo. Si el paquete se instala correctamente, semodule -l debería listar sepgsql-regtest como un paquete de políticas disponible:

$ cd .../contrib/sepgsql
$ make -f /usr/share/selinux/devel/Makefile
$ sudo semodule -u sepgsql-regtest.pp
$ sudo semodule -l | grep sepgsql
sepgsql-regtest 1.07

Tercero, activa sepgsql_regression_test_mode. Por razones de seguridad, las reglas en sepgsql-regtest no están habilitadas por defecto; el parámetro sepgsql_regression_test_mode habilita las reglas necesarias para lanzar las pruebas de regresión. Se puede activar mediante el comando setsebool:

$ sudo setsebool sepgsql_regression_test_mode on
$ getsebool sepgsql_regression_test_mode
sepgsql_regression_test_mode --> on

Cuarto, verifica que tu shell esté funcionando en el dominio unconfined_t:

$ id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

Consulta Section F.40.8 para obtener detalles sobre cómo ajustar tu dominio de trabajo, si es necesario.

Finalmente, ejecuta el script de pruebas de regresión:

$ ./test_sepgsql

Este script intentará verificar que has realizado todos los pasos de configuración correctamente y, a continuación, ejecutará las pruebas de regresión para el módulo sepgsql.

Después de completar las pruebas, se recomienda desactivar el parámetro sepgsql_regression_test_mode:

$ sudo setsebool sepgsql_regression_test_mode off

Es posible que prefieras eliminar por completo la política sepgsql-regtest:

$ sudo semodule -r sepgsql-regtest

F.40.4. Parámetros GUC #

sepgsql.permissive (boolean) #

Este parámetro permite que sepgsql funcione en modo permisivo, independientemente de la configuración del sistema. El valor predeterminado es off. Este parámetro solo se puede establecer en el archivo postgresql.conf o en la línea de comandos del servidor.

Cuando este parámetro está activado, sepgsql funciona en modo permisivo, incluso si SELinux en general está funcionando en modo enforcing. Este parámetro es útil principalmente para fines de prueba.

sepgsql.debug_audit (boolean) #

Este parámetro permite la impresión de mensajes de auditoría independientemente de la configuración de la política del sistema. El valor predeterminado es off, lo que significa que los mensajes se imprimirán de acuerdo con la configuración del sistema.

La política de seguridad de SELinux también tiene reglas para controlar si se registran o no accesos particulares. Por defecto, las violaciones de acceso se registran, pero los accesos permitidos no.

Este parámetro fuerza la activación de todos los registros posibles, independientemente de la política del sistema.

F.40.5. Características #

F.40.5.1. Clases de objetos controlados #

El modelo de seguridad de SELinux describe todas las reglas de control de acceso como relaciones entre una entidad sujeto (normalmente, un cliente de la base de datos) y una entidad objeto (como un objeto de la base de datos), cada una de las cuales se identifica mediante una etiqueta de seguridad. Si se intenta acceder a un objeto no etiquetado, el objeto se trata como si tuviera asignada la etiqueta unlabeled_t.

Actualmente, sepgsql permite asignar etiquetas de seguridad a esquemas, tablas, columnas, secuencias, vistas y funciones. Cuando sepgsql está en uso, las etiquetas de seguridad se asignan automáticamente a los objetos de base de datos admitidos en el momento de su creación. Esta etiqueta se denomina etiqueta de seguridad predeterminada y se decide de acuerdo con la política de seguridad del sistema, la cual toma como entrada la etiqueta del creador, la etiqueta asignada al objeto padre del nuevo objeto y, opcionalmente, el nombre del objeto construido.

Un nuevo objeto de base de datos básicamente hereda la etiqueta de seguridad del objeto padre, excepto cuando la política de seguridad tiene reglas especiales conocidas como reglas de transición de tipo (type-transition), en cuyo caso se puede aplicar una etiqueta diferente. Para los esquemas, el objeto padre es la base de datos actual; para las tablas, secuencias, vistas y funciones, es el esquema contenedor; para las columnas, es la tabla contenedora.

F.40.5.2. Permisos DML #

Para las tablas, se comprueba db_table:select, db_table:insert, db_table:update o db_table:delete para todas las tablas de destino referenciadas, según el tipo de sentencia; además, también se comprueba db_table:select para todas las tablas que contienen columnas referenciadas en la cláusula WHERE o RETURNING, como origen de datos para UPDATE, etc.

También se comprobarán los permisos a nivel de columna para cada columna referenciada. db_column:select se comprueba no solo en las columnas que se están leyendo mediante SELECT, sino en aquellas que se referencian en otras sentencias DML; también se comprobará db_column:update o db_column:insert para las columnas que están siendo modificadas por UPDATE o INSERT.

Por ejemplo, considera:

UPDATE t1 SET x = 2, y = func1(y) WHERE z = 100;

Aquí, se comprobará db_column:update para t1.x porque se está actualizando, se comprobará db_column:{select update} para t1.y porque se actualiza y se referencia a la vez, y se comprobará db_column:select para t1.z porque solo se referencia. También se comprobará db_table:{select update} a nivel de tabla.

Para las secuencias, se comprueba db_sequence:get_value cuando hacemos referencia a un objeto de secuencia mediante SELECT; sin embargo, ten en cuenta que actualmente no comprobamos los permisos en la ejecución de las funciones correspondientes como lastval().

Para las vistas, se comprobará db_view:expand, y luego se comprobarán otros permisos necesarios en los objetos que se expanden a partir de la vista, individualmente.

Para las funciones, se comprobará db_procedure:{execute} cuando el usuario intente ejecutar una función como parte de una consulta o mediante una invocación de acceso rápido (fast-path). Si esta función es un procedimiento de confianza, también se comprueba el permiso db_procedure:{entrypoint} para verificar si puede actuar como punto de entrada del procedimiento de confianza.

Para acceder a cualquier objeto de esquema, se requiere el permiso db_schema:search en el esquema contenedor. Cuando se hace referencia a un objeto sin calificación de esquema, no se buscará en los esquemas en los que este permiso no esté presente (al igual que si el usuario no tuviera el privilegio USAGE en el esquema). Si hay una calificación de esquema explícita, se producirá un error si el usuario no tiene el permiso necesario en el esquema especificado.

El cliente debe tener permiso para acceder a todas las tablas y columnas referenciadas, incluso si proceden de vistas que luego se expandieron, de modo que apliquemos reglas de control de acceso coherentes independientemente de la forma en que se haga referencia a los contenidos de la tabla.

El sistema de privilegios de base de datos predeterminado permite a los superusuarios de la base de datos modificar los catálogos del sistema mediante comandos DML y referenciar o modificar tablas toast. Estas operaciones están prohibidas cuando sepgsql está habilitado.

F.40.5.3. Permisos DDL #

SELinux define varios permisos para controlar las operaciones comunes de cada tipo de objeto, como la creación, alteración, eliminación y reetiquetado de la etiqueta de seguridad. Además, varios tipos de objetos tienen permisos especiales para controlar sus operaciones características, como la adición o eliminación de entradas de nombre dentro de un esquema particular.

La creación de un nuevo objeto de base de datos requiere el permiso create. SELinux concederá o denegará este permiso basándose en la etiqueta de seguridad del cliente y en la etiqueta de seguridad propuesta para el nuevo objeto. En algunos casos, se requieren privilegios adicionales:

  • CREATE DATABASE requiere adicionalmente el permiso getattr para la base de datos de origen o plantilla.

  • La creación de un objeto de esquema requiere adicionalmente el permiso add_name en el esquema padre.

  • La creación de una tabla requiere adicionalmente permiso para crear cada columna de tabla individual, de la misma manera que si cada columna de tabla fuera un objeto independiente de nivel superior.

  • La creación de una función marcada como LEAKPROOF requiere adicionalmente el permiso install. (Este permiso también se comprueba cuando se establece LEAKPROOF para una función existente).

Cuando se ejecuta el comando DROP, se comprobará drop en el objeto que se está eliminando. También se comprobarán los permisos para los objetos eliminados indirectamente a través de CASCADE. La eliminación de objetos contenidos dentro de un esquema particular (tablas, vistas, secuencias y procedimientos) requiere adicionalmente el permiso remove_name en el esquema.

Cuando se ejecuta el comando ALTER, se comprobará setattr en el objeto que se está modificando para cada tipo de objeto, excepto para los objetos subsidiarios como los índices o disparadores (triggers) de una tabla, donde los permisos se comprueban en su lugar en el objeto padre. En algunos casos, se requieren permisos adicionales:

  • Mover un objeto a un nuevo esquema requiere adicionalmente el permiso remove_name en el esquema antiguo y el permiso add_name en el nuevo.

  • Establecer el atributo LEAKPROOF en una función requiere el permiso install.

  • El uso de SECURITY LABEL en un objeto requiere adicionalmente el permiso relabelfrom para el objeto en conjunción con su antigua etiqueta de seguridad y el permiso relabelto para el objeto en conjunción con su nueva etiqueta de seguridad. (En los casos en que se instalan múltiples proveedores de etiquetas y el usuario intenta establecer una etiqueta de seguridad pero esta no es gestionada por SELinux, solo debería comprobarse setattr aquí. Esto no se hace actualmente debido a restricciones de la implementación).

F.40.5.4. Procedimientos de confianza #

Los procedimientos de confianza son similares a las funciones definidoras de seguridad (security definer) o a los comandos setuid. SELinux proporciona una característica que permite que el código de confianza se ejecute utilizando una etiqueta de seguridad diferente a la del cliente, generalmente con el fin de proporcionar un acceso altamente controlado a datos sensibles (por ejemplo, se pueden omitir filas o se puede reducir la precisión de los valores almacenados). El hecho de que una función actúe o no como un procedimiento de confianza está controlado por su etiqueta de seguridad y por la política de seguridad del sistema operativo. Por ejemplo:

postgres=# CREATE TABLE customer (
               cid     int primary key,
               cname   text,
               credit  text
           );
CREATE TABLE
postgres=# SECURITY LABEL ON COLUMN customer.credit
               IS 'system_u:object_r:sepgsql_secret_table_t:s0';
SECURITY LABEL
postgres=# CREATE FUNCTION show_credit(int) RETURNS text
             AS 'SELECT regexp_replace(credit, ''-[0-9]+$'', ''-xxxx'', ''g'')
                        FROM customer WHERE cid = $1'
           LANGUAGE sql;
CREATE FUNCTION
postgres=# SECURITY LABEL ON FUNCTION show_credit(int)
               IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
SECURITY LABEL

Las operaciones anteriores deben ser realizadas por un usuario administrador.

postgres=# SELECT * FROM customer;
ERROR:  SELinux: security policy violation
postgres=# SELECT cid, cname, show_credit(cid) FROM customer;
 cid | cname  |     show_credit
-----+--------+---------------------
   1 | taro   | 1111-2222-3333-xxxx
   2 | hanako | 5555-6666-7777-xxxx
(2 rows)

En este caso, un usuario normal no puede hacer referencia a customer.credit directamente, pero un procedimiento de confianza show_credit le permite imprimir los números de tarjeta de crédito de los clientes con algunos de los dígitos enmascarados.

F.40.5.5. Transiciones dinámicas de dominio #

Es posible utilizar la característica de transición dinámica de dominio de SELinux para cambiar la etiqueta de seguridad del proceso del cliente (el dominio del cliente) a un nuevo contexto, si la política de seguridad lo permite. El dominio del cliente necesita el permiso setcurrent y también dyntransition del dominio antiguo al nuevo.

Las transiciones dinámicas de dominio deben considerarse con cuidado, porque permiten a los usuarios cambiar su etiqueta, y por tanto sus privilegios, a su elección, en lugar de hacerlo según lo dispuesto por el sistema (como en el caso de un procedimiento de confianza). Por lo tanto, el permiso dyntransition solo se considera seguro cuando se utiliza para cambiar a un dominio con un conjunto de privilegios menor que el original. Por ejemplo:

regression=# select sepgsql_getcon();
                    sepgsql_getcon
-------------------------------------------------------
 unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
(1 row)

regression=# SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0-s0:c1.c4');
 sepgsql_setcon
----------------
 t
(1 row)

regression=# SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0-s0:c1.c1023');
ERROR:  SELinux: security policy violation

En este ejemplo anterior, se nos permitió cambiar del rango MCS más grande c1.c1023 al rango más pequeño c1.c4, pero se denegó el cambio de regreso.

Una combinación de transición dinámica de dominio y procedimiento de confianza permite un caso de uso interesante que se adapta al ciclo de vida típico de los procesos del software de pool de conexiones (connection pooling). Aunque su software de pool de conexiones no tenga permiso para ejecutar la mayoría de los comandos SQL, puede permitirle cambiar la etiqueta de seguridad del cliente mediante la función sepgsql_setcon() desde dentro de un procedimiento de confianza; eso debería requerir alguna credencial para autorizar la solicitud de cambio de la etiqueta del cliente. Después de eso, esta sesión tendrá los privilegios del usuario de destino, en lugar del gestor del pool de conexiones. El gestor del pool de conexiones puede revertir más tarde el cambio de etiqueta de seguridad utilizando de nuevo sepgsql_setcon() con un argumento NULL, invocado de nuevo desde un procedimiento de confianza con las comprobaciones de permisos adecuadas. El punto clave aquí es que solo el procedimiento de confianza tiene realmente permiso para cambiar la etiqueta de seguridad efectiva, y solo lo hace cuando se le proporcionan las credenciales adecuadas. Por supuesto, para un funcionamiento seguro, el almacén de credenciales (tabla, definición de procedimiento, o lo que sea) debe protegerse contra accesos no autorizados.

F.40.5.6. Miscelánea #

Rechazamos el comando LOAD en todos los casos, porque cualquier módulo cargado podría eludir fácilmente la aplicación de la política de seguridad.

F.40.6. Funciones de Sepgsql #

El Table F.32 muestra las funciones disponibles.

Table F.32. Funciones de Sepgsql

Función

Descripción

sepgsql_getcon () → text

Devuelve el dominio del cliente, que es la etiqueta de seguridad actual del cliente.

sepgsql_setcon ( text ) → boolean

Cambia el dominio del cliente de la sesión actual al nuevo dominio, si la política de seguridad lo permite. También acepta una entrada NULL como solicitud para la transición al dominio original del cliente.

sepgsql_mcstrans_in ( text ) → text

Traduce el rango MLS/MCS calificado dado al formato bruto (raw) si el demonio mcstrans se está ejecutando.

sepgsql_mcstrans_out ( text ) → text

Traduce el rango MLS/MCS bruto dado al formato calificado si el demonio mcstrans se está ejecutando.

sepgsql_restorecon ( text ) → boolean

Configura las etiquetas de seguridad iniciales para todos los objetos dentro de la base de datos actual. El argumento puede ser NULL, o el nombre de un archivo de especificaciones (specfile) que se utilizará como alternativa al predeterminado del sistema.


F.40.7. Limitaciones #

Permisos de lenguaje de definición de datos (DDL)

Debido a restricciones de la implementación, algunas operaciones DDL no comprueban los permisos.

Permisos de lenguaje de control de datos (DCL)

Debido a restricciones de la implementación, las operaciones DCL no comprueban los permisos.

Control de acceso a nivel de fila

PostgreSQL admite el acceso a nivel de fila, pero sepgsql no lo hace.

Canales encubiertos (Covert channels)

sepgsql no intenta ocultar la existencia de un determinado objeto, incluso si el usuario no tiene permitido referenciarlo. Por ejemplo, podemos inferir la existencia de un objeto invisible como resultado de conflictos de claves primarias, violaciones de claves extranjeras, etc., incluso si no podemos obtener los contenidos del objeto. La existencia de una tabla de alto secreto no se puede ocultar; solo esperamos ocultar su contenido.

F.40.8. Recursos externos #

Introducción a SE-PostgreSQL

Esta página de la wiki ofrece una breve descripción general, el diseño de seguridad, la arquitectura, la administración y las próximas características.

Guía del usuario y administrador de SELinux

Este documento proporciona un amplio espectro de conocimientos para administrar SELinux en tus sistemas. Se centra principalmente en los sistemas operativos Red Hat, pero no se limita a ellos.

Preguntas frecuentes sobre SELinux en Fedora

Este documento responde a las preguntas más frecuentes sobre SELinux. Se centra principalmente en Fedora, pero no se limita a Fedora.

F.40.9. Autor #

KaiGai Kohei