Recientemente, los desarrolladores de systemd tuvieron una discusión en la cual se puso sobre la mesa el tema de la reducción de las dependencias de la biblioteca libsystemd (la biblioteca encargada de implementar servicios e interactuar con systemd). Esto debido a que actualmente se plantea una cierta preocupación sobre el aumento de dependencias de terceros en libsystemd que no son controladas por el proyecto y esto aumenta de la superficie de ataque. El iniciador de la discusión destaca que libsystemd carga varias bibliotecas críticas, como libzstd, liblz4 y libgcrypt, además de liblzma y glibc. Esto plantea problemas de seguridad significativos, especialmente si estas bibliotecas de terceros se ven comprometidas.
En Fedora, por ejemplo, más de 150 paquetes dependen de libsystemd, lo que aumenta la complejidad y los riesgos asociados. La propuesta para abordar esto implica dividir libsystemd en varias librerias separadas, cada una responsable de una API específica. Esto permitiría cargar dependencias de terceros solo cuando sean necesarias, reduciendo así la exposición a posibles vulnerabilidades en bibliotecas no controladas directamente por los desarrolladores de systemd.
Sin embargo, los desarrolladores de systemd argumentan que esta separación sería problemática debido a la interconexión de los controladores presentes en libsystemd. Consideran que la división requeriría mucho trabajo y podría resultar en una pérdida de eficiencia o la necesidad de duplicar el código, lo que contrarrestaría los beneficios de seguridad buscados.
En lugar de una separación completa, libsystemd ha optado por un enfoque más dinámico al cargar dinámicamente las bibliotecas liblzma, libzstd y liblz4 cuando sea necesario, utilizando la llamada dlopen(). Se planea implementar un cambio similar para libgcrypt en futuras versiones para abordar tanto las preocupaciones de seguridad como las necesidades de eficiencia y mantenibilidad del código.
Creo que la mayoría de estas dependencias no son necesarias para implementar funciones centrales de libsystemd, como las mencionadas anteriormente.
Este problema puede significar dividir libsystemd en múltiples bibliotecas que implementen diferentes API, una de las cuales, por ejemplo, libsystemd-core, solo dependería de libc, y otras bibliotecas más especializadas agregarían otras dependencias. Además, si algunas de las dependencias solo son necesarias para ciertos servicios systemd, mueva las dependencias a esos servicios.
El efecto final de esto debería ser reducir la superficie de ataque y mejorar la seguridad del sistema.
Durante la discusión hubo un punto el cual la mayoría de los desarrolladores criticaron, y es que mencionan que la decisión de cargar bibliotecas de terceros de forma implícita mediante dlopen() en libsystemd generaría un trabajo adicional debido a la complejidad añadida en el diagnóstico y la falta de visibilidad de los enlaces, además de que mencionan que esto complica la identificación de llamadas API de libsystemd que se conectan a funciones de bibliotecas externas, ya que no es obvio en el código. Esta nueva forma de carga, aunque no cambia la arquitectura subyacente, oculta los componentes externos de los mantenedores y usuarios.
Lenart Pottering expresó su desacuerdo con la idea de dividir libsystemd en varias bibliotecas debido a las complicaciones que esto conllevaría en el intercambio de código y el mantenimiento de la estabilidad de la API y los espacios de nombres. Dividir libsystemd requeriría exponer todos los controladores internos o compilarlos estáticamente por separado en cada biblioteca, lo que podría aumentar el tamaño debido a la duplicación de código o dificultar la gestión de la estabilidad y coherencia del sistema.
En lugar de la división, la estrategia de cargar bibliotecas externas solo cuando sea necesario se considera óptima, además de que para abordar la complejidad añadida en el diagnóstico, se propone agregar campos adicionales a los archivos ELF con información sobre las dependencias dinámicas cargadas, permitiendo que los depuradores procesen esta información y la muestren en la salida de herramientas como readelf. Esto proporcionaría una mayor transparencia y visibilidad sobre las dependencias dinámicas utilizadas por libsystemd, facilitando así el diagnóstico y la depuración de problemas relacionados con las bibliotecas externas cargadas dinámicamente.
Lenart recomendó a los desarrolladores de aplicaciones que, en lugar de vincularse directamente con libsystemd para una función específica, se implemente un controlador de protocolo a nivel de aplicación.
Esta estrategia de implementar controladores de protocolo a nivel de aplicación ofrece varias ventajas:
- Reduce la dependencia de libsystemd y evita la carga de bibliotecas externas cuando no son necesarias.
- Proporciona más flexibilidad y control sobre la funcionalidad específica requerida por la aplicación.
- Simplifica el diagnóstico y la depuración al tener un control más directo sobre la implementación de funciones específicas.
- En general, esta aproximación promueve la modularidad y la independencia de las aplicaciones, mejorando la flexibilidad y la eficiencia en el desarrollo y mantenimiento del software.
Si estás interesado en poder conocer más al respecto, puedes consultar los detalles en el siguiente enlace.