SSO у self-hosted ПЗ: чому OIDC і SAML платні
Чому self-hosted застосунки ховають OIDC і SAML у платний план та який чекліст пройти до встановлення, щоб не отримати неочікуваний SSO-платіж.
Податок на SSO
«Податок на SSO» у self-hosted програмному забезпеченні — це модель, за якої застосунок безкоштовний, а єдиною платною функцією стає single sign-on (SSO). Ви можете запустити всю систему на власному VPS без ліцензійного ключа та обмеження за кількістю користувачів. Потім ви відкриваєте в документації сторінку автентифікації й виявляєте, що OpenID Connect (OIDC) або SAML (security assertion markup language) доступні лише в платному плані.
Це важливіше за звичайну функцію, доступну лише за плату, оскільки саме SSO дає змогу кільком self-hosted сервісам працювати як єдина система. Identity provider (IdP) надає один обліковий запис для кожної людини, одну політику паролів, одне місце для ввімкнення багатофакторної автентифікації (MFA) і одне місце для блокування доступу. Без IdP кожен застосунок зберігає власну невелику базу користувачів, і ви вручну обслуговуєте кожну з них.
Ця практика існує достатньо давно, щоб для неї з’явився публічний список. SSO Wall of Shame на sso.tax містить постачальників, які стягують значну додаткову плату за single sign-on. У списку є записи, починаючи з 2018 року, а його автор установив обґрунтований критерій: «Якщо підтримка SSO підвищує ціну на 10%, вас немає в цьому списку». Більшість у цьому списку — закрите програмне забезпечення. Та сама модель ціноутворення тепер з’являється і у проєктах із відкритим кодом, які ви розгортаєте самостійно.
Чому супроводжувачі розміщують single sign-on у платному плані
Є дві причини, і обидві обґрунтовані. Підтримка SSO потребує значних витрат, а це одна з небагатьох функцій, за які велика організація готова платити.
Витрати на підтримку реальні, оскільки інтеграція з провайдером ідентифікації ніколи не буває завершеною. Кожен IdP трохи по-різному форматує claims. Відображення груп, тривалість сеансу, URL перенаправлення та розбіжність системного часу окремо спричиняють помилки входу. Помилка входу може одночасно заблокувати всіх користувачів, тому такі звернення є терміновими. Далі з’являються додаткові запити: вкладені групи, відображення ролей, автоматичне створення облікових записів за допомогою SCIM (system for cross-domain identity management) і журнали аудиту, які перевірятиме команда з питань відповідності вимогам.
Дохід визначається арифметикою, а не недоброзичливістю. Компанія, яка не може підключити застосунок до власного IdP, узагалі не розгортатиме його. Тому SSO чітко розділяє користувачів, які платять, і тих, хто не платить. Проєкт із відкритим ядром має провести цю межу десь. SSO підходить для цього краще за майже будь-яку іншу функцію, тому його обирає так багато проєктів.
Є важливе уточнення до типової скарги: перш ніж припускати, що функцію вилучили, перевірте changelog, оскільки вилучення відображається в release notes. У всіх проєктах, які я перевірив для цього допису, платні функції SSO від початку створювалися для платного плану. Я не знайшов випадку, коли працездатний безкоштовний SSO згодом вилучили. Grafana є типовим прикладом. На її сторінці про SAML є однорядкова примітка: "Available in Grafana Enterprise and Grafana Cloud", тоді як загальний OAuth проти власного issuer працює у збірці з відкритим кодом.
Якою є фактична ціна SSO
Гроші — лише менша частина витрат. Платні тарифні плани продаються з розрахунку на користувача, тому рахунок зростає разом із командою, а хостинг і оновлення залишаються вашою відповідальністю.
Більша частина витрат — ручна робота з обліковими записами. Вона проявляється у чотирьох аспектах.
- Окреме сховище паролів для кожного застосунку. Тому повторно використаний пароль створює вразливість у всіх застосунках, де його застосовують.
- Виведення користувачів із системи на основі пам’яті. Потрібно пам’ятати кожен сервіс, до якого людина мала доступ. Забутий сервіс може виявитися найважливішим.
- Налаштування MFA окремо в кожному застосунку, якщо застосунок узагалі її підтримує.
- Спільні облікові записи. Саме до цього під тиском таких обмежень фактично приходять невеликі команди.
Останній пункт заслуговує на окреме пояснення. Якщо команда використовує один обліковий запис адміністратора в менеджері документів, журнал аудиту записує одне ім’я для всіх дій. Тому неможливо визначити, хто видалив рахунок. Дозволи для окремих користувачів також перестають працювати, оскільки користувач лише один. У цьому і полягає реальна шкода від ціни SSO: вона підштовхує невеликі команди до використання одного спільного облікового запису, що гірше за будь-яку альтернативу.
Чекліст перед розгортанням будь-якого застосунку
Виконайте його перед docker compose up, а не після того, як застосунок збереже 400 документів.
- Відкрийте в документації сторінку автентифікації та прочитайте примітку про тарифний план угорі. Платні функції позначаються бейджем або однорядковим описом доступності.
- Переконайтеся, що застосунок підтримує OIDC або SAML із вашим власним issuer, а не лише з фіксованим переліком публічних провайдерів.
- Перевірте зіставлення ролей і груп. Створення користувача — це лише половина роботи. Призначати дозволи вручну в десяти застосунках — найскладніша частина.
- Перевірте, чи приймає застосунок автентифіковане ім’я користувача в заголовку від довіреного проксі та чи можна явно вказати, якому проксі він довіряє.
- Перегляньте історію ліцензії в git і перевірте, чи підписують учасники CLA (contributor licence agreement).
- Перевірте процедуру деактивації доступу. З’ясуйте, що відбувається з API (application programming interface) токенами та активними сесіями, коли обліковий запис у IdP вимкнено.
Саме на пункті 2 найчастіше виникають розчарування. Кнопка «Sign in with Google» не означає підтримку OIDC із вашим identity provider: це фіксована інтеграція з одним постачальником. Повноцінна підтримка передбачає, що застосунок запитує URL issuer, а решту даних отримує через discovery. Сторону вашого провайдера можна перевірити однією командою.
curl -s https://id.example.com/.well-known/openid-configuration \
| jq '.issuer, .authorization_endpoint, .token_endpoint'Від справного провайдера повертаються три URL. Порожній результат або код 404 зазвичай означає, що шлях discovery неправильний. Цей шлях залежить від провайдера: Keycloak публікує його в /realms/<realm>/.well-known/openid-configuration. Якщо в застосунку взагалі немає поля для URL issuer, він не може взаємодіяти з вашим IdP, незалежно від заявленого переліку функцій.
Пункт 6 часто виявляється проблемою через кілька тижнів після звільнення працівника. Вимкнення облікового запису в IdP блокує нові входи. Воно не відкликає API-токен, який застосунок видав раніше, оскільки застосунок перевіряє цей токен самостійно й не звертається до IdP. Тому деактивація доступу складається з двох кроків: вимкніть обліковий запис у IdP, а потім видаліть користувача або його токени в кожному застосунку.
Що насправді означають позначки тарифних рівнів
У серпні 2026 року ці дані перевірено за документацією кожного проєкту. Почнімо з платної частини.
Grafana вказує SAML як "Available in Grafana Enterprise and Grafana Cloud", а також пропонує синхронізацію команд і provisioning через SCIM. Generic OAuth, GitHub OAuth, LDAP (lightweight directory access protocol) і auth proxy доступні у версії з відкритим кодом, тому невеликий self-hosted сервіс і надалі може виконувати вхід через власного провайдера. Платна межа проходить саме на рівні SAML, а не SSO загалом. Саме цю відмінність часто нівелює вислів "SSO tax".
Metabase формулює це пряміше. У документації сказано: "SAML authentication is only available on Pro and Enterprise plans (both self-hosted and on Metabase Cloud)." Версія з відкритим кодом підтримує вхід за паролем і LDAP.
Passbolt позначає документацію щодо SSO як Pro і Cloud, тому community edition його не підтримує. Серед документованих провайдерів є Keycloak і Entra ID.
Тепер інша сторона, оскільки така модель далеко не універсальна.
- GitLab Self-Managed має позначку "Tier: Free, Premium, Ultimate" на сторінці SAML, тому SAML для власного GitLab не потребує оплати.
- Paperless-ngx налаштовує OIDC через django-allauth за допомогою
PAPERLESS_SOCIALACCOUNT_PROVIDERS, приховує локальну форму входу за допомогоюPAPERLESS_DISABLE_REGULAR_LOGINі зіставляє claims із групами за допомогоюPAPERLESS_SOCIAL_ACCOUNT_SYNC_GROUPS. - Planka використовує
OIDC_ISSUER,OIDC_CLIENT_IDіOIDC_CLIENT_SECRET, типово задає для scopes значенняopenid profile emailі призначає користувачам роль адміністраторів на основі role claim черезOIDC_ADMIN_ROLES. - BookStack перемикається на цей режим за допомогою
AUTH_METHOD=oidc, а потім зіставляє групи провайдера з власними ролями черезOIDC_USER_TO_GROUPS=trueіOIDC_GROUPS_CLAIM. - Vaultwarden додав "support for SSO with OpenID Connect" у версії 1.35.0 27 December 2025 року на основі pull request від контриб’ютора, який раніше підтримував цю функцію у fork.
- listmonk підтримує вхід через OIDC разом із власними ролями користувачів починаючи з v4.0.0.
Враховуйте це під час вибору, а не після того, як уже зробили вибір. Kanban-дошка Planka та інші self-hosted альтернативи Trello не однаково працюють з ідентифікацією. Те саме стосується BookStack, Wiki.js і Outline. Безкоштовний OIDC — це характеристика, яку можна оцінювати так само, як обмеження сховища або наявність мобільних клієнтів. Якщо ви ще формуєте список, що варто self-hosted у 2026 році може бути зручною відправною точкою. І Paperless-ngx, менеджер документів Paperless-ngx, та Vaultwarden уже сьогодні надають безкоштовний OIDC.
Чому reverse proxy перед застосунком не є єдиним входом
Поширений обхідний варіант — forward auth. Reverse proxy затримує кожен запит, запитує сервіс автентифікації, чи виконано вхід у цьому браузері, і лише після цього передає запит застосунку. authentik називає це proxy provider із режимом forward auth для одного застосунку та окремим режимом для всього домену. Authelia і oauth2-proxy виконують те саме завдання.
Блок сайту Caddy має такий вигляд відповідно до власного прикладу authentik.
app.example.com {
forward_auth http://authentik-outpost:9000 {
uri /outpost.goauthentik.io/auth/caddy
copy_headers X-Authentik-Username X-Authentik-Email X-Authentik-Groups
trusted_proxies private_ranges
}
reverse_proxy app:8000
}Регістр цих назв заголовків у Caddy має значення, оскільки заголовок із невідповідною назвою надходить порожнім. Для кожного схваленого запиту outpost встановлює X-authentik-username, X-authentik-email, X-authentik-groups і ще кілька заголовків.
Ось що це дає. Ніхто не може отримати доступ до застосунку, не пройшовши спочатку через ваш identity provider. Тому невиправлена форма входу більше не доступна з інтернету, а MFA одночасно застосовується до всіх ресурсів за reverse proxy.
Ось чого це не дає: ідентифікації всередині застосунку. Застосунок і далі має власні облікові записи та власне визначення того, хто виконав вхід. Якщо всі проходять reverse proxy і потрапляють в один спільний обліковий запис адміністратора, ви маєте надійний зовнішній бар’єр і одну анонімну сесію за ним. У журналі аудиту й далі відображається одне ім’я. Розмежувати дозволи між користувачами все ще неможливо. Називати таку схему SSO — помилка безпеки, оскільки процес видалення доступу працює лише частково: видалення користувача з вашого IdP закриває зовнішній доступ, але створений ним API token усередині застосунку продовжує працювати для будь-кого, хто може напряму отримати доступ до застосунку.
Безпечна автентифікація через заголовок
Деякі застосунки приймають ім’я користувача від проксі. Це дає змогу ідентифікувати користувачів без платної SSO. У кожному проєкті цей параметр має іншу назву.
Grafana називає цю функцію auth proxy і постачає її вимкненою. Назва заголовка за замовчуванням — X-WEBAUTH-USER. Її можна замінити на будь-який заголовок, який встановлює ваш проксі.
[auth.proxy]
enabled = true
header_name = X-authentik-username
header_property = username
auto_sign_up = true
whitelist = 10.0.0.5whitelist — це параметр, який часто пропускають. У документації Grafana прямо зазначено, що він запобігає підробленню заголовка користувачами. Тому тут має бути лише адреса вашого проксі. Gitea має таку саму функцію під іншими назвами та постачається з безпечнішим значенням за замовчуванням.
[security]
ENABLE_REVERSE_PROXY_AUTHENTICATION = true
REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
REVERSE_PROXY_TRUSTED_PROXIES = 10.0.0.5/32
REVERSE_PROXY_LIMIT = 1За замовчуванням REVERSE_PROXY_TRUSTED_PROXIES має значення 127.0.0.0/8,::1/128, а REVERSE_PROXY_LIMIT визначає кількість проксі, яким Gitea довіряє в ланцюжку. Якщо встановити це обмеження на нуль, обробку заголовка буде повністю вимкнено.
У Paperless-ngx для цього використовується PAPERLESS_ENABLE_HTTP_REMOTE_USER разом із PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME. У документації наведено попередження, яке стосується всіх таких параметрів:
Це дасть змогу пройти автентифікацію, просто додавши до запиту заголовок Remote-User: <username>. Використовуйте з обережністю!
Безпеку автентифікації через заголовок забезпечують два правила. Обидва стосуються доступності сервісу. По-перше, застосунок має бути недоступним будь-яким іншим способом, крім проксі. Будь-хто, хто може відкрити socket-з’єднання з ним, може надіслати цей заголовок і увійти від імені будь-якого користувача. У Docker ports: ["8000:8000"] публікує порт на всіх інтерфейсах. Прив’яжіть його до loopback-адреси за допомогою ports: ["127.0.0.1:8000:8000"] або приберіть опублікований порт і підключіть проксі до тієї самої Docker network. По-друге, проксі має видаляти будь-яку копію цього заголовка, отриману від клієнта. Тоді застосунок бачитиме лише значення, яке проксі встановив після автентифікації.
Перевірте обидва правила. Виконайте першу команду на машині за межами вашого VPS, а другу — безпосередньо на сервері.
curl -si -H "Remote-User: admin" http://203.0.113.10:8000/ | head -n 1
ss -ltnp | grep 8000curl має завершитися помилкою підключення, а ss має вивести 127.0.0.1:8000, а не 0.0.0.0:8000. Якщо перший рядок містить HTTP/1.1 302 Found, це означає, що застосунок безпосередньо відповідає публічному інтернету. У такому разі будь-хто може увійти від імені будь-якого користувача, указавши його ім’я в заголовку.
Коли безкоштовного SSO немає, приймайте рішення, а не скаржтеся
Чотири варіанти в порядку, у якому я б їх розглядав.
- Виберіть застосунок із підтримкою OIDC. Якщо два проєкти виконують однакову роботу, але один безкоштовно взаємодіє з вашим identity provider, це реальна різниця в експлуатаційних витратах.
- Чесно використовуйте forward auth. Для адміністративного інструмента з одним обліковим записом і одним оператором достатньо проксі попереду. Ідентифікація кожного користувача всередині застосунку нічого не дає.
- Заплатіть. Якщо застосунок є важливим для вашої роботи, а ціна за seat відповідає розміру команди, ці кошти допомагають підтримувати проєкт. Альтернатива — витрачати на це власні вечори.
- Зверніться до розробників після пошуку в issue tracker. Підтримка OIDC у Vaultwarden з’явилася завдяки fork від учасника спільноти та pull request, який тривалий час перебував у роботі. Тому запит на функцію з готовою реалізацією іноді потрапляє до безкоштовної редакції.
Усе це неможливо без власного identity provider, і саме його слід розгорнути спочатку. Розгортання authentik на VPS дає вам провайдер OIDC і SAML, а також forward auth outpost, який використовувався вище. У порівнянні Keycloak, authentik і Zitadel описано компроміси, якщо ви поки не хочете остаточно визначатися.
Історія ліцензування і чому вона є в контрольному списку
Останній пункт контрольного списку стосується майбутнього, оскільки поточна структура рівнів є лише знімком стану. Два добре задокументовані випадки показують, як швидко змінюється ситуація в обох напрямках. 10 August 2023 HashiCorp застосувала Business Source License 1.1 до всіх майбутніх релізів, тоді як попередні релізи залишилися під MPL 2.0 (Mozilla Public License). У March 2024 Redis перейшла на SSPL (server side public license), а 1 May 2025 оголосила, що Redis 8 також випускається за AGPLv3 (GNU Affero General Public License).
Розглядайте ці приклади як свідчення механізму, а не мотивів. Ліцензія, яку ви читаєте сьогодні, застосовується до версії, яку ви встановлюєте сьогодні, а проєкт, якому належать усі авторські права, може самостійно змінити умови наступного релізу. Саме тому питання про CLA є в контрольному списку: широке відчуження авторських прав дає змогу одноосібно змінити ліцензію.
Перед використанням проєкту перевірте його історію самостійно.
git clone --filter=blob:none https://github.com/paperless-ngx/paperless-ngx.git
cd paperless-ngx
git log --follow --oneline -- LICENSEКороткий список комітів, переважно з моменту першого імпорту, є доброю ознакою. Кілька переписувань файлу ліцензії означають, що перед плануванням на основі поточних умов слід прочитати повідомлення до кожного коміту.
FAQ
Яка вартість SSO?
Вартість SSO — це практика стягнення плати за single sign-on як за преміумфункцію, тоді як решта продукту є безкоштовною або недорогою. У self-hosted програмному забезпеченні це виглядає як open source застосунок, який можна запускати без ліцензійного ключа, але вхід через OIDC або SAML доступний лише в платному тарифі. Назва походить від SSO Wall of Shame на sso.tax, де відстежують постачальників, які встановлюють за цю функцію значну додаткову плату. Для користувача, який самостійно розміщує програмне забезпечення, наслідок полягає в тому, що кожен застосунок зберігає власну базу користувачів, а облікові записи доводиться створювати й видаляти вручну.
Чи є reverse proxy із forward auth тим самим, що й SSO?
Ні. Forward auth захищає вхідну точку: проксі перевіряє користувача через identity provider до того, як будь-який запит потрапить до застосунку. Застосунок за проксі все одно використовує власні облікові записи, тому, якщо всі користувачі потрапляють до одного спільного входу, ви отримуєте одну анонімну сесію та журнал аудиту, де зазначено лише одне ім’я. Справжня ідентифікація окремих користувачів з’являється лише тоді, коли застосунок читає ім’я користувача із заголовка. Це можуть робити auth proxy у Grafana, reverse proxy authentication у Gitea та PAPERLESS_ENABLE_HTTP_REMOTE_USER у Paperless-ngx. Такі налаштування безпечні лише доти, доки до застосунку неможливо звернутися інакше, ніж через проксі, оскільки заголовок є звичайним рядком, який може надіслати будь-який клієнт.
Які self-hosted застосунки містять OIDC у безкоштовній редакції?
Перевірено за документацією проєктів у August 2026: Paperless-ngx, Planka, BookStack, Gitea, listmonk і Vaultwarden підтримують OIDC у безкоштовних збірках, а GitLab Self-Managed зазначає SAML у категорії Tier: Free. Open source збірка Grafana підтримує generic OAuth через власний issuer, тоді як SAML у ній є функцією Enterprise. Перед встановленням перевірте сторінку автентифікації самого проєкту, оскільки ці переліки змінюються разом із релізами.
Чи варто платити за тариф, який відкриває single sign-on?
Приймайте рішення на основі двох показників: скільком людям потрібні облікові записи та скільки застосунків інакше довелося б обслуговувати вручну. Для одного або двох адміністраторів forward auth перед локальним обліковим записом достатньо, а платний тариф майже нічого не дає. У команді, де люди приєднуються до роботи й залишають її, один обліковий запис, який не видалили під час звільнення користувача, може коштувати дорожче за ліцензію, а оплата фінансує підтримку, на яку ви розраховуєте. Якщо ціна не підходить, практичніше вибрати застосунок із вбудованою підтримкою OIDC, а не обходити обмеження застосунку, який її не має.