
Como parte de su diseño fundamental basado en la seguridad, las instantáneas están diseñadas para ejecutarse aislado del sistema subyacente. En la mayoría de los casos, la idea funciona bien y el acceso granular a los recursos del sistema mediante el mecanismo de interfaces permite a los desarrolladores de Snap enviar sus aplicaciones empaquetadas con un confinamiento estricto.
Sin embargo, hay algunos escenarios en los que incluso el uso liberal de conectores de interfaz no puede satisfacer completamente todos los requisitos funcionales de aplicaciones específicas. Ciertos programas necesitan acceso de todo el sistema a directorios y archivos, y otros pueden necesitar ejecutar archivos binarios arbitrarios como parte de su ejecución. Para ello, los snaps también se pueden instalar en el modo de confinamiento “clásico”, lo que les otorga un acceso similar al que tendría la aplicación si se instalara de forma tradicional. La solución funciona, pero ahora hay propuestas para hacer que el modo clásico sea aún más robusto y eficiente.
El problema con el confinamiento clásico es que elimina parte de la previsibilidad que existe en las instantáneas estrictamente confinadas. Mientras que uno debe esperar que un complemento se comporte de la misma manera en todos los sistemas compatibles, los complementos clásicos pueden depender de las bibliotecas del host para ejecutarse, o pueden asumir que ciertas bibliotecas (y/o versiones específicas) están presentes. Esto puede dar lugar a posibles conflictos en la forma en que se cargan y ejecutan las aplicaciones.
Para ejecutarse correctamente, los paquetes instantáneos confinados clásicamente deben requerir ejecutables dinámicos para cargar bibliotecas compartidas desde el sitio apropiado. base snap en lugar de usar el sistema de archivos raíz del host. Esto significa que las instantáneas clásicas se comportarían más como instantáneas estrictamente confinadas, lo que debería conducir a una mayor consistencia y previsibilidad de ejecución.
Una nueva propuesta en proceso describe lo que se necesita para verificar los parámetros de enlace dinámico en archivos binarios en un paquete instantáneo clásico.
Los paquetes instantáneos clásicos se ejecutan en el sistema de archivos raíz del host, que puede no coincidir con su entorno de compilación. Para evitar incompatibilidades, los archivos binarios en las instantáneas clásicas deben construirse con los parámetros de vinculación apropiados o parchearse para permitir la carga de bibliotecas compartidas desde su base siesta. En el caso de posibles problemas de vinculación dinámica, el autor de la instantánea debe tener en cuenta que es posible que su paquete no se ejecute como se esperaba.
Con la nueva propuesta, se deben cubrir los siguientes parámetros de vinculación dinámica:
- Rutas de la biblioteca en tiempo de ejecución: La sección dinámica de un archivo ELF contiene la entrada RPATH, que enumera las rutas de tiempo de ejecución a las bibliotecas compartidas que se van a buscar antes de las rutas establecidas en la variable de entorno LD_LIBRARY_PATH y la entrada RUNPATH. Se pueden especificar varias rutas separadas por dos puntos.
- ruta $ORIGIN – El valor especial $ORIGIN representa la ruta donde se encuentra el binario, lo que permite que la ruta de la biblioteca en tiempo de ejecución se establezca en relación con esa ubicación (por ejemplo: $ORIGIN/../lib para un ejecutable instalado en bin/ con bibliotecas en lib/) .
- Intérprete de archivos – La sección especial ELF .interp contiene la ruta al intérprete del programa. Si se usa, debe establecerse en la ruta del vinculador dinámico apropiado: el vinculador dinámico del paquete instantáneo que se está creando Si libc está en etapas, o el vinculador dinámico proporcionado por el base romper de lo contrario. Los desarrolladores deben tener en cuenta que los archivos binarios vinculados con una libc alternativa pueden usar valores de intérprete que apuntan a un vinculador dinámico diferente.
Archivos afectados y nueva ruta
Para ejecutarse como se esperaba, los archivos binarios en una aplicación de complemento clásica deben configurarse para buscar bibliotecas compartidas proporcionadas por el base complemento o incluido como parte del complemento de la aplicación. Esto se logra configurando la ruta de tiempo de ejecución a las bibliotecas compartidas en todos los binarios ELF (excepto los archivos de objetos reubicables) que están presentes en la carga útil del paquete.
La ruta del archivo ELF debe configurarse correctamente si:
- El proyecto tiene una base; y
- La base del proyecto no está desnuda; y
- El confinamiento es clásico, o la libc es escenificada
El valor de rpath debe establecerse para llegar a todas las entradas NECESARIAS en la sección dinámica del binario ELF. Si el binario ya contiene un rpath, entonces debería mantener solo aquellos que mencionan $ORIGIN. Las entradas de Rpath que apuntan a ubicaciones dentro de la carga útil deben cambiarse para que sean relativas a $ORIGIN. Sin embargo, esto no incluye ni admite la detección de rutas a bibliotecas compartidas cargadas con dlopen().
Hay varias estrategias para establecer rpath y el intérprete.
Configuración de rpath en tiempo de compilación
Un archivo binario ELF creado durante la ejecución del ciclo de vida de las piezas puede tener su valor rpath establecido mediante el uso de parámetros de vinculación apropiados. El enlazador normalmente se invoca indirectamente a través de un controlador de compilador; en el caso de gcc, los parámetros se pueden pasar al enlazador usando la opción -Wl:
$ gcc -o foo foo.o -Wl,-rpath=\$ORIGIN/lib,--disable-new-dtags -Llib -lbar
Se puede usar una estrategia similar para establecer rpath en un cgo binario:
package main
/*
#cgo LDFLAGS: -L${SRCDIR}/lib -Wl,-rpath=\$ORIGIN/lib -Wl,--disable-new-dtags -lbar
#include "bar.h"
*/import "C"
func main() {
C.bar()
}
En ambos casos, la inspección del contenido de la sección dinámica de ELF revela que el valor de rpath se ha establecido correctamente:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libbar.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000f (RPATH) Library rpath: [$ORIGIN/lib]
Aplicación de parches a los ejecutables generados
Una carga útil instantánea puede contener binarios ELF preconstruidos instalados desde fuentes arbitrarias (generalmente desde el archivo de distribución, después de instalar los paquetes de etapa). En este caso, rpath debe configurarse modificando el binario existente usando una herramienta como patchelf:
$ patchelf --force-rpath --set-rpath \$ORIGIN/lib foo
$ readelf -d a | grep RPATH
0x000000000000000f (RPATH) Library rpath: [$ORIGIN/lib]
Patchelf también se puede usar para cambiar el intérprete a un enlazador dinámico diferente:
$ patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2 foo
$ readelf -p .interp a
String dump of section '.interp':
[ 0] /lib64/ld-linux-x86-64.so.2
Por supuesto, no hay garantía de que se puedan parchear todos los binarios o de que se cubran todos los casos de uso. La aplicación de parches a binarios ELF para modificar rpath o entradas de intérprete puede fallar en ciertos escenarios, como ejecutables de go vinculados con el enlazador de go, o binarios que usan variantes de libc que requieren un intérprete no estándar. Además, la aplicación de parches hará que los archivos binarios firmados no superen la validación.
Como parte de la experiencia mejorada del desarrollador, la herramienta Snapcraft también proporcionará advertencias de linter, lo que debería ayudar a los creadores de instantáneas a comprender con mayor precisión si existe algún problema potencial con la creación de la instantánea utilizando el confinamiento clásico. Específicamente, el linter debería emitir advertencias si la carga útil contiene archivos binarios que pueden cargar bibliotecas compartidas potencialmente incompatibles.
- Se requieren parches y el intérprete binario ELF no está configurado en el enlazador dinámico correcto (de base o libc por etapas). Es posible que se necesite un manejo especial si el binario ELF usa un intérprete no estándar (en cuyo caso, de lo contrario, el linter emitiría una advertencia de falso positivo).
- Se requieren parches y el rpath binario ELF no está establecido en el valor necesario para cargar las dependencias de la biblioteca compartida desde el baseo desde el complemento si la dependencia no es parte del base.
- Además de estas advertencias, la herramienta Snapcraft también podría imprimir información sobre cómo establecer parámetros o aplicar parches a los archivos binarios existentes en el momento de la compilación.
- Snapcraft también puede realizar parches binarios ELF automáticamente de acuerdo con los parámetros establecidos en el archivo de configuración del proyecto. Independientemente de la política predeterminada, la opción de no realizar parches automáticos debe estar presente debido a limitaciones en el mecanismo de parches o porque los valores existentes no deben cambiarse.
El escenario de confinamiento clásico es difícil, ya que debe proporcionar un comportamiento y resultados repetibles y consistentes en un entorno impredecible (la máquina del usuario). El equipo de Snapcraft está tratando de hacer que la experiencia sea lo más elegante posible, para que los desarrolladores puedan empaquetar de manera confiable sus aplicaciones como instantáneas clásicas y enviarlas a sus usuarios. En este artículo, cubrimos algunas de las herramientas y métodos utilizados para mejorar esta funcionalidad. Si tiene alguna pregunta o idea sobre las instantáneas clásicas, únase nuestro foro y cuéntanos lo que piensas.