ATALAIA
  1. Inicio
  2. Recursos
  3. Blog
  4. Una puerta para cada instalación
Cadena de suministro · Guía

Una puerta para cada instalación

Pasa todos los paquetes por un único proxy, haz que las versiones nuevas esperen unos días, desactiva los scripts de instalación y deja que decida el lockfile.

3 min de lecturaEquipo Atalaia

Registry proxynpmpipLockfiles

La mayoría de las organizaciones instalan paquetes desde decenas de sitios: cada portátil, cada runner, cada Dockerfile, todos hablando directamente con un registry público. Eso hace imposible aplicar una política, porque no hay dónde ponerla.

La solución es una sola barrera. Toda instalación pasa por ella, y la barrera aplica unas pocas reglas simples. Esta guía cubre las cuatro que hacen casi todo el trabajo.

Paso 1: un único proxy de registry

Ejecuta un proxy de caché por cada ecosistema que uses y apunta todos los clientes a él. Varios gestores de repositorios de código abierto y autoalojados pueden hacerlo. El proxy descarga del registry público en la primera petición, cachea el resultado y sirve tus paquetes internos desde la misma dirección.

# .npmrc (repo root, committed)
registry=https://packages.internal.example/npm/

# pip.conf
[global]
index-url = https://packages.internal.example/pypi/simple

Después hazlo la única vía. Bloquea la salida directa a registries públicos desde los runners de CI, para que un Dockerfile olvidado falle con ruido en lugar de saltarse la puerta en silencio. En los portátiles, distribuye la configuración con tu gestión de dispositivos o un script de bootstrap.

El proxy también te da algo que hoy rara vez tienes: un registro de cada paquete y versión que tu organización descarga realmente. Ese registro es lo que convierte el próximo aviso público de un caos en una búsqueda.

Paso 2: haz que las versiones nuevas esperen

Las versiones maliciosas suelen detectarse y retirarse rápido. Un cooldown, a veces llamado edad mínima de release, significa que una versión debe llevar publicada unos días antes de que tus builds la acepten. Pierdes casi nada, porque muy pocas correcciones son tan urgentes como para que unos días importen, y esas se pueden permitir a mano.

Puedes aplicarlo en el proxy, en el gestor de paquetes o en el bot de actualizaciones:

# pnpm-workspace.yaml (value in minutes: 3 days)
minimumReleaseAge: 4320

# .github/dependabot.yml
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    cooldown:
      default-days: 5

Renovate tiene un ajuste equivalente, minimumReleaseAge. Elige un único sitio donde aplicarlo y documenta el proceso de excepciones, para que nadie lo esquive cuando vaya con prisa.

Paso 3: bloquea los scripts de instalación

Los scripts que se ejecutan al instalar son donde la mayoría de los paquetes maliciosos hacen su trabajo. Desactívalos por defecto y permite solo la lista corta de paquetes que de verdad compilan algo.

# npm: .npmrc
ignore-scripts=true

# pnpm: package.json (only these may run build scripts)
{
  "pnpm": {
    "onlyBuiltDependencies": ["esbuild", "sharp"]
  }
}

Las versiones recientes de pnpm ya rechazan los scripts de build de las dependencias salvo que estén en la lista de permitidos. Con npm, ignore-scripts también impide que se ejecuten los scripts de tu propio proyecto al instalar, así que pruébalo primero en un repositorio y ejecuta explícitamente los pocos builds que necesites. En Python, prefiere wheels frente a distribuciones de código fuente cuando puedas; pip install --only-binary :all: rechaza todo lo que ejecutaría setup.py.

Paso 4: manda el lockfile

Un gate solo es útil si el resolver no puede elegir algo nuevo a escondidas detrás de él. Haz commit de los lockfiles y usa los comandos de instalación que se niegan a modificarlos:

npm ci                          # fails if package-lock.json is out of sync
pnpm install --frozen-lockfile
pip install --require-hashes -r requirements.txt

Los requirements con hash fijado hacen que, aunque una versión se sustituya en el registry, la instalación falle en lugar de aceptar otros bytes. Trata los cambios en el lockfile como código: deben aparecer en una pull request, y los diffs grandes sin explicación merecen una pregunta.

Despliegue gradual

Hazlo en este orden, porque cada paso hace más seguro el siguiente:

  1. Monta el proxy y apunta el CI hacia él. Vigila los logs durante una semana para saber qué descargas en realidad.
  2. Apunta los portátiles a él y bloquea la salida directa a registries desde los runners.
  3. Añade el periodo de espera en una sola capa, con una vía escrita para solicitar excepciones.
  4. Desactiva los scripts de instalación en un repositorio, arregla lo que se rompa y luego hazlo el valor por defecto.
  5. Pasa todos los pipelines a instalaciones frozen o con hash.

El resultado es aburrido en el mejor sentido. Siguen llegando paquetes nuevos, pero por una sola puerta, unos días más tarde, sin ejecutar código al entrar y solo en las versiones exactas que alguien revisó.

Qué hacer el lunes

  • Haz grep en tus Dockerfiles y en la configuración de CI buscando URLs de registries públicos y npm install sin ci.
  • Comprueba si algún repositorio no tiene lockfile.
  • Lista los paquetes de tu aplicación principal que declaran install scripts y decide cuáles los necesitan de verdad.
  • Elige quién es responsable del proxy. Una puerta sin dueño acaba siendo una puerta que todos se saltan.

Dónde encaja en la línea

En la torre (en desarrollo):

Habladlo

Una llamada de 30 minutos. Sin diapositivas, sin lista de precios y con un siguiente paso en cualquier caso.