Volver a la wiki

Resumen

Modelo Django que representa un perfil de configuración para un equipo de red (switch, SAI, AP WiFi, firewall, etc.). Almacena credenciales de acceso (SNMP v2c/v3, SSH) y metadatos del dispositivo (vendor, modelo, interfaces, OIDs).

Cifrado en reposo (T1): Las credenciales SNMP (snmp_community, snmp_v3_auth_key, snmp_v3_priv_key) y SSH (ssh_password_encrypted) se cifran automáticamente en save() con Fernet vía CredentialManager. Nunca quedan en plaintext en la BD.

Estructura

Campos principales

CampoTipoDescripciónCifrado
organizationFK → OrganizationTenant (multi-tenancy RLS)No
ip_addressCharField(15)Dirección IP del equipoNo
hostnameCharField(255)Nombre FQDN o aliasNo
vendorCharField(100)Vendor (Cisco, Juniper, etc.)No
modelCharField(255)Modelo del dispositivoNo
serial_numberCharField(100)Número de serieNo
locationTextFieldUbicación físicaNo
supports_snmpBooleanField¿Responde SNMP?No
supports_sshBooleanField¿Permite SSH?No
supports_netconfBooleanField¿NETCONF disponible?No
supports_restconfBooleanField¿RESTCONF disponible?No
SNMP v2c/v3
snmp_versionCharField(10)“v2c” o “v3”No
snmp_communityTextFieldCommunity string SNMP v2cSÍ (Fernet)
snmp_v3_usernameCharField(100)USM usernameNo
snmp_v3_auth_protocolCharField(20)MD5, SHA, SHA256, etc.No
snmp_v3_auth_keyTextFieldAuth passphrase v3SÍ (Fernet)
snmp_v3_priv_protocolCharField(20)DES, AES128, AES256, etc.No
snmp_v3_priv_keyTextFieldPrivacy passphrase v3SÍ (Fernet)
SSH
ssh_portIntegerFieldPuerto SSH (default 22)No
ssh_usernameCharField(100)Usuario SSHNo
ssh_password_encryptedTextFieldContraseña SHA256 + saltSÍ (Fernet)
Metadatos SNMP
snmp_sys_descrTextFieldOID 1.3.6.1.2.1.1.1.0No
snmp_sys_object_idCharField(100)OID 1.3.6.1.2.1.1.2.0No
snmp_uptimeBigIntegerFieldUptime en centisegundosNo
last_verifiedDateTimeFieldÚltimo polling exitosoNo
linked_monitoring_target_idFK → MonitoringTargetTarget vinculado (observatory)No
created_atDateTimeFieldTimestamp creaciónNo
updated_atDateTimeFieldTimestamp actualizaciónNo

Accesores de descifrado (T1)

Properties que descifran las credenciales en el punto de uso. Devuelven plaintext (o vacío si token corrupto):

Constante: SNMP_SECRET_FIELDS = ("snmp_community", "snmp_v3_auth_key", "snmp_v3_priv_key") — lista de campos cifrados, reutilizada por reencrypt_credentials.

Métodos clave

save(*args, **kwargs)

Sobrescrito (T1). Antes de guardar:

for field in self.SNMP_SECRET_FIELDS:
    setattr(self, field, CredentialManager.encrypt_if_plain(getattr(self, field)))
super().save(*args, **kwargs)

Idempotente: valores ya cifrados (tokens Fernet) no se re-cifran. SSH password ya es cifrado por set_ssh_password().

update_last_verified()

Actualiza timestamp de último polling exitoso (usado por servicios de discovery).

Puntos de lectura (descifrado automático)

Las credenciales SNMP se descifran en estos puntos de uso exactos:

  1. SNMPService.from_config(): descifra community/v3 keys del dict config antes de pasar a pysnmp.
  2. DeviceDiscoveryService.build_snmp_auth_from_profile(): descifra credenciales del profile.
  3. Endpoints Deep-discover (network/api/discovery.py, monitoring/api/targets.py): usan snmp_community_plain, etc.
  4. WS consumer _send_targets(): descifra antes de serializar para el Agente.
  5. REST sentinel.get_agent_targets(): idem.
  6. Modal Config (network/api/assign.py): descifra para mostrar al usuario.
  7. Provisioning (monitoring/services/provisioning.py): extrae credenciales (cifradas) al construir MonitoringTarget.config.
  8. DeviceProfileOut resolver (graphql): descifra snmp_community en la respuesta (statu quo, deuda A4 sa1).

Relaciones

Migraciones (T1)

network/0049_alter_deviceprofile_snmp_community_and_more

Altera los 3 campos SNMP de CharField a TextField (el token Fernet es más largo que 255).

network/0050_encrypt_snmp_credentials

Cifra todos los DeviceProfile existentes en la BD.

Seguridad

Ejemplos de uso

Crear un profile SNMP v2c

from network.models import DeviceProfile

profile = DeviceProfile(
    organization=org,
    ip_address="10.0.1.5",
    hostname="core-switch-01",
    vendor="Cisco",
    supports_snmp=True,
    snmp_version="v2c",
    snmp_community="s3cr3t",  # Será cifrado en save()
)
profile.save()

# Leer en claro:
print(profile.snmp_community_plain)  # "s3cr3t"
print(profile.snmp_community)  # "gAAAAA..." (token Fernet)

Usar en un poller SNMP

from monitoring.services.snmp_service import SNMPService

snmp = SNMPService.from_config(
    ip=profile.ip_address,
    config=profile_config_dict,  # Dict con creds cifradas
)
# SNMPService.from_config descifra automáticamente
result = snmp.get("1.3.6.1.2.1.1.1.0")

Véase también

Subir