Вводные
- Требуется реализовать аутентификацию и авторизацию внешних пользователей при доступе к HTTP сервисам 1С
- В 1С хранится только ключ связывания пользователя со спр. Партнеры
Вариант реализации
Развернуть сервис аутентификации Keycloack, который выдает access_token в формате JWT по предъявлению login и password
Поставить перед 1С HTTP публикацией прокси, который проверит JWT токен, при его валидности из него получит данные о правах доступа до конкретного сервиса и при наличии прав переадресует запрос к публикации.
Схема
Настройка Apache
<?xml version="1.0" encoding="UTF-8"?>
<point xmlns="http://v8.1c.ru/8.2/virtual-resource-system"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
base="/test1"
ib="Srvr=DESKTOP-0KSO717.malikov.lan;Ref=test1;Usr=ext_user;Pwd=123456">
<standardOdata enable="false"
reuseSessions="autouse"
sessionMaxAge="20"
poolSize="10"
poolTimeout="5"/>
<analytics enable="true"/>
<httpServices publishExtensionsByDefault="true">
</httpServices>
</point>
В конфигурации добавил
- HTTP сервис "test_http"
- роль "ВнешнийПользователь" с правами доступа к ВнешнееСоединение и к HTTP сервису
- сервисный пользователь с ролью "ВнешнийПользователь"
В "/etc/apache2/ports.conf" заменил "Listen 80" на "Listen 127.0.0.1:8080"
Запуск GateKeeper
Скачиваю архив с релизом с https://github.com/gogatekeeper/gatekeeper, распаковываю, копирую файл "gatekeeper" в "/usr/bin"
Запускаю сервис с конфигурацией sudo gatekeeper --config gate_config.yml
# Является идентификатором клиента для клиента OAuth2 (в данном случае ваше приложение является клиентом OAuth2).
client-id: client_token
# gatekeeper получит информацию о сервере авторизации через openid-configuration
# хорошо известный URI сервера авторизации, согласно RFC8414.
# gatekeeper получит эти метаданные от discovery-url+/.well-known/openid-configuration, как зарегистрировано в IANA
discovery-url: http://auth.malikov.lan:8080/auth/realms/onec_token
# Если этот параметр установлен, по умолчанию gatekeeper будет отклонять все запросы к вышестоящему каналу.
# Затем это можно смягчить определением resources.
enable-default-deny: true
# настраивает основной интерфейс прослушивания
listen: :80
# Это говорит gatekeeper, как связаться с вышестоящим (вашему приложению/службой, защищаемые gatekeeper)
upstream-url: http://127.0.0.1:8080
# Если значение true, gatekeeper просто вернет 401 Unauthorized вместо перенаправления пользователя на сервер авторизации.
no-redirects: true
# Они сообщают gatekeeper, как аутентифицировать или авторизовать ресурсы на вышестоящем уровне.
resources:
- uri: /test1*
methods:
- GET
roles:
- client:test1
- client:test2
# в сочетании с ролями выше, переключает условное с И на ИЛИ
require-any-role: true
groups:
- users
- admins
Настраиваю KeyCloak
Создаю realm "onec_token", в нем создаю client "client_token" c типом public
Для GateKeeper обязателен параметр aud, делаю мапинг значений для clients в этот параметр
Источник: https://stackoverflow.com/questions/53550321/keycloak-gatekeeper-aud-claim-and-client-id-do-not-match
В ограничениях присутствуют группы, делаю их мапинг для clients
В realm создаю роль "client:test1" и группу "users", создаю пользователя добавляю его в группу и добавляю ему роль.
Изменяю длительность действия access_token с 5 мин до 1 часа, сделал для тестов.
Проверка ограничения
Отправляю запрос без аутентификации, получаю 401
Запросом к keycloack получаю токен
curl --location --request POST 'http://auth.malikov.lan:8080/auth/realms/onec_token/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'username=ext_user' \
--data-urlencode 'password=ext_user' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=client_token' \
--data-urlencode 'redirect_uri=http://loalhost'
Из тела ответа беру значение access_token, добавляю его в bearer аутентификацию основного запроса, повторяю запрос, получаю в ответе идентификатор user.
Использую перенаправленные от GateKeeper заголовки авторизации.
Функция test_httpGET(Запрос)
СтрокаАвторизации = Запрос.Заголовки.Получить("X-Auth-Subject");
Соответствие = Новый Соответствие;
Соответствие.Вставить("uid", СтрокаАвторизации);
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, Соответствие);
Результат = Запись.Закрыть();
Ответ = Новый HTTPСервисОтвет(200);
Ответ.Заголовки.Вставить("Content-Type", "application/json");
Ответ.УстановитьТелоИзСтроки(Результат);
Возврат Ответ;
КонецФункции
При установке параметра "enable-authorization-header" передается весь токен в заголовок Authorization, но это может конфликтовать с авторизацией на уровне 1С.
Итог
За счет внешних сервисов можно организовать безопасный доступ к HTTP публикациям 1С.
Как организовать gatekeeper в качестве сервиса отдельный вопрос. Самое простое через docker. Возможно имеет смысл скомпоновать в один образ gatekeeper с WS компонентой 1С.
Проверку на подделку JWT не делал, тема отдельного блока тестов, думаю в данной реализации работает корректно.
Благодарю за внимание.