3 puntos por GN⁺ 2023-07-06 | 1 comentarios | Compartir por WhatsApp
  • suc es Simple Unix Chat, una implementación de algunas funciones clave de un chat estilo Slack o Mattermost usando componentes básicos de UNIX y Bash; el bucle central tiene 5 líneas, aunque el script real es más largo
  • Los mensajes se leen línea por línea desde la entrada estándar, se les agrega la fecha y el nombre de usuario, y luego se anexan al archivo del canal en /var/lib/suc/; la autenticación y el control de permisos se delegan a SSH y al modelo de permisos de UNIX
  • A diferencia del servidor de Mattermost, que solo en código Go tiene alrededor de 500 mil líneas, suc destaca que puede lograr un comportamiento central similar con mucho menos código
  • usuc combina tail -f, rlwrap, pygmentize y el prefijo de comando : para manejar la lectura de canales, la salida con estilo tipo Markdown y el intercambio de resultados de comandos
  • Como los canales son archivos de texto simples, se integran fácilmente con herramientas UNIX como grep, tail, bat, lnav, hooks de Git y scripts de bots

La complejidad que suc busca reducir

  • suc significa Simple Unix Chat; tiene menos funciones que Slack, Mattermost o IRC, y su total de líneas supera las 5, pero el bucle central para escribir mensajes está compuesto por 5 líneas de Bash
  • Las funciones objetivo son chat en tiempo real con texto enriquecido, intercambio de archivos, control de acceso detallado, automatización e integración con herramientas externas, cifrado en tránsito, cifrado opcional del almacenamiento y autenticación de usuarios
  • La implementación pequeña es posible porque aprovecha componentes básicos consistentes y combinables que ofrecen las implementaciones modernas de UNIX
  • En contraste, el servidor de Mattermost tiene unas 500 mil líneas solo en Go, y suc considera que puede implementar las funciones centrales de Mattermost con el 0.005% de ese código

El bucle de 5 líneas para escribir mensajes

  • El bucle principal en Bash de suc lee una línea desde la entrada estándar, le agrega la fecha y el nombre de usuario real, y luego la anexa al archivo /var/lib/suc/"$1"
  • El script real es más largo por el preprocesamiento de seguridad y el cálculo del color del nombre de usuario, pero la acción central de escribir el historial del chat se resume en tres pasos
    • Leer una línea desde la entrada estándar
    • Agregar la fecha y el nombre de usuario real
    • Anexarla al archivo del canal bajo /var/lib/suc/
  • La autenticación, el control de acceso, el cifrado y el texto enriquecido no los maneja código interno de suc, sino SSH, las API de control de acceso de UNIX y la modularidad basada en texto

La autenticación corre por cuenta de SSH

  • El proceso suc lo ejecuta un usuario que ya fue autenticado, así que suc en sí no tiene código de autenticación
  • La autenticación remota la maneja ssh, como en un servidor UNIX normal
    • Secretos compartidos, como contraseñas
    • Desafíos criptográficos
    • Dispositivos para generar contraseñas de un solo uso (OTP)
    • MFA combinando varios métodos
  • ssh también autentica al servidor ante el cliente para evitar ataques de intermediario y cifra todos los datos entre cliente y servidor
  • La instalación de suc depende del host UNIX y de la configuración del servidor ssh
    • El usuario debe existir en el sistema
    • El servidor ssh debe permitir el inicio de sesión remoto de ese usuario
  • En GNU Guix, con configuración declarativa se pueden agregar usuarios y claves SSH en una sola línea, y si esa línea desaparece, el usuario se elimina durante la reconfiguración

El control de acceso corre por cuenta del modelo de permisos de UNIX

  • suc no incluye código ni para autenticación ni para control de acceso, y a este enfoque lo llama agnosticismo de seguridad
  • El kernel de UNIX conoce usuarios, grupos, procesos y archivos, y decide los permisos de lectura y escritura de archivos según el propietario efectivo del proceso
  • Los archivos de canal en /var/lib/suc pertenecen al usuario suc, y se configuran permisos de lectura por grupo para que los usuarios puedan leer ciertos canales
  • Los usuarios normales no escriben directamente en los archivos: ejecutan suc, y el kernel se encarga de hacer coincidir o bloquear permisos
  • En comunidades más restrictivas se pueden usar binarios setuid por grupo
    • Por ejemplo, suc_blue pertenece al usuario blue y solo lo puede ejecutar el grupo blue
    • Los usuarios del grupo blue pueden escribir en el canal blue mediante suc_blue
    • Los usuarios del grupo red no pueden leer ni escribir en el canal blue
  • El script auxiliar para crear canales suc_channel.sh tiene unas 80 líneas, y los usuarios de GNU Guix pueden crear el binario setuid, el usuario, el grupo y el archivo del canal necesarios con una sola línea: (suc-private-channel "red" "red")
  • Las actualizaciones transaccionales de GNU Guix garantizan que un cambio de configuración se aplique por completo o no se aplique en absoluto, y permiten volver al estado anterior

Texto enriquecido y usuc

  • Muchas apps de chat renderizan texto con motores HTML, y el cliente de escritorio de Mattermost usa Electron
  • suc usa un archivo de texto por canal, y los usuarios pueden verlo con herramientas de línea de comandos como tail o cat
  • Aprovechando los códigos de escape ANSI y el soporte de UTF-8 del terminal, se puede crear una experiencia básica de texto enriquecido con colores y emojis
  • suc solo escribe en el archivo del canal; la lectura la maneja usuc
    • suc es un binario con privilegios, así que su lógica y sus dependencias externas deben mantenerse al mínimo
    • usuc se ejecuta con los permisos del usuario que lo invoca, por lo que puede incorporar funciones con más libertad
  • usuc une varias herramientas pequeñas para funcionar como cliente de chat
    • rlwrap aporta historial y edición de línea
    • Selecciona el binario suc con setuid apropiado según el propietario del archivo del canal
    • Usa tail -f -n 20 para mostrar las últimas 20 líneas y las que se agreguen después
    • Verifica si la línea de entrada empieza con :
    • Procesa el texto ingresado por el usuario con pygmentize
  • pygmentize trata la entrada estándar como Markdown y genera texto ANSI con color, así que permite soportar marcado como **bold** con una sola línea de código

Comandos de chat e integración con el shell

  • Si un mensaje en usuc empieza con :, no se envía como mensaje normal; en su lugar, se ejecuta ese comando y luego se envía su salida mediante suc
  • Por ejemplo, si se ejecuta gum style --border=rounded --bold --foreground=#F00 "Hello World !", en el canal aparecerá un texto con borde y estilo
  • Los comandos se ejecutan en el espacio de nombres del usuario que invocó usuc, así que cada persona puede tener sus propias macros de chat sin afectar a los demás
  • En the dam se usa, por ejemplo, para lanzar dados en partidas de RPG de mesa con comandos como roll 2d6

Pipes de texto e integración con herramientas externas

  • En lugar de usar la función de ejecución de comandos de usuc, el usuario puede hacer pipe directamente desde el shell hacia suc con la salida de un comando
  • Si se envía a suc la salida con color de bat, se pueden compartir fragmentos de código en el canal con resaltado de sintaxis
  • make test > testlog || (suc devops < testlog ; exit 1) es un ejemplo de una sola línea en Bash que envía el log al canal devops cuando fallan las pruebas
  • Esa línea puede ir dentro del hook update de un repositorio Git para ejecutar pruebas al hacer push, rechazar la actualización si fallan y avisar al equipo de DevOps
  • Tareas equivalentes a las integraciones de Slack o Mattermost pueden construirse con pipes de texto y scripts de UNIX

Lectura de canales, notificaciones y bots

  • Como los canales de suc son archivos de texto que se actualizan continuamente, se pueden leer y procesar líneas nuevas con tail -f
  • Se puede construir un pipeline que muestre notificaciones con notify-send cuando llegue un mensaje nuevo, y usar grep para filtrar solo nombres o palabras clave como “build failure”
  • Para combinar varios canales en un solo feed se puede usar tail -f /var/lib/suc/*
  • Con lnav se puede recordar la posición de lectura, usar marcadores, colores por canal, parsear fecha, nombre de usuario y campos personalizados, además de aplicar filtros y consultas SQL
  • Un bot puede implementarse si puede leer y escribir en un canal al mismo tiempo
    • El ejemplo busca expresiones de longitud como 33000 ft en el canal
    • Las convierte a metros con el comando units
    • Luego escribe en el mismo canal una respuesta como [metric_bot] 33000 ft is 10058.4 meters.

Comparaciones y conclusión

  • Se mencionan como punto de comparación sistemas grandes como Slack, Mattermost o Discord, pero se considera que comparaciones más justas incluirían también IRC, talk y write
  • Se evalúa que IRC tiene menos funciones que suc, ya que necesita un bouncer para leer historial de chat anterior
  • Se dice que no se logró hacer funcionar talk, ytalk y wall en distribuciones modernas de Linux
  • suc delega la autenticación a SSH, el control de acceso y la componibilidad a UNIX, y mantiene los canales como archivos de texto
  • Mientras una herramienta pueda leer y escribir texto, puede desarrollarse en cualquier lenguaje

1 comentarios

 
GN⁺ 2023-07-06
Opiniones de Hacker News
  • Inmediatamente me hizo pensar en “Master Foo and the Ten Thousand Lines”: http://catb.org/~esr/writings/unix-koans/ten-thousand.html
    Es la historia en la que Master Foo le dice a un programador visitante que “una sola línea de script de shell contiene más naturaleza Unix que diez mil líneas de C”

  • Si la función central definida de algo se puede reproducir de forma demasiado fácil y trivial, entonces esa función podría no ser el núcleo ni la fuente real de su valor
    Me hace preguntarme cuánto del desarrollo de UI desde los años 80 ha sido absorbido y automatizado por la ingeniería de software, o si todavía sigue siendo territorio del arte humano puro

    • Es cierto. Por ejemplo, el valor agregado central de Slack no es “enviar mensajes entre usuarios autenticados”
      Incluso antes de que apareciera Slack y desplazara a otras cosas, ya había docenas de apps, tanto comerciales como de código abierto, que hacían eso
      El valor central de Slack está en el siguiente nivel. Ofrece una UI/UX visualmente atractiva y muy intuitiva, de modo que casi cualquiera puede sentarse y usarla de inmediato sin estudio, configuración, manuales ni capacitación. No hace falta conocimiento técnico y no necesitas entender nada más allá de “introduce tu nombre de usuario y contraseña”; el resto resulta evidente
    • Es una observación muy aguda. Entonces el valor de Slack es la facilidad de uso, y suc claramente no es para usuarios comunes
      Pero eso plantea una pregunta más difícil. Comparado con la alternativa de capacitar un poco más a usuarios que ya saben hacer una invocación muy básica de línea de comandos, ¿vale lo que cuesta Slack? ¿Y qué pasa si además consideras que Slack retiene los datos y no te los devuelve? ¿Qué tan caro es salir de Slack?
      Es cierto que suc es incómodo, pero parece posible converger hacia algo menos voluminoso y menos propietario que Slack
  • Me gusta el logro en sí. Es genial ver lo que se puede hacer con la composabilidad
    Dicho eso, la comparación con Slack y similares no me convence mucho. Ningún ingeniero querría hacerse responsable en producción de algo armado así a base de unir piezas. ¿Cómo harías la contratación de talento, las pruebas y la depuración, y los logs y la analítica?
    Este artículo replica parte de la funcionalidad de chat de apps tipo Slack, pero la función de chat no es el 100% de estas apps. Son cosas construidas en contextos completamente distintos, así que atacar el estado actual por voluminoso parece una interpretación retorcida al estilo Stockton Rush. Claro que existe software inflado, pero “mamá, mira, hice un submarino con cachivaches; por lo tanto, los submarinos caros son tontos” no suena como la refutación que el autor pretendía

    • La idea es que, si reduces la superficie del código base, gran parte de esas cosas pasan a importar menos
      Las utilidades individuales suelen ser fáciles de entender, probar y depurar por separado, y el logging ya viene integrado en el sistema. ¿Acaso no existe syslog? Y además, ¿por qué haría falta la analítica?
    • Mientras leía, pensé lo mismo. Es una ingeniería excelente y fue divertido de leer, pero habría sido más honesto compararlo no con Slack sino simplemente con IRC
      Aun así, sí me da mucha curiosidad cómo se vería una herramienta tipo Slack si priorizara agresivamente la simplicidad por encima de las funciones nuevas. Parece que el capitalismo en su conjunto no fomenta este tipo de desarrollo, así que no hay muchos ejemplos de software empresarial tan simple como razonablemente podría ser. De todos modos, es un experimento mental interesante
  • De forma parecida, también existe ii, que funciona directamente sobre IRC: https://tools.suckless.org/ii/
    Usa un único archivo estándar para todas las conversaciones, y la autenticación y el control de acceso los gestiona directamente la red IRC, además de que ya existen muchas UI. Igual que en este artículo, un plugin puede ser cualquier cosa que lea ciertos archivos y escriba en otros

  • Como el diagrama de craigslist, sería divertido hacer un diagrama de spawn of unix reuniendo startups que recortan nichos de funcionalidad de Unix
    https://thegongshow.tumblr.com/post/345941486/the-spawn-of-craigslist-like-most-vcs-that-focus
    ftp/rsync => Dropbox
    suc => Slack
    ...

  • Si ibas a saltártelo porque el título parece clickbait, sí tiene algo de eso, pero en realidad fue un artículo bastante bueno
    Trata sobre una utilidad llamada “suc” (Simple Unix Chat), que implementa funciones de servidor como las de Slack o Discord con una base de código diminuta
    Lo novedoso es que aprovecha herramientas y metodologías Unix existentes en lugar de reinventarlas. SSH se encarga de la autenticación, los canales son simplemente archivos, y el control de administradores/moderadores se maneja con grupos de usuarios y permisos de archivos. Cosas como texto con formato o subida de archivos se pueden resolver escribiendo los datos que quieras en el canal y dejando que el cliente los interprete. Los bots también son facilísimos si conectas un pipe al archivo del canal
    Con solo las 5 líneas para escribir mensajes al canal que salen en el artículo no vas a crear un clon perfecto de Slack, pero impresiona bastante hasta dónde se puede llegar con herramientas Unix realmente simples

    • Las plataformas de chat existentes no “reinventaron” herramientas Unix; simplemente no las usaron porque no eran adecuadas para construir esto encima
      Hay una razón por la que casi no existen sistemas de producción grandes escritos con el paradigma de “todo es un archivo” y scripts de shell. Para sistemas no triviales que realmente se usarían en el mundo real, ese enfoque es inaceptable
    • Para la mayoría de los usuarios finales, Slack es un cliente
      Incluye UI de administración, UI de gestión de usuarios, extensibilidad, webhooks, etc. Decir “si todos inician sesión en esa máquina pueden chatear con otros usuarios en Linux” no resuelve nada de eso. Todas las variantes de *nix ya tenían eso desde antes de que naciera la mayoría de los lectores de aquí
    • Aquí parecería encajar muy bien un archivo append-only, porque así se puede garantizar que los usuarios normales y los bots no editen publicaciones de otras personas
      Lamentablemente, esta función de Plan 9 todavía no se ha portado a Linux. Son bits de permisos parecidos a “lectura” o “escritura”, pero mucho más específicos
    • “Aprovecha herramientas y metodologías Unix existentes” casi siempre termina siendo una forma de decir “implementó X con una cantidad increíblemente baja de líneas de código”
      No culpo al autor, y de hecho el artículo lo reconoce con bastante honestidad
  • Slack y Discord tienen compartir pantalla y chat de voz. Eso no son extras, sino funciones bastante decisivas para su dominio del mercado

    • Son decisivas, pero siento que la calidad cada vez va peor
  • Aunque el post del blog trata sobre suc y desde el inicio reconoce con honestidad que el título puede llevar un poco a confusión, al final deja un mensaje importante que no tiene relación directa con suc
    Casi todo el software moderno está horriblemente inflado. La idea de que un servidor de chat completo necesite 1.7 millones de líneas es absurda. Parafraseando a Bill Gates, siento que ni esforzándome podría “gastar” tantas líneas de código en un servidor de chat
    No solo por poner el problema sobre la mesa o proponer una alternativa, sino también por recordar que no hace falta reinventar la rueda, merece aplausos. Implementar de forma ingeniosa aprovechando propiedades de sistemas y subsistemas existentes —por ejemplo, usar SSH para autenticación y cifrado— aunque sea para fines distintos de los imaginados originalmente, pero respetando la intención de esas propiedades, es un muy buen ejemplo del espíritu hacker
    Ojalá el mundo del software pudiera aprender mucho más de esta idea. Menores costos, tiempos de desarrollo más cortos y menos horas de ingeniería desperdiciadas reescribiendo código funcionalmente equivalente benefician a desarrolladores, empresas, usuarios finales y, en última instancia, a toda la humanidad. Me gustaría ver más destrucción creativa de este tipo, y también probar técnicas así de ingeniosas en proyectos futuros

    • Como referencia, la mayoría de las líneas de TypeScript están en las pruebas E2E[0] y en el directorio webapp[1]. Como su nombre indica, webapp contiene el “código cliente de la aplicación web de Mattermost”
      Así que en realidad lo correcto sería contar solo las líneas de código Go
      [0] https://github.com/mattermost/mattermost/tree/master/e2e-tests
      [1] https://github.com/mattermost/mattermost/tree/master/webapp
    • Estuve en una reunión para elegir un framework de autenticación web para usar en algún sitio, y se alargó tanto por bikeshedding que durante la misma reunión terminé implementándolo yo mismo sobre las funciones de autenticación que HTTP ya ofrece, y hasta hice una demo antes de que terminara
    • En principio estoy de acuerdo, pero buena parte de esa inflación viene de intentar soportar varias plataformas con una sola base de código
      Eso normalmente es una buena idea porque acelera la salida al mercado y reduce el tamaño de la organización. Pero implica un gran trade-off de complejidad y abstracción. Cuanto mayor es el nivel de abstracción, peor puede ponerse. Así que tampoco es que no haya ninguna razón
  • Actualmente, la mayoría de las distribuciones impiden crear scripts bash setuid por razones de seguridad.
    Pero con una entrada específica en sudoers se puede lograr casi el mismo efecto.

    • En realidad no es solo bash, sino casi todos los scripts con shebang; la excepción vendrían siendo los scripts de Perl. En Perl, el modo taint se activa automáticamente cuando uid != euid.
      https://perldoc.perl.org/perlsec#Taint-mode
      Algunos sistemas pueden tener una condición de carrera fundamental:
      https://perldoc.perl.org/perlsec#Shebang-Race-Condition
      También vale la pena ver el uso de sudo -T:
      https://perldoc.perl.org/perlsec#Using-Sudo
      Da pena que en Ruby hayan eliminado recientemente el modo taint. El seguimiento de contaminación es un mecanismo potente que puede ir mucho más allá de los scripts. Por ejemplo, si en Rack/Rails #html_safe se hubiera manejado con seguimiento de contaminación, habría funcionado de forma más rigurosa y podría haber hecho fallar en el momento en que una cadena de usuario contaminada se concatenara/interpolara/formateara dentro de un fragmento SQL, bloqueando por diseño una clase de inyección SQL.
      Antes también había gente que usaba el seguimiento de contaminación con mucha eficacia en CGI/mod_perl. No es una solución mágica ni de lejos, pero sigue siendo una herramienta bastante efectiva.
    • Usa un wrapper en C que llama al script de shell. Se ve feo y hay que endurecer un poco más el script, pero funciona :)
      https://gitlab.com/edouardklein/suc/-/blob/master/suc_wrapper.c
    • ¿De verdad existe alguna distribución que permita eso? Todo lo que leí cuando intenté hacerlo decía que setuid no se aplica, porque lo que se ejecuta no es el archivo en sí, sino el objetivo indicado en el shebang.
      Hasta ver el archivo wrapper en el repositorio de GitLab, mientras leía esperaba que hubiera alguna forma de hacerlo.
  • Esto ya es otro tema, pero me sorprende que no se haya mencionado talk(1): https://man.netbsd.org/talk.1

    • O también está write(2)[1]. Yo pensé de inmediato en ntalk, porque esa fue la última vez que usé algo así en la misma máquina. En ese entonces IRC no estaba cifrado.
      Lo bueno es que se puede hacer que solo reciba localmente, y entonces hace falta una cuenta de usuario en esa máquina. Por eso encaja bien en lugares como SDF o en clústeres más grandes. La autenticación no hace falta implementarla uno mismo: se la puedes dejar a PAM o BSD_Auth. Tampoco hace falta encargarse uno mismo de los sockets ni del cifrado: se lo puedes dejar a TLS. También se puede entrar a un shell seguro por SSH y luego usar tmux y (n)talk.
      Claro, (n)talk está escrito en C, así que no son 5 líneas de bash.
      [1] https://man7.org/linux/man-pages/man2/write.2.html