Permisos y slots¶
Para quien crea plugins: lo que puede declarar un manifiesto, lo que comprueba el instalador y las reglas que se aplican a un plugin mientras funciona. El catálogo de abajo es la lista entre la que elegir; cómo aísla el servidor a un plugin, y lo que ese aislamiento no puede impedir, se explica en Sandbox de plugins y modelo de amenazas.
Lo que se comprueba al instalar¶
Un plugin se rechaza al instalarlo cuando:
- falta
kind, o no es nipluginnitheme; - a un
kind: pluginle faltalanguageoentrypoint, o unkind: themelos declara, o declarapermissionsocapabilities; - un permiso o una capacidad es desconocido;
- un módulo de interfaz apunta a un slot que no está en la lista de slots;
- una entrada de
external_fetchincumple las reglas de las peticiones externas, o se declaranetworksin ninguna entrada; - el ZIP incumple los límites de extracción;
- el análisis estático encuentra un patrón crítico, o un aviso que el
instalador trata como tal (deserialización insegura, ofuscación). Los
binarios compilados, los archivos comprimidos incrustados y el código nativo
se rechazan salvo que el manifiesto declare
native-binary.
Los comentarios se eliminan antes del análisis, así que una línea comentada no se señala. Las categorías están en Sandbox de plugins y modelo de amenazas.
Antes de instalar, el administrador ve los permisos y las capacidades declarados, y lo que ha encontrado el análisis; los slots, los hosts y la firma se muestran al actualizar y en el Panel de seguridad. Nada de lo que declara un plugin se concede sin avisar.
Las condiciones en las que funciona el plugin¶
Declare lo que declare el manifiesto:
- el plugin ve su propia carpeta, no los datos del servidor;
- no tiene acceso directo a la red: un plugin con el permiso
networkllega a los hosts de su listaexternal_fetcha través de la llamadaFetchdel servidor, y a nada más; - sin
subprocess, no puede lanzar procesos hijos (los hilos sí funcionan); - funciona con unos límites de recursos que el administrador puede ajustar: por defecto, 256 MiB de memoria, 64 procesos e hilos, 100 MiB de disco para su carpeta y una prioridad de CPU más baja.
Firmar es opcional mientras desarrollas. Cómo firmar un plugin, y la política de firmas que puede imponer un servidor: Escribir un plugin.
Catálogo de permisos¶
Un permiso se declara en permissions; cualquier otro valor hace que se
rechace la instalación. Recibir webhooks (webhook.receiver) y aportar
metadatos (metadata.provider) son capacidades, que se declaran en
capabilities: consulta la
Referencia de la API de plugins.
| Permiso | Lo que concede |
|---|---|
network |
El backend puede llamar a Fetch. Tiene que ir con una lista external_fetch no vacía; sin ella, se rechaza la instalación. El proceso en sí sigue sin tener ningún socket. |
subprocess |
El backend puede lanzar procesos hijos. Sin él, el filtro del núcleo del lanzador rechaza fork, vfork y clone sin CLONE_THREAD, y la capa seccomp rechaza execve en los plugins interpretados. Los hilos siguen funcionando en ambos casos. El administrador ve un aviso específico. |
native-binary |
El paquete puede incluir .so, .dll, .exe, .wasm, .jar, archivos comprimidos incrustados o código nativo que el análisis no puede leer. El administrador ve ese código opaco y tiene que aceptarlo. Obligatorio para un plugin go. |
filesystem |
Intención declarada de tocar el sistema de archivos fuera de la carpeta del plugin. Se registra y se muestra al administrador; el sandbox no da acceso a ningún archivo fuera de la carpeta del plugin, se declare lo que se declare. |
media.read |
GetMediaItem y ListMediaItems en el servidor: la ficha completa del medio. |
media.write |
Reservado para las llamadas de escritura de metadatos, que hoy no se sirven. |
api.library |
Las mismas llamadas de lectura de medios que media.read. |
api.user |
Reservado para GetUserPreferences, que hoy no se sirve. |
api.metadata |
Reservado para UpdateMetadata, que hoy no se sirve. |
notification.sender |
Reservado para SendNotification, que hoy no se sirve. |
public |
La interfaz puede leer una versión segura de los elementos multimedia (id, título, tipo, año, resumen, póster), nunca rutas ni flujos. Consulta evey.media.*. |
prefs |
La interfaz puede leer y escribir las preferencias del usuario actual en el espacio de nombres propio del plugin. Consulta evey.prefs.*. |
hardware.gpu |
El proceso recibe el nodo de renderizado DRM del servidor (el renderD1xx real, nunca un nodo card). Es una superficie de ataque del controlador del núcleo y una frontera de memoria de la GPU compartida con el transcodificador: una GPU bloqueada puede atascar la transcodificación. Sin él, los nodos de la GPU quedan totalmente fuera de la lista permitida de Landlock; con él, el lanzador añade una regla IOCTL_DEV limitada a ese único archivo (Landlock ABI 5 o posterior; en núcleos más antiguos, el acceso en lectura y escritura basta para enviar comandos). El Panel de seguridad muestra un aviso crítico una vez instalado el plugin. Concédelo solo a plugins que ejecutarías fuera de un sandbox. |
Reglas de las peticiones externas¶
Se declaran en external_fetch: [...] y se comprueban al instalar
(validateExternalFetch). Es todo o nada: una sola entrada no válida hace
que se rechace toda la instalación.
Una entrada es un nombre de host. Un prefijo de esquema y una ruta final se
descartan al analizar; solo se guarda el nombre de host. Los hosts coinciden
exactamente, un comodín *.example.com se rechaza en la instalación y un sufijo de puerto se ignora.
| Entrada | Válida | Motivo |
|---|---|---|
api.tmdb.org |
Sí | Nombre de host exacto, la forma normal |
https://api.tmdb.org |
Sí | El esquema y la ruta se descartan al analizar, se guarda el nombre de host |
*.tmdb.org |
No | Los comodines se rechazan en la instalación; lista cada host |
* |
No | Un comodín solo se rechaza |
192.168.1.1, ::1 |
No | Las direcciones IP literales se rechazan, en las dos familias |
Mientras funciona, el intermediario del servidor añade sus propios límites,
que el manifiesto no puede ampliar: solo http y https, solo GET y
HEAD, el host de destino se compara con tu lista sin distinguir mayúsculas
de minúsculas (un sufijo :port en una entrada se ignora), las direcciones
privadas o reservadas se rechazan después de la resolución DNS, el cuerpo se
limita a 8 MiB y la petición a 20 segundos. Las redirecciones se vuelven a
comprobar con la lista y con la regla de direcciones.
La misma lista de hosts es el connect-src del marco del plugin, con el
prefijo https://: un widget puede llamar directamente a esos hosts, solo por
https.
Lista de slots permitidos¶
Los plugins solo pueden inyectar interfaz en los slots permitidos de forma explícita. Las rutas críticas no están, a propósito.
| Slot | Requiere sesión | Notas |
|---|---|---|
home.section |
Sí | Zona de contenido de la página principal |
home.hero |
Sí | Zona del banner principal |
library.sidebar |
Sí | Barra lateral de la vista de biblioteca |
media.detail.sidebar |
Sí | Barra lateral del detalle de un medio |
player.overlay |
Sí | Capa sobre el reproductor |
settings.user.appearance |
Sí | Sección Apariencia de los ajustes del usuario |
sidebar.bottom |
Sí | Zona inferior del panel de navegación |
admin.nav |
Sí | Una entrada de navegación en el panel de administración que abre la página propia del plugin, en su sandbox; no inyecta nada en las pantallas de administración existentes |
Nunca permitidos: admin.* (salvo admin.nav), login.*, setup.*,
profile.security.*. Son rutas críticas para la autenticación o los
privilegios, e inyectar algo ahí es una vía para escalar privilegios o hacer
phishing.
El contenido de admin.nav va en un sandbox como cualquier otro slot: se
muestra en un <iframe sandbox="allow-scripts"> sin allow-same-origin, en
una página cuya política es default-src 'none', así que es contenido de
terceros mostrado en la zona de administración, no una capacidad de
administración concedida al plugin.
El puente limita lo que un marco puede pedir a la página: evey.toast como
mucho 3 mensajes por segundo, 200 caracteres, sin HTML; evey.navigate solo
/home, /library, /media, /player, /settings y /search;
evey.resize limitado a 50-800 px. El marco no tiene ningún token: la página
anfitriona emite un token de cinco minutos limitado a (plugin, usuario) para
las llamadas public y prefs, y el servidor rechaza por defecto cualquier
método que ese token no nombre.
Proceso de actualización¶
- El administrador sube un ZIP nuevo.
PreviewPluginUpdateejecuta el análisis estático y la comprobación de la firma, y devuelve la diferencia de permisos, capacidades, slots yexternal_fetchcon la versión instalada. - Si la diferencia no está vacía, el administrador tiene que aceptarla antes
de que se aplique la actualización (
requires_ack = true; por la ruta de subida,accept_permission_changes=true). - El hash del paquete se toma en la vista previa y se comprueba al aplicar: el ZIP que se analizó es el ZIP que se instala.
- Se comprueba la continuidad de la clave de firma: una actualización
firmada con otra clave distinta de la de la versión instalada, o que ya
no está firmada, se rechaza hasta que el administrador acepte al nuevo
editor. Con
EVEY_PLUGINS_COSIGN_POLICY=enforce, una firma no fiable o no válida en el paso de aplicar bloquea la actualización aunque la vista previa haya pasado.