Guillermo Valencia - Blog 
Este sitio web utiliza JavaScript. Por favor, habilítalo en tu navegador para que todo funcione correctamente.


Pihole, bloquea publicidad y sitios maliciosos a nivel de DNS

Guillermo Valencia

Publicado el 31 de agosto de 2018

Tags: Linux

Pihole es un pequeño software encargado de bloquear publicidad, rastreadores de Internet y sitios maliciosos a nivel de red. Se trata de un servidor DNS ideal para ser utilizado en una red privada.

Requiere de una distribución Linux para poder ser ejecutado, incluso una simple Raspberry nos puede servir.

Pihole tiene la capacidad de bloquear todo el tráfico malicioso tanto en sitios tradicionales como PCs como en lugares no convencionales, como televisores inteligentes, consolas de videojuegos, dispositivos móviles y en definitiva cualquier aparato que tengamos en casa que se conecte a Internet.

Puedes descargar Pihole desde su página web: https://pi-hole.net/.

La instalación es muy sencilla, tan sólo es necesario ejecutar el siguiente comando desde la consola Linux.


curl -sSL https://install.pi-hole.net | bash

 

Una vez instalado, podemos acceder a su consola de configuración vía HTTP escribiendo la IP del dispositivo donde hemos instalado Pihole, por ejemplo: http://192.168.1.50

Pihole

 

En la consola de administración podemos configurar las listas de bloqueo de sitios maliciosos. También se pueden agregar manualmente nuestros propios dominios a bloquear.

Por ejemplo, yo tengo varias listas con bloqueos de servidores de tracking de marcas como Samsung, Xiaomi, LG, Google, Windows, etc… que uso habitualmente para proteger mi intimidad. Las tengo subidas en Github por si te interesan: https://github.com/enreda81/PiHoleBlockList

Para que todo funcione, debemos configurar cada dispositivo que tengamos en casa para que utilice como servidor DNS primario la IP donde tenemos montado Pihole (en mi ejemplo era la 192.168.1.50). Para que sea automático, lo ideal es modificar la configuración de nuestro router para que automaticamente asigne dicha IP como servidor DNS a todos nuestros dispositivos.



TDD como metodología de desarrollo y pruebas

Guillermo Valencia

Publicado el 07 de agosto de 2018

Tags: Agile, Pruebas

Desarrollo guiado por pruebas de software, o Test-driven development (TDD) es una práctica de ingeniería de software que implica escribir en primer lugar las pruebas antes que la propia funcionalidad, por lo que generalmente la prueba siempre debería fallar, si funciona puede ser por dos motivos:

  • La prueba está mal realizada o planteada.
  • La funcionalidad deseada ya existe.

Una vez implementada la funcionalidad, se deben ejecutar todas las pruebas unitarias existentes y refactorizar el código que haya sido agregado o modificado, lo que implica una eliminación de deuda técnica y constante mejora de la calidad del aplicativo.
TDD

 

Es decir, TDD está enfocado al desarrollo ágil, debe permitir un contacto directo con el usuario, identificando cuanto antes cualquier problema que pueda surgir de cada historia de usuario. En primer lugar, siempre se planteará la prueba antes de la codificación, es una manera de al menos recordarnos que una funcionalidad debe ser probada.

Con cada elemento implementado, será necesario repetir toda nuestra batería de pruebas, refactorizando el código de cara a nuestra mejora continua de la deuda técnica adquirida.
Adicionalmente, será necesario actualizar el diseño técnico (si procede), así como alimentar el catálogo de pruebas UAT y los manuales de usuario afectados.



Pruebas e Inversión de Control

Guillermo Valencia

Publicado el 02 de agosto de 2018

Tags: Patrones

Es habitual que tengamos la necesidad de integrar funcionalidad expuesta por componentes y servicios que están fuera del alcance del desarrollo que queremos probar. Las dependencias con sistemas externos afectan a la complejidad de la estrategia de pruebas, ya que es necesario contar con sustitutos de estos servicios externos durante el desarrollo.

Ejemplos típicos de estas dependencias son servicios Web, sistemas de envío de correo, fuentes u orígenes de datos, o simplemente, dispositivos hardware.

Estos sustitutos, muchas veces son exactamente iguales que el servicio original, pero en un entorno diferente al entorno de producción, es el caso de los entornos de desarrollo o de pruebas.

En otros casos es frecuente encontrarse con simuladores que exponen el mismo interfaz, pero realmente no realizan las mismas tareas que el sistema real, o las realizan contra un entorno controlado.

Para poder probar los escenarios más importantes, es necesario sincronizar el código de pruebas y el simulador de forma que desde la prueba se pueda saber a priori cómo será el comportamiento y así poder asegurar que el código a probar se ha ejecutado en las condiciones esperadas.
Simuladores

 

Este modelo de pruebas es muy conveniente para las pruebas de integración entre los diferentes módulos software de nuestra aplicación. Para ello es necesario que utilicemos interfaces que nos desacoplen de los sistemas externos, de manera que nuestros casos de uso del modelo de dominio trabajen directamente con las interfaces.

Para cada interfaz tendremos al menos dos implementaciones, una contra la fuente externa (o fuente real) y otra que la simule para nuestras pruebas.
Interfaces a implementaciones

 

¿Cómo elegiremos una u otra implementación dependiendo del entorno de ejecución?
Aquí nos surge un problema, y es que nuestra lógica de negocio debe ser capaz de instanciar tanto el objeto real como el objeto de pruebas, y decidir en cada caso cual debe utilizar.
Objetos reales o simulados

 

Y es aquí donde surge el término Inversión de control (IoC), que es el mecanismo de programación en el que el flujo de ejecución de un programa se invierte respecto a los métodos de programación tradicionales, en los que la interacción se expresa de forma imperativa haciendo llamadas a métodos o funciones. Tradicionalmente el programador especifica la secuencia de decisiones y procedimientos que pueden darse durante el ciclo de vida de un programa mediante llamadas a métodos, pero en su lugar, en la inversión de control se especifican respuestas deseadas a sucesos o solicitudes de datos concretas, dejando que algún tipo de entidad o arquitectura externa lleve a cabo las acciones de control que se requieran en el orden necesario y para el conjunto de sucesos que tengan que ocurrir.

Gracias a la orientación a objetos se puede definir un interfaz que cumplan tanto el objeto real como el objeto simulado, y hacer que el código a probar dependa tan sólo del interfaz que ambos implementan. Y se usará el patrón “Inversión de control” como mecanismo que permita elegir entre los dos tipos de objetos.

 

Dicho patrón puede ser implementado de diferentes formas. Las más comunes son:

  • Service Locator: en esta implementación, un servicio (Locator) es el encargado de saber qué instancia de un tipo concreto (interfaz) debe instanciar y retornar.
  • Service Locator

  • Dependency Injection: en esta implementación, existe un servicio (Builder) encargado de inyectar una implementación concreta de la interfaz al caso de uso. Esta implementación puede ser inyectada de varias maneras: por constructor, por propiedad o por parámetro.
  • Dependency Injection

Existen diferentes librerías que implementan el patrón “Inversión de Control”, como Microsoft Unity (https://msdn.microsoft.com/en-us/library/dn178463(v=pandp.30).aspx ) o StructureMap (http://structuremap.github.io/)



Auditoría de un Modelo de Datos

Guillermo Valencia

Publicado el 02 de julio de 2018

Tags: SQL Server

Me solicitaron realizar la auditoría del modelo de datos de una aplicación interna de la compañía, desarrollada en ASP.NET MVC (.Net Framework 4.6) con persistencia en BD SQL Server. En principio el tamaño de la aplicación era pequeño (apenas 100.000 líneas de código fuente en C#), cuando descubrí que la mayor parte de la capa de negocio de la aplicación estaba implementada en el propio modelo de datos, mediante el uso de procedimientos almacenados.
En concreto, descubrí más de 800 tablas y 1200 procedimientos almacenados. Aquello de pequeño tenía poco. Realmente el modelo de datos que me encontré era bastante inmantenible.
Para obtener el número de tablas, vistas y procedimientos almacenados utilicé la siguiente consulta:


SELECT 
    CASE TYPE 
        WHEN 'U' 
            THEN 'User Defined Tables' 
        WHEN 'P'
            THEN 'Stored Procedures'        
	 WHEN 'V'
            THEN 'Views'
    END, 
    COUNT(*)     
FROM SYS.OBJECTS
WHERE TYPE IN ('U', 'P', 'V')
AND Left(name, 3) NOT IN ('sp_', 'xp_', 'ms_')
GROUP BY TYPE

 

Otra cosa que me llamó la atención fue la gran cantidad de tablas que no disponían de clave primaria, más de 80 tablas.
Para ello utilicé la siguiente consulta.


SELECT SCHEMA_NAME(sys.tables.schema_id) + '.' + OBJECT_NAME(sys.tables.object_id) AS TableName
FROM sys.tables
WHERE objectproperty(OBJECT_ID,'TableHasPrimaryKey') = 0

 

También quise echarle un ojo a las claves foráneas, la mayor parte de ellas no tenían un índice asociado, lo que repercutía negativamente en el rendimiento a la hora de realizar consultas.
Usé la siguiente consulta:


SELECT SCHEMA_NAME(ST.schema_id) + '.' + OBJECT_NAME(ST.object_id) AS TableName 
, string_agg(Keys.Name, ', ') AS NombleForeignKey
FROM sys.foreign_keys keys
INNER JOIN sys.foreign_key_columns TheColumns
ON Keys.Object_ID=constraint_object_id  
INNER JOIN sys.tables ST
ON ST.object_id = keys.parent_Object_ID
LEFT OUTER JOIN sys.index_columns ic
ON ic.object_ID=TheColumns.parent_Object_Id
AND ic.column_ID=TheColumns.parent_Column_Id
AND TheColumns.constraint_column_ID=ic.key_ordinal
WHERE ic.object_ID IS NULL
group by SCHEMA_NAME(ST.schema_id) + '.' + OBJECT_NAME(ST.object_id)
ORDER BY TableName

 

Más tarde me pidieron la optimización de varios planes de ejecución de consultas y procedimientos almacenados que habían observado que tenían graves problemas de rendimiento. Aparte del propio SQL Server Management Studio, utilicé una herramienta gratuita que recomiendo y que me fue de gran ayuda. Directamente la herramienta te propone aquellos índices necesarios para mejorar los tiempos de respuesta de las consultas. Le herramienta se llama SentryOne Plan Explorer, y puede ser descargada gratuitamente desde su página web: https://www.sentryone.com/plan-explorer



El Patrón CQRS

Guillermo Valencia

Publicado el 12 de marzo de 2018

Tags: Patrones

CQRS, del inglés “Command Query Responsibility Segregation” es un patrón arquitectónico que recomienda codificar los comandos y las consultas de forma separada, es decir, separar las consultas que no modifican el estado del sistema en una pila diferente y optimizada de los comandos que si modifican el estado del sistema.

CQRS puede ser implementado igualmente con Entity Framework, pero limitando las operaciones de cambio para que no tengamos la tentación de utilizarlas desde donde no debamos, saltándonos todas las reglas de negocio y directrices arquitectónicas.
Para ello, podemos utilizar un simple contenedor que encapsule la instancia del DBContext de EF y que solo exponga las operaciones de sólo lectura. Se puede ver un ejemplo en el siguiente enlace: https://msdn.microsoft.com/es-es/magazine/mt683793.aspx

La principal ventaja de CQRS es que eliminamos complejidad en el diseño del sistema, no es incompatible con DDD, sino que se trata de un patrón adicional a implementar dentro de DDD, haciendo que el dominio se centre en el modelo que modifica el estado del sistema, simplificando y optimizando la lógica encargado de las operaciones de lectura, generando un código más simple.

Aplicar CQRS no quiere decir que desde la capa de presentación instanciemos accedamos directamente a la capa de persistencia de datos, sino que utilizaremos un “microservicio” ubicado en la capa de Infraestructura que nos dará acceso a las operaciones de lectura de datos.

Patrón CQRS