Chat Unix đơn giản
(the-dam.org)suclà Simple Unix Chat, triển khai một phần các tính năng cốt lõi của kiểu chat như Slack·Mattermost bằng cách kết hợp các thành phần cơ bản của UNIX với Bash; vòng lặp cốt lõi chỉ có 5 dòng, dù script thực tế dài hơn- Mỗi tin nhắn được đọc từng dòng từ đầu vào chuẩn, gắn ngày giờ và tên người dùng rồi thêm vào tệp kênh trong
/var/lib/suc/, còn xác thực và kiểm soát quyền được giao cho SSH và mô hình quyền của UNIX - Không giống máy chủ Mattermost có khoảng 500.000 dòng mã Go,
sucnhấn mạnh rằng có thể tạo ra hành vi cốt lõi tương tự với lượng mã nhỏ hơn rất nhiều usuckết hợptail -f,rlwrap,pygmentizevà tiền tố lệnh:để xử lý đọc kênh, hiển thị kiểu Markdown và chia sẻ kết quả chạy lệnh- Vì kênh chỉ là các tệp văn bản đơn giản, chúng có thể dễ dàng ghép nối với các công cụ UNIX như
grep,tail,bat,lnav, Git hook và script bot
Độ phức tạp mà suc muốn cắt giảm
suclà viết tắt của Simple Unix Chat; ít tính năng hơn Slack, Mattermost hay IRC, và tổng số dòng mã cũng vượt quá 5 dòng, nhưng vòng lặp cốt lõi để gửi tin nhắn được tạo bằng 5 dòng Bash- Các tính năng mục tiêu gồm chat rich text thời gian thực, chia sẻ tệp, kiểm soát truy cập chi tiết, tự động hóa và tích hợp công cụ bên ngoài, mã hóa khi truyền, mã hóa khi lưu trữ tùy chọn và xác thực người dùng
- Việc hiện thực gọn nhẹ này khả thi nhờ tận dụng những thành phần cơ bản nhất quán và có thể kết hợp mà các hệ UNIX hiện đại cung cấp
- Để đối chiếu, riêng máy chủ Mattermost đã có khoảng 500.000 dòng mã Go, và tác giả cho rằng
succó thể hiện thực tính năng cốt lõi của Mattermost chỉ với 0,005% lượng mã đó
Vòng lặp ghi tin nhắn 5 dòng
- Vòng lặp Bash cốt lõi của
sucđọc một dòng từ đầu vào chuẩn, gắn ngày và tên người dùng thực rồi thêm vào tệp/var/lib/suc/"$1" - Script thực tế dài hơn vì có phần tiền xử lý phục vụ bảo mật và tính màu tên người dùng, nhưng hành vi cốt lõi để ghi lịch sử chat có thể tóm lại thành ba bước
- Đọc một dòng từ đầu vào chuẩn
- Gắn ngày và tên người dùng thực
- Ghi nối tiếp vào tệp kênh dưới
/var/lib/suc/
- Xác thực, kiểm soát truy cập, mã hóa và rich text không do mã bên trong
sucxử lý mà do SSH, API kiểm soát truy cập của UNIX và tính mô-đun dựa trên văn bản đảm nhiệm
SSH đảm nhiệm xác thực
- Tiến trình
sucđược chạy bởi người dùng đã xác thực từ trước, nên bản thânsuckhông có mã xác thực - Việc xác thực từ xa do
sshxử lý như trên một máy chủ UNIX thông thường- Bí mật dùng chung như mật khẩu
- Thử thách mật mã
- Thiết bị tạo mật khẩu dùng một lần (OTP)
- MFA kết hợp nhiều phương thức
sshcũng xác thực máy chủ với phía client để ngăn tấn công trung gian, đồng thời mã hóa toàn bộ dữ liệu giữa client và server- Việc cài đặt
sucphụ thuộc vào máy chủ UNIX và cấu hình máy chủssh- Người dùng phải tồn tại trên hệ thống
- Máy chủ
sshphải cho phép người dùng đó đăng nhập từ xa
- Trên GNU Guix, có thể thêm người dùng và khóa SSH chỉ với một dòng cấu hình khai báo; khi dòng cấu hình biến mất, người dùng cũng bị xóa trong quá trình tái cấu hình
Mô hình quyền UNIX đảm nhiệm kiểm soát truy cập
suckhông chỉ không chứa mã xác thực mà còn không chứa cả mã kiểm soát truy cập; tác giả gọi cách tiếp cận này là security agnosticism- Kernel UNIX biết về người dùng, nhóm, tiến trình và tệp, đồng thời quyết định quyền đọc/ghi tệp dựa trên chủ sở hữu hiệu lực của tiến trình
- Các tệp kênh trong
/var/lib/sucthuộc sở hữu của người dùngsuc, và quyền đọc theo nhóm được cấu hình để người dùng có thể đọc một số kênh nhất định - Người dùng thông thường không ghi trực tiếp vào tệp mà chạy
suc; việc đối chiếu quyền và chặn truy cập do kernel xử lý - Với cộng đồng cần giới hạn chặt hơn, có thể dùng binary
setuidtheo từng nhóm- Ví dụ
suc_bluethuộc sở hữu của người dùngbluevà chỉ nhómbluemới được thực thi - Người dùng trong nhóm
bluecó thể ghi vào kênhbluethông quasuc_blue - Người dùng nhóm
redkhông thể đọc hay ghi vào kênhblue
- Ví dụ
- Script hỗ trợ tạo kênh
suc_channel.shdài khoảng 80 dòng, và người dùng GNU Guix có thể tạo binary setuid cần thiết, người dùng, nhóm và tệp kênh chỉ với một dòng(suc-private-channel "red" "red") - Cập nhật giao dịch của GNU Guix bảo đảm thay đổi cấu hình либо được áp dụng hoàn toàn, либо không áp dụng gì cả, và có thể rollback về trạng thái hoạt động trước đó
Rich text và usuc
- Nhiều ứng dụng chat render văn bản bằng engine HTML, còn client desktop Mattermost dùng Electron
sucdùng một tệp văn bản cho mỗi kênh, và người dùng có thể xem chúng bằng các công cụ dòng lệnh nhưtailhoặccat- Bằng cách tận dụng mã escape ANSI của terminal và hỗ trợ UTF-8, có thể tạo màu sắc, emoji và trải nghiệm rich text cơ bản
succhỉ ghi vào tệp kênh, còn việc đọc dousucđảm nhiệmsuclà binary có đặc quyền nên cần giữ logic và phụ thuộc bên ngoài ở mức tối thiểuusucchạy với quyền của người gọi nên có thể bổ sung tính năng thoải mái hơn
usuckết nối nhiều công cụ nhỏ để đóng vai trò client chat- Cung cấp lịch sử và chỉnh sửa dòng với
rlwrap - Chọn binary
sucsetuid phù hợp theo chủ sở hữu tệp kênh - Hiển thị 20 dòng cuối và các dòng mới thêm bằng
tail -f -n 20 - Kiểm tra xem dòng nhập có bắt đầu bằng
:hay không - Xử lý văn bản người dùng nhập bằng
pygmentize
- Cung cấp lịch sử và chỉnh sửa dòng với
pygmentizecoi đầu vào chuẩn là Markdown và xuất văn bản màu ANSI, nên có thể hỗ trợ markup như**bold**chỉ với một dòng mã
Lệnh chat và tích hợp shell
- Nếu tin nhắn bắt đầu bằng
:,usucsẽ không gửi như tin nhắn thường mà thực thi lệnh đó rồi gửi kết quả đầu ra quasuc - Ví dụ, chạy
gum style --border=rounded --bold --foreground=#F00 "Hello World !"sẽ làm văn bản có viền và kiểu dáng xuất hiện trong kênh - Vì lệnh được chạy trong namespace của người dùng gọi
usuc, mỗi người có thể có macro chat riêng mà không ảnh hưởng tới người khác - Trên the dam, nó được dùng để tung xúc xắc trong tabletop RPG với các lệnh như
roll 2d6
Tích hợp text pipe và công cụ bên ngoài
- Thay vì dùng chức năng chạy lệnh của
usuc, người dùng có thể pipe trực tiếp đầu ra của lệnh từ shell vàosuc - Nếu gửi đầu ra có màu của
batvàosuc, có thể chia sẻ đoạn mã trong kênh với syntax highlighting make test > testlog || (suc devops < testlog ; exit 1)là ví dụ một dòng Bash gửi log vào kênhdevopskhi test thất bại- Một dòng này có thể được đặt trong hook
updatecủa kho Git để chạy test khi push, từ chối cập nhật nếu thất bại và đồng thời báo cho đội DevOps - Những việc tương đương tính năng tích hợp của Slack hay Mattermost có thể được xây dựng bằng UNIX text pipe và script
Đọc kênh, thông báo và bot
- Kênh
suclà các tệp văn bản được cập nhật liên tục, nên có thể đọc và xử lý dòng mới bằngtail -f - Có thể tạo pipeline hiện thông báo bằng
notify-sendkhi có tin nhắn mới, hoặc lọc chỉ những từ khóa như tên người dùng hay “build failure” bằnggrep - Để gộp nhiều kênh vào một feed, có thể dùng
tail -f /var/lib/suc/* - Với lnav, có thể nhớ vị trí đã đọc, dùng bookmark, tô màu theo kênh, parse ngày giờ·tên người dùng·trường tùy chỉnh, lọc và chạy truy vấn SQL
- Bot có thể được triển khai nếu nó vừa đọc vừa ghi được vào kênh
- Ví dụ minh họa là tìm các biểu thức độ dài như
33000 fttrong kênh - Dùng lệnh
unitsđể đổi sang mét - Ghi phản hồi như
[metric_bot] 33000 ft is 10058.4 meters.vào cùng kênh
- Ví dụ minh họa là tìm các biểu thức độ dài như
Đối tượng so sánh và kết luận
- Tác giả lấy Slack, Mattermost, Discord làm đối tượng so sánh, nhưng cho rằng đối tượng công bằng hơn còn nên bao gồm IRC,
talk,write - IRC bị đánh giá là kém tính năng hơn
sucvì cần bouncer nếu muốn đọc lại lịch sử chat cũ - Tác giả cho biết chưa thể làm cho
talk,ytalk,wallhoạt động trên các bản phân phối Linux hiện đại sucgiao xác thực cho SSH, giao kiểm soát truy cập và tính kết hợp cho UNIX, và giữ kênh ở dạng tệp văn bản- Chỉ cần công cụ có thể đọc và ghi văn bản thì có thể được viết bằng bất kỳ ngôn ngữ nào
1 bình luận
Ý kiến trên Hacker News
Ngay lập tức tôi nhớ đến “Master Foo and the Ten Thousand Lines”: http://catb.org/~esr/writings/unix-koans/ten-thousand.html
Đây là câu chuyện Master Foo nói với một lập trình viên đến thăm rằng “một dòng shell script chứa nhiều bản tính Unix hơn 10 nghìn dòng C”
Nếu chức năng cốt lõi đã được định nghĩa của một thứ gì đó có thể được tái tạo quá dễ dàng và tầm thường, thì chức năng đó có thể không phải là cốt lõi hay nguồn gốc thực sự của giá trị
Tôi tự hỏi từ thập niên 1980 đến nay, bao nhiêu phần của việc phát triển UI đã được kỹ nghệ phần mềm hấp thụ và tự động hóa, hay nó vẫn còn là lĩnh vực của nghệ thuật thuần túy của con người
Ngay cả trước khi Slack xuất hiện và lấn át các thứ khác, đã có hàng chục ứng dụng làm việc đó, cả thương mại lẫn mã nguồn mở
Giá trị cốt lõi của Slack nằm ở bước tiếp theo. Nó cung cấp UI/UX đẹp mắt và cực kỳ trực quan, để gần như ai cũng có thể ngồi xuống và dùng ngay mà không cần học, cấu hình, đọc manual hay được đào tạo. Không cần kiến thức kỹ thuật, và chỉ cần hiểu đến mức “nhập tên người dùng và mật khẩu” là phần còn lại đã trở nên hiển nhiên
Nhưng rồi lại nảy sinh câu hỏi khó hơn. So với phương án đào tạo thêm cho người dùng vốn đã biết thực hiện một lệnh dòng lệnh rất cơ bản, mức giá của Slack có đáng không? Và nếu tính thêm chuyện Slack giữ dữ liệu và không trả lại thì sao? Chi phí rời bỏ Slack đắt đến mức nào?
Đúng là suc bất tiện, nhưng có vẻ nó có thể hội tụ về một thứ gì đó ít cồng kềnh và ít độc quyền hơn Slack
Tôi thích thành quả này. Thật tuyệt khi thấy tính kết hợp có thể tạo ra những gì
Tuy vậy, việc so sánh với Slack và các thứ tương tự thì không thật sự thuyết phục với tôi. Sẽ không có kỹ sư nào muốn chịu trách nhiệm trong production cho một thứ được ghép nối đủ kiểu như thế này. Tuyển người kiểu gì, kiểm thử và debug ra sao, còn log và phân tích thì làm thế nào?
Bài viết này tái tạo một phần chức năng chat của các ứng dụng kiểu Slack, nhưng chức năng chat không phải là 100% của những ứng dụng đó. Chúng được xây dựng trong những bối cảnh hoàn toàn khác nhau, nên việc công kích hiện trạng là cồng kềnh có vẻ như một kiểu diễn giải méo mó theo phong cách Stockton Rush. Phần mềm đúng là có sự cồng kềnh, nhưng “mẹ ơi nhìn này, con làm được tàu ngầm từ đống đồ linh tinh, vậy nên những chiếc tàu ngầm đắt tiền thật ngốc” nghe không giống như lời phản bác mà tác giả định đưa ra
Các utility riêng lẻ thường dễ hiểu, dễ kiểm thử và dễ debug như những đơn vị tách biệt, còn logging thì đã có sẵn trong hệ thống rồi. Chẳng phải đã có syslog sao? Và vì sao lại cần phân tích?
Dù vậy, tôi thật sự tò mò một công cụ kiểu Slack sẽ trông ra sao nếu nó ưu tiên sự đơn giản một cách quyết liệt hơn là thêm tính năng mới. Có vẻ chủ nghĩa tư bản nói chung không khuyến khích kiểu phát triển này, nên không có nhiều ví dụ về phần mềm doanh nghiệp đơn giản ở mức hợp lý có thể đạt được. Dù sao đây vẫn là một thí nghiệm tư duy thú vị
Tương tự, cũng có ii hoạt động trực tiếp trên IRC: https://tools.suckless.org/ii/
Nó dùng một tệp chuẩn duy nhất cho mọi cuộc trò chuyện, còn xác thực và kiểm soát truy cập thì do chính mạng IRC quản lý, và đã có sẵn rất nhiều UI. Cũng như bài này, plugin có thể là bất cứ thứ gì đọc một tệp nào đó và ghi sang tệp khác
Giống như sơ đồ craigslist, sẽ thú vị nếu làm một sơ đồ spawn of unix tập hợp các startup cắt ra từ những khe chức năng của Unix
https://thegongshow.tumblr.com/post/345941486/the-spawn-of-craigslist-like-most-vcs-that-focus
ftp/rsync => Dropbox
suc => Slack
...
Nếu bạn định bỏ qua vì tiêu đề trông như câu view, thì đúng là có phần câu view thật, nhưng thực ra đây là một bài khá hay
Bài viết nói về một tiện ích tên là “suc” (Simple Unix Chat), dùng một codebase cực nhỏ để hiện thực các chức năng kiểu máy chủ như Slack hay Discord
Điểm mới là nó tận dụng các công cụ và phương pháp luận Unix sẵn có thay vì phát minh lại chúng. Xác thực do SSH xử lý, kênh chỉ đơn giản là một tệp, còn quyền quản trị/kiểm duyệt được xử lý bằng nhóm người dùng và quyền tệp. Những thứ như văn bản có định dạng hay tải tệp lên thì chỉ cần ghi dữ liệu mong muốn vào kênh và để client tự diễn giải. Bot cũng rất dễ làm nếu nối pipe vào tệp kênh
Chỉ với 5 dòng ghi tin nhắn vào kênh như trong bài thì chưa thể tạo ra một bản sao Slack hoàn chỉnh, nhưng vẫn khá ấn tượng khi thấy có thể đi xa đến đâu chỉ với những công cụ Unix thật đơn giản
Có lý do khiến gần như không tồn tại hệ thống production quy mô lớn nào được viết bằng mô hình “mọi thứ là tệp” và shell script. Với các hệ thống không quá tầm thường để dùng trong thực tế, cách đó là không thể chấp nhận được
Trong đó có UI quản trị, UI quản lý người dùng, khả năng mở rộng, webhook, v.v. Chuyện “mọi người đăng nhập vào cái máy đó thì có thể chat với người dùng khác trên Linux” không giải quyết được bất kỳ thứ gì trong số này. Những tính năng như vậy đã có trên mọi *nix từ trước khi phần lớn độc giả ở đây ra đời
Đáng tiếc là tính năng Plan 9 này vẫn chưa được port sang Linux. Nó là các bit quyền tương tự “đọc” hay “ghi”, nhưng cụ thể hơn nhiều
Không phải để chê tác giả, và bài viết cũng thừa nhận điều này rất thẳng thắn
Slack và Discord có chia sẻ màn hình và chat thoại. Đây không phải tính năng phụ mà là yếu tố khá mang tính quyết định với vị thế thống trị thị trường của họ
Ngay từ đầu, bài blog này cũng thẳng thắn thừa nhận rằng nó nói về suc và tiêu đề có thể hơi gây hiểu nhầm, nhưng rốt cuộc vẫn có một thông điệp quan trọng không liên quan trực tiếp đến suc
Gần như mọi phần mềm hiện đại đều phình to một cách khủng khiếp. Ý nghĩ rằng cần tới 1,7 triệu dòng cho một máy chủ chat giàu tính năng là điều vô lý. Nói theo kiểu Bill Gates, dù có cố thì tôi cũng không nghĩ mình có thể “tiêu thụ” ngần ấy dòng code cho một máy chủ chat
Không chỉ nêu vấn đề hay đưa ra một phương án thay thế, bài này còn đáng được tán thưởng vì nhắc lại rằng không cần phát minh lại bánh xe. Việc triển khai một cách thông minh bằng cách tận dụng các thuộc tính của hệ thống và hệ thống con sẵn có, chẳng hạn dùng SSH cho xác thực và mã hóa, dù khác mục đích ban đầu nhưng vẫn phù hợp với ý đồ của các thuộc tính đó, là một ví dụ hay về tinh thần hacker
Tôi mong thế giới phần mềm có thể học được nhiều từ ý tưởng này. Chi phí thấp hơn, thời gian phát triển ngắn hơn, và giảm thời gian kỹ sư bị lãng phí để viết lại cùng một loại code về mặt chức năng đều có lợi cho lập trình viên, công ty, người dùng cuối, và rốt cuộc là toàn nhân loại. Tôi muốn thấy nhiều hơn kiểu phá hủy sáng tạo này trong tương lai, và bản thân cũng muốn thử áp dụng những kỹ thuật thông minh như vậy trong các dự án sau này
Vì vậy thực ra hợp lý hơn nếu chỉ đếm số dòng Go
[0] https://github.com/mattermost/mattermost/tree/master/e2e-tests
[1] https://github.com/mattermost/mattermost/tree/master/webapp
Đây thường là ý tưởng tốt vì giúp tăng tốc độ phát hành và giảm quy mô tổ chức. Nhưng nó đi kèm đánh đổi lớn về độ phức tạp và mức độ trừu tượng. Mức trừu tượng càng cao thì đôi khi càng tệ hơn. Vậy nên cũng không phải là hoàn toàn vô cớ
Hiện nay, đa số các bản phân phối chặn việc tạo script bash setuid vì lý do bảo mật.
Tuy vậy, với một số mục sudoers nhất định, vẫn có thể đạt được hiệu quả gần như tương tự.
https://perldoc.perl.org/perlsec#Taint-mode
Trên một số hệ thống có thể tồn tại điều kiện tranh chấp ở mức nền tảng:
https://perldoc.perl.org/perlsec#Shebang-Race-Condition
Cũng đáng tham khảo việc dùng
sudo -T:https://perldoc.perl.org/perlsec#Using-Sudo
Khá tiếc là chế độ taint gần đây đã bị loại khỏi Ruby. Theo dõi taint là một cơ chế mạnh, có thể hữu ích vượt xa phạm vi script. Chẳng hạn, nếu Rack/Rails xử lý
#html_safebằng theo dõi taint thì nó có thể hoạt động triệt để hơn nhiều, đến mức sẽ nổ ngay khi một chuỗi người dùng bị taint bị nối/chèn nội suy/định dạng vào một mảnh SQL, từ đó ngăn chặn được cả một lớp SQL injection ngay từ thiết kế.Trước đây cũng từng có những người dùng theo dõi taint rất hiệu quả với CGI/mod_perl. Nó tuyệt đối không phải lời giải vạn năng, nhưng vẫn là một công cụ khá hiệu quả.
https://gitlab.com/edouardklein/suc/-/blob/master/suc_wrapper.c
Trước khi xem file wrapper trong kho GitLab, lúc đọc tôi vẫn hy vọng có cách nào đó làm được.
Đây là chuyện khác, nhưng tôi ngạc nhiên vì không thấy nhắc đến talk(1): https://man.netbsd.org/talk.1
Điểm hay là có thể cấu hình chỉ nhận cục bộ, và như vậy sẽ cần có tài khoản người dùng trên máy đó. Vì thế nó rất hợp với những nơi như SDF hoặc các cụm lớn hơn. Bạn cũng có thể giao phần xác thực cho PAM hay BSD_Auth thay vì tự làm. Tương tự, có thể giao socket hay mã hóa cho TLS thay vì tự xử lý. Cũng có thể SSH vào shell an toàn rồi dùng tmux cùng với (n)talk.
Tất nhiên, (n)talk được viết bằng C nên không phải kiểu 5 dòng bash.
[1] https://man7.org/linux/man-pages/man2/write.2.html