¿Cómo puedo decirle a SELinux que permita el acceso nginx a un socket Unix sin audit2allow?

9

Tengo solicitudes de reenvío de nginx a gunicorn a través de un socket unix en /run/gunicorn/socket. Por defecto, SELinux no permite este comportamiento:

grep nginx /var/log/audit/audit.log
type=SERVICE_START msg=audit(1454358912.455:5390): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=nginx comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
type=AVC msg=audit(1454360194.623:7324): avc:  denied  { write } for  pid=9128 comm="nginx" name="socket" dev="tmpfs" ino=76151 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:httpd_sys_content_t:s0 tclass=sock_file
type=SYSCALL msg=audit(1454360194.623:7324): arch=c000003e syscall=42 success=no exit=-13 a0=c a1=1f6fe58 a2=6e a3=7ffee1da5710 items=0 ppid=9127 pid=9128 auid=4294967295 uid=995 gid=993 euid=995 suid=995 fsuid=995 egid=993 sgid=993 fsgid=993 tty=(none) ses=4294967295 comm="nginx" exe="/usr/sbin/nginx" subj=system_u:system_r:httpd_t:s0 key=(null)
type=AVC msg=audit(1454361591.701:13343): avc:  denied  { connectto } for  pid=9128 comm="nginx" path="/run/gunicorn/socket" scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:system_r:initrc_t:s0 tclass=unix_stream_socket
type=SYSCALL msg=audit(1454361591.701:13343): arch=c000003e syscall=42 success=no exit=-13 a0=c a1=1f6fe58 a2=6e a3=7ffee1da5950 items=0 ppid=9127 pid=9128 auid=4294967295 uid=995 gid=993 euid=995 suid=995 fsuid=995 egid=993 sgid=993 fsgid=993 tty=(none) ses=4294967295 comm="nginx" exe="/usr/sbin/nginx" subj=system_u:system_r:httpd_t:s0 key=(null)

En todas partes que miro (por ejemplo, aquí y aquí ), las instrucciones para permitir que este haga una solicitud a nginx, SELinux rechace la solicitud y luego la ejecute audit2allowpara permitir futuras solicitudes. No puedo entender ninguno chcono semanagecomando que permita este comportamiento explícitamente.

¿Es esta la única forma? Parece ridículo que no se pueda configurar una política que permita que nginx escriba en un socket sin que primero se haya denegado un intento y luego se ejecute una herramienta que permita cosas que fueron denegadas. ¿Cómo sabes exactamente qué se está habilitando? ¿Cómo se supone que esto funciona si está configurando máquinas bajo automatización?

Estoy usando CentOS 7.

drs
fuente
Debe mostrarnos los mensajes denegados de AVC y sería bueno saber qué sistema operativo y qué versión está ejecutando también.
user9517
@lain buen punto.
Dres.

Respuestas:

23

Parece ridículo que no se pueda configurar una política que permita que nginx escriba en un socket sin que primero se haya denegado un intento y luego se ejecute una herramienta que permita cosas que fueron denegadas.

Pues no, SELinux es un control de acceso obligatorio, las cosas están denegadas por defecto y tienes que permitir explícitamente algo. Si los autores de la política no han considerado una pila particular (franken) o los autores de un demonio no lo han hecho una política escrita y consciente de SELinux, entonces usted está solo. Debe analizar qué están haciendo sus servicios y cómo están interactuando con SELinux y elaborar su propia política para permitirlo. Hay herramientas disponibles para ayudarlo a audit2why , audit2allow , etc.

... ¿Es esta la única forma?

No, pero depende de lo que intente hacer y de cómo intente hacerlo en cuanto a cuál es la solución. Por ejemplo, es posible que desee vincular nginx (httpd_t) al puerto 8010 (puerto no reservado_t). Cuando comienzas nginx falla

Starting nginx: nginx: [emerg] bind() to 0.0.0.0:8010 failed (13: Permission denied)

y usted (eventualmente) mira en el registro de auditoría y encuentra

type=AVC msg=audit(1457904756.503:41673): avc:  denied  { name_bind } for
pid=30483 comm="nginx" src=8010 scontext=unconfined_u:system_r:httpd_t:s0
tcontext=system_u:object_r:port_t:s0 tclass=tcp_socket

Puede ejecutar esto a través de audit2alllow y aceptar ingenuamente sus hallazgos

allow httpd_t port_t:tcp_socket name_bind;

que luego permite que httpd_t se conecte a cualquier puerto tcp. Esto puede no ser lo que quieres.

Puede usar sesearch para investigar la política y ver a qué tipos de puertos puede acceder httpd_t name_bind

sesearch --allow -s httpd_t | grep name_bind
...
allow httpd_t http_port_t : tcp_socket name_bind ;
allow httpd_t http_port_t : udp_socket name_bind ;
...

Entre otros tipos, http_t puede unirse a http_port_t. Ahora puedes usar semanage para cavar un poco más profundo.

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

El puerto 8010 no está en la lista. Como queremos que nginx se una al puerto 8010, no es irracional agregarlo a la lista http_port_t

semanage port -a -t http_port_t -p tcp 8010

Ahora se permitirá a nginx name_bind al puerto 8010 y no a todos los puertos tcp como se indicó anteriormente.

¿Cómo sabes exactamente qué se está habilitando?

Los cambios en la política son bastante fáciles de leer, ejecutando sus mensajes arriba a través de audit2allow obtenemos

allow httpd_t httpd_sys_content_t:sock_file write;
allow httpd_t initrc_t:unix_stream_socket connectto;

que parecen bastante explicativos.

El primero de ellos se refiere al archivo con inum 76151. Puede usar find para obtener su nombre (find / -inum 76151) y luego usar semanage fcontext -a -t ...para cambiar la política y restorecon para arreglar el contexto.

El segundo se refiere a /run/gunicorn/socketque nuevamente tiene el contexto equivocado. Usando sesearch podemos ver que http_t puede conectarse a unix_stream_sockets de tipo (entre otros) http_t. Entonces podemos cambiar el contexto en consecuencia, por ejemplo

semanage fcontext -a -t httpd_t "/run/gunicorn(/.*)?"
restorecon -r /run

Esto establece el contexto de / run / gunicorn y el árbol | archivos debajo de él a httpd_t.

¿Cómo se supone que esto funciona si está configurando máquinas bajo automatización?

Debe analizar el sistema y realizar los cambios apropiados en la prueba. Luego, utiliza sus herramientas de automatización para implementar los cambios, Puppet y Ansible tienen soporte para esto.

Por supuesto, puede hacerlo todo en producción con SElinux configurado en permisivo. Recopile todos los mensajes, analícelos, decida sus cambios y despliéguelos.

Hay mucho más que saber sobre SELinux, pero ese es el límite de mis habilidades, Michael Hampton es mejor y Mathew Ife es mucho mejor de nuevo, pueden tener más para agregar.

usuario9517
fuente
1
Su consejo es exhaustivo y me acerca mucho más a resolver estos problemas yo mismo, aunque todavía me deja un poco corto. allow httpd_t httpd_sys_content_t:sock_file write;no me explica tanto como esperaba. Lo que se esta diciendo que la política de que las necesidades de archivo que se cambió a (es decir, lo que sucede después -ten el semanagecomando?
DRS
Además, recibo instrucciones de uso cuando uso sus semanagecomandos directamente. Necesito agregar un --addargumento.
Dres.
En realidad, también debería decir que después de cambiar el tipo de archivo de socket a httpd_var_run_tcomo Michael Hampton señaló a continuación, el audit2allowmensaje es:allow httpd_t var_run_t:sock_file write;
Dres.
Parece que lo configuraste como var_run_tno httpd_var_run_t.
user9517
@lain, hmm .. sin dados. Ahora audit2allowdiceallow httpd_t var_run_t:sock_file write;
doctores
2

El tipo que desea usar no lo es httpd_sys_content_t. Esto es para archivos estáticos que el servidor web debe servir a los agentes de usuario.

Para un socket utilizado para la comunicación entre procesos, el tipo que está buscando es httpd_var_run_t.

Sin embargo, tenga en cuenta que debido a que ejecutó gunicorn sin restricciones, puede haber problemas adicionales para comunicarse con él.

Michael Hampton
fuente
3
¡Gracias! Parece que esto se encargó de uno de los problemas de SELinux. ¿Alguna sugerencia sobre cómo configurar gunicorn (o cualquier otro servicio) confinado?
Dres.
1

Probé las respuestas anteriores sin éxito, en mi caso estoy usando un servidor nginx como interfaz para una aplicación uwsgi que usa sockets unix para comunicarlos, mi sistema operativo es un servidor Fedora 26.

Los sockets de Unix se crean en el directorio /var/local/myapp:

/var/local/myapp/server.sock    
/var/local/myapp/stats.sock

Para configurar SELinux tuve que agregar el tipo de contexto: httpd_sys_rw_content_t

semanage fcontext -at httpd_sys_rw_content_t "/var/local/myapp(/.*)?"
restorecon -R -v '/var/local/myapp' 
rsc1975
fuente