3 ポイント 投稿者 GN⁺ 2023-07-06 | 1件のコメント | WhatsAppで共有
  • suc は、Slack・Mattermost 風チャットのいくつかの中核機能を UNIX の基本要素 と Bash の組み合わせで実装する Simple Unix Chat であり、中核ループは 5 行だが実際のスクリプトはより長い
  • メッセージは標準入力から 1 行ずつ読み込まれ、日付とユーザー名を付けて /var/lib/suc/ のチャンネルファイルに追記され、認証と権限制御 は SSH と UNIX 権限モデルに委ねられる
  • Mattermost サーバーが Go コードだけで約 50 万行 あるのに対し、suc ははるかに小さなコードで類似した中核動作を作れる点を強調している
  • usuctail -frlwrappygmentize: コマンド接頭辞を組み合わせて、チャンネルの読み取り、Markdown スタイル出力、コマンド結果の共有を処理する
  • チャンネルが単純なテキストファイルなので、greptailbatlnav、Git hook、ボットスクリプトのような UNIX ツールと簡単に連携できる

suc が減らそうとしている複雑さ

  • suc は Simple Unix Chat の略で、Slack、Mattermost、IRC より機能は少なく、全体の行数も 5 行を超えるが、メッセージを書き込む 中核ループ は 5 行の Bash で構成される
  • 目標機能は、リアルタイムのリッチテキストチャット、ファイル共有、きめ細かなアクセス制御、自動化と外部ツール統合、転送中の暗号化、保存時の選択的暗号化、ユーザー認証である
  • 小さな実装が可能なのは、現代の UNIX 実装が提供する 一貫性があり組み合わせ可能な基本要素 を活用しているためである
  • Mattermost サーバーは Go コードだけで約 50 万行 と対照的で、suc は Mattermost の中核機能をその 0.005% のコードで実装できると見なしている

5 行のメッセージ書き込みループ

  • suc の中核 Bash ループは、標準入力から 1 行を読み取り、日付と実ユーザー名を付けて /var/lib/suc/"$1" ファイルに追記する
  • 実際のスクリプトはセキュリティのための前処理とユーザー名の色計算のためにもっと長いが、チャット履歴を書き込む中核動作は 3 つに要約できる
    • 標準入力から 1 行読む
    • 日付と実ユーザー名を付ける
    • /var/lib/suc/ 配下のチャンネルファイルに追記する
  • 認証、アクセス制御、暗号化、リッチテキストは suc 内部コードではなく、SSH、UNIX アクセス制御 API、テキストベースのモジュール性が担う

認証は SSH が担う

  • suc プロセスはすでに認証済みのユーザーが実行するため、suc 自体には 認証コードがない
  • リモート認証は一般的な UNIX サーバーと同様に ssh が処理する
    • パスワードのような共有シークレット
    • 暗号学的チャレンジ
    • ワンタイムパスワード(OTP)生成デバイス
    • 複数方式を組み合わせた MFA
  • ssh はサーバーもクライアントに対して認証し、中間者攻撃を防ぎ、クライアントとサーバー間のすべてのデータを暗号化する
  • suc の導入は UNIX ホストと ssh サーバー設定に依存する
    • ユーザーがシステム上に存在している必要がある
    • ssh サーバーがそのユーザーのリモートログインを許可している必要がある
  • GNU Guix では宣言的設定により、ユーザーと SSH キーを 1 行で追加でき、その設定行が消えると再構成時にユーザーが削除される

アクセス制御は UNIX 権限モデルが担う

  • suc は認証だけでなくアクセス制御コードも含まず、この方式を セキュリティ不可知論(security agnosticism)と呼んでいる
  • UNIX カーネルはユーザー、グループ、プロセス、ファイルを把握しており、プロセスの 実効所有者(effective owner)を基準にファイルの読み書き権限を判断する
  • /var/lib/suc のチャンネルファイルは suc ユーザーが所有し、グループごとの読み取り権限によって、ユーザーが一部のチャンネルを読めるように構成される
  • 一般ユーザーは直接ファイルに書き込まず suc を実行し、権限の照合と拒否はカーネルが処理する
  • より制限の厳しいコミュニティでは、グループごとの setuid バイナリを置くことができる
    • たとえば suc_blueblue ユーザーが所有し、blue グループだけが実行できる
    • blue グループのユーザーは suc_blue を通じて blue チャンネルに書き込める
    • red グループのユーザーは blue チャンネルを読んだり書いたりできない
  • チャンネル作成補助スクリプト suc_channel.sh は約 80 行で、GNU Guix ユーザーは (suc-private-channel "red" "red") の 1 行で必要な setuid バイナリ、ユーザー、グループ、チャンネルファイルを作成できる
  • GNU Guix の トランザクション更新 は、設定変更が完全に適用されるか、まったく適用されないかを保証し、以前の動作状態へロールバックできる

リッチテキストと usuc

  • 多くのチャットアプリは HTML エンジンでテキストをレンダリングし、Mattermost のデスクトップクライアントは Electron を使用している
  • suc はチャンネルごとに 1 つのテキストファイルを使い、ユーザーは tailcat のようなコマンドラインツールでそれを見られる
  • ターミナルの ANSI エスケープコード と UTF-8 対応を活用すれば、色、絵文字、基本的なリッチテキスト体験を作れる
  • suc はチャンネルファイルへの書き込みだけを行い、読み取りは usuc が担当する
    • suc は権限付きバイナリなので、ロジックと外部依存を最小限にすべきである
    • usuc は呼び出しユーザー権限で実行されるため、機能をより自由に入れられる
  • usuc は複数の小さなツールをつなぎ合わせてチャットクライアントの役割を果たす
    • rlwrap で履歴と行編集機能を提供
    • チャンネルファイル所有者に応じて適切な setuid suc バイナリを選択
    • tail -f -n 20 で直近 20 行とその後に追加される行を表示
    • 入力行が : で始まるか確認
    • ユーザーが入力したテキストを pygmentize で処理
  • pygmentize は標準入力を Markdown として見て ANSI カラーテキストを出力するため、**bold** のようなマークアップを 1 行のコードでサポートできる

チャットコマンドとシェル統合

  • usuc はメッセージが : で始まる場合、通常メッセージとして送らず、そのコマンドを実行して 出力結果suc に送る
  • たとえば gum style --border=rounded --bold --foreground=#F00 "Hello World !" を実行すると、枠線とスタイルが適用されたテキストがチャンネルに表示される
  • コマンドは usuc を呼び出したユーザーの名前空間で実行されるため、ユーザーは他のユーザーに影響を与えず、自分専用のチャットマクロを持てる
  • the dam では、テーブルトーク RPG 中に roll 2d6 のようなコマンドでサイコロを振る用途に使っている

テキストパイプと外部ツール統合

  • ユーザーは usuc のコマンド実行機能の代わりに、シェルでコマンド出力を直接 suc にパイプできる
  • bat の色付き出力結果を suc に送れば、コード断片をシンタックスハイライト付きでチャンネルに共有できる
  • make test > testlog || (suc devops < testlog ; exit 1) は、テスト失敗時にログを devops チャンネルへ送る Bash ワンライナーの例である
  • このワンライナーは Git リポジトリの update hook に入れられ、push 時にテストを実行し、失敗したら更新を拒否したうえで DevOps チームに通知できる
  • Slack や Mattermost の統合機能に相当する作業を、UNIX のテキストパイプとスクリプトで構成できる

チャンネル読み取り、通知、ボット

  • suc チャンネルは継続的に更新されるテキストファイルなので、tail -f で新しい行を読んで処理できる
  • 新着メッセージが来たら notify-send で通知を出すパイプラインを作れ、grep で名前や “build failure” のようなキーワードだけをフィルタできる
  • 複数チャンネルを 1 つのフィードにまとめるには tail -f /var/lib/suc/* を使える
  • lnav を使えば、読み取り位置の記憶、ブックマーク、チャンネル別色分け、日付・ユーザー名・ユーザー定義フィールドの解析、フィルタリング、SQL クエリを活用できる
  • ボットはチャンネルを読み取りつつ同時に書き込めれば実装可能である
    • 例では、チャンネル内で 33000 ft のような長さ表現を探す
    • units コマンドでメートル単位に変換する
    • [metric_bot] 33000 ft is 10058.4 meters. のような応答を同じチャンネルに書き込む

比較対象と結論

  • 比較対象として Slack、Mattermost、Discord のような大規模システムを挙げているが、より公平な比較対象には IRC、talkwrite も含まれると見ている
  • IRC は過去のチャット履歴を読むのに bouncer が必要という点で、suc より機能が少ないと評価している
  • talkytalkwall は現代の Linux ディストリビューションで動くようにはできなかったとしている
  • suc は認証を SSH に、アクセス制御と組み合わせ可能性を UNIX に任せ、チャンネルはテキストファイルとして維持する
  • ツールはテキストを読み書きできさえすれば、どの言語でも書ける

1件のコメント

 
GN⁺ 2023-07-06
Hacker Newsのコメント
  • すぐに「Master Foo and the Ten Thousand Lines」を思い出した: http://catb.org/~esr/writings/unix-koans/ten-thousand.html
    Master Fooが訪ねてきたプログラマに「シェルスクリプト1行には、Cの1万行よりも多くのUnixの本質がある」と語る話

  • あるものの定まった中核機能があまりに簡単かつ些細に再現できるなら、その機能は実際の価値の中核や源泉ではないのかもしれない
    1980年代以降のUI開発において、ソフトウェア工学がどれほど多くの部分を取り込み自動化してきたのか、それとも今なお純粋に人間の芸術の領域なのか気になる

    • その通り。たとえばSlackの中核的な付加価値は「認証されたユーザー同士でメッセージを送る」ことではない
      Slackが登場して他を押しのける前から、商用・オープンソースの両方にすでにその役割を果たすアプリは何十個もあった
      Slackの中核的な価値はその次の段階にある。見た目がよく非常に直感的なUI/UXを提供し、ほとんど誰でも勉強や設定、マニュアル、教育なしに座ったその場ですぐ使える。技術知識は不要で、「ユーザー名とパスワードを入力する」以上のことを理解しなくても、あとは自明に見える
    • とても鋭い視点だ。だとするとSlackの価値はユーザーフレンドリーさであり、sucは明らかに一般ユーザー向けではない
      ただ、そうなるともっと難しい疑問が出てくる。ごく基本的なコマンドライン呼び出しができるユーザーをさらに教育する代替案と比べて、Slackの価格にはそれだけの価値があるのか? Slackがデータを囲い込んで返さない点まで考慮するとどうか? Slackから離脱するコストはどれほど高いのか?
      sucは不便なのは確かだが、Slackよりも肥大化せず、独占的でもない何かに収束できる気はする
  • 成果そのものは気に入った。組み合わせ可能性で何が作れるかを見るのはすばらしい
    ただ、Slackなどと比較するのはあまりしっくりこない。こんなふうにあれこれつぎはぎしたものを、本番環境で責任を持ちたいエンジニアはいないだろう。人材採用はどうするのか、テストやデバッグはどうするのか、ログや分析はどうするのか?
    この記事はSlack系アプリのチャット機能の一部を複製しているが、チャット機能がこうしたアプリの100%ではない。まったく異なる環境で作られたものなので、既存の状態を肥大化していると攻撃するのは、Stockton Rush流のねじれた解釈のように見える。ソフトウェアに肥大化があるのは事実だが、「ママ見て、がらくたで潜水艦を作ったよ。だから高価な潜水艦は愚かだよね」というのが著者の意図した反論のようには聞こえない

    • コードベースの表面積が小さくなれば、そうしたことのかなりの部分は重要ではなくなる、というのが要点だ
      個々のユーティリティはたいてい独立した単位として理解・テスト・デバッグしやすいし、ロギングはすでにシステムに組み込まれている。syslogがあるだろう? それに分析はなぜ必要なんだ?
    • 読みながら自分もそう思った。見事なエンジニアリングで読んでいて楽しかったが、Slackではなく単にIRCと比較したほうが正直だったと思う
      それでも、Slackのようなツールが新機能より単純さを積極的に優先したらどんな姿になるのかは本当に気になる。資本主義全体がこういう開発を奨励していないようで、合理的に可能な限り単純な企業向けソフトウェアの例は多くない。それでも興味深い思考実験だ
  • 同様に、IRCの上で直接動くiiもある: https://tools.suckless.org/ii/
    すべての会話に単一の標準ファイルを使い、認証とアクセス制御はIRCネットワークが直接管理し、すでに多くのUIがある。この記事と同じく、プラグインはあるファイルを読み、別のファイルに書き込むものであれば何でもよい

  • craigslistの図のように、Unixの機能のすき間を切り出したスタートアップを集めたspawn of unix図を作ったら面白そうだ
    https://thegongshow.tumblr.com/post/345941486/the-spawn-of-craigslist-like-most-vcs-that-focus
    ftp/rsync => Dropbox
    suc => Slack
    ...

  • タイトルが釣りっぽく見えて読み飛ばそうとしているなら、ある程度は釣り気味ではあるものの、実際にはかなり良い記事だった
    sucSimple Unix Chat)というユーティリティを扱っており、ごく小さなコードベースで Slack や Discord のようなサーバー機能を実装している
    新しい点は、既存の Unix ツールや方法論を再発明せずに活用していることだ。認証は SSH が処理し、チャンネルは単なるファイルであり、管理者/モデレーターの制御はユーザーグループとファイル権限で処理される。書式付きテキストやファイルアップロードのようなものは、チャンネルに必要なデータを書き込み、クライアント側で解釈させればよい。ボットもチャンネルファイルにパイプをつなげば非常に簡単だ
    記事に出てくる、チャンネルにメッセージを書き込む 5 行だけで完璧な Slack クローンを作れるわけではないが、本当に単純な Unix ツールだけでどこまで行けるのかという点ではかなり印象的だ

    • 既存のチャットプラットフォームは Unix ツールを「再発明」したのではなく、その上に構築するのに適していないから使わなかっただけだ
      「すべてはファイル」というパラダイムやシェルスクリプトで書かれた大規模な本番システムがほぼ 0 件であるのには理由がある。現実で使われるような、ささいでないシステムには受け入れられないやり方だからだ
    • ほとんどのエンドユーザーにとって Slack は クライアント
      そこには管理 UI、ユーザー管理 UI、拡張性、Webhook などが含まれる。「全員がその箱にログインすれば Linux 上で他のユーザーとチャットできる」というだけでは、そのどれも満たせない。そうした機能は、ここの読者の大半が生まれる以前からあらゆる *nix に存在していた
    • ここでは 追記専用ファイル がうまく合いそうだ。一般ユーザーやボットが他人の投稿を編集できないことを保証できるからだ
      残念ながら、この Plan 9 の機能はまだ Linux に移植されていない。「読み取り」や「書き込み」に似た権限ビットだが、はるかに具体的だ
    • 「既存の Unix ツールや方法論を活用する」というのは、ほぼ常に「驚くほど少ないコード行数で X を実装した」という説明になる
      著者を責めているわけではなく、記事でもこの点は非常に率直に認めている
  • Slack と Discord には 画面共有音声チャット がある。これは付加機能ではなく、市場支配力を左右するかなり決定的な機能だ

    • 決定的ではあるが、品質はだんだん落ちている気がする
  • このブログ記事が suc に関するもので、タイトルがやや誤解を招きうることも冒頭から正直に認めているが、最終的には suc とは直接関係のない重要なメッセージがある
    ほとんどすべての現代ソフトウェアはひどく肥大化している。機能豊富なチャットサーバーに 170 万行 が必要だという考えはばかげている。Bill Gates 風に言い換えるなら、頑張ってもチャットサーバーにそれだけのコード行を「消費」できる気がしない
    単に問題提起や代案の提示にとどまらず、車輪を再発明する必要はないと改めて思い出させてくれた点に拍手を送りたい。既存のシステムやサブシステムの性質を、たとえば認証や暗号化に SSH を使うように、本来想定された用途とは違っていてもその性質の意図には沿う形で賢く実装するのは、ハッカー精神の良い例だ
    ソフトウェアの世界がこの考え方から多くを学べるとよい。より低いコスト、より短い開発期間、機能的に同じコードを書き直すことに浪費されるエンジニアリング時間の削減は、開発者にも企業にもエンドユーザーにも、最終的には人類全体にも利益になる。こうした創造的破壊を今後もっと見たいし、自分も今後のプロジェクトでこうした賢いやり方を取り入れてみたい

    • ちなみに TypeScript のコード行の大半は E2E テスト[0] と webapp ディレクトリ[1] にある。名前のとおり、webapp には「Mattermost Web アプリのクライアントコード」が入っている
      なので実際には Go のコード行だけを数えるほうが適切だ
      [0] https://github.com/mattermost/mattermost/tree/master/e2e-tests
      [1] https://github.com/mattermost/mattermost/tree/master/webapp
    • あるサイトで使う Web 認証フレームワークを選ぶ会議にいたことがあるが、バイクシェディング のせいで長引きすぎたので、会議中に HTTP がすでに提供している認証機能をベースに自分で実装し、終わるころにはデモまで見せたことがある
    • 原則としては同意するが、肥大化のかなりの部分は複数プラットフォームを単一コードベースでサポートしようとすることから生じている
      これはたいていリリース速度を上げ、組織規模を小さくできるので良いアイデアだ。しかし複雑性と抽象化という大きなトレードオフがある。抽象化のレベルが高くなるほど悪化することもある。だから理由がまったくないわけではない
  • 現在、ほとんどのディストリビューションではセキュリティ上の理由から setuid bashスクリプト を作れないようにしてある
    ただし、特定の sudoers エントリによってほぼ同じ効果を得ることはできる

    • 実際には bash だけでなく、ほぼすべての shebangスクリプト がそうで、例外は Perl スクリプトくらいである。Perl は uid != euid のときに taint モードが自動的に有効になるためだ
      https://perldoc.perl.org/perlsec#Taint-mode
      一部のシステムには根本的な競合状態があり得る:
      https://perldoc.perl.org/perlsec#Shebang-Race-Condition
      sudo -T の利用も参考になる:
      https://perldoc.perl.org/perlsec#Using-Sudo
      Ruby で最近 taint モードが削除されたのは残念だ。taint 追跡はスクリプトをはるかに超えて使える強力なメカニズムである。たとえば Rack/Rails の #html_safe を taint 追跡で処理していれば、より徹底して動作し、汚染されたユーザー文字列を SQL 断片に連結・補間・整形した瞬間にエラーにして、SQL インジェクションのある種の問題を設計上防ぐこともできたはずだ
      以前は CGI/mod_perl で taint 追跡を非常に効果的に使う人たちもいた。万能な解決策では決してないが、それでもかなり有効なツールである
    • シェルスクリプトを呼び出す Cラッパー を使う。見た目はよくないしスクリプトももう少し堅牢化する必要はあるが、動く :)
      https://gitlab.com/edouardklein/suc/-/blob/master/suc_wrapper.c
    • そんなことを許すディストリビューションなんて本当にあるのか? 自分で試そうとしたときに読んだ資料はどれも、setuid は適用されないと言っていた。実行されるのはファイル自体ではなく、shebang に書かれた対象だからだ
      GitLab リポジトリのラッパーファイルを見るまでは、読みながら何か方法があることを期待していた
  • これは別の話だが talk(1) に触れていないのは意外だ: https://man.netbsd.org/talk.1

    • あるいは write(2) もある[1]。私はすぐに ntalk を思い出したが、同じマシン上でそういうものを最後に使ったのがその頃だったからだ。当時の IRC は暗号化されていなかった
      良い点はローカルでのみ受信するようにでき、その場合はそのマシンのユーザーアカウントが必要になることだ。だから SDF のような場所や、より大きなクラスターにも向いている。認証は自前で行わず、PAM や BSD_Auth に任せればよい。ソケットや暗号化も自前で行わず、TLS に任せればよい。SSH でセキュアシェルに入り、その後 tmux と (n)talk を使うというやり方もできる
      もちろん (n)talk は C で書かれているので、bash 5行ではない
      [1] https://man7.org/linux/man-pages/man2/write.2.html