Идентичност и достъп
pacod-id
Едно място, където хора и машини влизат — и където се записва какво имат право да правят във всички ваши продукти.
pacod-id е OpenID Connect доставчик за собствени приложения, с акаунти, организации, присвоявания на роли и поток от промени, който продуктите следят, за да решават за достъпа локално. Написан на Rust, с клиентски библиотеки за Rust, TypeScript и Python.
- Apache-2.0
- Версия 0.1.0
- Предварителна
Възможности
-
OpenID Connect доставчик
Вход за вашите собствени приложения, проверен с тестовия пакет за съответствие на OpenID Foundation.
-
Акаунти и организации
Хора, организации, присвоявания на роли, служебни акаунти, поща и одитна следа.
-
Един механизъм за правила, три порта
Манифести на продукти, scopes, allowed, has_role и touches — на Rust, TypeScript и Python, като и трите минават едни и същи YAML тестови вектори.
-
Поток за оторизация
Продуктите следят поток от промени в присвояванията на роли и решават за достъпа локално, без допълнително извикване при всяка заявка.
-
Токени, направени както трябва
Compact JWS с RS256 и ES256 върху ring, JWKS, introspection и token exchange.
-
Брандиран вход
Хоствани страници за вход и акаунт с бранд за всеки продукт, в светла и тъмна тема, на четири езика.
-
Готови клиенти
Rust клиентски crate, Python пакет с FastAPI зависимости и поток за вход за SvelteKit като backend-for-frontend.
-
Готов за работа
Docker образ, среда за разработка с поща и симулиран Google, както и Helm чарт.
Мястото в системата
Вход за цяло семейство продукти
pacod-id е услугата за идентичност зад продуктите на pacod. Хората влизат веднъж и достигат до конзолата на шлюза, платформата за агенти, CMS студиото и базата знания с един и същ акаунт. Машините също влизат — като служебни акаунти.
Услугата съществува, за да не се налага всеки продукт сам да поддържа пароли, писма за акаунти
и административни страници. Акаунтите са в един общ пул: sub на даден човек е един и същ във
всяка област (realm). Между продуктите се различава как изглежда входът и кой какво може да прави
там — и pacod-id записва и двете.
Това е OpenID Connect доставчик за вашите собствени приложения. Всеки клиент е описан в конфигурацията: няма динамична регистрация и няма екран за съгласие.
Области, клиенти и токени
Областта (realm) е един issuer хост със собствен бранд, бисквитка за вход, езици и правила:
дължина на паролата, продължителност на сесията, дали всеки може да се регистрира, кои роли
управляват акаунтите. pacod-id избира областта по хоста на заявката, така че два бранда могат
да споделят един пул от акаунти и въпреки това да изглеждат като два продукта. Брандът на
областта е един от петте вградени: gateway, cms, kb, както и shop и bazaar за
измисления продукт, който използват документацията и тестовете.
Клиентът е едно приложение от един от три вида:
| Вид | Типична употреба | Grants |
|---|---|---|
confidential |
Backend-for-frontend на продукт, който пази тайна | authorization_code, refresh_token |
public |
Нативно или мобилно приложение; кодовете му пази само PKCE | authorization_code, refresh_token |
service |
Сървърът на продукт, който говори с pacod-id | client_credentials, token exchange |
Предлага се само authorization code flow, като PKCE (S256) се изисква от всеки клиент, включително от confidential клиентите.
- Access токените са JWT (RFC 9068), RS256 или ES256 според клиента, валидни по подразбиране 5 минути (от 2 до 15). Те казват кой, в коя сесия и с какъв scope, но не носят роли.
- id_token живее 5 минути и носи адреса и името, когато scopes ги поискат.
- Refresh токените са непрозрачни и се сменят при всяко използване. Ако след 30-секунден гратисен период бъде представен вече заменен токен, всички сесии на акаунта се прекратяват и собственикът получава писмо.
- Token exchange (RFC 8693) позволява на услугата на даден продукт да действа от името на
човек, в рамките на неговите права, като
actпосочва услугата.
Входът с Google се включва поотделно за всяка област. Когато сесия приключи, всеки клиент, който е имал grant в нея и има back-channel logout URI, получава logout токен.
Решения за достъпа на място
Ролите нарочно не се записват в access токените. При стартиране всеки продукт изпраща манифест:
своите типове scopes, своите роли и техните права, както и кой какво може да присвоява. Scopes
образуват дърво — от platform, по избор през organisation:<id>, до собствените типове на продукта,
например gw.workspace:ws_1. Присвояването е роля, притежавана в един scope, и правата ѝ
важат надолу: роля в организация важи във всяко работно пространство под нея.
product: shop
scope_types:
profile: { parent: platform }
roles:
admin:
at: [platform]
permissions: [admin.read, admin.write, profiles.assign]
protected: true
profile_editor:
at: [profile]
permissions: [profile.edit]
grant:
default: admin.write
pacod-id съхранява присвояванията и проверява всяка тяхна промяна: за да се даде роля, е нужно
правото за присвояване от манифеста в този scope, а роля с protected никога не губи последния
си притежател.
Механизмът за правила отговаря на allowed, has_role, touches и can_grant. Той съществува
на Rust, TypeScript и Python. И трите минават едни и същи YAML тестови вектори
(policy/engine-vectors/). Векторите са спецификацията, така че решението е едно и също, където
и да бъде взето.
Потокът за оторизация
Всеки продукт пази собствено копие на присвояванията и следи промените:
- При стартиране зарежда snapshot: своя каталог, организациите и своите scopes,
присвояванията на своите роли, деактивираните и изтритите акаунти и наскоро прекратените
сесии — всичко към един пореден номер,
seq. - След това чака промените след този
seqчрез long poll, до 30 секунди на заявка. Отговорът идва веднага щом нещо се промени: PostgreSQLLISTENсъбужда чакащата заявка. - Промените се пазят 7 дни. Курсор, по-стар от това, получава 410 и продуктът зарежда snapshot отново.
Събитията обхващат присвоявания, акаунти, сесии, служебни токени, scopes и каталози. Записите
връщат seq на потока, така че продукт, който трябва да прочете собствения си запис, изчаква
копието му да навакса. Клиентските библиотеки за Rust и Python правят всичко това в своя кеш на
присвояванията.
Измерено с make measure на една машина за разработка (Docker Desktop на Apple silicon),
промяна достига до вече чакащ последовател с p99 около 10 ms при цел под една секунда.
Компонентите
| Част | Какво |
|---|---|
crates/pacod-id |
Услугата: OpenID Connect доставчик, акаунти, организации, присвоявания на роли, потокът за оторизация, поща, одит |
crates/pacod-id-authz |
Механизмът за правила |
crates/pacod-id-jose |
Compact JWS (RS256, ES256) върху ring и JWKS |
crates/pacod-id-client |
Проверка на токени, кешът на присвояванията, introspection, token exchange за продукти на Rust |
python/ |
Пакетът pacod_id: механизъм за правила, проверка на токени, FastAPI зависимости |
packages/* |
@pacod/id-authz, @pacod/ui, @pacod/id-client, @pacod/id-sveltekit |
ui/ |
Хостваните страници за вход и акаунт |
Услугата е един изпълним файл на Rust с три listener-а:
| Listener | Порт | За |
|---|---|---|
| публичен | 8070 | Браузъри и приложения, през TLS reverse proxy: крайните точки на OpenID Connect и хостваните страници |
| вътрешен | 8071 | Само сървърите на продуктите: токени, потокът, introspection, прекратяване на сесии, административният API |
| метрики | 9170 | /healthz, /readyz и /metrics |
Хостваните страници за вход, акаунт и администрация са статичен SvelteKit build, който
изпълнимият файл сервира сам. @pacod/ui съдържа дизайн токените, темите на брандовете и
компонентите за вход, които използва всеки продукт на pacod — включително този уебсайт.
Модел на сигурността
Основните защити според модела на заплахите:
- Пароли: Argon2id на минимума на OWASP (19 MiB, 2 преминавания), 12 знака по подразбиране и списък със забранени често срещани пароли. Ограниченията на опитите за вход се пазят в PostgreSQL и са общи за всички инстанции: по акаунт, по познат браузър и по IP адрес.
- Без разузнаване на акаунти: входът, възстановяването на парола и регистрацията отговарят еднакво, независимо дали за адреса има акаунт.
- Ключове за подписване: създава ги самият pacod-id, пазят се запечатани в базата под ключ
за криптиране на ключове (
PID_KEK) и се ротират на всеки 90 дни, като следващият ключ се публикува ден по-рано. Аварийната ротация е една команда. - Съхранени тайни: refresh токените и връзките от писмата — като SHA-256, клиентските тайни и служебните токени — като HMAC с отделен pepper. Собствените редове в лога на pacod-id никога не съдържат парола, токен или ключ.
- Изложеност: публичният listener не обслужва вътрешни и административни пътища, а NetworkPolicy на Helm chart-а допуска вътрешния порт само от източниците, които посочите. На общия edge в пълния стек на pacod маршрут има само публичният listener.
- Образ: статичен musl изпълним файл върху distroless, без root права и без shell.
Експлоатация
Областите и клиентите се описват в един TOML файл; тайните — никога. Тайната на клиента идва от
променливата, която посочва неговият secret_env.
[[realm]]
name = "gateway"
issuer = "https://id.example.com"
brand = "gateway"
locales = ["en", "bg", "de", "ru"]
self_signup = false
[[client]]
id = "pacod-gw-console"
realm = "gateway"
kind = "confidential"
secret_env = "PGW_UI_CLIENT_SECRET"
redirect_uris = ["https://console.example.com/auth/callback"]
audience = "pacod-gw"
pacod-id check-config проверява файла без база данни. Освен него услугата се нуждае от
PID_DATABASE_URL, PID_KEK и PID_TOKEN_PEPPER, всяка зададена като променлива, като файл
или чрез директория с тайни.
- База данни: PostgreSQL, със собствена роля, която не е superuser. Миграциите само добавят и се изпълняват при всяко стартиране под advisory lock.
- Инстанции: произволен брой споделят една база. Всяка изпраща чакащите писма и logout-и и презарежда ключовете и каталозите; една от тях, която държи lease, ротира ключовете и изтрива старите редове.
- Поща: никоя заявка не чака SMTP. Писмата отиват в outbox, в бранда и езиците на областта, и се изпращат повторно в продължение на около осем часа.
- Първи администратор: създава се при стартиране от
PID_BOOTSTRAP_ADMIN_*. - Наблюдение:
/readyzпроверява базата и ключа за подписване./metricsдава броя и продължителността на заявките по listener. Логовете са в JSON, без ред за всяка заявка. Входовете, промените на роли и действията на администраторите отиват в одитния журнал, който се пази 400 дни и се чете от административните страници.
Шлюз, който досега сам е вписвал хората, може да ги прехвърли с pacod-id import gw: паролите,
административните токени и връзките в поканите продължават да работят.
make compose-up стартира среда за разработка в pacod-net — Docker мрежата, в която влиза
всеки продукт на pacod. Compose конфигурацията ѝ е в два файла. docker-compose.yml съдържа
само pacod-id. docker-compose.standalone.yml добавя собствени копия на общите компоненти, от
които се нуждае: PostgreSQL (pacod-postgres), pacod-db-init, който при всяко стартиране
създава ролята pacod_id и нейната база, ако липсват, и пощенски sink (pacod-mailpit), плюс
симулирания Google (id-mock-idp). Средата слуша само на 127.0.0.1, с issuer на
http://127.0.0.1:26070.
Пълният стек на pacod, pacod-demo (работещ на
demo.pacod.io), зарежда само първия файл. Той пуска един PostgreSQL,
един пощенски sink, един edge и един pacod-id за всички продукти, с една-единствена област за
всички тях. platform/ съдържа дела на pacod-id от тази конфигурация: скрипта му за базата,
маршрута му в общия Caddy и задачата му в Prometheus. При общ PostgreSQL сървър моделът на
заплахите съветва всеки продукт да има собствена роля, която не е superuser: продукт, който се
свързва като superuser, може да чете таблиците на pacod-id.
Какво още липсва
- pacod-id 0.1.0 още не е издаден. Надграждане, резервно копие и възстановяване не са изпробвани.
- Helm chart-ът минава
helm lintи проверките на схемите, но още не е инсталиран на клъстер. Истински SMTP сървър и истинският Google не са пробвани; mock-ът от средата за разработка — да. - Тестовете за съответствие на OpenID Foundation са пускани локално: плановете Config, Basic и RP-Initiated Logout минават без нито едно неизпълнено условие, а един тест на Back-Channel Logout е блокиран от собствения сертификат на тестовия пакет. Локалните пускания не са сертификация.
- Още няма MFA и passkeys, а оттам и claim
acr; документацията ги посочва като планирани. - Няма claims за адрес и телефон; scope
profileдаваnameиlocale. - Вътрешният listener говори обикновен HTTP и освен ограниченията при вход няма общо ограничение на заявките.
Още документация
- Runbook: инсталиране и експлоатация.
- HTTP API: всяка крайна точка и събитията в потока.
- Модел на заплахите: защити и остатъчни рискове.
- Измервания: латентност на token endpoint и на потока.
- Резултати от тестовете за съответствие и тестовите вектори на механизма за правила.
Екранни снимки
Технологии
- Написан на
- Rust
- TypeScript
- Python
- Технологии
- Rust
- axum
- PostgreSQL
- OpenID Connect
- SvelteKit
- Docker
- Helm
Бърз старт
make test-servicesmake verifymake e2emake imagemake compose-upРаботи с
-
pacod-gw
Конзолата на шлюза може да вписва хората през pacod-id и проверява ролите в работните пространства с неговия механизъм за правила.
-
pacod-ap
Приложението и административната конзола на платформата за агенти влизат през pacod-id като confidential клиент.
-
pacod-cms
Студиото вписва хората през pacod-id и съхранява там ролите за всяка организация и за всеки сайт.
-
pacod-kb
Потребителският интерфейс на базата знания е OIDC клиент на pacod-id; ролите viewer, editor и admin се предоставят там.
Лиценз
Apache-2.0