Estoy escribiendo un demonio de servidor HTTP en C (hay razones por las cuales), administrándolo con el archivo de unidad systemd.
Estoy reescribiendo una aplicación diseñada hace 20 años, alrededor de 1995. Y el sistema que utilizan es que se procesan y luego se configuran, y el procedimiento estándar.
Ahora, en mi trabajo anterior, la política habitual era que nunca ejecutara ningún proceso como root. Usted crea un usuario / grupo para ello y se ejecuta desde allí. Por supuesto, el sistema ejecutó algunas cosas como root, pero podríamos lograr todo el procesamiento de la lógica de negocios sin ser root.
Ahora, para el demonio HTTP, puedo ejecutarlo sin root si no hago un chroot dentro de la aplicación. Entonces, ¿no es más seguro que la aplicación nunca se ejecute como root?
¿No es más seguro ejecutarlo como mydaemon-user desde el principio? En lugar de comenzar con root, chrooting, luego setuid a mydaemon-user?
capabilities(7)
.Respuestas:
Parece que otros han perdido su punto, lo cual no fue una razón por la cual usar raíces cambiadas, lo que, por supuesto, claramente ya sabe, ni qué más puede hacer para poner límites a los demonios, cuando también sabe claramente acerca de correr bajo los auspicios de cuentas de usuario sin privilegios; pero por qué hacer esto dentro de la aplicación . En realidad, hay un ejemplo bastante claro de por qué.
Considere el diseño del
httpd
programa dæmon en el paquete de archivos públicos de Daniel J. Bernstein. Lo primero que hace es cambiar la raíz al directorio raíz que se le dijo que usara con un argumento de comando, luego soltar los privilegios a la ID de usuario sin privilegios y la ID de grupo que se pasan en dos variables de entorno.Los conjuntos de herramientas de administración de Dæmon tienen herramientas dedicadas para cosas como cambiar el directorio raíz y soltar las identificaciones de usuarios y grupos sin privilegios. La runa de Gerrit Pape tiene
chpst
. Mi conjunto de herramientas nosh tienechroot
ysetuidgid-fromenv
. El s6 de Laurent Bercot tienes6-chroot
ys6-setuidgid
. Wayne Marshall Perp tieneruntool
yrunuid
. Etcétera. De hecho, todos tienen el propio conjunto de herramientas daemontools de M. Bernsteinsetuidgid
como antecedente.Se podría pensar que se podría extraer la funcionalidad
httpd
y utilizar herramientas tan dedicadas. Luego, como imagina, ninguna parte del programa del servidor se ejecuta con privilegios de superusuario.El problema es que uno, como consecuencia directa, tiene que hacer mucho más trabajo para configurar la raíz modificada, y esto expone nuevos problemas.
Con Bernstein
httpd
como está, los únicos archivos y directorios que están en el árbol de directorios raíz son los que se publicarán en el mundo. No hay nada más en el árbol en absoluto. Además, no hay ninguna razón para que exista ningún archivo de imagen de programa ejecutable en ese árbol.Pero mover el cambio de directorio raíz en un programa cargado en cadena (o systemd), y de repente el archivo de la imagen de programa para
httpd
, ninguna biblioteca compartida que se cargue, y cualquier archivo especial en/etc
,/run
y/dev
que el cargador de programa o tiempo de ejecución C acceso a la biblioteca durante la inicialización del programa (que puede ser bastante sorprendente si ustedtruss
/strace
un programa C o C ++), también tiene que estar presente en la raíz modificada. Dehttpd
lo contrario , no se puede encadenar y no se cargará / ejecutará.Recuerde que este es un servidor de contenido HTTP (S). Potencialmente puede servir cualquier archivo (legible por el mundo) en la raíz modificada. Esto ahora incluye cosas como sus bibliotecas compartidas, su cargador de programas y copias de varios archivos de configuración de cargador / CRTL para su sistema operativo. Y si por algún medio (accidental) significa que el servidor de contenido tiene acceso para escribir cosas, un servidor comprometido posiblemente puede obtener acceso de escritura para la imagen del programa por
httpd
sí mismo, o incluso el cargador de programas de su sistema. (Recuerde que ahora tiene dos conjuntos paralelos de/usr
,/lib
,/etc
,/run
, y/dev
directorios para mantener seguro.)Nada de esto es el caso donde
httpd
cambia la raíz y se caen los privilegios.Por lo tanto, ha intercambiado tener una pequeña cantidad de código privilegiado, que es bastante fácil de auditar y que se ejecuta justo al inicio del
httpd
programa, ejecutándose con privilegios de superusuario; por tener una superficie de ataque de archivos y directorios muy expandida dentro de la raíz modificada.Es por eso que no es tan simple como hacer todo externamente al programa de servicio.
Tenga en cuenta que esto es, sin embargo, un mínimo de funcionalidad dentro de
httpd
sí mismo. Todo el código que hace cosas como buscar en la base de datos de cuentas del sistema operativo la ID de usuario y la ID de grupo para colocar en esas variables de entorno en primer lugar es externo alhttpd
programa, en simples comandos auditables independientes comoenvuidgid
. (Y, por supuesto, es una herramienta UCSPI, por lo que no contiene ninguna de código para que escuche en el puerto TCP correspondiente (s) o para aceptar conexiones, los que están siendo el dominio de los comandos tales comotcpserver
,tcp-socket-listen
,tcp-socket-accept
,s6-tcpserver4-socketbinder
,s6-tcpserver4d
, y así sucesivamente).Otras lecturas
httpd
. publicfile . cr.yp.to.httpd
. Los softwares de Daniel J. Bernstein, todo en uno . Softwares Jonathan de Boyne Pollard. 2016gopherd
. Los softwares de Daniel J. Bernstein, todo en uno . Softwares Jonathan de Boyne Pollard. 2017fuente
Creo que muchos detalles de su pregunta podrían aplicarse igualmente
avahi-daemon
, lo que vi recientemente. (Sin embargo, podría haber perdido otro detalle que difiere). Ejecutar avahi-daemon en un chroot tiene muchas ventajas, en caso de que avahi-daemon se vea comprometido. Éstos incluyen:El punto 3 podría ser particularmente agradable cuando estás no usando dbus o similares ... Creo usos avahi-daemon dbus, por lo que se asegura de mantener el acceso al sistema de dbus incluso desde dentro del chroot. Si no necesita la capacidad de enviar mensajes en el sistema dbus, negar esa capacidad podría ser una buena característica de seguridad.
Tenga en cuenta que si se reescribe avahi-daemon, podría optar por confiar en systemd por seguridad, y usar, por ejemplo
ProtectHome
. Propuse un cambio a avahi-daemon para agregar estas protecciones como una capa adicional, junto con algunas protecciones adicionales que no están garantizadas por chroot. Puede ver la lista completa de opciones que propuse aquí:https://github.com/lathiat/avahi/pull/181/commits/67a7b10049c58d6afeebdc64ffd2023c5a93d49a
Parece que hay más restricciones que podría haber usado si avahi-daemon no usara chroot, algunas de las cuales se mencionan en el mensaje de confirmación. Sin embargo, no estoy seguro de cuánto se aplica.
Tenga en cuenta que las protecciones que utilicé no habrían limitado el demonio de abrir archivos de socket Unix (punto 3 anterior).
Otro enfoque sería utilizar SELinux. Sin embargo, estaría vinculando su aplicación a ese subconjunto de distribuciones de Linux. La razón por la que pensé en SELinux positivamente aquí, es que SELinux restringe el acceso que los procesos tienen en dbus, de una manera muy precisa. Por ejemplo, creo que a menudo se puede esperar que
systemd
no esté en la lista de nombres de autobuses a los que necesita para poder enviar mensajes :-)."Me preguntaba si usar sandboxing systemd es más seguro que chroot / setuid / umask / ..."
Resumen: ¿por qué no ambos? Decodifiquemos un poco lo anterior :-).
Si piensa en el punto 3, usar chroot proporciona más confinamiento. ProtectHome = y sus amigos ni siquiera intentan ser tan restrictivos como chroot. (Por ejemplo, ninguna de las listas negras de opciones de systemd nombradas
/run
, donde tendemos a colocar archivos de socket Unix).chroot muestra que restringir el acceso al sistema de archivos puede ser muy poderoso, pero no todo en Linux es un archivo :-). Hay opciones de systemd que pueden restringir otras cosas, que no son archivos. Esto es útil si el programa se ve comprometido, puede reducir las características del kernel disponibles, en las que podría intentar explotar una vulnerabilidad. Por ejemplo, avahi-daemon no necesita enchufes bluetooth y supongo que su servidor web tampoco :-). Por lo tanto, no le dé acceso a la familia de direcciones AF_BLUETOOTH. Simplemente incluya en la lista blanca AF_INET, AF_INET6 y quizás AF_UNIX, usando la
RestrictAddressFamilies=
opción.Lea los documentos de cada opción que use. Algunas opciones son más efectivas en combinación con otras, y algunas no están disponibles en todas las arquitecturas de CPU. (No porque la CPU sea mala, sino porque el puerto de Linux para esa CPU no estaba tan bien diseñado. Creo).
(Hay un principio general aquí. Es más seguro si puedes escribir listas de lo que quieres permitir, no lo que quieres negar. Como definir un chroot te da una lista de archivos a los que puedes acceder, y esto es más robusto que decir que quieres bloquear
/home
).En principio, puede aplicar las mismas restricciones usted mismo antes de setuid (). Todo es solo código que puedes copiar de systemd. Sin embargo, las opciones de la unidad systemd deberían ser significativamente más fáciles de escribir, y dado que están en un formato estándar, deberían ser más fáciles de leer y revisar.
Por lo tanto, le recomiendo leer la sección de sandboxing
man systemd.exec
en su plataforma de destino. Pero si desea el diseño más seguro posible, no tendría miedo de probarchroot
(y luego eliminar losroot
privilegios) en su programa también . Hay una compensación aquí. El usochroot
impone algunas restricciones en su diseño general. Si ya tiene un diseño que usa chroot, y parece hacer lo que necesita, eso suena bastante bien.fuente
Si puede confiar en systemd, entonces es más seguro (¡y más simple!) Dejar el sandboxing a systemd. (Por supuesto, la aplicación también puede detectar si se ha lanzado sandboxed por systemd o no, y sandbox en sí mismo si todavía es root). El equivalente del servicio que describe sería:
Pero no tenemos que parar allí. systemd también puede hacer muchos otros sandboxing por usted; aquí hay algunos ejemplos:
Consulte
man 5 systemd.exec
muchas más directivas y descripciones más detalladas. Si hace que su demonio sea activable por socket (man 5 systemd.socket
), incluso puede usar las opciones relacionadas con la red: el único enlace del servicio al mundo exterior será el socket de red que recibió de systemd, no podrá conectarse a nada más. Si es un servidor simple que solo escucha en algunos puertos y no necesita conectarse a otros servidores, esto puede ser útil. (Las opciones relacionadas con el sistema de archivos también pueden hacerRootDirectory
obsoleto, en mi opinión, por lo que quizás ya no tenga que molestarse en configurar un nuevo directorio raíz con todos los binarios y bibliotecas necesarios).Las versiones más recientes de systemd (desde v232) también son compatibles
DynamicUser=yes
, donde systemd asignará automáticamente el usuario del servicio para usted solo durante el tiempo de ejecución del servicio. Esto significa que no tienen que registrar un usuario permanente para el servicio, y funciona bien siempre y cuando el servicio no escribe en ningún ubicaciones del sistema de archivos que noStateDirectory
,LogsDirectory
yCacheDirectory
(que también se puede declarar en el archivo de la unidad - veaman 5 systemd.exec
, nuevamente, y qué systemd administrará, teniendo cuidado de asignarlos correctamente al usuario dinámico).fuente