• No results found

Coordinate Space

In document pylinac Documentation (Page 94-101)

Debido a que desde el Frontend no se tiene acceso al Origin Server (por motivos de seguridad explicados en la sección anterior) se debió crear un proceso que dada una indicación desde el Frontend, se ejecute la acción correspondiente en el Origin Server.

Como se vio en la sección 7.5.1 (acerca de borrar y subir archivos a la platafor- ma), al intentar borrar un video se ejecutará el script de python borrarArchivo.py dentro del Backend. Esto tendrá como objetivo eliminar todo el contenido asociado a ese video.

Para realizar esta acción se utiliza, al igual que se hizo para subir videos, el cliente SFTP de la librería Paramiko de Python. A través de ese cliente, el Backend se conectará al Origin Server y ejecutará el comando remove sobre el contenido a borrar.

Server. Por otro lado, si el formato de video es HLS, se deberá borrar la Master- List junto con todos los archivos asociados (MasterList de cada calidad y chunk correspondientes).

Se puede observar el script utilizado a continuación. Notar la función recursiva (función rm línea 6) necesaria para poder eliminar el video HLS en su totalidad.

Código 7.3: borrarArchivo.py i m p o r t p a r a m i k o i m p o r t sys , os # F u n c i o n r e c u r s i v a para b o r r a r un d i r e c t o r i o j u n t o con sus a r c h i v o s def rm ( path ): f i l e s = sftp . l i s t d i r ( path ) for f in f i l e s :

f i l e p a t h = os . path . join ( path , f ) try : sftp . r e m o v e ( f i l e p a t h ) e x c e p t I O E r r o r : rm ( f i l e p a t h ) sftp . r m d i r ( path ) ### Tomo v i d e o como p a r a m e t r o v i d e o = str ( sys . argv [ 1 ] ) . s p l i t ( " . " ) r u t a D e s t i n o = str ( sys . argv [2]) c l i e n t e = str ( sys . argv [3]) e x t e n s i o n = str ( sys . argv [4]) ## C l i e n t e P a r a m i k o s s h _ c l i e n t = p a r a m i k o . S S H C l i e n t () s s h _ c l i e n t . s e t _ m i s s i n g _ h o s t _ k e y _ p o l i c y ( p a r a m i k o . A u t o A d d P o l i c y ()) ## C o n e c t o c l i e n t e s s h _ c l i e n t . c o n n e c t ( h o s t n a m e =" o r i g i n . vcdn " , port =22 , user name =" root " , k e y _ f i l e n a m e ="/ home / c e n t o s / K e y P a i r . pem ") # Crea un o b j e t o S F T P C l i e n t ()

sftp = s s h _ c l i e n t . o p e n _ s f t p () # Se pasa el v i d e o

sftp . r e m o v e ( r u t a D e s t i n o + v i d e o [ 0 ] + " _ "+ c l i e n t e + " . " + e x t e n s i o n )

# Si es HLS , hay que b o r r a r la c a r p e t a con los . ts if e x t e n s i o n == " m3u8 ":

rm ( r u t a D e s t i n o + v i d e o [ 0 ] + " _ "+ c l i e n t e ) # Se c i e r r a n los c l i e n t e s

sftp . c l o s e ()

Capítulo 8

Seguridad

Todo sistema necesita tener su cuota de seguridad, en función de la criticidad que implicaría ser vulnerado. En el caso de una CDN, donde el contenido es el activo clave y lo que tiene valor para el usuario, obviamente se debe tener una protección muy importante, lo que también tiene mucho valor para los clientes. A pesar de que la seguridad no forma parte del foco de este proyecto, sin dudas era un tema a considerar durante el desarrollo del mismo y un objetivo si se piensa en tener un sistema completo.

Para tener al menos una protección básica, se debieron considerar las princi- pales vías de acceso al contenido: el Frontend que está en una zona de seguridad crítica y justamente está diseñado para servir el contenido de forma “legítima”, y por otro lado toda la red privada de backend, que es donde se almacena todo el contenido y deben autenticarse todas las peticiones recibidas.

Para el primer punto se debe asegurar que ningún tipo de usuario o atacante sea capaz de ingresar al sitio web y pueda ver el contenido sin estar autentica- do. En el segundo caso ya es más complejo y orientado a ataques a más “bajo nivel”, como acceder a la red privada o simplemente hacer peticiones a un Edge Server haciéndose pasar por un usuario autenticado. En las siguientes secciones se detallarán las soluciones implementadas para hacer frente a estos puntos.

8.1.

Frontend

Para la seguridad del frontend se decidió utilizar el sistema de autenticación y autorización propio de Django. Este permite verificar credenciales de usuario y definir que acciones puede realizar cada uno.

Django incluye modelos de autenticación tanto para usuarios como para grupos de usuarios. Para este proyecto se utilizó el modelo de roles como forma de manejar las funcionalidades habilitadas para cada uno, debido a que existen tres roles bien diferenciados: Administrador, Cliente y Usuario.

LoginInView y LoginOutView

Al implementar este modelo de autenticación, se logró tener un control ade- cuado según el rol con el que se ingresa al sistema, en el cual se solicita usuario y clave, y se redirige a su panel correspondiente.

Si un usuario no está logeado, o sí lo está pero intenta acceder a determinada funcionalidad ajena a su perfil, el sistema lo rechazará. De esta forma al mane- jar sesiones autenticadas, se puede evitar que usuarios no registrados, o que por ejemplo no hubiesen pago la cuota de su suscripción, puedan acceder al contenido.

8.2.

Backend

Para enfrentar posibles ataques a la red interna o accesos ilícitos al contenido se debió hacer énfasis en diseñar una arquitectura de red en donde el contenido esté alojado en la parte más segura, y que además no exista forma de obtener el contenido directamente desde un Edge, como lo hace un usuario legítimo, pero sin estar autenticado.

8.2.1.

Red interna

Durante la sección 3.2 se explicó en detalle la arquitectura de red y como se podrían mapear al modelo de zonas de seguridad. En esta sección se tratarán los puntos que fueron considerados para esa definición.

En primer lugar se buscó que el Origin Server, en donde está alojado todo el contenido, esté ubicado únicamente en la red privada, siendo esta la zona de más seguridad. Para esto se debió tener en consideración que a este servidor se debía subir el contenido proveniente del Frontend. No parecía seguro tener al Frontend y al Origin Server compartiendo una misma red interna, por lo que se decidió crear una red intermedia (ver fig. 8.1) y hacer pasar el flujo de subida de archivos a través del servidor Backend. De esta forma el Frontend sube los archivos al Backend mediante esa red intermedia, y éste que sí comparte una red con el Origin Server puede depositar los archivos, ahí si ya dentro de una red más segura. También se pasaron a través del Backend otras funcionalidades que tenían un flujo directo desde la red externa a la red privada, como la modificación de los tiempos de caché (visto en la sección 6.5 Modificación Caché) o la función para clientes de borrar videos (sección 7.5.1).

Además, los Edge Server debían poder acceder al contenido alojado en el Origin Server, y a su vez ser accesibles desde la red pública. Para mantener la separación se utilizó el balanceador de carga, asignándole una IP pública al mismo pero siendo sus miembros de balanceo solamente pertenecientes a la red privada y con el puerto específico para servir.

Figura 8.1: Red interna

Por otro lado, para agregar otra capa de seguridad a nivel de capa 3 y capa 4, se utilizaron los Grupos de seguridad, una función provista por OpenStack que permite hacer filtros de firewalling al nivel de las capas mencionadas. Se diseñó un perfil para cada VM (ver figura 8.2) en función de los servicios y puertos que debía disponibilizar, desde donde ser accesible, y hacia donde debía acceder.

Figura 8.2: Grupos de seguridad creados

A modo de ejemplo en la figura 8.3 se puede ver los filtros aplicados para el servidor DNS. Éste debe servir todas las peticiones DNS provenientes de cualquier origen, además de tener reglas de egreso a cualquier destino. Además se habilitó el ping y el acceso por SSH desde la red de mantenimiento.

8.2.2.

Acceso al contenido

Para cualquier persona que tenga conocimientos en el área, le sería fácil poder entender las peticiones que realiza el navegador para obtener un video y ser capaz de replicarlas. De esta forma, alguien así podría tener acceso a toda la plataforma de forma gratuita, compartir esta información, o incluso tendría la posibilidad de por ejemplo servir el mismo contenido en su propio sitio web.

Esto sin dudas sería muy negativo para la plataforma, por lo cual se tuvo que hacer especial énfasis en impedirlo. Esta fue quizás la parte más importante implementada en conceptos de seguridad, al menos lo fue en cuanto a tiempo de desarrollo e investigación dedicada.

En primer lugar se planteó cuál sería la forma para considerar que un usuario está autorizado para reproducir un video dado. La situación elegida fue que el usuario estará autorizado cuando tenga su sesión abierta (habiendo previamente ingresado sus credenciales de acceso) y elija un video a reproducir. En cualquier otro caso no se debería autorizar una petición de contenido.

Para lograr esto, se agregó un atributo al objeto Usuario llamado token, que cumpliría justamente la función que su nombre indica, ser un código que permita la autorización a una petición dada. De esta forma, cada vez que un usuario selecciona para ver un video, se le asignará un token aleatorio de 128 caracteres, el cual automáticamente se borrará cuando el usuario finalice la sesión en el sitio web. Este código deberá ser adjuntado a cada petición que forme parte de la reproducción del video.

Figura 8.4: Creación y borrado de token

Por otro lado, los Edge Server, que son quienes reciben las peticiones del con- tenido, deben tener la capacidad de decidir si autorizar o rechazar cada una de ellas. Para esto, se utilizó el plugin de ATS llamado authproxy.so[52] (mencionado en la sección 6.2.3 sobre ATS), que es capaz de redirigir las peticiones recibidas a un servidor de autenticación. Su funcionamiento es simple: cuando un Edge Server con este plugin activado recibe una petición, redirige la misma al servicio de au- tenticación que se indique; si este último responde con el código HTTP 200 OK, el Edge Server dejará pasar la petición y devolverá el contenido (ver fig 8.5). En caso de que el servidor de autenticación responda con cualquier otro código HTTP, la petición será rechazada.

Figura 8.5: Funcionamiento AuthProxy Plugin

El siguiente paso fue hacer un servidor de autenticación, que a su vez debía consumir la base de datos de usuarios de Django, ya que el par usuario-token estaba allí registrado. Se decidió entonces crear un nuevo sitio o ruta dentro de la plataforma web: la URL http://frontend.vcdn:8088/proxyAuthentication sería un servicio que responda 200 OK, en caso de que el usuario y token incluidos en el GET estén en la base de datos de Django, y 401 Unauthorized en cualquier otro caso.

Tanto para el envío de los parámetros usuario-token por parte de la petición, como para la verificación de los mismos por parte de este nuevo servicio, se debió decidir qué método de autenticación utilizar. Se analizó la posibilidad de tres for- mas distintas: autenticación básica de HTTP, enviar los parámetros como query strings dentro de la URL, o utilizar header HTTP. Por su simplicidad de imple- mentación y ser un caso de seguridad considerada intermedia entre las otras dos, se decidió enviar los parámetros en la URL (autenticación básica es considerada insegura, mientras que el uso de los header es de los más frecuentes, pero agre- ga alguna complejidad). La implementación consistió en agregar a la URL de la ubicación del video, los parámetros del propio usuario conectado, de la siguiente forma:

http://frontend.vcdn:8088/media/video1?user=Usuario1&token=12345

El funcionamiento completo de autenticación fue probado con la herramienta de software libre Postman, y luego de haberlo validado, reproduciendo videos MP4 desde el Frontend también correctamente. Al probar con el contenido HLS, se notificó que éste no podía reproducirse correctamente: al hacer la petición de la

parámetros ya no se agregaban a la URL. Luego de investigar posibles soluciones, no se pudo sortear el problema, por lo que se decidió pasar a utilizar header HTTP como método para enviar y leer los parámetros de autenticación

VideoJS no permitía asignar ningún header en la petición, pero se encontró la herramienta XHook [53], la cual era capaz de interceptar las peticiones XHR (XML Http Request) y, por ejemplo, agregarle un header a la misma. Se decidió entonces enviar dentro de “X_USER” y “X_TOKEN” los parámetros correspon- dientes. Nuevamente el funcionamiento fue comprobado primero con Postman y luego con el reproductor, y se pudo reproducir HLS perfectamente.

Lamentablemente al volver probar el uso de MP4 se encontró que este había dejado de funcionar. Luego de realizar pruebas se detectó que no se estaban en- viando los header indicados, debido a que las peticiones al reproducir MP4 eran de tipo “media” y no “xhr”, por lo que no eran interceptadas por XHook. Al investigar en profundidad, se llegó a la conclusión de que VideoJS no soportaba asignación de header en la reproducción de videos MP4, esto fue confirmado en un issue de VideoJS en GitHub [54] por Gary Katsevman, segundo mayor contribuidor de este reproductor [55].

Se tenía entonces que con parámetros en la URL sólo funcionaba MP4, mien- tras que incluyendo header sólo lo hacía HLS. Rápidamente se decidió hacer un reproductor VideoJS que en caso de estar reproduciendo un video MP4 envíe pa- rámetros en la URL, y si utilizaba un video HLS no asignase parámetros en la URL y los header fuesen asignados por XHook. El flujo de trabajo del reproductor quedó de la siguiente forma:

Figura 8.6: Reproductor con envío de datos para autenticación

También fue necesario cambiar el funcionamiento de la de verificación en el otro extremo (el “proxyAuthentication”), ya que luego de las modificaciones, debía contemplar ambos tipos de autenticación (ver fig 8.7).

Figura 8.7: Flujo de trabajo del Proxy Autentication desarrollado

Gracias a esto se completó el sistema de peticiones autenticadas para todo el contenido, pero la solución implementada trajo consigo dos inconvenientes a resolver. Por un lado, durante la etapa de pruebas se detectó que el contenido de algunas peticiones no estaba siendo enviado directamente desde caché. Por otra parte, al enviar algún header personalizado, el navegador estaba activando el método de seguridad de Intercambio de Recursos de Origen Cruzado [56] conocido como CORS (Cross-origin resource sharing), el cual por su funcionamiento genera un comportamiento no deseado.

Con respecto al primer problema, se hizo una investigación de porqué el con- tenido no estaba siendo respondido desde caché, ya que se veía claramente que el contenido sí estaba siendo almacenado allí. Luego de analizar en profundidad, se pudo deducir que al agregar algún header y parámetros para autenticación, ATS guardaba esa información como parte de la petición mantenida en caché. Esto significa que los únicos casos en los que las peticiones serían respondidas desde caché iban a ser las que pidieran el mismo video y además tengan los mismos pa- rámetros/header. Este caso no se cumpliría nunca ya que ningún usuario distinto comparte el par usuario-token.

Fue necesario entonces utilizar herramientas para que a la hora de almacenar una petición en caché, no se considerasen ni los header ni los parámetros que vi-

nieran consigo. Se utilizaron los plugin de ATS cachekey.so y header_rewrite.so (también detallados en la sección 6.2.3), el primero cumple la función de no consi- derar los parámetros que van dentro de la URL a la hora de mantener en caché el contenido, mientras que el segundo hace lo propio con los header. De esta forma, en cada petición se ignoran los atributos de autenticación, y por lo tanto ya no son consideradas distintas dos peticiones con distintos pares usuario-token, por lo cual se es capaz de responder directo desde caché correctamente.

El otro inconveniente a solucionar es el causado por los efectos del CORS. Al agregar “X_USER” y “X_TOKEN” y además estar haciendo una petición a un origen distinto al del Frontend, en este caso apuntando a un Edge Server, el navegador hace que antes de enviar la petición del contenido con un GET se active el llamado prevuelo, mejor conocido como preflight de CORS. Con éste se solicita permiso para utilizar header personalizados, es decir, que no son los por defecto. Su funcionamiento es mostrado a continuación.

Figura 8.8: Funcionamiento CORS

El mismo consiste en enviar una petición OPTIONS indicando los métodos y header personalizados a utilizar, además del origen desde el cual se comenzó la pe- tición (Frontend). Luego el destino (Edge Server) responderá al método OPTIONS según lo que esté configurado, habilitando o rechazando los parámetros utilizados, el método solicitado y el origen desde el cual proviene. Esto genera un problema importante, ya que cada una de estas peticiones tiene el siguiente recorrido:

Figura 8.9: Flujo del método OPTIONS

Este método es redigirido al servidor de autenticación debido a que el auth- proxy.so no permite filtrar por método antes de hacer la redirección, por lo que no es posible evitar este paso. Es inevitable también la petición al Origin Server, ya que ATS en sí mismo no permite guardar en caché las respuestas del OPTIONS. Además conceptualmente el ATS es un proxy reverso, por lo que tampoco existe la forma de configurarlo para que tenga la autoridad de responder por sí mismo a estas peticiones.

Fue un problema de gran importancia debido a que limitaba uno de los benefi- cios más importantes que tiene una CDN que es la baja latencia al tener respuestas de nodos cercanos de la red. Tal vez para la reproducción de MP4 no sería un gran problema porque este procedimiento se realiza una única vez (por haber única- mente un GET de un archivo), pero analizando en el caso de HLS se ve que para pedir cada playlist o chunk se debería completar este flujo, lo cual sería imposible de manejar en una CDN.

Luego de investigar en profundidad se encontró que las posibles opciones para minimizar este efecto eran modificar el funcionamiento del ATS y sus plugin. Por un lado hacer que sólo sea necesario autenticar las peticiones GET y no las OPTIONS, y por el otro que exista la posibilidad de guardar en caché las respuestas a estas últimas (importante que esto se fije únicamente por origen de la petición y no por el recurso pedido, ya que por recurso, al pedir dos chunk distintos ya no aplica la respuesta de caché). En cualquiera de los casos era necesario modificar el código de ATS y sus plugin, lo cual se alejaba mucho del primer objetivo de agregar una capa de seguridad básica.

Finalmente para corregir este problema se buscó enviar los datos de autentica- ción en un header de los básicos utilizados por defecto que no activase el prevuelo

y no en un header personalizado. Se buscaron las distintas opciones [57], y median- te ensayo y error se encontró que el único que cumplía con esta condición era el Content-Language header, por lo que se decidió enviar el par usuario-token en éste, separados por un caracter delimitador (luego el proxyAuthentication extraerá los datos de este header). Esta no es una solución definitiva, sino más bien temporal, pero suficiente para sortear el problema y estar en consideración para una posible mejora.

Capítulo 9

Pruebas

En esta sección se detallarán las distintas pruebas realizadas para evaluar el funcionamiento y rendimiento del sistema. Se decidió separarlas en secciones para diferenciarlas según su funcionalidad. Para realizar las pruebas, como a lo largo de todo el proyecto, se utilizaron únicamente herramientas de software libre tales como Postman, Wireshark y Apache JMeter.

9.1.

Escalabilidad

Este es un punto clave en el proyecto, por esta razón se decidió hacer énfasis en estudiar la respuesta del sistema al estresar sus Edge generándoles tráfico. En primera instancia se realizaron pruebas en la infraestructura propia, concluyendo rápidamente que no se iban a poder hacer con carga debido a sus altos tiempos de respuesta. Por lo tanto se utilizó luego la infraestructura provista por ANTEL logrando casos más representativos de la realidad.

9.1.1.

Infraestructura propia

Durante la implementación de este proyecto, ya se notó que la creación de las

In document pylinac Documentation (Page 94-101)