Underc0de

Programación Scripting => Python => Mensaje iniciado por: DtxdF en Junio 15, 2020, 04:49:38 AM

Título: Ultra Tesla: Crear redes distribuidas de servicios, fácil, rápido y seguro
Publicado por: DtxdF en Junio 15, 2020, 04:49:38 AM
Ultra Tesla es un proyecto de código abierto para crear redes distribuidas de servicios fácil, rápido y seguro, usando el poder de computo de múltiples ordenadores. El proyecto permite unificar los distintos servidores en una única interfaz que es proporcionada para el mismo usuario creando una sensación de transparencia.

(https://raw.githubusercontent.com/UltraTesla/UTesla/master/screenshots/nodes.png?token=APYQNALFOKKMJUVI6GXHZPK644PXG)

Mi primer "Hello World":

Ultra Tesla utiliza servicios, que son básicamente mini-programas que se adhieren a la ejecución del servidor, siguiendo una sintaxis en HTTP como la siguiente: <scheme>://<netloc>/<Service>/<Sub-Service>/<...>

Donde <scheme> es 'http' o 'https' según la configuración del servidor; <netloc> es básicamente la dirección del servidor; <Service> es el nombre del servicio, que para este ejemplo será "hello_world" y los demás que son <Sub-Service> y <...> son básicamente otros mini-programas que se tendrán que usar anteponiendo el nombre del servicio anterior, por ejemplo, <Service>/<Sub-Service>/<Sub-Service 1>/<Sub-Service 2>/<...> y así según lo que el administrador haya propuesto.


Código (python) [Seleccionar]
class Handler:
    async def get(self):
    await self.write('Hello World!')


Un ejemplo real con el servicio nombrado como complements/hello_world.py en una URL se vería como lo siguiente (suponiendo que la dirección del servidor sea localhost): http://localhost:17000/hello_world

Contactando al servidor:

Lo siguiente es un script simple para poder contactarse con el servidor y obtener la salida del servicio 'hello_world'

Código (python) [Seleccionar]
import asyncio

from modules.Infrastructure import client

async def main():
    UTeslaClient = client.UTeslaClient('<Nombre de usuario>')
    await UTeslaClient.set_server_key('<Clave pública del servidor>')
    await UTeslaClient.set_user_keys('<Clave pública del usuario>', '<Clave privada del usuario>')

    cmd = {
        'token' : '<Token de acceso>'

    }

    Response = await UTeslaClient.fetch(
        'http://localhost:17000/hello_world',
        cmd,
        method='GET'

    )

    dec_body = UTeslaClient.get_message(Response.body)

    print(dec_body)

if __name__ == '__main__':
    asyncio.run(main())


Deberíamos obtener la siguiente respuesta:

Código (text) [Seleccionar]
Hello World!

Creando un usuario:

El administrador será el encargado de crear los usuarios, aunque perfectamente se podría automatizar como un sistema de registro cualquiera con una interfaz de usuario cualquiera, para este ejemplo se usará el plugin 'add' de UTeslaCLI.

Código (bash) [Seleccionar]
# Para requerir ayuda
./UTeslaCLI add --help
# Creamos el usuario
./UTeslaCLI add -u <Nombre de usuario> -p <Contraseña> -i <Ruta de la clave pública>


Creación del par de claves:

Tanto el servidor como el usuario deben tener sus par de claves para que la comunicación sea satisfactoria. Esto es un proceso inicial que deben hacer antes de hacer cualquier cosa (independientemente de si es un usuario o el administrador del servidor).

En el cliente:

El cliente va a requerir usar el plugin 'generate_keys' para la creación del par de claves

Código (bash) [Seleccionar]
# Para requerir ayuda
./UTeslaCLI generate_keys --help
# Generamos el par de claves
./UTeslaCLI generate_keys -bit_size <Tamaño del par de claves en bits> -out-public-key <Nombre del archivo de la clave pública> -out-private-key <Nombre del archivo de la clave privada>


Si se ejecuta sin parámetros se generará con un tamaño de claves por defecto de 3072 bit's y las claves se mostrarán en la salida. Lo recomendable sería dejar el tamaño de claves pre-determinado y guardar el par de claves en un lugar seguro.

Ahora lo que tendría que hacer el usuario que desea registrarse es enviar la clave pública al administrador del servidor para que éste lo registre satisfactoriamente.


En el servidor:

El proceso para generar el par de claves en el lado del servidor será mucho más simple; se hará automáticamente cuando se ejecute.

Código (bash) [Seleccionar]
./UTesla

El tamaño del par de claves estará definido en UTesla.ini, en la sección 'Crypt Limits' y en la clave 'rsa_key_length'.


Lo recomendable es dejar el tamaño de claves pre-determinado tanto del lado del cliente y del servidor, y se debe tener en cuenta que el tamaño de claves debe ser igual o equivalente en las dos partes.

Creación del token de acceso:

UTesla requiere de un token de acceso para realizar la mayoría de acciones, en este caso el cliente deseoso por obtener su token tiene que usar el plugin 'generate_token'

Código (bash) [Seleccionar]
./UTeslaCLI generate_token <La URL del servidor> -u <El nombre de usuario> -p <La contraseña del usuario> -s <La clave pública del servidor> -i <La clave pública del usuario> -I <La clave privada del usuario>

Una vez hecho lo anterior y la petición haya sido satisfactoria, se mostrará el token de acceso, el cual debe ser almacenado en un lugar seguro para su posterior uso.

Uso de la distribución de servicios:

Supongamos que en el mundo haya dos servidores y los administradores se conozcan, uno de éstos contiene uno o más servicios que otro no tiene, pero un cliente que requiera el uso de ese servicio usando la URL del primer servidor (por ejemplo) no tendría ningún inconveniente, ya que si el servicio deseado no se encuentra en el primer servidor, éste hará una búsqueda en la red y determinará qué servidor es el que contiene ese servicio, posteriormente obtendría una respuesta como si fuera el mismo cliente y luego la redirige hacia el cliente mismo, por lo que todo sería transparente.

Lo anterior es muy útil si se desea separar ciertos servicios entre servidores, por cuestiones económicas, de recursos, una mejoría en la implementación o cualquier cosa que se desee, al fin y al cabo, el usuario es el que lo decide.

Para estos ejemplos el primer servidor será 'http://localhost:17000' y el segundo 'http://localhost:17001'. El segundo tiene un servicio llamado 'hello_friend', pero nosotros "los clientes" vamos a creer que el servicio está en el primer servidor, por lo cual lo usaremos para interactuar con éste.


complements/hello_friend.py:
Código (python) [Seleccionar]
class handler:
    async def get(self):
        await self.write('hello friend!')


Por diseño, el administrador del segundo servidor debe crear un usuario como si fuera cualquier otro. Por lo que debe saber cómo crearlo, cosa que ya se hablo en anteriores secciones. En este caso nosotros lo llamaremos 'utesla'.

Además, como ya se aclaró en anteriores secciones, la mayoría de operaciones requieren de un token de acceso, por lo se va a suponer que se tiene hecho este paso para el nuevo usuario utesla.

Una vez aclarado todo lo que se tiene que hacer, vamos un paso más allá y obtengamos los servicios del segundo servidor (http://localhost:17001) para el primer servidor (http://localhost:17000)


Código (bash) [Seleccionar]
./UTeslaCLI add_network http://localhost:17001 -s <Clave pública del segundo servidor> -p keys/key.pub -P keys/key.priv -u utesla -t <Token de acceso>

Como esto es un ejemplo, se usan las claves que por defecto se almacenan en la carpeta 'keys' del primer servidor; se usa el usuario 'utesla' que ya debe estar creado en el segundo servidor y nos contactamos con el servidor 'http://localhost:17001' para obtener sus servicios.

Como paso final, el cliente desea obtener el servicio 'hello_friend' y si todo ha salido bien, obtendrá su posterior salida, aunque lo peculiar de todo esto es que el cliente hará una petición al primer servidor en vez de al segundo.

Lo siguiente será muy parecido a lo que se observó en anteriores secciones, pero lo único que cambió es el nombre del servicio:


Código (python) [Seleccionar]
import asyncio

from modules.Infrastructure import client

async def main():
    UTeslaClient = client.UTeslaClient('<Nombre de usuario>')
    await UTeslaClient.set_server_key('<Clave pública del servidor>')
    await UTeslaClient.set_user_keys('<Clave pública del usuario>', '<Clave privada del usuario>')

    cmd = {
        'token' : '<Token de acceso>'

    }

    Response = await UTeslaClient.fetch(
        'http://localhost:17000/hello_friend',
        cmd,
        method='GET'

    )

    dec_body = UTeslaClient.get_message(Response.body)

    print(dec_body)

if __name__ == '__main__':
    asyncio.run(main())


Deberíamos obtener la siguiente respuesta:

Código (text) [Seleccionar]
Hello Friend!

Como se puede apreciar, es un proceso muy sencillo y lo mejor, es transparente para el usuario.

Sub servicios:

Ya se ha explicado anteriormente qué son los sub servicios, pero no se han explicado cómo crearlos. Al igual que la creación de plugins y servicios es muy sencillo.

Lo primero que hay que hacer es crear un servicio, como 'greeting' (complements/greeting.py).


Código (python) [Seleccionar]
class Handler:
    pass


Se pueden colocar los métodos HTTP si se desea, pero para este caso no lo necesitamos

Ahora para la creación de sub servicios simplemente se crea una carpeta en 'complements' con el mismo nombre del servicio mas '-folder'.


Código (bash) [Seleccionar]
mkdir complements/greeting-folder

En esa carpeta se tienen que agregar más archivos que sigan una estructura similar a la del servicio padre, como por ejemplo:

complements/greeting-folder/hello_friend.py:
Código (python) [Seleccionar]
class Handler:
    async def get(self):
        await self.write('Hello Friend!')


Ahora un cliente puede hacer una petición usando una URL parecida a: <Scheme>://<netloc>/greeting/hello_friend y obtendría:

Código (text) [Seleccionar]
Hello Friend!

Adentro de esa carpeta se crean más carpetas (si se desea), pero lo peculiar de todo esto es que no necesitan usar una sintaxis similar a "<Nombre del servicio>-folder", simplemente creando una carpeta e insertando nuevos servicios que sigan una estructura similar a los anteriores es más que suficiente.

Código (bash) [Seleccionar]
mkdir complements/greeting-folder/farewell

complements/greeting-folder/farewell/bye.py:
Código (python) [Seleccionar]
class Handler:
    async def get(self):
        await self.write('Bye!')


Y el cliente puede usar una estructura similar a: <scheme>://<netloc>/greeting/farewell/bye

Módulos:

Los módulos son muy útiles en caso de que se necesite integrar ficheros de python a los complementos, para que la escritura sea más limpia y siga mejores prácticas.

Para crear un módulo es necesario crear primero un servicio, en este caso 'math' (complements/math.py)


Código (python) [Seleccionar]
class Handler:
    async def post(self):
        sum = self.modules.get('sum')
        num1 = self.get_argument('num1', 0)
        num2 = self.get_argument('num2', 0)

        await self.write('Result:' + str(sum.calculate(num1, num2)))


Ahora se debe crear una carpeta con el nombre del servicio mas '-modules' y ahí se agregan ficheros de python como cualquier otro; siguiendo lo que necesita el código anterior:

complements/math-modules/sum.py:

Código (python) [Seleccionar]
def calculate(x, y):
    return x + y


Eso sería todo, muy sencillo y más modular.

Instalación:

Código (bash) [Seleccionar]
git clone --recursive https://github.com/UltraTesla/UTesla.git
cd UTesla


Arch Linux:

Código (bash) [Seleccionar]
sudo pacman -S mariadb

Debian:

Código (bash) [Seleccionar]
sudo apt-get install mariadb-server libmariadb-dev python3-dev

Red Hat Enterprise Linux 8.2

Código (bash) [Seleccionar]
sudo yum install python3-devel gcc mariadb mariadb-server mariadb-devel

General:

Código (bash) [Seleccionar]
sudo systemctl start mariadb.server
python3 -m pip install -r requirements.txt
./UTesla


Es recomendable configurar primero antes de iniciar UTesla (como el nombre de usuario y la contraseña en MySQL)

Contribuyendo:

Al igual que cada proyecto nuevo creado, soy imperfecto y puedo equivocarme, pero con todo el gusto del mundo aceptaré tu contribución, ya sea por código o no, porque hay que recordar que se trata de abarcar muchos idiomas de distintas regiones del mundo y en éso puede haber múltiples defectos.

Notas:


~ DtxdF
Título: Re:Ultra Tesla: Crear redes distribuidas de servicios, fácil, rápido y seguro
Publicado por: Gabriela en Junio 15, 2020, 06:46:13 AM

      @DtxdF (https://underc0de.org/foro/index.php?action=profile;u=71723)

Muy bueno tu trabajo, claro y limpio.
Una guía útil y documentada para los que deseen adherir a Ultra Tesla.
Gracias por compartirlo a la Comunidad!

Saludos

Gabriela

Título: Re:Ultra Tesla: Crear redes distribuidas de servicios, fácil, rápido y seguro
Publicado por: DtxdF en Junio 15, 2020, 12:12:57 PM
Hola @Gabriela (https://underc0de.org/foro/index.php?action=profile;u=37488)  ;D, muchas gracias a ti por leerlo, y como siempre es un placer compartir estos aportes  ;)

~ DtxdF
Título: Re:Ultra Tesla: Crear redes distribuidas de servicios, fácil, rápido y seguro
Publicado por: ??????? en Junio 15, 2020, 05:52:38 PM
Excelente trabajo compañero, se agradece estos grandes aportes y proyectos que haces para la comunidad  ;) ;).

-Kirari
Título: Re:Ultra Tesla: Crear redes distribuidas de servicios, fácil, rápido y seguro
Publicado por: DtxdF en Junio 16, 2020, 12:14:53 AM
Gracias @Kirari (https://underc0de.org/foro/index.php?action=profile;u=92682) lo que sea para aprender y enseñar  ;D

~ DtxdF
Título: Re:Ultra Tesla: Crear redes distribuidas de servicios, fácil, rápido y seguro
Publicado por: Nomad en Septiembre 23, 2020, 11:53:58 AM
Excelente artículo, muy claro y sobre todo informativo.
Título: Re:Ultra Tesla: Crear redes distribuidas de servicios, fácil, rápido y seguro
Publicado por: DtxdF en Octubre 01, 2020, 11:48:43 AM
Gracias @Nomad (https://underc0de.org/foro/index.php?action=profile;u=58496), ya vendrán más al respecto :D

~ DtxdF