bits & bicycles
80 subscribers
2 photos
1 video
25 links
Download Telegram
capabilities - root по кусочкам

Что-то мне подсказывает, uid 0 был выбран для суперпользователя по банальной причине: удобно писать if (uid && !allowed(uid, ...)) goto fail;.

Со временем пришло осознание, что во множестве ситуаций нужен не весь root, а только кусочек. Ну странно же давать право менять модули ядра nginx'у, которому всего-то надо забиндить 80й порт.

В итоге, права суперпользователя нарезали на capabilities. В 6.1.3 их целых 43 штуки. Например, можно запретить всё и выдать только разрешения биндить 0-1023 порты CAP_NET_BIND_SERVICE и посылать raw-пакеты CAP_NET_RAW. Или выдать разрешение выдавать разрешения CAP_SETPCAP.

capabilities, так же как и uid, – атрибут не пользователя, а процесса (на самом деле треда, но не важно). Напоминаю, пользователей не существует. Раньше у процесса был только атрибут-число uid, теперь к нему добавился атрибут-битсет capabilities. При этом capabilities и uid никак не связаны. У тебя может быть всемогущий uid 1337 и никчёмный uid 0.

Посмотреть этот битсет можно в #procfs

$ grep '^Cap' /proc/self/status
CapInh: 10000000a80425fb
CapPrm: 20000000a80425fb
CapEff: 30000000a80425fb
CapBnd: 40000000a80425fb
CapAmb: 5000000000000000


Работать с привелегиями можно через пару syscall'ов capset и prctl. Привелегии можно повышать, понижать, понижать временно, настраивать наследование дочерними процессами.

Также для повышения привилегий есть механизм setcap на файле, аналогичный suid биту. Но это если файловая система поддерживает атрибуты у файлов, если ты ретроград с ext1, то остаётся только пить галоперидол получать классический root.

https://flapenguin.me/bnb/capabilities
👍3🤩3🔥2💯1
Тут у Casey Muratori вышло видео с рассуждением о том, как Clean Code подходы убивают производительность софта. (Настоятельно рекомендую канал, кстати, набрасывает Casey как боженька. Только держите в голове, что он и сам фанатик.)

Видео о том, как адепты сферического ООП пропагандируют, что всё должно быть слабо связано, полиморфно, инкапсулировано и изолировано. И, конечно же, раздроблено на двухстрочники в отдельных файлах. Тогда, мол, поддерживать проект будет просто, а о производительности позаботится компилятор с рантаймом.

И как ты уже догадался, кроме тебя о производительности никто не позаботится. Сферический ООП по лучшим практикам замедляет твой код на порядок. И речь не о преждевременной оптимизации. Преждевременная оптимизация и заведомо неэффективный код – разные вещи.

Я хочу немного порассуждать о первой части обещания, о лёгкой поддержке. Реальность в том, что абстракции почти всегда плохие и текут. Да текут так, что разработчику надо знать реализацию самой сущности и всех остальных абстракций вокруг. И это, внезапно, совсем не помогает бороться со сложностью, а делает только хуже.

На реддите основная претензия к видео – “представьте, что завтра вам надо будет добавить серобуромалиновые полигоны с дырками”. На это я могу ответить только одно: а представьте, что нет. Представьте, что не надо. Или что через 2 года, когда будет надо, текущей абстракции всё равно не хватит и её придётся пересобирать с нуля.

"Clean Code подходы" и прочий сферический SOLID-OOP, как и любой другой инструмент, хорошо работает для одних целей и плохо для других. Но его почему-то воспринимают как нерушимую истину. Из-за этого не следовать ему может быть страшно и даже стыдно. Но самое страшное, что произойдёт – ты поругаешься с ярыми евангелистами, не напишешь FizzBuzzEnterpriseEdition, а где-то далеко горько заплачет один Дядя Боб.

BTW Мне нравится, что люди начали замечать, что книга то – говно.

#performance #architecture
🔥3🌭2
groups – наш пользователь и человек, и пароход, и wheel

Я говорил, что gid у процесса очень похож на uid. Но на самом деле не совсем похож.

gid - такой же произвольный uint32, как и uid. Только uid у твоего процесса один, а gid'ов может быть много. Много групп groups, к которым у процесса есть доступ, и одна активная, собственно, gid.

Увидеть это можно запустив обычный id:

❯ id uid=1000(flapenguin) gid=1001(flapenguin) groups=1001(flapenguin),998(docker)


Но зачем?

Нужна возможность как-то получить процесс, который может и cron настраивать и docker запускать. Т.е. с доступом в разграниченные части fs. И именно groups дают тебе такие права. У процесса таких может быть аж 65536 (2^16).

У отдельного gid'а задача более прозаичная. Он нужен как дефолт для операций типа open(..., O_CREAT) и clone.

И то и другое наследуется при создании процесса. И то и другое может менять только владелец capability CAP_SETGID через setgid и setgroups.

Именно поэтому после добавления себя в группу тебе надо перелогиниться. Перелогинивает тебя сессионный менеджер, у которого этот самый CAP_SETGID есть. Будь то systemd-loging или sshd.

Кстати, в #POSIX'е есть setuid-утилита newgrp, которая за тебя перелогинится насоздаёт лишних процессов. Не рекомендую.

$ ps -ft
PID TTY      STAT   TIME COMMAND
1638690 pts/0    Ss     0:00 -bash
1638804 pts/0    S      0:00  _ newgrp
1638805 pts/0    S      0:00      _ bash
1638815 pts/0    S      0:00          _ newgrp
1638816 pts/0    S      0:00              _ bash
1638829 pts/0    R+     0:00                  _ ps -ft


https://flapenguin.me/bnb/groups
2🍾1
scm_rights - посылаем дескрипторы почтой

Всё это время я тут задвигал про uid'ы и gid'ы. Это неплохая система доступов, позволяющая делать комплексные вещи.

Но бывает, твоя задача не решается в рамках такой системы. Ну вот не лезет и всё тут.

Допустим, тебе надо разделить accept соединения и его обработку. Потому что работа твоего сервера с capability CAP_NET_BIND_SERVICE – принимать соединения. Потому что работать же с этими соединениями можно хоть из под nobody. Потому что тебе хочется как-то понизить права.

Да, ты можешь форкнуться и понизить нового себя в правах. Но наследование дескриптора произойдёт только в момент форка. Так что форкаться тебе придётся на каждое соединение. Очевидно, что быстро работать это не может.

Да, ты можешь поднять внутри своего основного процесса колхозный RPC и перекладывать через него данные. Просто доставать данные из одной трубы, чтобы переложить в другую.

Но зачем всё это, если ты можешь отправлять fd'шки почтой по unix-сокетам.

Буквально, по unix-сокетам можно отправить почти любой открытый дескриптор соседу. Фича называется SCM_RIGHTS, часть socket ancillary data. И это не что-то новое. Фича есть в #POSIX'е минимум 15 лет.

Используется это добро, например, в DRI Xserver'а и в wayland'е, чтобы отдать пользователю преднастроеный дескриптор /dev/video. В nodejs (через поддержку в libuv) используется для ipc и балансировки cluster'ом.

Есть ещё SO_PEERCRED, чтобы узнать uid/gid процесса с другой стороны сокета, и SCM_CREDENTIALS, чтобы отправить любые uid/gid. Которые проверятся ядром, а его не обманешь. Такая бесплатная авторизация в локальных демонах. Зачем при такой фиче нужен kostilique .Xauthority для меня загадка.

https://flapenguin.me/bnb/scm_rights
🔥4🌭1
euid - от кого запустили sudo

Уже достаточно давно я разбирал как запускать всякое от root’а через setuid флажки.

Сегодня расскажу про вторую половинку этого механизма. Без которой ничего, на самом деле, не заведётся.

Потому что мало просто выбросить свою старую идентичность и стать root’ом. sudo нужно знать от кого его запустили, иначе как он будет свой /etc/sudoers читать. А ping было бы неплохо иметь возможность SIGSTOP’нуть или SIGKILL’нуть не из под root’а.

Никакой сложной системы тут нет и решение проблемы поистине дубовое. Старая идентичность при запуске setuid бинарников не выбрасывается, а перекладывается в соседние поля процесса.

sudo знает кто его запустил из saved user id и saved group id.

Ядро знает, кому можно kill’ять ping из real user id и real group id.

Поля эти есть у каждого процесса и у них даже есть описание в man 7 capabilities. Оно старательно пытается выглядеть не как ой-нам-тут-надо-было-прокинуть-получилось-как-получилось, но костыли за широкими мануалами не спрячешь.

Тем не менее:
- euid Effective User ID — это про проверки unix permission в ядре (то, что делает root’а root’ом)
- suid Saved User ID — это способ сообщить setuid процесс’у от чьего имени его запустили
- ruid Real User ID — это разрешение процессам с таким же uid посылать любые сигналы

Посмотреть на всё это добро можно через procfs. Дока последнего говорит, что они лежат в /proc/1337/status в таком формате

Uid | Real, effective, saved set, and file system UIDs
Gid | Real, effective, saved set, and file system GIDs


https://flapenguin.me/bnb/euid
🌭2🤯1
This media is not supported in your browser
VIEW IN TELEGRAM
А вы знали, что для регистрации на гитхабе теперь надо крутить собаку?
🔥4😁2
Почему ON CONFLICT DO NOTHING тебя ненавидит и конфликтует

В стандарте SQL есть такая конструкция INSERT ... ON CONFLICT (constraint) DO UPDATE, также известная как UPSERT. Нужна чтобы добавить или подобновить строку. на И есть её брат-близнец INSERT ... ON CONFLICT (constraint) DO NOTHING, которая просто превращается в no-op, если строка уже есть.

Но DO NOTHING этот работает не совсем интуитивно. По крайней мере, в #postgresql.

Представь, что у тебя есть CREATE TABLE numbers (value INT UNIQUE) и ты выполняешь одновременно два одинаковых запроса в разных сессиях (горизонтальное масштабирование и вот это вот всё): INSERT INTO numbers (value) VALUES (1) ON CONFLICT DO NOTHING.

Одной сессии повезёт больше и она добавит строку, пусть даже в транзакции. Другой сессии же придётся встать в очередь и ждать. Потому что любое предположение о другой транзакции может быть неправильным: строку могут удалить следующим запросом, да всю транзакцию могут отменить, в конце концов.

И вот ты вроде написал ON CONFLICT DO NOTHING, а де-факто в PostgreSQL получил не DO NOTHING, а DO NOTHING NOTHING OR LOCK numbers_value_unique_ix. Насколько долгий lock? На lock_timeout, который ты, конечно же, забыл настроить для своей сессии.

“Ну и что” – скажешь ты, “мы хотели одну строку и получили в итоге одну строку, пусть и подождали немного”. И, действительно, обычно нет ничего страшного если строка одна. Но если ты UPSERT’ишь с сотню одинаковых строк в нескольких сессиях, то конфликтов становится слишком много и ты рискуешь выстроить все свои INSERT’ы в очередь, которая просто не будет успевать разгребаться.

https://flapenguin.me/bnb/psql-on-conflict
🤔3👍1😢1
В vscode в последнем релизе завезли аналог ngrok’а
Да ещё и с авторизацией из коробки
https://code.visualstudio.com/docs/editor/port-forwarding
👍3🐳1
Ребята и Timescale'а собрали State of #postgresql 2023
https://www.timescale.com/state-of-postgres/2023

Из прикольного: четверть людей ходят в посгрю из js/ts, т.е. из nodejs'а. (И я один из них)

На первых двух местах расширений предсказуемо postgis и, собственно, сам timescaledb. Оба расширения очень крутые, советую и тебе попробовать, если ты ещё не
👌6
ringbuffer даром

Кольцевой буфер (он же ring buffer) – это структура данных #datastructure, которая позволяет тебе писать с одного конца (write) и читать с другого (read). Она классно подходит, когда тебе нужны FIFO очереди, потоки и всякие системы, где читатели с писателями работают отдельно друг от друга.

                        v read
|kcheburek..............topke|
^ write


Но есть проблема. Ты лишаешься простоты последовательных блоков памяти, как при записи, так и при чтении. Теперь у тебя два куска памяти. Ну или всё таки один. Как получится, зависит от текущего положения внутри буфера. Приходится писать какие-то проверки, данные копировать в непрерывную память снаружи. Неудобно.

Ну и вроде ничего не поделаешь. Память ведь одним куском идёт.

Но доступ к памяти устроен чуть хитрее. Отдельный кусок процессора, MMU, транслирует адреса по табличке (для простоты давай считать, что блоками по 4KiB). И, что главное, разные адреса до трансляции могут вести в одно и тот же адрес в итоге. Работаешь ты с виртуальными адресами, а в итоге получешь адрес в физической памяти.

virtual            mmu     physical
0x00001???
0x00002??? - ---\ /-->0x00002???
0x00003??? \------>0x00003???
0x00004??? - ------/
...


Я думаю, ты уже понял куда я клоню. Ты можешь отобразить одни и те же 4KiB (или сколько там тебе надо) друг за другом. И получишь ring buffer даром! В #POSIX эта штука контроллируется, как и большинство настроек виртуальной памяти, через mmap. Физических адресов тебе, конечно, в user-space никто не даст, но они тебе и не нужны.

// Ищем место под 2 блока подряд в виртуальной памяти
base = mmap(0, 2 * size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
// Создаём файл в памяти (технический момент, полноценный файл нам не нужен)
int fd = memfd_create("/oh/my/ringbuffer", 0);
ftruncate(fd, size);
// Отображаем блоки в файл
mmap(base , size, PROT_xxx, MAP_FIXED | MAP_SHARED, fd, 0);
mmap(base + size, size, PROT_xxx, MAP_FIXED | MAP_SHARED, fd, 0);
read = write = base;


Всё, можешь читать свои 17 байт в read или писать во write. И не париться какая сейчас позиция в буфере: начало, середина, или вообще предпоследний байт.

https://flapenguin.me/bnb/mmap-ringbuffer
🍓6
Псс, спишь? А я вышел из спячки спустя полгода там затащили #sqlite в #nodejs. Пока за experimental флагом. https://nodejs.org/docs/latest/api/sqlite.html
Ну, как затащили, выставили наружу тот, что и так был в процессе.

Круто, что сделали нормальный синхронный API как у better-sqlite3. А не "крутим треды и тратим на ipc в десять раз больше чем на полезную работу, зато асинхронно! 👍" в стиле sqlite3.

Теперь можно хранить стейт своей программы на фс в нормальном формате, а не в гигантских json'ах. А с PRAGMA journal_mode=WAL; (и небольшим тюном busy_timeout) можно даже получить что-то типа shared memory между cluster worker'ами или worker_thread'ами.

И всё это без бинарных зависимостей или сборки gyp'ом! Прям чудеса!
Please open Telegram to view this post
VIEW IN TELEGRAM
👏4🦄42👌1
Кстати про shared memory. В js’е же давно есть SharedArrayBuffer! Слово shared в названии есть, что-то про память есть, поддерживается везде. Звучит ровно как то что надо!

Насколько давно он существует? Ну… его выключали из-за Spectre/Meltdown, версия хрома тогда была двухзначной, а “короной” называли пиво, да головные уборы монархов 🤴. Если тебе кажется, что это было вчера, то проснись, Нео, это было 6.5 лет назад.

Минус один – “молодой человек это не для вас написано”. API этот для людей, которые используют web как таргет для компиляции и как способ дистрибуции своих приложений. Вот эти все Unity и Unreal Engine’ы.

Никто никогда этого не скрывал, в презентациях пропоузала от 2015 года примерно так и написано “надо как-то pthread’ы собирать в asm.js ” (да, тогда ещё был asm.js). Да и автор пропозулала почти прямо об этом пишет 🤓

some of these applications are in the form of asm.js, a simple JS subset, that is a popular target language for C++ compilers; game engines originally written in C++ are being recompiled to JS to run on the web as asm.js programs.

.. if we want JS applications on the web to continue to be viable alternatives to native applications on each platform, we have to give JS the ability to run well on multiple CPUs.

Можешь, конечно, попытаться что-то написать на этих readUint/writeUint и Atomics.wait(). Кто-то даже пишет. Но удовольствие так себе.

Так что shared memory в js’е как бы и есть, но её как бы и нет.
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍1🦄1
TIL structuredClone не сохраняет prototype chain. structuredClone(foo).__proto__ !== foo.__proto__.

В целом, нормальное поведение, описано в спеке (даже не спрашивай что оно делает в html спеке, а не в #js). structuredClone – это deepClone для ipc между Worker’ами. Откуда ему корректные прототипы взять?

Какие-то прототипы, очевидно, всё равно нужны. Object, нужен, Array там. Их в спеке целый список, можно грепнуть по [[Type]]. Date ещё есть, Map, Set. Даже RegExp и RangeError есть.

А null'а – нет. И я не понимаю, что мешало его добавить
'toString' in {} === true 👍
'toString' in structuredClone({}) === true 👍
'toString' in Object.create(null) === false 👍
'toString' in structuredClone(Object.create(null)) === true 😐

Так что не "не сохраняет prototype chain", а скорее "не для всего сохраняет, тут да, там нет, хз короч, дебажь". Полчаса в никуда потратил 😫
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔1🍾1
express.js 5.0.0 вышел. И 10 лет не прош... oh wait.
Есть ли там что-то, что стоило ждать столько времени? Нет, лол
🗿3👾2😐1
Картиночка никогда не потеряет актуальность

В node v22 завезли --strip-types (пока experimental). Для трансляции используют парсер из swc, предварительно собранный в wasm 😐
Please open Telegram to view this post
VIEW IN TELEGRAM
🥴2👍1🌚1🦄1
bits & bicycles
TIL structuredClone не сохраняет prototype chain. structuredClone(foo).__proto__ !== foo.__proto__. В целом, нормальное поведение, описано в спеке (даже не спрашивай что оно делает в html спеке, а не в #js). structuredClone – это deepClone для ipc между Worker’ами.…
Нормального варианта, кстати, нет
cloneDeep из lodash'а – игрушка дьявола
Клонирует объекты буквально по полям и не вызывает конструкторы, гад! Все твои инварианты отправляются в мусорку.

cloneDeepWith уже получше, можно руками подёргать .clone() где надо.

Можно сделать свой cloneDeepWithKnowledge 🤓, который будет учитывать какой-нибудь Symbol.for('clone'). (Обязательно npm пакетом из пяти строк и сорока файлов!)
Но знать про него кроме тебя никто не будет.

Копирование объектов, банально, не first-class-citizen в js, не смотря на все эти {...}.
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔4🥰1🗿1
TIL в node.js завезли кеширование скомпилированных модулей
Пока из коробки не включили. Cоветую прописать export NODE_COMPILE_CACHE=~/.cache/nodejs в свой ~/.profile
🔥2🥰1