Chat Unix simples
(the-dam.org)sucé o Simple Unix Chat, que implementa alguns recursos centrais de chats no estilo Slack/Mattermost com uma combinação de primitivos básicos do UNIX e Bash; o loop central tem 5 linhas, embora o script real seja mais longo- As mensagens são lidas uma linha por vez da entrada padrão, recebem data e nome de usuário e são acrescentadas ao arquivo do canal em
/var/lib/suc/; autenticação e controle de permissões ficam a cargo do SSH e do modelo de permissões do UNIX - Em contraste com o servidor Mattermost, que tem cerca de 500 mil linhas só de código Go,
sucdestaca que é possível criar um comportamento central semelhante com muito menos código usuccombinatail -f,rlwrap,pygmentizee o prefixo de comando:para lidar com leitura de canais, saída em estilo Markdown e compartilhamento de resultados de comandos- Como os canais são simples arquivos de texto, eles podem ser facilmente conectados a ferramentas UNIX como
grep,tail,bat,lnav, hooks do Git e scripts de bots
A complexidade que suc tenta reduzir
sucé a sigla de Simple Unix Chat; ele tem menos recursos que Slack, Mattermost e IRC, e o total de linhas passa de 5, mas o loop central que grava mensagens é composto por 5 linhas de Bash- Os recursos-alvo são chat em tempo real com rich text, compartilhamento de arquivos, controle de acesso granular, automação e integração com ferramentas externas, criptografia em trânsito, criptografia opcional em repouso e autenticação de usuários
- A implementação pequena é possível porque aproveita os primitivos consistentes e combináveis oferecidos por implementações UNIX modernas
- O servidor Mattermost aparece como contraste, com cerca de 500 mil linhas só de código Go; a visão é que
succonsegue implementar as funções centrais do Mattermost com 0,005% desse código
Loop de 5 linhas para escrever mensagens
- O loop Bash central de
suclê uma linha da entrada padrão, adiciona a data e o nome real do usuário, e a acrescenta ao arquivo/var/lib/suc/"$1" - O script real é mais longo por causa de pré-processamento de segurança e do cálculo de cores para nomes de usuário, mas a operação central de escrever o histórico do chat se resume a três coisas
- Ler uma linha da entrada padrão
- Anexar a data e o nome real do usuário
- Acrescentar ao arquivo do canal em
/var/lib/suc/
- Autenticação, controle de acesso, criptografia e rich text não ficam no código interno de
suc, mas sim em SSH, APIs de controle de acesso do UNIX e modularidade baseada em texto
A autenticação fica a cargo do SSH
- O processo
sucé executado por um usuário já autenticado, portanto o própriosucnão tem código de autenticação - A autenticação remota é tratada por
ssh, como em um servidor UNIX comum- Segredos compartilhados, como senhas
- Desafios criptográficos
- Dispositivos geradores de senhas de uso único (OTP)
- MFA combinando vários métodos
sshtambém autentica o servidor para o cliente, evitando ataques man-in-the-middle, e criptografa todos os dados entre cliente e servidor- A instalação de
sucdepende de um host UNIX e da configuração de um servidorssh- O usuário precisa existir no sistema
- O servidor
sshprecisa permitir o login remoto desse usuário
- No GNU Guix, é possível adicionar usuários e chaves SSH em uma linha com configuração declarativa; quando a linha de configuração desaparece, o usuário é removido durante a reconfiguração
O controle de acesso fica a cargo do modelo de permissões do UNIX
sucnão inclui código de controle de acesso, assim como não inclui autenticação; essa abordagem é chamada de agnosticismo de segurança (security agnosticism)- O kernel UNIX conhece usuários, grupos, processos e arquivos, e decide permissões de leitura e escrita de arquivos com base no dono efetivo (effective owner) do processo
- Os arquivos de canal em
/var/lib/sucpertencem ao usuáriosuc, e são configurados para permitir que usuários leiam alguns canais por meio de permissões de leitura por grupo - Usuários comuns não escrevem diretamente nos arquivos; eles executam
suc, e o kernel cuida da correspondência de permissões e do bloqueio - Em comunidades mais restritivas, é possível ter binários
setuidpor grupo- Por exemplo,
suc_bluepertence ao usuáriobluee só pode ser executado pelo grupoblue - Usuários do grupo
bluepodem escrever no canalbluepor meio desuc_blue - Usuários do grupo
rednão podem ler nem escrever no canalblue
- Por exemplo,
- O script auxiliar de criação de canais
suc_channel.shtem cerca de 80 linhas, e usuários do GNU Guix podem criar o binário setuid, o usuário, o grupo e o arquivo de canal necessários com uma única linha(suc-private-channel "red" "red") - As atualizações transacionais do GNU Guix garantem que alterações de configuração sejam aplicadas completamente ou não sejam aplicadas, e permitem voltar a um estado anterior de funcionamento
Rich text e usuc
- Muitos apps de chat renderizam texto com motores HTML, e o cliente desktop do Mattermost usa Electron
sucusa um arquivo de texto por canal, e o usuário pode visualizá-lo com ferramentas de linha de comando comotailoucat- Com suporte do terminal a códigos de escape ANSI e UTF-8, é possível criar cores, emojis e uma experiência básica de rich text
sucapenas escreve no arquivo do canal; a leitura fica a cargo deusuc- Como
sucé um binário privilegiado, sua lógica e dependências externas devem ser minimizadas usucroda com as permissões do usuário chamador, então pode incluir recursos com mais liberdade
- Como
usucatua como cliente de chat ao encadear várias ferramentas pequenas- Fornece histórico e edição de linha com
rlwrap - Escolhe o binário
sucsetuid adequado conforme o dono do arquivo do canal - Mostra as últimas 20 linhas e novas linhas posteriores com
tail -f -n 20 - Verifica se a linha de entrada começa com
: - Processa o texto digitado pelo usuário com
pygmentize
- Fornece histórico e edição de linha com
pygmentizeinterpreta a entrada padrão como Markdown e emite texto colorido ANSI, permitindo suporte a marcações como**bold**com uma linha de código
Comandos de chat e integração com shell
- Se uma mensagem começa com
:,usucnão a envia como mensagem comum; ele executa o comando e envia o resultado da saída parasuc - Por exemplo, ao executar
gum style --border=rounded --bold --foreground=#F00 "Hello World !", um texto com borda e estilo aparece no canal - Os comandos são executados no namespace do usuário que chamou
usuc, permitindo que cada usuário tenha seus próprios macros de chat sem afetar outros usuários - Em the dam, isso é usado durante RPGs de mesa para rolar dados com comandos como
roll 2d6
Pipes de texto e integração com ferramentas externas
- Em vez de usar o recurso de execução de comandos de
usuc, o usuário pode encaminhar diretamente a saída de comandos parasuca partir do shell - Ao enviar a saída colorida de
batparasuc, é possível compartilhar trechos de código no canal com destaque de sintaxe make test > testlog || (suc devops < testlog ; exit 1)é um exemplo de one-liner em Bash que envia o log para o canaldevopsquando os testes falham- Esse one-liner pode entrar no hook
updatede um repositório Git; no push, ele executa os testes e, em caso de falha, rejeita a atualização e avisa a equipe de DevOps - Trabalhos equivalentes às integrações do Slack ou Mattermost podem ser montados com pipes de texto UNIX e scripts
Leitura de canais, notificações e bots
- Um canal
sucé um arquivo de texto continuamente atualizado, entãotail -fpode ler e processar novas linhas - É possível criar um pipeline que exiba notificações com
notify-sendquando novas mensagens chegam, e usargreppara filtrar apenas nomes ou palavras-chave como “build failure” - Para combinar vários canais em um único feed, é possível usar
tail -f /var/lib/suc/* - Com lnav, é possível usar memória da posição lida, favoritos, cores por canal, parsing de data, nome de usuário e campos personalizados, filtragem e consultas SQL
- Bots podem ser implementados se conseguirem ler e escrever no canal ao mesmo tempo
- O exemplo procura expressões de comprimento como
33000 ftno canal - Converte para metros com o comando
units - Escreve uma resposta como
[metric_bot] 33000 ft is 10058.4 meters.no mesmo canal
- O exemplo procura expressões de comprimento como
Comparações e conclusão
- Slack, Mattermost e Discord são citados como sistemas grandes para comparação, mas considera-se que comparações mais justas também incluiriam IRC,
talkewrite - O IRC é avaliado como tendo menos recursos que
sucporque precisa de um bouncer para ler o histórico de chats - Segundo o texto,
talk,ytalkewallnão puderam ser colocados para funcionar em distribuições Linux modernas sucdeixa a autenticação com o SSH, o controle de acesso e a composabilidade com o UNIX, e mantém canais como arquivos de texto- As ferramentas podem ser escritas em qualquer linguagem, desde que consigam ler e escrever texto
1 comentários
Comentários do Hacker News
Isso imediatamente me lembrou de “Master Foo and the Ten Thousand Lines”: http://catb.org/~esr/writings/unix-koans/ten-thousand.html
É a história em que Master Foo diz a um programador visitante que “uma única linha de script de shell contém mais da natureza Unix do que dez mil linhas de C”
Se a funcionalidade central definida de algo pode ser recriada de forma fácil e trivial demais, então talvez essa função não seja o cerne nem a fonte real do seu valor
Fico me perguntando quanto do desenvolvimento de UI desde os anos 1980 a engenharia de software absorveu e automatizou, ou se isso ainda pertence ao domínio da arte puramente humana
Mesmo antes de o Slack surgir e empurrar outras opções para fora, já existiam dezenas de apps, tanto comerciais quanto open source, que faziam isso
O valor central do Slack está no passo seguinte. Ele oferece uma UI/UX visualmente agradável e muito intuitiva, de modo que quase qualquer pessoa pode sentar e usar imediatamente sem estudo, configuração, manual ou treinamento. Não é preciso conhecimento técnico e nem entender mais do que “digite nome de usuário e senha”; o resto parece autoevidente
Mas isso levanta uma pergunta mais difícil. Comparado à alternativa de treinar melhor usuários que já sabem fazer chamadas básicas de linha de comando, o preço do Slack vale a pena? E considerando ainda que o Slack prende seus dados e não os devolve? Quão caro é o custo de sair do Slack?
O suc é desconfortável de usar, sem dúvida, mas parece possível convergir para algo menos inchado e menos monopolista que o Slack
Gostei do resultado em si. É legal ver o que dá para construir com composabilidade
Só que a comparação com Slack e afins não me convence muito. Nenhum engenheiro vai querer se responsabilizar em produção por algo montado assim, remendando peças. Como contratar gente para isso, e como fazer testes e depuração, logs e análise?
Este post replica parte da funcionalidade de chat de apps do tipo Slack, mas chat não é 100% do que esses apps são. São coisas feitas em contextos completamente diferentes, então atacar o estado atual como inchado parece uma leitura torta à la Stockton Rush. Existe inchaço em software, claro, mas “olha mãe, fiz um submarino com tralha, logo submarinos caros são idiotas” não soa como a refutação que o autor pretendia fazer
Utilitários individuais normalmente são fáceis de entender, testar e depurar isoladamente, e logging já vem embutido no sistema. Afinal, não existe o syslog? E por que análise seria necessária?
Ainda assim, fico realmente curioso sobre como seria uma ferramenta tipo Slack se priorizasse agressivamente a simplicidade acima de novos recursos. Parece que o capitalismo como um todo não incentiva esse tipo de desenvolvimento, então há poucos exemplos de software corporativo tão simples quanto seria razoavelmente possível. Mesmo assim, é um experimento mental interessante
De forma parecida, também existe o ii, que roda diretamente sobre IRC: https://tools.suckless.org/ii/
Usa um único arquivo padrão para todas as conversas, a autenticação e o controle de acesso são geridos pela própria rede IRC, e já existem muitas UIs. Assim como neste post, plugins podem ser qualquer coisa que leia um arquivo e escreva em outro
Seria divertido fazer um diagrama spawn of unix reunindo startups que recortaram nichos das funcionalidades do Unix, no estilo do diagrama do craigslist
https://thegongshow.tumblr.com/post/345941486/the-spawn-of-craigslist-like-most-vcs-that-focus
ftp/rsync => Dropbox
suc => Slack
...
Se o título parece clickbait e você está prestes a pular, ele até tem um certo ar de isca mesmo, mas na prática era um texto muito bom
Ele fala de um utilitário chamado “suc” (Simple Unix Chat), que implementa funcionalidades de servidor no estilo Slack e Discord com uma base de código minúscula
A novidade é que ele aproveita ferramentas e metodologias Unix já existentes em vez de reinventá-las. A autenticação fica por conta do SSH, os canais são simplesmente arquivos, e o controle de administrador/moderador é feito com grupos de usuários e permissões de arquivo. Coisas como texto formatado ou upload de arquivos podem ser resolvidas escrevendo os dados desejados no canal e deixando o cliente interpretar. Bots também ficam muito fáceis, basta ligar um pipe ao arquivo do canal
Não dá para fazer um clone perfeito do Slack só com as 5 linhas mostradas no texto para escrever mensagens em um canal, mas é bem impressionante até onde dá para chegar com ferramentas Unix realmente simples
Existe um motivo para haver praticamente zero sistemas grandes de produção escritos com o paradigma de “tudo é arquivo” e shell scripts. Para qualquer sistema minimamente não trivial que precise ser usado no mundo real, isso se torna inaceitável
Junto com isso vêm UI de administração, UI de gerenciamento de usuários, extensibilidade, webhooks etc. “Todo mundo faz login naquela máquina e pode conversar com outros usuários no Linux” não resolve nada disso. Todas as variantes de *nix já tinham isso desde antes de a maioria dos leitores daqui nascer
Infelizmente, esse recurso do Plan 9 ainda não foi portado para Linux. É um bit de permissão parecido com “leitura” ou “escrita”, mas muito mais específico
Não é uma crítica ao autor; o próprio texto deixa isso bem claro e com bastante honestidade
Slack e Discord têm compartilhamento de tela e chat por voz. Isso não é um extra, e sim algo bastante decisivo para a dominância deles no mercado
O post do blog é sobre o suc, e ele admite com honestidade desde o início que o título pode induzir um pouco ao erro, mas no fim há uma mensagem importante que não tem relação direta com o suc
Quase todo software moderno é absurdamente inchado. A ideia de que um servidor de chat rico precise de 1,7 milhão de linhas é absurda. Parafraseando Bill Gates, acho que eu não conseguiria “consumir” tantas linhas de código em um servidor de chat nem se me esforçasse
Não foi só levantar o problema ou sugerir uma alternativa; também merece aplausos por lembrar que não é preciso reinventar a roda. Implementar algo de forma inteligente aproveitando propriedades de sistemas e subsistemas já existentes — por exemplo, usar SSH para autenticação e criptografia — mesmo que fora do propósito originalmente imaginado, mas ainda alinhado à intenção dessas propriedades, é um ótimo exemplo do espírito hacker
Tomara que o mundo do software consiga aprender bastante com esse conceito. Custos mais baixos, prazos de desenvolvimento menores e menos tempo de engenharia desperdiçado reescrevendo código funcionalmente equivalente são bons para desenvolvedores, empresas, usuários finais e, no fim das contas, para a humanidade. Quero ver mais destruição criativa desse tipo no futuro, e também gostaria de aplicar esse tipo de técnica inteligente em projetos futuros
Então o mais correto seria contar apenas as linhas de código em Go
[0] https://github.com/mattermost/mattermost/tree/master/e2e-tests
[1] https://github.com/mattermost/mattermost/tree/master/webapp
Isso normalmente é uma boa ideia porque acelera o lançamento e reduz o tamanho da organização. Mas há um grande trade-off em complexidade e abstração. Quanto mais alto o nível de abstração, pior isso pode ficar. Então não é como se não houvesse motivo nenhum
Atualmente, a maioria das distribuições impede a criação de scripts bash setuid por motivos de segurança.
Mas é possível obter quase o mesmo efeito com uma entrada específica no sudoers.
https://perldoc.perl.org/perlsec#Taint-mode
Em alguns sistemas, pode haver uma condição de corrida fundamental:
https://perldoc.perl.org/perlsec#Shebang-Race-Condition
Também vale a pena ver o uso de
sudo -T:https://perldoc.perl.org/perlsec#Using-Sudo
É uma pena que o modo taint tenha sido removido recentemente do Ruby. O rastreamento de contaminação é um mecanismo poderoso que pode ir muito além de scripts. Por exemplo, se o
#html_safedo Rack/Rails fosse tratado com rastreamento de contaminação, ele funcionaria de forma mais rigorosa e poderia falhar assim que uma string de usuário contaminada fosse concatenada/interpolada/formatada em um fragmento SQL, bloqueando por projeto uma classe de injeções SQL.Antigamente, também havia gente que usava rastreamento de contaminação de forma muito eficaz com CGI/mod_perl. Nunca é uma solução universal, mas ainda é uma ferramenta bastante eficaz.
https://gitlab.com/edouardklein/suc/-/blob/master/suc_wrapper.c
Até ver o arquivo wrapper no repositório do GitLab, eu estava lendo na esperança de que houvesse algum jeito.
Isso é outro assunto, mas achei surpreendente não terem mencionado o talk(1): https://man.netbsd.org/talk.1
O lado bom é que dá para restringir o recebimento ao ambiente local, e aí passa a ser necessário ter uma conta de usuário naquela máquina. Por isso, combina bem com lugares como o SDF ou clusters maiores. Você não precisa implementar autenticação por conta própria; pode deixar isso com PAM ou BSD_Auth. Também não precisa lidar diretamente com sockets nem criptografia; pode deixar isso com TLS. Outra possibilidade é entrar em um shell seguro via SSH e usar tmux com (n)talk.
Claro, (n)talk foi escrito em C, então não são 5 linhas de bash.
[1] https://man7.org/linux/man-pages/man2/write.2.html