Usar NSEC3 en vez de NSEC en un dominio DNSSEC con gestión de DNS dinámico

En la configuración que describo en Activación de DNSSEC en BIND con DNS dinámico, el servidor DNS BIND genera registros NSEC en vez de NSEC3. ¿Y qué? ¿Por qué importa eso?.Lo explico clarito (creo) en Por qué es conveniente usar NSEC3 en vez de NSEC en un dominio DNSSEC.

Con todo esto ya entendemos por qué es conveniente mejorar la receta descrita en Activación de DNSSEC en BIND con DNS dinámico y pasar a usar NSEC3.

Nada más simple:

$ rndc signing -nsec3param 1 0 10 auto example.org

El primer parámetro es el hash a emplear (en este caso SHA1). El cero indica los flags a aplicar que, en este caso, no nos interesan. El 10 es el número de iteraciones. La sal en sí es el siguiente campo. Si indicamos auto, el programa proporcionará 64 bits aleatorios. Personalmente eso me parece poco, prefiero 160 bits, aunque eso incrementará el tamaño de la respuesta DNS:

$ python3 -c "import secrets; print(secrets.token_hex(40))"
611a1cf6aa1b891b4909ed9d5ebe9d11b452368b642ca7a73b4e96a7ae54639a589c4bef8afc651d
$ rndc signing -nsec3param 1 0 10 611a1cf6aa1b891b4909ed9d5ebe9d11b452368b642ca7a73b4e96a7ae54639a589c4bef8afc651d example.org

A partir de este momento la zona usará NSEC3. Veamos el mismo ejemplo propuesto en Activación de DNSSEC en BIND con DNS dinámico:

51PKTFM618079E60H7F0Q6IF0VT6P0D1.z.bt.jcea.es. 30 IN NSEC3 1 0 10 673A6224876B0486 OEPHDNT06U7U9UURA3UAPV240OVO88SP A RRSIG
51PKTFM618079E60H7F0Q6IF0VT6P0D1.z.bt.jcea.es. 30 IN RRSIG NSEC3 8 5 30 20170620071663 ...

Como vemos, la respuesta es similar al caso NSEC, pero ahora la cadena indica el rango de hashes cubierto por la respuesta NSEC3.

Si por algún motivo queremos volver a usar NSEC en la zona, haremos:

$ rndc-signing -nsec3param none example.org

Mantenimiento

El valor de la sal hay que cambiarla de vez en cuando, tal y como se describe en Activación de DNSSEC en BIND con DNS dinámico.

Por qué es conveniente usar NSEC3 en vez de NSEC en un dominio DNSSEC

Para empezar con los principios básicos, puedes leer Introducción a DNSSEC.

Gracias a DNSSEC un atacante malicioso no podrá manipular un registro DNS porque su firma digital RRSIG no puede ser falsificada. Pero, ¿puede falsificar la no existencia de un registro? Es decir, ¿puede negar falsamente que un registro DNS dado no exista?.

La respuesta es que no. Cuando DNSSEC gestiona una zona, añade multitud de registros propios. Los más evidentes son las firmas digitales RRSIG, pero hay más. Por ejemplo, los registros NSEC.

Los registros NSEC proporcionan authenticated denial of existence. Es decir, una prueba criptográfica de que un registro DNS no existe.

Su funcionamiento se describe en el RFC 4034 y el RFC 4035. Simplificando mucho, DNSSEC ordena todos los registros de la zona de forma canónica y genera registros NSEC para cubrir los huecos.

Por ejemplo, si una zona tiene los registros registro1 y registro3, pero NO registro2, una petición de DNS de registro2 se responderá indicando que el registro no existe y, además, aportará una prueba criptográfica de ello. Dicha prueba consiste en el registro NSEC asociado al nombre registro1 en el campo authority de la respuesta DNS, indicando que el siguiente nombre de la zona es registro3.

Leer más…

Activación de DNSSEC en BIND con DNS dinámico

Aunque DNSSEC como protocolo lleva mucho tiempo entre nosotros, lo cierto es que el soporte software ha mejorado muy lentamente. Por fortuna, el progreso ha sido paulatino pero constante.

Las versiones modernas de BIND (la versión 9.10 de BIND se publicó en la primavera de 2014), uno de los servidores DNS más populares, disponen ahora de gestión completa y automática de DNSSEC para el caso de zonas DNS gestionadas mediante DNS dinámico.

Si estás verde en el tema DNSSEC, te recomendaría que leyeses primero mi artículo Introducción a DNSSEC.

Para el caso de zonas gestionadas mediante DNS dinámico, BIND funciona de la siguiente manera:

  • Cualquier cambio en la zona se almacena en una pequeña base de datos binaria no documentada, gestionada por BIND de forma interna y automática.

  • Esa pequeña base de datos se vuelca a texto cada cierto tiempo, típicamente cada diez minutos. Ese documento de texto tiene el formato habitual de una zona DNS normal y corriente.

    En caso de corrupción de la base de datos de la zona, BIND puede utilizar el backup de texto para reconstruirla.

    Podemos incluso hacer ediciones manuales de la zona simplemente parando BIND, borrando la base de datos de la zona y editando su fichero de backup en texto. Cuando lancemos BIND otra vez, la base de datos se reconstruirá a partir del texto.

Que la gestión se realice de esta manera posibilita que BIND efectúe actualizaciones en la zona. Esto es necesario por la propia gestión de DNS dinámico (crear, borrar y modificar registros), pero la misma flexibilidad permite que BIND automatice la gestión DNSSEC de la zona. BIND lo hace todo, incluyendo actualizar el número de serie de la zona para sincronizar los servidores de DNS secundarios.

Leer más…

Sincronización de sonido cuando la tasa de audio es irregular

Entradas anteriores sobre este tema:

Hoy me he encontrado con un caso nuevo: tasa de audio irregular con huecos en las marcas de tiempo.

La fuente original en este caso está montada a base de trozos de audio, y entre unos audios y otros hay huecos. Dichos huecos no son trozos de audio en silencio, sino que ni siquiera existen tramas de audio durante esos segundos. Es decir, revisando las marcas temporales de las tramas de audio, vemos que hay huecos.

¿Qué ocurre cuando un reproductor se encuentra con esos huecos?. Pues que simplemente meterá silencio. Bien.

El problema surge cuando exportamos una pista de sonido con huecos en las marcas temporales a un formato que no incluye marcas temporales o bien que las normaliza. El efecto es que esos huecos desaparecen. Desaparecen esas pausas.

Leer más…

Elimina los mensajes duplicados en tu IMAP4 (II)

En Elimina los mensajes duplicados en tu IMAP4 presento un pequeño programa Python que busca y elimina los correos electrónicos duplicados en nuestros buzones IMAP4. Os remito a dicho artículo para entender por qué necesito todo esto.

Para ello, el programa analiza el contenido de todas las carpetas IMAP4, extrae las cabeceras de todos los mensajes y los compara buscando duplicados.

Este proceso es simple, pero lento en tiempo y costoso para el servidor IMAP4, ya que debe analizar todos y cada uno de los mensajes del sistema. En configuraciones de correo como la mía, con cientos de buzones, millones de mensajes y docenas de gigabytes de contenido, estos defectos se notan.

En esta ocasión presento un segundo programa Python para realizar la misma tarea de manera diferente:

Leer más…

Elimina los mensajes duplicados en tu IMAP4

Como ya se ha explicado en el pasado, utilizo getmail para alimentar el servidor IMAP4 que tengo en mi propio portátil.

Getmail tiene varios problemas. Por ejemplo, guarda la lista de mensajes procesados solo cuando la descarga IMAP4 se completa con normalidad. Si ocurre cualquier problema, el proceso volverá a empezar desde el principio. Dado mi volumen de correo, tener el portátil apagado una semana supone una descarga de correo de varias horas, y si la conexión se corta durante el proceso, getmail volverá a empezar.

Esto ocasiona retrasos y molestias innecesarias, pero el mayor problema es que tendré mensajes duplicados. O triplicados. O cuadruplicados.

Aunque se trata de un problema ocasional, invita a errores: da lugar a una lista inmensa de mensajes a revisar, o puedes borrar un mensaje respondido y quedarte con una copia que no tendrá la marca de contestado.

He escrito el siguiente programa para hacer frente a este problema:

Leer más…

Determinar automáticamente el desfase de audio en un fichero MKV (III)

Entradas anteriores sobre este tema:

Hoy me he encontrado con un caso nuevo: tasa de audio irregular:

$ ffmpeg -i FUENTE.mkv -af ashowinfo -map 0:2 -y -f alaw -frames:a 5 /dev/null
[Parsed_ashowinfo_0 @ 48dde0] n:0 pts:0 pts_time:0 pos:22428 fmt:s16p channels:2 chlayout:stereo rate:48000 nb_samples:1152 checksum:A45225DC plane_checksums: [ B6F411EF 7CE013ED ]
[Parsed_ashowinfo_0 @ 48dde0] n:1 pts:110592 pts_time:2.304 pos:94534 fmt:s16p channels:2 chlayout:stereo rate:48000 nb_samples:1152 checksum:7497E13F plane_checksums: [ 1D98739E 8C006DA1 ]
[Parsed_ashowinfo_0 @ 48dde0] n:2 pts:111744 pts_time:2.328 pos:94918 fmt:s16p channels:2 chlayout:stereo rate:48000 nb_samples:1152 checksum:214BB177 plane_checksums: [ 63345BBA 03B155BD ]
[Parsed_ashowinfo_0 @ 48dde0] n:3 pts:112896 pts_time:2.352 pos:96214 fmt:s16p channels:2 chlayout:stereo rate:48000 nb_samples:1152 checksum:9DE0FD2F plane_checksums: [ D6BB799A 1CFC8395 ]
[Parsed_ashowinfo_0 @ 48dde0] n:4 pts:114048 pts_time:2.376 pos:96598 fmt:s16p channels:2 chlayout:stereo rate:48000 nb_samples:1152 checksum:0499F32E plane_checksums: [ D707739B 7D847F93 ]
size=      11kB time=00:00:02.40 bitrate=  38.4kbits/s

Aquí vemos que el primer frame de audio está bien, pero el segundo se reproduce a los 2.304 segundos. Hay más de dos segundos de silencio sin audio en el archivo. Vemos también que hay un frame de audio cada 24 milisegundos. El ajuste de audio que hay que aplicar es, por tanto, de 2.280 milisegundos:

$ mkvmerge -o DESTINO.mkv -y 1:2280 -y 2:2280 FUENTE.mkv

No deja de sorprenderme la cantidad de cosas extrañas que uno se encuentra por ahí...

Addendum: Cómo acceder al "code object" de una clase Python

En Cómo acceder al "code object" de una clase Python explico cómo acceder al code object de una clase Python y comprobar así que estamos deserializando datos con la versión correcta del código.

Dejando al margen la conveniencia o no de recurrir a estos chanchullos, hay un problema grave para mi caso de uso: el code object de una clase Python cambia no solo cuando cambia el código fuente de la clase, sino también cuando cambia su entorno.

Veamos un ejemplo:

 #!/usr/bin/env python3

 import dis, marshal, hashlib

 def wrapper():
     class prueba:
         pass

 code = wrapper.__code__
 dis.dis(code)
 code_dump = marshal.dumps(code)
 print()
 print(hashlib.sha256(code_dump).hexdigest())

Si ejecutamos el código varias veces, siempre obtendremos el mismo resultado:

6             0 LOAD_BUILD_CLASS
              2 LOAD_CONST               1 (<code object prueba at 0x7f7dcba98930, file "./z.py", line 6>)
              4 LOAD_CONST               2 ('prueba')
              6 MAKE_FUNCTION            0
              8 LOAD_CONST               2 ('prueba')
             10 CALL_FUNCTION            2
             12 STORE_FAST               0 (prueba)
             14 LOAD_CONST               0 (None)
             16 RETURN_VALUE

d2945184e581612f4f30458a3e70afa92382356e83422c9c8f95bc28fd83fd8c

Aquí vemos el contenido de la función y el hash del code object.

Observemos qué ocurre si hacemos un cambio mínimo en el programa. Añadamos simplemente una línea en blanco antes de la función que nos interesa:

 #!/usr/bin/env python3


 import dis, marshal, hashlib

 def wrapper():
     class prueba:
         pass

 code = wrapper.__code__
 dis.dis(code)
 code_dump = marshal.dumps(code)
 print()
 print(hashlib.sha256(code_dump).hexdigest())

Hemos añadido una línea en blanco antes de importar los módulos. Ejecutar este código produce lo siguiente:

7             0 LOAD_BUILD_CLASS
              2 LOAD_CONST               1 (<code object prueba at 0x7fe54e688930, file "./z.py", line 7>)
              4 LOAD_CONST               2 ('prueba')
              6 MAKE_FUNCTION            0
              8 LOAD_CONST               2 ('prueba')
             10 CALL_FUNCTION            2
             12 STORE_FAST               0 (prueba)
             14 LOAD_CONST               0 (None)
             16 RETURN_VALUE

e0298eb04b1d22a786bbac588204830ecec87310294c1308d3dbde820fa90a03

La función es idéntica, pero su hash ha cambiado.

El problema fundamental es que un dato incluído en el code object es el número de línea donde empieza la función:

>>> import z
>>> z.wrapper.__code__
>>> dir(z.wrapper.__code__)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_kwonlyargcount', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
>>> z.wrapper.__code__.co_firstlineno
6

Por tanto, el code object puede variar no solo porque hemos modificado el código fuente relevante, sino también porque hemos cambiado la posición relativa del código en el fichero.

Dada esta situación y mi caso de uso, he movido los objetos relevantes a su propio fichero de código fuente. De esta forma evito que cambios periféricos alteren el hash de lo que me interesa.

Determinar automáticamente el desfase de audio en un fichero MKV (II)

En Determinar automáticamente el desfase de audio en un fichero MKV explico cómo determinar automáticamente el desajuste de audio en un archivo multimedia cuando el audio está desfasado. ¿Qué ocurre cuando lo que está desfasado es el vídeo?

En Sincronización de sonido cuando la tasa de refresco de vídeo es irregular explico cómo extraer las marcas temporales de cada fotograma del archivo. Si el desajuste es constante a lo largo de todo el fichero multimedia, lo que nos interesa es conocer el desajuste de los primeros fotogramas. Para ello podemos hacer algo como:

$ ffmpeg -i FUENTE.mkv -vf "showinfo" -map 0:0 -y -f mjpeg -frames:v 10 /dev/null

Leer más…