[PHP, Nginx, Yii, Lua] Есть ли корпоративная жизнь на удаленке и как ее обеспечить: интеграция внутренней системы аутентификации
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
В 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)
end
function 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)
end
end
function 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
end
end
os = 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 = {}
end
local domain = ngx.var.host
if session.data.domains[domain] == nil then
if (not hasAccessByIp() and not hasAccessByLogin()) then
session:close()
authenticationPrompt()
else
session:close()
end
else
session:close()
end
Алгоритм работы скрипта довольно простой:
- Поступает HTTP-запрос от пользователя;
- OpenResty запускает скрипт auth.lua;
- Скрипт определяет запрашиваемый домен и отправляет два запроса на внешний бэкенд;
- Первый ― на проверку IP-адреса пользователя в базу;
- Если IP отсутствует, выводит браузерное окно для ввода логина и пароля, отправляет второй запрос на проверку доступа;
- В любой другой ситуации выводит окно Вход.
На GitHub я выложил рабочий пример, который можно быстро развернуть с помощью Docker.Немного расскажу о том, как выглядит управление в нашей системе.Отмечу, что 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, пользуйтесь им” не получится, конечно, поэтому весь процесс миграции был очень тщательно задокументирован, а я выступал в качестве технической поддержки первые пару месяцев. Сейчас, спустя время, все процессы наладились, были внесены корректировки и добавлены новые фичи (например, автоматические письма новым сотрудникам с доступами и инструкциями).Самое главное ― получилось создать для сотрудников единую учётную запись для всех внутренних и внешних систем, которыми пользуемся в работе, а также автоматизировать процесс получения этой учётной записи и всей нужной информации сразу же в первый день работы новых сотрудников.Ссылки по теме
- Мой пример Basic Digest аутентификации на Lua и OpenResty
- GoogleAuthenticator на PHP
- OAuth2 сервер на PHP
===========
Источник:
habr.com
===========
Похожие новости:
- [Настройка Linux, Nginx, Серверное администрирование, Хранение данных] Домашний медиа сервер minidlna
- [Высокая производительность, Хостинг, Серверная оптимизация, Компьютерное железо] Как сделать кластерный сервер на ARM процессоре и тестирование VPS на AWS Graviton2
- [IT-инфраструктура, Офисы IT-компаний, Видеоконференцсвязь, Удалённая работа] Джентельменский набор для переговорки: как оборудовать удобную переговорную комнату
- [PHP, Программирование, Тестирование веб-сервисов] Работа с частичными моками в PHPUnit 10
- [Тестирование IT-систем, PHP, Программирование] Практики при работе с PHPUnit
- [PHP] Не мокайте то, чем вы не владеете (перевод)
- [PHP, Программирование] От версии 8 к 8.1: новый виток развития PHP (перевод)
- [Системное администрирование, PHP, Программирование, Разработка систем связи] Голосовое меню своими руками
- [Управление разработкой, Управление проектами] Как работать в команде, которая пишет на 5 языках
- [PHP, Тестирование веб-сервисов] Так как же не страдать от функциональных тестов?
Теги для поиска: #_php, #_nginx, #_yii, #_lua, #_autentifikatsija (аутентификация), #_yii2, #_php, #_lua, #_udalennaja_rabota (удаленная работа), #_docker, #_oauth2, #_php, #_nginx, #_yii, #_lua
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:10
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
В digital-агентстве Convergent, где я работаю, в потоке множество проектов, и у каждого из них может быть собственная админка. Есть несколько окружений (дев, стейдж, лайв). А ещё есть разные внутрикорпоративные сервисы (как собственной разработки, так и сторонние вроде Redmine или Mattermost), которыми ежедневно пользуются сотрудники. Наша команда всегда была распределённой между несколькими офисами, но с учётом событий последнего года все сотрудники перешли на удалёнку. Так мы столкнулись с необходимостью организовать всё многообразие внутренних и клиентских сервисов в единой системе. В данной статье я хочу поделиться опытом создания собственной внутренней системы аутентификации на основе OpenResty, а также спецификации OAuth2. В качестве основного языка программирования мы используем PHP, а фреймворк ― Yii 2.Суммирую необходимый функционал:
Упрощенная схема взаимодействия между пользователями и серверамиПервое, что нужно было сделать, ― это закрыть доступ по умолчанию ко всем тестовым окружениям и инфраструктурным сервисам. Сотрудники в таком случае могут получить доступ ко всему путём добавления своего IP в вайтлист (об этом позже), а клиенты получают доступ точечно.Фронт-контроллером в данном случае выступает OpenResty ― это модифицированная версия nginx, которая в т. ч. поддерживает из коробки язык Lua. На нём я написал прослойку, через которую проходят все HTTP(s)-запросы.Вот так может выглядеть код скрипта аунтентификации (в упрощенном варианте):auth.lua function authenticationPrompt()
ngx.header.www_authenticate = 'Basic realm="Restricted by OpenResty"' ngx.exit(401) end function 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) end end function 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 end end os = 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 = {} end local domain = ngx.var.host if session.data.domains[domain] == nil then if (not hasAccessByIp() and not hasAccessByLogin()) then session:close() authenticationPrompt() else session:close() end else session:close() end
На GitHub я выложил рабочий пример, который можно быстро развернуть с помощью Docker.Немного расскажу о том, как выглядит управление в нашей системе.Отмечу, что 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 =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:10
Часовой пояс: UTC + 5