CreaRack-SL

Herencia de credenciales SSH en Terminal desde Auto-Provision

Descripción general

En s114 (2026-06-08), el Terminal ahora hereda automáticamente las credenciales SSH que se proporcionan durante el descubrimiento Auto-Provision. Antes, esas credenciales se usaban solo para verificar conectividad durante el discovery y luego se descartaban, obligando al usuario a re-teclearlas en Terminal.

Cambio de comportamiento:

  • Antes: Descubres un dispositivo en Auto-Provision → ingresas usuario/contraseña SSH para escaneo → credenciales se descartan → en Terminal repites los datos.
  • Ahora: Descubres dispositivo → ingresas credenciales → si SSH funciona, se guardan cifradas en DeviceProfile → al conectar desde Terminal, vienen pre-rellenadas y el usuario solo clickea “Conectar”.

Flujo técnico

1. Persistencia en Auto-Provision (provision_stages.py)

Cuando provision_device() ejecuta discover_ssh() y el SSH tiene éxito:

  • Se capturan ssh_username y ssh_password (string en claro, en tránsito).
  • Antes de guardar en BD, se cifran con Fernet vía CredentialManager.encrypt_credential().
  • Se almacenan como ssh_password_encrypted en el nuevo campo de DeviceProfile.
  • Regla importante: un re-discovery sin credenciales SSH (p.ej., solo SNMP) no borra las ya guardadas. Solo actualiza si hay nuevas credenciales válidas.

2. Nuevo endpoint de descifrado (GET /auto-provision/profiles/{profile_id}/credentials)

  • Ruta: /api/network/auto-provision/profiles/{profile_id}/credentials
  • Permisos: require_perm(network, edit) + validación de organización (RLS).
  • Respuesta:
    {
      "has_credentials": true,
      "ip": "10.5.5.5",
      "username": "admin",
      "password": "contraseña_descifrada",
      "port": 22
    }
  • Si no hay credenciales: devuelve has_credentials: false + campos vacíos (no inventa datos).

3. Integración en Terminal UI (terminal.js)

La función quickConnectHost() ahora es async:

async quickConnectHost(ip, name, profileId, hasStored) {
  // ... pre-rellena IP y puerto ...
  if (hasStored && profileId) {
    const creds = await ApiService.get(`/api/network/auto-provision/profiles/${profileId}/credentials`);
    if (creds.has_credentials) {
      document.getElementById('manual-ssh-username').value = creds.username;
      document.getElementById('manual-ssh-password').value = creds.password;
    }
  }
  this.openManualConnectionModal();
}

4. Contexto en la plantilla (templates/terminal/index.html)

Los dispositivos detectados ahora exponen:

  • profile_id: ID del DeviceProfile para llamar al endpoint.
  • has_stored_ssh: propiedad @property de DeviceProfile que verifica ssh_username + ssh_password_encrypted.

Se actualiza el atributo data-args de los elementos:

<li data-action="TerminalApp.quickConnectHost" 
    data-args='["10.5.5.5", "Router-A", 123, true]'>

Cifrado de credenciales

Las credenciales se cifran con la misma estrategia que el management_config de racks:

  • Algoritmo: Fernet (AES-128 simétrica).
  • Almacén de claves: core.security.CredentialManager.
  • Descifrado: bajo demanda via profile.get_ssh_password() (solo si existe ssh_password_encrypted).

Seguridad:

  • Las claves en la BD están siempre cifradas.
  • Solo se descifran en respuesta a una petición gateada por require_perm(network, edit).
  • El usuario debe tener permiso de edición en Network para acceder a las credenciales en claro.

Migración de base de datos

Migración aplicada: network/0047 (2026-06-08 10:52).

migrations.AddField(model_name='deviceprofile', name='ssh_username', ...)
migrations.AddField(model_name='deviceprofile', name='ssh_password_encrypted', ...)

Retrocompatibilidad: Los dispositivos ya existentes no heredan automáticamente credenciales antiguas (nunca se guardaron). Para que uno existente tenga credenciales, hay que re-ejecutar Auto-Provision con las claves en ese dispositivo.

Testing

Test suite: tests/api/test_terminal_hosts.py::TestProfileSshCredentials:

  • test_roundtrip_decrypts(): valida cifrado/descifrado.
  • test_terminal_host_exposes_has_stored_ssh(): verifica que terminal_hosts() expone la bandera.
  • test_endpoint_returns_decrypted_creds(): valida respuesta del endpoint.
  • test_endpoint_no_creds(): sin credenciales → devuelve empty/false.
  • test_viewer_cannot_get_credentials(): RLS + permisos → viewer rechazado (403).

Impacto en UX

Para el usuario final:

  1. Descubre un dispositivo en Auto-Provision → ingresa credenciales SSH.
  2. Si SSH funciona, se guardan automáticamente.
  3. En Terminal, elige el dispositivo detectado → se pre-rellena usuario/contraseña → escribe comando.
  4. Ahorro: ~30 segundos por conexión si hay 10 dispositivos.

Titulares en RELEASE_NOTES (s114):

Ahora hereda las credenciales SSH que diste en Auto-Provision… Si el SSH funcionó durante el descubrimiento, esas credenciales se guardan cifradas. Al conectarte desde el Terminal a ese dispositivo, vienen ya rellenadas.

Véase también

  • [[entity—network—endpoint—get-profile-credentials]]
  • [[entity—network—model—device-profile]]
  • [[feature—terminal—detected-devices-support]]
  • [[concept—network—auto-provision]]
  • [[concept—saas—encryption-and-secrets]]