# Modelo de seguridad

Qué pieza de mikroscope corre en el router MikroTik y cuál en tu máquina, a qué llega cada una, dónde vive cada credencial y cómo informar de un fallo.

Source: https://docs.jmrp.io/mikroscope/es/security/

El router aloja un agente que escucha y nunca abre conexiones hacia fuera, y toda credencial que
abre algo distinto del agente se queda en tu máquina. La tabla de abajo es el mapa entero: qué se
ejecuta dónde, a qué llega y qué credencial guarda.

## Qué se ejecuta dónde

| Pieza                                                            | Corre                                      | Llega a                                                                                                                         | Credenciales                                                                    |
| ---------------------------------------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `mikroscope-agent`                                               | en un contenedor scratch dentro del router | sirve HTTP solo en la dirección de la veth; **ninguna conexión saliente**                                                       | no presenta ninguna; un token bearer opcional que _exige_                       |
| `mikroscope doctor`, `install`, `upgrade`, `uninstall`, `status` | tu máquina, cuando los ejecutas            | el router por tu propio ssh de administrador; `install`, `upgrade` y `status` además sondean el `/healthz` del agente, y `doctor` lee `/healthz` y después el anillo del agente, como mucho sus 10 000 muestras más recientes; `uninstall --targets dashboard` o `data` llega además a tu Grafana y a los almacenes | tu clave ssh; `doctor` presenta además el token del agente si hay uno; `GRAFANA_TOKEN` y las credenciales de los destinos para esos objetivos |
| `mikroscope plan`, `install --dry-run`, `upgrade --dry-run`      | tu máquina                                 | nada: construyen la imagen e imprimen el listado sin conectarse al router                                                       | ninguna, salvo que `plan --rsc` reciba un token (abajo)                         |
| `mikroscope record`, `forward`                                   | tu máquina                                 | el agente por HTTP; la API binaria del router para la capa de la API de `forward`, el transporte relay y `record --log-markers` | el token del agente si hay uno; un usuario de la API dedicado y de solo lectura; `GRAFANA_TOKEN` con `--grafana` |
| `mikroscope mark`                                                | tu máquina                                 | los ficheros locales de la grabación; la API binaria del router solo con `--log-markers`; nunca el agente                       | el usuario de la API, solo con `--log-markers`                                  |
| destinos                                                         | tu máquina, dentro de `forward`            | sirven `/metrics` para tu Prometheus; envían a InfluxDB y a los demás destinos que indiques                                     | sus tokens, en tu entorno                                                       |
| `mikroscope dashboards publish`, `import`, `check`               | tu máquina                                 | tu Grafana; `publish` crea o corrige allí las fuentes de datos y la carpeta                                                     | `GRAFANA_TOKEN`, en tu entorno; `publish` además las credenciales de los destinos que copia en las fuentes de datos |

`plan`, `install --dry-run` y `upgrade --dry-run` nunca llegan al router, así que tampoco hacen las
comprobaciones de propiedad; esas solo se hacen en un `install` real.

La herramienta no informa de nada a ningún sitio. No hay comprobación de actualizaciones. En el
código, el único código de red del agente es su servidor HTTP. Las conexiones salientes viven todas
en la CLI — los destinos (HTTP, TCP o UDP), el cliente de Grafana, el cliente de la API de RouterOS,
el transporte directo, la sonda de `/healthz` y la lectura del anillo de `doctor`, las tres últimas por
HTTP sin cifrar — y cada una va solo a una dirección que tú le diste o, para el agente, a la `.2` de
`--subnet` (`172.30.10.2` salvo que la cambies).

La exposición Prometheus del propio colector, `forward --prom <dirección>`, escucha en la dirección
que le pases y sirve `/metrics` sin autenticación. Enlázala a una dirección a la que solo llegue tu
Prometheus.

## Credenciales en el router

**Por la API binaria, `/container/print` devuelve todas las propiedades de todos los contenedores,
`cmd` y `envlist` incluidas, a un usuario con solo `read,api`** ([verificado](https://docs.jmrp.io/mikroscope/es/about/status/#verified-read-user-envlist)).
Si un usuario así puede leer también los valores de las entradas de la envlist, en `/container/envs`, está [sin probar](https://docs.jmrp.io/mikroscope/es/about/status/#not-tested).
El diseño supone que sí: lo que haya en una envlist se trata como legible por cualquier usuario
`read` de ese router, no solo por los administradores.

Por eso el agente no tiene destino push: un token de destino en el router lo podría leer cualquier
usuario `read`. Si algún día llega el push, será agente → colector con el mismo NDJSON, nunca
agente → InfluxDB.

Lo que `install` sí pone en la envlist `<name>-env` es configuración, y nada que abra otra cosa:

Las entradas que install escribe en la envlist del agente:

| Clave | Se escribe | Viene de | Contiene |
| --- | --- | --- | --- |
| `MIKROSCOPE_TAG` | siempre | `--name` | la marca de propiedad `mikroscope:<name> (managed by mikroscope)`, que se escribe la primera y se borra la última; el agente la ignora |
| `RATE_HZ` | siempre | `--rate`, por defecto `10`, 1–100 | la cadencia del muestreador, en Hz |
| `BUFFER_S` | siempre | `--buffer`, por defecto `60`, 10–3600 | la longitud del anillo, en segundos |
| `PORT` | siempre | `--port`, por defecto `9123`, 1–65535 | el puerto HTTP del agente |
| `ADDR` | siempre | `--subnet` | la dirección del agente, la `.2` de la /30; el agente solo escucha ahí |
| `MEM_LIMIT_MB` | siempre | `--mem-limit-mb`, 8–1024 | el límite blando de memoria de Go del agente, en MiB; se deriva del anillo (cadencia × búfer × línea, × 2,5, con un mínimo de 16 MiB y un máximo de tres cuartos de `--memory-max` mientras en él aún quepa el anillo) salvo que lo fije `--mem-limit-mb` |
| `FLOOR_HZ` | solo cuando es mayor que 0 | `--floor-hz`, por defecto `0`, 0–1000 | una sola cadencia para todas las fuentes de nivel, en Hz |
| `CAPTURE_MB` | siempre | `--capture-mb`, por defecto `4`, 0–256 | el presupuesto de capturas por disparo, en MiB; `0` las desactiva |
| `TRIGGERS` | solo cuando se da | `--triggers` | las condiciones de disparo; sin ella, el agente usa su conjunto por defecto |
| `TOKEN` | solo cuando se da | `--token` | el token bearer que exige el agente, de `--token` o `MIKROSCOPE_TOKEN`, con o sin `--expose` |

La última fila es el único secreto que sí vive en el router. Se escribe siempre que se dé `--token`
o `MIKROSCOPE_TOKEN`, con o sin `--expose`, y como el resto de la envlist se trata como legible por
cualquier usuario `read`. Abre las rutas HTTP del propio agente y nada más. `plan` y `--dry-run` lo imprimen
como `value="(token)"`, la línea de opciones como `token=(set)`, y la línea de arranque del agente en
el log del router como `token=true`.

`plan --rsc` es la excepción a ese enmascarado. Escribe la instalación como un script de RouterOS
para ejecutarlo en el propio router, así que la línea de la envlist tiene que llevar el token real;
el propio script lo dice en su cabecera. Un `.rsc` generado con un token dentro es una credencial:
[Salvaguardas del instalador](https://docs.jmrp.io/mikroscope/es/security/installer/) explica cómo tratarlo.

## Credenciales del colector

En tu máquina, las credenciales que abren algo distinto del agente se leen solo del entorno, nunca
de una opción, con una excepción: el DSN de PostgreSQL, abajo. El código da el motivo: una opción
se ve en `ps` y en el historial de la shell.

- La contraseña del usuario de la API: `MIKROSCOPE_API_PASSWORD`. La dirección y el usuario vienen de
  `--api` y `--api-user`, o de `MIKROSCOPE_API_ADDR` y `MIKROSCOPE_API_USER`.
- Credenciales de los destinos: `MIKROSCOPE_INFLUX_TOKEN`, `MIKROSCOPE_LOKI_TOKEN`,
  `MIKROSCOPE_OTLP_TOKEN`, `MIKROSCOPE_ELASTIC_AUTH`, `MIKROSCOPE_TELEGRAF_TOKEN`.
- PostgreSQL (`--postgres`, o `MIKROSCOPE_POSTGRES_DSN`): el DSN es una opción, así que deja la
  contraseña fuera de él y que el driver la lea de `PGPASSWORD` o de `~/.pgpass`, como hace psql.
  Una contraseña escrita en un DSN pasado como opción se ve en `ps`, y venga de donde venga el DSN,
  `forward --grafana` y `dashboards publish` copian la contraseña escrita en él en la fuente de datos
  de Grafana que crean. Una contraseña que solo está en el entorno o en el fichero de contraseñas no
  se copia.
- `forward --grafana` y `dashboards publish` copian también el token de InfluxDB
  (`MIKROSCOPE_INFLUX_TOKEN`, que puede escribir) y `MIKROSCOPE_ELASTIC_AUTH` en las fuentes de datos
  que crean. Para dar a Grafana una credencial de solo lectura, crea tú la fuente de datos y pasa
  `--grafana-datasource-uid`.
- Grafana: `GRAFANA_TOKEN`.

El token del agente es la excepción: tiene una opción `--token` además de `MIKROSCOPE_TOKEN`. El
mismo razonamiento vale para él, así que prefiere la variable.

La CLI no lee `.env` por su cuenta. Exporta las variables a la shell que la ejecuta, por ejemplo con
`set -a; . ./.env; set +a`.

## Origen de la imagen del agente

El contenedor ejecuta una imagen, y la vía que la pone ahí decide en qué estás confiando.

- `install` con una cadena de herramientas de Go y un checkout construye la imagen en tu máquina a
  partir del código que tienes delante y la sube por tu propia sesión ssh. Confías en tu propio
  árbol.
- `install --agent-tar <fichero>` sube el tar que publica la release, por esa misma sesión ssh. La
  CLI comprueba que el tar es una imagen del agente de mikroscope de la arquitectura que nombra
  `--arch` antes de enviarlo; comprobar que es el fichero que publicó la release te toca a ti,
  contra `checksums.txt`.
- `install --remote-image <referencia>` no sube nada: es el propio router quien descarga la imagen
  del registro que nombra la referencia, que mikroscope escribe en `remote-image=` con su host,
  `registry-1.docker.io` para Docker Hub; el ajuste global `/container/config registry-url` ni hace
  falta ni se escribe. Confías en ese registro y en el camino del router hasta él, y mikroscope no
  verifica nada de lo que llega. `/container/config` guarda además un único usuario y contraseña de
  registro para todo el equipo, y un login de Docker Hub enviado a GHCR hace fallar la descarga con `auth error` ([probado](https://docs.jmrp.io/mikroscope/es/about/status/#install-routes-tested));
  [Salvaguardas del instalador](https://docs.jmrp.io/mikroscope/es/security/installer/) explica qué supone y qué
  comprueba `doctor`.
- `plan --rsc` escribe esas mismas órdenes como un script de RouterOS para pegarlo o hacerle
  `/import`; la imagen sigue teniendo que llegar por una de las dos vías anteriores que no necesitan
  una subida desde la CLI.

## Ajustes del contenedor

`install` crea un contenedor con estos ajustes, todos impresos por `plan` antes de escribir
nada ([dónde se verificaron](https://docs.jmrp.io/mikroscope/es/about/status/#un-equipo-dos-versiones-de-routeros)):

- **`privileged=yes` por defecto**, `--privileged=false` para renunciar a ello. El ajuste necesita RouterOS 7.24 o posterior, cuyo [registro de cambios](https://download.mikrotik.com/routeros/7.24/CHANGELOG) añadió el modo privilegiado para contenedores. Quita el espacio de nombres de usuario del contenedor ([verificado](https://docs.jmrp.io/mikroscope/es/about/status/#verified-privileged-namespaces)), que es lo que hace legibles el log del
  kernel, `/proc/slabinfo`, `/proc/pagetypeinfo` y los contadores ECC de la MTD. **No** quita el
  espacio de nombres de red ni el de PID: ni contadores de interfaz, ni la tabla conntrack del
  router, ni vista de los procesos de RouterOS. [Modo privilegiado](https://docs.jmrp.io/mikroscope/es/limits/privileged/)
  tiene lo que hace legible.
- **`memory-max=64M`** (`--memory-max`), aplicado como límite cgroup del contenedor, con el límite
  blando de Go del agente (`--mem-limit-mb`) dentro de él. Si no pasas un número, se deriva del
  anillo (cadencia × búfer × línea, × 2,5, con un mínimo de 16 MiB y un máximo de tres cuartos de
  `memory-max` mientras en él aún quepa el anillo): 16 MiB con los valores por defecto.
- **`restart-policy=on-failure`**, limitado a cinco reintentos separados diez segundos, para que una
  imagen rota no pueda entrar en bucle al arrancar.
- **`start-on-boot=yes`**, o `no` con `--ephemeral`, cuya raíz vive en el disco tmpfs y no sobrevive
  a un reinicio. Que la instalación sobreviva a un reinicio con `yes` está [sin probar](https://docs.jmrp.io/mikroscope/es/about/status/#not-tested).
- **`logging=yes`**, para que las líneas de ciclo de vida del agente lleguen al log del router.
- **`ignore-remote-image-change=yes`**: con el valor por defecto, RouterOS vigila la imagen y, en
  cuanto se borra el tar, detiene y elimina el contenedor y lo vuelve a extraer minutos después ([verificado](https://docs.jmrp.io/mikroscope/es/about/status/#verified-remote-image-change)).
  `install` borra el tar justo después de la extracción, y por eso fija este ajuste.
- Raíz e imagen en el disco que elegiste con `--disk`: la flash interna por defecto.
- **Ningún bind mount.** `install` no monta ninguna ruta del anfitrión dentro del contenedor.

El agente lee `/proc`, `/sys` (`/sys/fs/cgroup` para su propia contabilidad, `/sys/class/thermal` y
`/sys/class/mtd` para el equipo) y — con privileged — `/dev/kmsg` y los contadores de rendimiento del
hardware. No escribe nada en su raíz durante la ejecución: el anillo y las capturas por disparo se
guardan en memoria. Atrapa SIGTERM, porque RouterOS mata en el acto a un contenedor que no lo hace.

El último ajuste de la lista es deliberado. Montar `/proc` y `/sys` del anfitrión en un contenedor
privilegiado no añade nada, porque los espacios de nombres aguantan, pero el `/` del anfitrión
expone el sistema de ficheros de la flash de RouterOS, configuración y secretos incluidos
([Montajes del host](https://docs.jmrp.io/mikroscope/es/limits/namespaces/#montar-las-rutas-del-host-no-cruza-la-frontera)).
mikroscope no hace esto, y un contenedor que construyas tú tampoco debería.

## Informar de una vulnerabilidad

Informa de una vulnerabilidad en privado, nunca con sus detalles en un issue ni en una discusión
públicos: abre la [pestaña Security](https://github.com/jmrplens/mikroscope/security) del
repositorio y elige **Report a vulnerability**. GitHub abre entonces un aviso privado que solo ve el
mantenedor. Si ese botón no aparece, abre una discusión en
[General](https://github.com/jmrplens/mikroscope/discussions/categories/general) que pida un canal
privado y no diga nada más del problema, y espera ahí una respuesta que te lo indique; los issues
pasan por formularios que piden los detalles, así que un issue no puede quedarse tan vacío. Incluye
la versión (`mikroscope version`, y la del agente, de la línea `agent:` de `mikroscope status`, si es
distinta), la versión de RouterOS y la placa, lo que gana un atacante y la forma más corta de
reproducirlo. Si una prueba de concepto necesita una credencial, una dirección de router o un
export, no lo envíes: descríbelo.

La política en sí es [`SECURITY.md`](https://github.com/jmrplens/mikroscope/blob/main/SECURITY.md). Promete un acuse de recibo en una semana y, una vez
confirmado el informe, una corrección publicada antes que el aviso; quien quiera que se le mencione
aparece en el aviso, y quien no, no se nombra. Solo se da soporte a la última versión, porque una corrección sale como una
etiqueta nueva y no como un parche sobre una anterior. También dice qué merece informarse: todo lo
que rompa la separación que describe esta página, como que algo distinto del colector configurado
llegue al agente o que una credencial aparezca donde no debe. Los requisitos que impone RouterOS, y
que el token del agente sea legible para cualquier usuario `read`, son decisiones documentadas, no
vulnerabilidades.

## Véase también

- [Usuario de la API](https://docs.jmrp.io/mikroscope/es/security/api-user/): el usuario de RouterOS que necesita el
  colector, y la política que recibe.
- [Exponer en la LAN](https://docs.jmrp.io/mikroscope/es/security/expose/): las dos reglas de cortafuegos, y por qué
  el token pasa a ser obligatorio.
- [Salvaguardas del instalador](https://docs.jmrp.io/mikroscope/es/security/installer/): los objetos sobre los que no
  construye, y los valores que no mete en una orden.
- [Modo privilegiado](https://docs.jmrp.io/mikroscope/es/limits/privileged/): lo que lee la concesión de
  privilegio por defecto, y lo que no.
