Русский
Русский
English
Статистика
Реклама

Есть ли корпоративная жизнь на удаленке и как ее обеспечить интеграция внутренней системы аутентификации

В digital-агентстве Convergent, где я работаю, в потоке множество проектов, и у каждого из них может быть собственная админка. Есть несколько окружений (дев, стейдж, лайв). А ещё есть разные внутрикорпоративные сервисы (как собственной разработки, так и сторонние вроде Redmine или Mattermost), которыми ежедневно пользуются сотрудники.

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

В данной статье я хочу поделиться опытом создания собственной внутренней системы аутентификации на основе OpenResty, а также спецификации OAuth2. В качестве основного языка программирования мы используем PHP, а фреймворк Yii 2.

Суммирую необходимый функционал:

  • Единое место управления всеми доступами. Здесь происходит выдача и отзыв доступов в административные панели сайтов и списки доменов;

  • По умолчанию весь доступ запрещён, если не указано обратное (доступ к любым доменам контролируется с помощью OpenResty);

  • Аутентификация для сотрудников;

  • Аутентификация для клиентов.

Закрытый доступ к сайтам и инфраструктуре

Начну со схемы, как мы организовали инфраструктуру доступов.

Упрощенная схема взаимодействия между пользователями и серверамиУпрощенная схема взаимодействия между пользователями и серверами

Первое, что нужно было сделать, это закрыть доступ по умолчанию ко всем тестовым окружениям и инфраструктурным сервисам. Сотрудники в таком случае могут получить доступ ко всему путём добавления своего IP в вайтлист (об этом позже), а клиенты получают доступ точечно.

Фронт-контроллером в данном случае выступает OpenResty это модифицированная версия nginx, которая в т. ч. поддерживает из коробки язык Lua. На нём я написал прослойку, через которую проходят все HTTP(s)-запросы.

Вот так может выглядеть код скрипта аунтентификации (в упрощенном варианте):

auth.lua
function authenticationPrompt()    ngx.header.www_authenticate = 'Basic realm="Restricted by OpenResty"'    ngx.exit(401)endfunction hasAccessByIp()    local ip = ngx.var.remote_addr    local domain = ngx.var.host    local port = ngx.var.server_port    local res, err = httpc:request_uri(os.getenv("AUTH_API_URL") .. "/ip.php", {        method = "GET",        query = "ip=" .. ip .. '&domain=' .. domain .. '&port=' .. port,        headers = {          ["Content-Type"] = "application/x-www-form-urlencoded",        },        keepalive_timeout = 60000,        keepalive_pool = 10,        ssl_verify = false    })    if res ~= nil then        if (res.status == 200) then            session.data.domains[domain] = true            session:save()            return true        elseif (res.status == 403) then            return false        else            session:close()            ngx.say("Server error: " .. res.body)            ngx.exit(500)        end    else        session:close()        ngx.say("Server error: " .. err)        ngx.exit(500)    endendfunction hasAccessByLogin()    local header = ngx.var.http_authorization    local domain = ngx.var.host    local port = ngx.var.server_port    if (header ~= nil) then        header = ngx.decode_base64(header:sub(header:find(' ') + 1))        login, password = header:match("([^,]+):([^,]+)")        if login == nil then            login = ""        end        if password == nil then            password = ""        end        local res, err = httpc:request_uri(os.getenv("AUTH_API_URL") .. '/login.php', {            method = "POST",            body = "username=" .. login .. '&password=' .. password .. '&domain=' .. domain .. '&port=' .. port,            headers = {              ["Content-Type"] = "application/x-www-form-urlencoded",            },            keepalive_timeout = 60000,            keepalive_pool = 10,            ssl_verify = false        })        if res ~= nil then            if (res.status == 200) then                session.data.domains[domain] = true                session:save()                return true            elseif (res.status == 403) then                return false            else                session:close()                ngx.say("Server error: " .. res.body)                ngx.exit(500)            end        else            session:close()            ngx.say("Server error: " .. err)            ngx.exit(500)        end    else        return false    endendos = require("os")http = require "resty.http"httpc = http.new()session = require "resty.session".new()session:start()if (session.data.domains == nil) then    session.data.domains = {}endlocal domain = ngx.var.hostif session.data.domains[domain] == nil then    if (not hasAccessByIp() and not hasAccessByLogin()) then        session:close()        authenticationPrompt()    else        session:close()    endelse    session:close()end

Алгоритм работы скрипта довольно простой:

  • Поступает HTTP-запрос от пользователя;

  • OpenResty запускает скрипт auth.lua;

  • Скрипт определяет запрашиваемый домен и отправляет два запроса на внешний бэкенд;

  • Первый на проверку IP-адреса пользователя в базу;

  • Если IP отсутствует, выводит браузерное окно для ввода логина и пароля, отправляет второй запрос на проверку доступа;

  • В любой другой ситуации выводит окно Вход.

На GitHub я выложил рабочий пример, который можно быстро развернуть с помощью Docker.

Немного расскажу о том, как выглядит управление в нашей системе.

Отмечу, что IP-адреса попадают в базу при аутентификации пользователя. Это сделано специально для сотрудников. Такие адреса помечают как временные и доступные ограниченное время, после чего они удаляются.

В случаях, когда нужно разрешить межсерверное взаимодействие, через панель управления можно добавить IP-адреса вручную.

Для клиентов же создаются простые доступы по паре логин-пароль. Такие доступы ограничиваются в пределах определенных доменных адресов.

Управление доступами по паролю и по IP-адресуУправление доступами по паролю и по IP-адресу

Управление базой сотрудников подтягивается в ID через синхронизацию с другой внутренней системой. Каждый новый сотрудник при добавлении в нашу CRM автоматически получает учётную запись в ID, а также все нужные письма с доступами и инструкциями для последующей настройки и работы.

Форма аутентификации для сотрудниковФорма аутентификации для сотрудников

Каждый день мы начинаем работу с данной аутентификации, таким образом добавляя свой IP-адрес в белый список для получения доступа к инфраструктуре.

Двухфакторная аутентификация

Для обеспечения двухфакторной аутентификации для сотрудников решено было добавить Google Authenticator. Такой механизм защиты позволяет больше обезопасить себя от утечки доступов. Для PHP есть готовая библиотека sonata-project/GoogleAuthenticator. Пример интеграции можно посмотреть здесь.

Интересный нюанс, с которым мы столкнулись в процессе, это зависимость генерируемого кода от времени на устройстве пользователя. Выяснилось, что у некоторых сотрудников время на смартфоне немного отличалось от реального.

OAuth и OpenID

Третье, и не менее важное для нас, создание OAuth-сервера. За основу мы взяли модуль thephpleague/oauth2-server. Для Yii 2 готового решения не было, поэтому написали собственную имплементацию сервера. OAuth2 достаточно обширная тема, расписывать её работу в данной статье не буду. Библиотека имеет хорошую документацию. Также она поддерживает различные фреймворки, включая Laravel и Symfony.

Таким образом, любой сторонний сервис, который поддерживает кастомные OAuth2 конфигурации, достаточно просто подключается к нашей системе. Значимой фишкой такой интеграции стало подключение нашего ID к Mattermost. Последний в бесплатной версии поддерживает только аутентификацию с помощью GitLab, которую удалось эмулировать через наш сервис.

Также для всех наших проектов на Yii был разработан модуль для подключения ID. Это позволило вынести всё управление доступами в админпанели для сотрудников в централизованное место. Кстати, если интересно, я писал статью о модульном подходе, который мы применили в нашем digital-агентстве.

Заключение

Процесс адаптации компании под новую систему был относительно сложный, т. к. это потребовало доработки инфраструктуры и обучения работе с системой всех сотрудников компании. Просто так сказать вот у нас теперь ID, пользуйтесь им не получится, конечно, поэтому весь процесс миграции был очень тщательно задокументирован, а я выступал в качестве технической поддержки первые пару месяцев. Сейчас, спустя время, все процессы наладились, были внесены корректировки и добавлены новые фичи (например, автоматические письма новым сотрудникам с доступами и инструкциями).

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

Ссылки по теме

Источник: habr.com
К списку статей
Опубликовано: 30.04.2021 12:16:20
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Php

Nginx

Yii

Lua

Аутентификация

Yii2

Удалённая работа

Docker

Oauth2

Openresty

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru