Hoy en el programa: ¿Dónde más puede aplicar Google Apps Script si las ideas normales han terminado? Automatización del trabajo con VPNBook a través de una cadena de scripts en diferentes idiomas que no conozco. Nedo-cURL por Mikrotik. Telegrama a través de un lugar, para no estar en otro, la auto-supervisión lo permite.
Parte 1. Sin título
Hace un año, escribí una nota "
Casi OCR para obtener una contraseña de VPNBook. PHP + Mikrotik " sobre cómo configurar la recuperación automática de contraseña en un enrutador Mikrotik para acceso VPN gratuito a través de VPNBook. El comienzo de la historia está ahí.
Desde entonces, ha fluido mucha agua, en Rusia bloquearon el sitio VPNBook, pero no los servidores VPN públicos, que están publicados en él. Ese script PHP para decodificar una imagen PNG de una contraseña en una cadena de texto ahora también debería funcionar cuando se inicia en un servidor cuyo tráfico no pasa a través del sistema de bloqueo. Pero hace un tiempo, al experimentar con el servicio script de Google Apps (GAS)
script.google.com , decidí abandonar el script PHP en un servidor web externo, reemplazándolo parcial o totalmente con un script GAS que se ejecuta como una aplicación web (aplicación web). No entendía la política de ejecución y las restricciones de GAS, pero todo lo que hice funciona en una cuenta gratuita de Google y todavía no pide dinero. No tengo el objetivo de describir Google Apps Script en detalle. GAS se basa en el lenguaje JavaScript, puede usar bibliotecas JS de terceros, puede publicar el script como una aplicación web, que puede estar disponible para todos sin autorización. Las capacidades de la implementación actual de GAS no fueron suficientes para mí, así que tuve que salir y buscar soluciones.
Al principio decidí escribir un proxy para imágenes PNG. Se suponía que el script web debía solicitar una imagen de contraseña del sitio web de VPNBook (recuerdo que la contraseña se publicó allí en PNG) y se la dio al cliente que llamó a este script para decodificar. Tal forma de sortear la cerradura. Aquí se cumplió la primera restricción de GAS. Resulta que el script no puede renderizar MIME image / png, sino solo formatos de texto, JSON, TEXT, XML, etc. Pero había una manera de evitar esto. Puede codificar PNG a Base64 y devolver una cadena de texto al cliente. Hay scripts similares en Internet, por ejemplo
techslides.com/image-proxy-with-google-app-scripts . Simplemente simplifiqué uno de ellos. Solo necesitaba una imagen y generar solo cadenas Base64. El resultado es un script que consta de una sola función doGet: un controlador de solicitud GET que devuelve una cadena en respuesta.
function doGet() { var response = UrlFetchApp.fetch('https://www.vpnbook.com/password.php'); var b64 = Utilities.base64Encode(response.getContent());
Ejemplo de salida del navegador:
iVBORw0KGgoAAAANSUhEUgAAAGQAAAANAQMAAABl11mFAAAABlBMVEX29vZMTExY89ZbAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAVUlEQVQImWNgIBrwSzCw/2ZgOADhSc5gYJCG8wxQedLdCcYFNXcgPHOZsxuSZxx7BuFZzsjdcJi34TBU5Y3cjc3IvM3McJ7kjNxtzDwwffwSIB7UTACt/h52C5DFqQAAAABJRU5ErkJggg==
Luego viene el script PHP, que se puede colocar en el servidor dentro de la zona con bloqueo de recursos. Es muy similar al script del artículo anterior, excepto por un pequeño cambio en los parámetros de la llamada cURL. Debe permitir que cURL pase por HTTP / 1.1 302 movido temporalmente, porque GAS, cuando se le llama, redirige desde la dirección del script web a una dirección temporal dinámica:
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
Y decodificación Base64:
$imgOCR = imagecreatefromstring(base64_decode($output));
Este script PHP decodifica la contraseña PNG y la devuelve como una cadena de texto. Además, como en el primer artículo en la parte sobre Mikrotik. El enrutador recoge la contraseña usando fetch.
El resultado fue un esquema de trabajo de 2 servicios intermedios frente a Mikrotik.
Parte 2. Presione sobre GAS. Deshazte del script decodificador PHP
Durante los experimentos con GAS, surgió la idea de abandonar el decodificador de contraseña en PHP y reescribirlo en GAS. Y aquí se descubrió un gran problema: el script de Google no tiene funciones de procesamiento PNG, lo único que se puede hacer es convertir PNG a una matriz de bytes. No se trataba de manipulaciones con partes de imágenes y píxeles. Me subí a Github en busca de la biblioteca JS para trabajar con PNG, encontré muchos de ellos: PNG.js, UPNG.js, pngjs. Algunos no admiten la profundidad de color de 1 bit de un píxel PNG (imagen con contraseña). Tiraron de varias bibliotecas de compresión zlib. En general, todo me pareció un poco engorroso, y decidí escribir por mi cuenta un convertidor primitivo solo para mi imagen PNG en un mapa de bits con la función de acceder a píxeles mediante coordenadas XY. Luego vino una inmersión completa en formato PNG: editor hexadecimal, estándares de lectura, montones de descripciones en la red. Y finalmente, me encontré con la sección PNG del archivo IDAT, empaquetada con zlib, que contenía una matriz de píxeles.
Se requería una función para desempaquetar zlib, que por supuesto no estaba en GAS. Sorprendentemente, tienen gzip / ungzip y zip / unzip, pero no zlib. Después de leer sobre gzip (el segundo nivel de inmersión después del formato PNG), llegué a la conclusión de que no sería posible ensamblar una "bicicleta" en forma de cuasi-archivo gzip de la sección IDAT, aunque la compresión zlib se usa allí y allá. Porque Para construir un archivo gzip válido, necesita saber la longitud de los datos desempaquetados, que no podría obtener sin desempacarlos :) Y con la longitud incorrecta, GAS consideró que el archivo estaba dañado. Al final, recurrí a Github y encontré una gran solución: zlib.js para la biblioteca de Google Apps Script (https://github.com/hinimub/zlib.js/blob/develop/README.en.md). Que fue especialmente preparado para la integración en proyectos de GAS a través de bibliotecas de claves de proyecto. Entonces el rompecabezas comenzó a converger. Después de escribir la descompresión de la matriz de píxeles y la función para acceder a las coordenadas del píxel XY, fue posible transferir el script del decodificador de PHP a GAS.
Se calculó por separado una tabla hash de un diccionario de posibles caracteres de contraseña. Esta es una acción única que hice en un programa de terceros (en LabVIEW, hola, colegas). Cada carácter en la imagen se puede asignar como 8 bits (sin sangría) x 10 líneas. 1 byte es suficiente para codificar 8 píxeles de una línea de un carácter. Puede almacenar una cadena de píxeles en un número entero (byte) y el carácter completo como una secuencia de 10 bytes. Resulta 10 números hexadecimales por personaje. A continuación, el decodificador GAS repite su progenitor PHP.
El resultado es un script que funciona completamente en GAS. function doGet() {
'A', 'FCC6C3C6FCC6C3C3C6FC': 'B', '3E63C1C0C0C0C0C1633E': 'C', 'FCC6C3C3C3C3C3C3C6FC': 'D', 'FEC0C0C0FCC0C0C0C0FE': 'E', 'FFC0C0C0FCC0C0C0C0C0': 'F function doGet() {
'' C3C3C3C3FFC3C3C3C3C3 ':' H '' 7E18181818181818187E ':' I '' 1E666666466C38 ':' J '' C3C6CCD8F0F0D8CCC6C3 ':' K '' C0C0C0C0C0C0C0C0C0FE ':' L', function doGet() {
'C3E3F3F3DBDBCFC7C7C3': 'N', '3C66C3C3C3C3C3C3663C': 'O', 'FEC3C3C3FEC0C0C0C0C0': 'P', '3C66C3C3C3C3DBCF663D': 'Q', 'FEC3C3C3FEF8CCC6C3C3': 'R', '7EC3C0C07E333C37E function doGet() {
000B6DBDBDBDBDBDB ':' m '' 000DCE6C3C3C3C3C3 ':' n '' 0003C66C3C3C3663C ':' o '' 000DCE6C3C3C3E6DC ':' p '' 0003B67C3C3C3673B ':' q '' 000DE736060606060 ' function doGet() {
El script implementa solo el método GET. Al ejecutar una solicitud GET a este script, publicado como aplicación web, la respuesta contendrá inmediatamente la contraseña decodificada en forma de cadena.
Parte 3. Mikrotik y movido temporalmente 302
Por lo tanto, tenemos un script que se ejecuta en servidores externos de aplicaciones web, que es independiente de los bloqueos y devuelve una contraseña de texto sin formato. Y parece que no hay nada más fácil que solicitarlo con el comando fetch en RouterOS Mikrotik. Pero entonces otra sorpresa me esperaba. En respuesta a la solicitud (se cambiaron las direcciones reales), fetch devuelve "302 movido temporalmente".
[admin@MikroTik] /environment> :put ([/tool fetch url="https://script.google.com/macros/s/A.....A/exec" http-method=get output=user as-value]->"data") failure: closing connection: <302 Moved Temporarily "https://script.googleusercontent.com/macros/echo?user_content_key=....."> 173.194.222.138:443 (4) [admin@MikroTik] /environment>
Al comienzo del artículo, ya escribí sobre esto. Al acceder a la URL persistente conocida de la secuencia de comandos de la aplicación web, Google redirige a una URL temporal, que a su vez devuelve una respuesta a la solicitud. Pero a diferencia de PHP cURL, fetch RouterOS no sabe cómo realizar redirecciones, sino que devuelve el error. Pero forum.mikrotik.com no lo hizo de inmediato, pero había una solución alternativa. Puede redirigir la salida de búsqueda estándar desde la consola a un archivo llamando a la ejecución asincrónica en una tarea separada envolviendo: ejecutar. Luego puede recuperar la URL de redireccionamiento y volver a buscarla con la nueva dirección. Que se hace a continuación.
Aquí está el texto completo del script Mikrotik para trabajar con la aplicación web GAS Parte 4. Proxy de Telegram GAS
Decidí dedicar esta parte a la próxima iteración de integrar el servicio Telegram en Mikrotik. El uso de GAS aquí sería de interés puramente académico si no fuera por la realidad de bloquear el servicio de Telegram, incluido api.telegram.org, a través del cual los bots trabajan con el servicio. La idea repite la idea al principio del artículo sobre la representación de la solicitud de imágenes PNG.
En este caso, la aplicación web GAS está escrita para solicitudes proxy de Mikrtotik a api.telegram.org. Como base, tomé una secuencia de comandos preparada de manzoorwanijk, WPTelegram Google Script
gist.github.com/manzoorwanijk/ee9ed032caedf2bb0c83dea73bc9a28e . Este script puede representar muchos métodos de API de Telegram (pero no todos). En args, puede pasar un objeto JSON que contenga los parámetros de solicitud, por ejemplo
{"chat_id":"123","text":"HelloWorld"}
. Pero para mi tarea de enviar mensajes de texto desde RouterOS Mikrtotik, la implementación parecía complicada y la simplifiqué. En última instancia, generalmente puede escribir varios scripts de aplicaciones web para representar varios métodos de API de Telegram. Aquí está mi implementación para el método sendMessage. Se puede simplificar aún más incorporando el nombre del método sendMessage que se llama, e incluso bot_token y chat_id en el cuerpo de la función requestHandler.
function doGet(e) { if(typeof e !== 'undefined'){ return ContentService.createTextOutput(requestHandler(e)); } } function doPost(e) { if(typeof e !== 'undefined'){ return ContentService.createTextOutput(requestHandler(e)); } } function requestHandler(e){ if (typeof e.parameter.bot_token === 'undefined'){ return 'Error! Bot token not provided'; } else if (typeof e.parameter.method === 'undefined') { return 'Error! Method name not provided'; } else if (typeof e.parameter.chat_id === 'undefined') { return 'Error! Chat id not provide'; } else if (typeof e.parameter.text === 'undefined') { return 'Error! Text not provide'; } if (e.parameter.method === 'sendMessage') { var data = { "method": "post", "muteHttpExceptions": true, payload : 'chat_id=' + e.parameter.chat_id + '&text=' + e.parameter.text } return UrlFetchApp.fetch('https://api.telegram.org/bot' + e.parameter.bot_token + '/' + e.parameter.method, data).getContentText(); } }
Después de publicar el script en la aplicación web, puede ejecutar una solicitud en el navegador GET para verificar:
https://script.google.com/macros/s/A.....A/exec?bot_token=3.....3&method=sendMessage&chat_id=2.....3&text=testtext123
O en la solicitud POST de RouterOS:
:do { /tool fetch url=("https://script.google.com/macros/s/A.....A/exec") keep-result=no http-method=post http-data=("bot_token=3.....3&method=sendMessage&chat_id=2.....3&text=testtext123") } on-error={ }
La solicitud está envuelta en do-on-error, porque, como se muestra arriba, la primera llamada para recuperar arrojará una excepción "Moved Temporalmente 302" y el script sin el controlador de error se detendrá en este punto. Una llamada para recuperar sin reenviar es suficiente para que se envíe el mensaje, por lo que no es necesaria una segunda llamada para recuperar si no necesita el objeto JSON devuelto por la API de Telegram.
Parte 5. Final
Traje mis aplicaciones reales en la unión de Google Apps Script con otros servicios. Puedes llegar a mucho más. Por ejemplo, escriba un bot de Telegram en GAS, que responderá con una contraseña VPNBook con solicitudes de almacenamiento en caché para reducir la carga en el VPNBook (Servicio de caché), y todo esto estará en un script GAS. Puede escribir en GAS un sistema de registro o configuraciones de respaldo para Mikrtotik, que se colocarán en archivos de Google Docs y Google Sheets, y mucho más.