Uso de NGINX y NGINX Plus con SELinux

NGINX | 17 de agosto de 2018

Nota del editor: La entrada del blog titulada «NGINX: cambios en SELinux al actualizar a RHEL 6.6/CentOS 6.6» redirige aquí. Este artículo ofrece información actualizada y de carácter general.

La configuración predeterminada de Security-Enhanced Linux (SELinux) en las versiones modernas de Red Hat Enterprise Linux (RHEL) y las distribuciones relacionadas puede ser muy estricta, ya que prioriza la seguridad frente a la comodidad. Aunque la configuración predeterminada no limita el funcionamiento de NGINX Open Source ni de NGINX Plus con sus configuraciones predeterminadas, otras funciones que configure pueden quedar bloqueadas a menos que las permita explícitamente en SELinux. En este artículo se describen los posibles problemas y las formas recomendadas de resolverlos.

[Editor – Este artículo se aplica tanto a NGINX Open Source como a NGINX Plus. Para facilitar la lectura, se utiliza el término «NGINX» en todo el artículo.

CentOS es una distribución relacionada, derivada originalmente de RHEL, y es compatible con NGINX y NGINX Plus. Además, NGINX Plus es compatible con las distribuciones relacionadas Amazon Linux y Oracle Linux. Su configuración predeterminada de SELinux puede diferir de la de CentOS y RHEL; consulte la documentación del proveedor.]

Descripción general de SELinux

SELinux está habilitado de forma predeterminada en los servidores RHEL y CentOS modernos. Cada objeto del sistema operativo (proceso, descriptor de archivo, archivo, etc.) está etiquetado con un contexto de SELinux que define los permisos y las operaciones que puede realizar el objeto. En RHEL 6.6/CentOS 6.6 y versiones posteriores, NGINX está etiquetado con el contexto httpd_t:

# ps auZ | grep nginxunconfined_u:system_r:httpd_t:s0 3234 ? Ss 0:00 nginx: master process /usr/sbin/nginx \
                                                -c /etc/nginx/nginx.conf
unconfined_u:system_r:httpd_t:s0 3236 ? Ss 0:00 nginx: worker process

El contexto httpd_t permite a NGINX escuchar en los puertos habituales de los servidores web, acceder a los archivos de configuración de /etc/nginx y acceder al contenido de la ubicación raíz de documentos estándar (/usr/share/nginx). No permite muchas otras operaciones, como actuar de proxy para ubicaciones ascendentes o comunicarse con otros procesos mediante sockets.

Deshabilitar temporalmente SELinux para NGINX

Para deshabilitar temporalmente las restricciones de SELinux para el contexto httpd_t, de modo que NGINX pueda realizar las mismas operaciones que en los sistemas operativos sin SELinux, asigne el contexto httpd_t al dominio permissive. Consulte la siguiente sección para obtener más información.

# semanage permissive -a httpd_t

Cambiar los modos de SELinux

SELinux puede ejecutarse en los modos «obligatorio», «permisivo» o «deshabilitado» (también denominados «dominios»). Antes de realizar un cambio en la configuración de NGINX que pueda infringir los permisos predeterminados (estrictos), puede cambiar SELinux del modo «obligatorio» al modo «permisivo», ya sea en su entorno de pruebas (si dispone de uno) o en el entorno de producción. En el modo «permisivo», SELinux permite todas las operaciones, pero registra aquellas que habrían infringido la política de seguridad en el modo «obligatorio».

Para añadir httpd_t a la lista de dominios permisivos, ejecute este comando:

# semanage permissive -a httpd_t

Para eliminar httpd_t de la lista de dominios permisivos, ejecute:

# semanage permissive -d httpd_t

Para establecer globalmente el modo en «permisivo», ejecuta:

# setenforce 0

Para establecer globalmente el modo en enforcing, ejecuta:

# setenforce 1

Resolución de excepciones de seguridad de SELinux

En el modo permisivo, las excepciones de seguridad se registran en el registro de auditoría predeterminado de Linux, /var/log/audit/audit.log. Si detecta un problema que solo se produce cuando NGINX se ejecuta en el modo obligatorio, revise las excepciones registradas en el modo permisivo y actualice la política de seguridad para permitirlas.

Problema 1: la conexión al proxy está prohibida

De forma predeterminada, la configuración de SELinux no permite que NGINX se conecte a servidores HTTP, FastCGI u otros servidores remotos, tal como indica un mensaje del registro de auditoría como el siguiente:

type=AVC msg=audit(1415714880.156:29): avc:  denied  { name_connect } for  pid=1349 \
  comm="nginx" dest=8080 scontext=unconfined_u:system_r:httpd_t:s0 \
  tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket
type=SYSCALL msg=audit(1415714880.156:29): arch=c000003e syscall=42 success=no \
  exit=-115 a0=b \a1=16125f8 a2=10 a3=7fffc2bab440 items=0 ppid=1347 pid=1349 \
  auid=1000 uid=497 gid=496 euid=497 suid=497 fsuid=497 egid=496 sgid=496 fsgid=496 \
  tty=(none) ses=1 comm="nginx" exe="/usr/sbin/nginx" \
  subj=unconfined_u:system_r:httpd_t:s0 key=(null)

El comando «audit2why» interpreta el código del mensaje (1415714880.156:29):

# grep 1415714880.156:29 /var/log/audit/audit.log | audit2whytype=AVC msg=audit(1415714880.156:29): avc:  denied  { name_connect } for  pid=1349 \
  comm="nginx" dest=8080 scontext=unconfined_u:system_r:httpd_t:s0 \
  tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket
 
        Was caused by:
        One of the following booleans was set incorrectly.
        Description:
        Allow httpd to act as a relay
 
        Allow access by executing:
        # setsebool -P httpd_can_network_relay 1
        Description:
        Allow HTTPD scripts and modules to connect to the network using TCP.
 
        Allow access by executing:
        # setsebool -P httpd_can_network_connect 1

La salida de audit2why indica que puede permitir que NGINX establezca conexiones de proxy habilitando una o ambas opciones booleanas httpd_can_network_relay y httpd_can_network_connect. Puede habilitarlas de forma temporal o permanente; para hacerlo de forma permanente, añada la opción ‑P tal como se muestra en la salida.

Comprender las opciones booleanas

El comando sesearch proporciona más información sobre las opciones booleanas y está disponible si instala el paquete setools (yum install setools). Aquí mostramos la salida de las opciones httpd_can_network_relay y httpd_can_network_connect.

La opción booleana httpd_can_network_relay Boolean Option

Esta es la salida del comando sesearch relativa a la opción httpd_can_network_relay:

# sesearch -A -s httpd_t -b httpd_can_network_relayFound 10 semantic av rules:
   allow httpd_t gopher_port_t : tcp_socket name_connect ;
   allow httpd_t http_cache_client_packet_t : packet { send recv } ;
   allow httpd_t ftp_port_t : tcp_socket name_connect ;
   allow httpd_t ftp_client_packet_t : packet { send recv } ;
   allow httpd_t http_client_packet_t : packet { send recv } ;
   allow httpd_t squid_port_t : tcp_socket name_connect ;
   allow httpd_t http_cache_port_t : tcp_socket name_connect ;
   allow httpd_t http_port_t : tcp_socket name_connect ;
   allow httpd_t gopher_client_packet_t : packet { send recv } ;
   allow httpd_t memcache_port_t : tcp_socket name_connect ;

Esta salida indica que httpd_can_network_relay permite que los procesos etiquetados con el contexto httpd_t (como NGINX) se conecten a puertos de varios tipos, incluido el tipo http_port_t:

# semanage port -l | grep http_port_thttp_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000

Para añadir más puertos (en este caso, 8082) al conjunto de puertos permitidos para http_port_t, ejecuta:

# semanage port -a -t http_port_t -p tcp 8082

Si la salida de este comando indica que un puerto ya está definido, como en el siguiente ejemplo, significa que el puerto está incluido en otro conjunto. No lo reasigne a http_port_t, ya que podría afectar negativamente a otros servicios.

# semanage port -a -t http_port_t -p tcp 8080
/usr/sbin/semanage: Port tcp/8080 already defined
# semanage port -l | grep 8080
http_cache_port_t              tcp      3128, 8080, 8118, 8123, 10001-10010

La opción booleana httpd_can_network_connect Boolean Option

Esta es la salida del comando sesearch relativa a la opción httpd_can_network_connect:

# sesearch -A -s httpd_t -b httpd_can_network_connectFound 1 semantic av rules:
   allow httpd_t port_type : tcp_socket name_connect ;

Este resultado indica que httpd_can_network_connect permite que los procesos etiquetados con el contexto httpd_t (como NGINX) se conecten a todos los tipos de sockets TCP que tengan el atributo port_type. Para enumerarlos, ejecute:

# seinfo -aport_type -x

Problema 2: El acceso al archivo está prohibido

De forma predeterminada, la configuración de SELinux no permite que NGINX acceda a archivos ubicados fuera de las ubicaciones autorizadas conocidas, tal como indica un mensaje del registro de auditoría como el siguiente:

type=AVC msg=audit(1415715270.766:31): avc:  denied  { getattr } for  pid=1380 \
  comm="nginx" path="/www/t.txt" dev=vda1 ino=1084 \
  scontext=unconfined_u:system_r:httpd_t:s0 \
  tcontext=unconfined_u:object_r:default_t:s0 tclass=file

El comando audit2why interpreta el código del mensaje (1415715270.766:31):

# grep 1415715270.766:31 /var/log/audit/audit.log | audit2whytype=AVC msg=audit(1415715270.766:31): avc:  denied  { getattr } for  pid=1380 \
  comm="nginx" path="/www/t.txt" dev=vda1 ino=1084 \
  scontext=unconfined_u:system_r:httpd_t:s0 \
  tcontext=unconfined_u:object_r:default_t:s0 tclass=file
 
    Was caused by:
        Missing type enforcement (TE) allow rule.
 
        You can use audit2allow to generate a loadable module to allow this access.

Cuando el acceso a los archivos está prohibido, tiene dos opciones.

Opción 1: Modificar la etiqueta del archivo

Modifique la etiqueta del archivo para que NGINX (como proceso etiquetado con el contexto httpd_t pueda acceder al archivo:

# chcon -v --type=httpd_sys_content_t /www/t.txt

De forma predeterminada, esta modificación se elimina cuando se vuelve a etiquetar el sistema de archivos. Para que el cambio sea permanente, ejecute:

# semanage fcontext -a -t httpd_sys_content_t /www/t.txt
# restorecon -v /www/t.txt

Para modificar las etiquetas de grupos de archivos, ejecuta:

# semanage fcontext -a -t httpd_sys_content_t '/www(/.*)?'# restorecon -Rv /www

Opción 2: Ampliar los permisos del dominio httpd_t Domain Permissions

Amplíe la política de httpd_t para permitir el acceso a ubicaciones de archivos adicionales:

# grep nginx /var/log/audit/audit.log | audit2allow -m nginx > nginx.te# cat nginx.te
 
module nginx 1.0;
 
require {
        type httpd_t;
        type default_t;
        type http_cache_port_t;
        class tcp_socket name_connect;
        class file { read getattr open };
}
 
#============= httpd_t ==============
allow httpd_t default_t:file { read getattr open };
 
#!!!! This avc can be allowed using one of these booleans:
#     httpd_can_network_relay, httpd_can_network_connect
allow httpd_t http_cache_port_t:tcp_socket name_connect;

Para generar una política compilada, incluye la opción «-M»:

# grep nginx /var/log/audit/audit.log | audit2allow -M nginx

Para cargar la política, ejecute semodule -i, y luego compruebe que se haya cargado correctamente con semodule -l:

# semodule -i nginx.pp
# semodule -l | grep nginx
nginx 1.0

Este cambio se mantiene después de reiniciar el sistema.

Problema 3: NGINX no puede vincularse a puertos adicionales

De forma predeterminada, la configuración de SELinux no permite que NGINX escuche (bind()) en puertos TCP o UDP distintos de los predeterminados incluidos en la lista de permitidos del tipo http_port_t:

# semanage port -l | grep http_port_thttp_port_t                    tcp      80, 443, 488, 8008, 8009, 8443

Si intenta configurar NGINX para que escuche en un puerto que no está en la lista de permitidos (con la directiva listen en el contexto http, stream o mail de la configuración de NGINX), se producirá un error al verificar (nginx -t) o volver a cargar la configuración de NGINX, tal como indica esta entrada del registro de NGINX:

YYYY/MM/DD hh:mm:ss [emerg] 46123#0: bind() to 0.0.0.0:8001 failed (13: Permission denied)

Puede utilizar semanage para añadir el puerto deseado (en este caso, el 8001) al tipo http_port_t:

# semanage port -a -t http_port_t -p tcp 8001

Recarga NGINX con la nueva configuración.

# nginx -s reload

Problema 4: Hay demasiados archivos abiertos Error

Cuando se supera el límite del número de archivos abiertos (RLIMIT_NOFILE), aparece el siguiente mensaje en el registro de errores:

Too many files are open

Cuando el proceso de trabajo de NGINX genera el error

En la mayoría de los casos, el proceso de trabajo de NGINX notifica este error, pero no se puede utilizar la directiva worker_rlimit_nofile de NGINX para aumentar el límite porque SELinux no permite la llamada al sistema setrlimit(); así se indica en los siguientes mensajes de los registros de errores y auditoría.

  • En /var/log/nginx/error.log para CentOS/RHEL 7.4+:
YYYY/MM/DD hh:mm:ss [alert] 12066#0: setrlimit(RLIMIT_NOFILE, 2342) failed (13: Permission denied)
  • En /var/log/nginx/error.log para CentOS/RHEL 8.0+:
YYYY/MM/DD hh:mm:ss [alert] 3327#0: setrlimit(RLIMIT_NOFILE, 65535) failed (1: Operation not permitted)
  • En /var/log/audit/audit.log para CentOS/RHEL 7.4+ y /var/log/messages para CentOS/RHEL 8.0+:
type=AVC msg=audit(1437731200.211:366): avc:  denied  { setrlimit } for pid=12066 \  comm="nginx" scontext=system_u:system_r:httpd_t:s0 \
  tcontext=system_u:system_r:httpd_t:s0 tclass=process

Para aumentar el límite, ejecuta este comando como usuario root:

$ setsebool -P httpd_setrlimit 1

Cuando el proceso maestro de NGINX genera el error

Si el proceso maestro de NGINX notifica el error, debe actualizar el archivo systemd unit de NGINX. Esto establece el límite de descriptores de archivo tanto para el proceso maestro como para los procesos de trabajo.

  1. Cree un directorio para la configuración de nginx.service:
$ mkdir /etc/systemd/system/nginx.service.d
  1. Añade las siguientes líneas a /etc/systemd/system/nginx.service.d/nofile_limit.conf:
[Service]LimitNOFILE=65535
  1. Recarga la configuración del demonio systemd y reinicia NGINX:
$ systemctl daemon-reload$ systemctl restart nginx.service

Recursos adicionales

SELinux es una herramienta compleja y potente para gestionar los permisos del sistema operativo. Puede encontrar más información en los siguientes documentos.


Compartir
Etiquetas: F5 NGINX, 2018, Tech

Sobre el autor

Owen Garrett
Owen GarrettDirector sénior de Gestión de Producto

Más artículos de Owen Garrett

Artículos relacionados del blog

Únete a F5 NGINX en AppWorld 2024
NGINX | 04/01/2024

Únete a F5 NGINX en AppWorld 2024

Descubre las numerosas razones para asistir al evento insignia de F5, que incluirá un emocionante anuncio sobre la cartera de productos NGINX.

Rotación de certificados SSL/TLS sin reinicios en NGINX Open Source
NGINX | 26/09/2023

Rotación de certificados SSL/TLS sin reinicios en NGINX Open Source

La función de diccionario compartido permite configurar NGINX sin necesidad de reiniciarlo al renovar los certificados SSL/TLS.

Exposición de APIs en Kubernetes
NGINX | 10/03/2022

Exposición de APIs en Kubernetes

La unidad 2 de Microservices March 2022 se centra en cómo exponer tus API en Kubernetes. Aquí tienes enlaces a los recursos que necesitas: la retransmisión en directo del 14 de marzo, artículos de blog y otros recursos de preparación, y el laboratorio práctico «Protege las API de Kubernetes mediante la limitación de solicitudes».

NGINX Sprint 2.0: visión clara, código renovado y nuevos compromisos con el código abierto
NGINX | 23/08/2021

NGINX Sprint 2.0: visión clara, código renovado y nuevos compromisos con el código abierto

Hoy, en NGINX Sprint 2.0, anunciamos cómo respaldaremos nuestra visión del futuro de las aplicaciones modernas con una mayor presencia en la comunidad de código abierto, incluida la participación en el proyecto comunitario Kubernetes, nuevos proyectos de software de código abierto y la arquitectura de referencia para aplicaciones modernas.

Presentamos NGINX Plus R22
NGINX | 09/06/2020

Presentamos NGINX Plus R22

NGINX Plus R22 incorpora nuevas funcionalidades, como la compatibilidad con OCSP para verificar la revocación de certificados en TLS mutuo, el uso de varios proveedores de identidad OIDC en una configuración, el seguimiento en tiempo real de la limitación de solicitudes y conexiones en el panel de NGINX Plus y mejoras en NGINX JavaScript.

Presentamos NGINX Plus R17
NGINX | 11/12/2018

Presentamos NGINX Plus R17

NGINX Plus R17 incorpora compatibilidad con la limitación de velocidad en dos fases y con TLS 1.3, la versión más reciente del protocolo Transport Layer Security. La configuración de OpenID Connect es más sencilla y NGINX ModSecurity WAF es el doble de rápido que antes. También se ha actualizado el módulo JavaScript de NGINX.