Статья будет полезна тем:
Предисловие
Сервисы, которые становятся доступны через Kubernetes, имеют богатый набор способов авторизации. Один из наиболее модных – это заголовок Authorization: Bearer — это, например: JWT-авторизация (JSON Web Token) с передачей множества ключей, а следовательно, и значений, в одном заголовке. Встречаются и Basic-авторизации, например для Registry (хранилище образов Docker). Данная авторизация не использует Cookie и автоматически добавляется браузером (кроме Safari — там есть нюансы, которые мы пока не решаем) ко всем запросам к серверу.
Проблема 1:
Не получается авторизоваться через Firefox & Safari в интерфейсе Storage OS, показывается Loader, и на этом всё.
Мини-гипотеза:
Проблема в проксировании. Быстрая проверка показала результат: если авторизация без использования проксирования (универсальный безопасный доступ по сертификату), то всё работает. Так в чём же дело?
Проанализировав сетевой стек, мы поняли, что используется заголовок Authorization, однако ранее, в ходе настройки проксирования сервисов Rancher, было выяснено, что этот заголовок передаётся проксируемому сервису и содержит данные авторизации по сертификату, поэтому было решено его просто удалять по завершении процесса авторизации (FakeBasicAuth).
Проблема 2:
Во многих web-серверах авторизация по персональному сертификату реализована через эмуляцию Basic-авторизации (по сути, вмешательство в запрос пользователя), вероятно с целью уменьшения изменений в основном коде web-сервера. Называется такой способ FakeBasicAuth. После установления такого заголовка web-сервером затирается заголовок Authorization, который приходит от пользователя.
Гипотезы:
Видимое состояние — цель:
Storage OS авторизует, можно настраивать этот сервис, сохраняя единый подход к открытию доступности сервисов во внешний интернет.
Дополнительная цель:
Унифицированный, быстрый и безопасный доступ по http с сохранением функциональности всех возможных сервисов, работающих по стандарту http (например, REST API для мобильного приложения или Registry Docker).
Как проверить?
Проверка гипотез:
1. Исходя из предыдущего опыта, попробуем найти способ в интернете по таким запросам: apache authentification external basic via cert.
Нашлась более-менее адекватная статья про ldap. Вот таким образом:
И далее прокинуть заголовок в дополнительных заголовках через конструкцию
Но, к сожалению, эта конструкция не подразумевает создания идентичного заголовка, заголовку запроса пользователя, да и env формируется некорректно.
Поэтому способ на основе стандартных rewrite-ов оказался бесполезным и сложным.
2. Если не можем стандартно, значит, надо обратиться к lua, ранее видели, что есть блоки обработки запросов, которые выполняются до обработки сертификатов, снова посмотрим на блок-схему из статьи lua_load_resty_core и инструкцию module_lua_writinghooks c конструкцией early.
Получается, что мы можем задействовать тот же самый скрипт (Как мы в ZeroTech подружили Apple Safari и клиентские сертификаты с websocket-ами), чтобы сохранить заголовок Authorization до замены им на FakeBasicAuth.
В Lua это теперь выглядит так:
Примечание:
Жирным шрифтом отмечены новые конструкции. И раз мы знаем, что env, полученный из Lua, доступен только для expr-выражений, добавляем конструкцию рядом с шифрованием zt-cert токена:
#исходящие Cookie пользователю
#передаём заголовки проксируемому сервису
Доступность данных для передачи сервису проверяли через передачу данных обратно пользователю в браузер:
Самое интересное здесь — это способ проверки наличия данных, чтобы не передавать заголовок проксируемому сервису, если он не пришёл снаружи от браузера пользователя. За это отвечает вторая часть конструкции:
Завершение:
Готовых решений в интернете на текущий момент нет, потрачено около трех часов на поиск и попытки проверить вариации, так как не хотелось «изобретать велосипед».
Добавлено 5 строк, 3 из которых можно смело удалить. Как вы думаете, какие? — Пишите ваши варианты ответов в комментариях к статье.
Я не хотел писать об этом опыте, так как по-сути — это всего 2 строки и заголовок Authorization дойдёт до адресата, но решил всё же поделиться информацией, так как здесь используется хороший багаж знаний предыдущих исследований о сертификатах (речь об этой статье). К тому же вряд ли найдутся смельчаки написать что-то своё и настолько простое на неизвестном языке.
- кому необходимо задействовать несколько видов авторизации в одном запросе к серверу;
- кто хочет открывать сервисы мира Kubernetes/Docker в общий интернет, не задумываясь о способах защиты конкретного сервиса;
- думает, что всё уже кем-то сделано, и хотел бы сделать мир немного удобнее и безопаснее.
Предисловие
Сервисы, которые становятся доступны через Kubernetes, имеют богатый набор способов авторизации. Один из наиболее модных – это заголовок Authorization: Bearer — это, например: JWT-авторизация (JSON Web Token) с передачей множества ключей, а следовательно, и значений, в одном заголовке. Встречаются и Basic-авторизации, например для Registry (хранилище образов Docker). Данная авторизация не использует Cookie и автоматически добавляется браузером (кроме Safari — там есть нюансы, которые мы пока не решаем) ко всем запросам к серверу.
Проблема 1:
Не получается авторизоваться через Firefox & Safari в интерфейсе Storage OS, показывается Loader, и на этом всё.
Мини-гипотеза:
Проблема в проксировании. Быстрая проверка показала результат: если авторизация без использования проксирования (универсальный безопасный доступ по сертификату), то всё работает. Так в чём же дело?
Проанализировав сетевой стек, мы поняли, что используется заголовок Authorization, однако ранее, в ходе настройки проксирования сервисов Rancher, было выяснено, что этот заголовок передаётся проксируемому сервису и содержит данные авторизации по сертификату, поэтому было решено его просто удалять по завершении процесса авторизации (FakeBasicAuth).
Проблема 2:
Во многих web-серверах авторизация по персональному сертификату реализована через эмуляцию Basic-авторизации (по сути, вмешательство в запрос пользователя), вероятно с целью уменьшения изменений в основном коде web-сервера. Называется такой способ FakeBasicAuth. После установления такого заголовка web-сервером затирается заголовок Authorization, который приходит от пользователя.
Гипотезы:
- Область видимости заголовка FakeBasicAuth поддаётся ещё большему ограничению, так что восстанавливается исходный заголовок для передачи на проксируемый ресурс таким образом, что будет передаваться только оригинальный заголовок, если он был.
- Область видимости заголовка Authorization может быть сконструирована так, что заголовок будет сохранён до активации механизма FakeBasicAuth и восстановлен после.
Видимое состояние — цель:
Storage OS авторизует, можно настраивать этот сервис, сохраняя единый подход к открытию доступности сервисов во внешний интернет.
Дополнительная цель:
Унифицированный, быстрый и безопасный доступ по http с сохранением функциональности всех возможных сервисов, работающих по стандарту http (например, REST API для мобильного приложения или Registry Docker).
Как проверить?
- docker login registry-rancher.xxx.ru — используя ключи и логин/пароль.
- storageos-rancher.xxx.ru/#/login — используя логин и пароль из конфигов secret rancher.xxx.ru/p/c-84bnv:p-qj9qm/secrets/kube-system:init-secr… (не работает в Safari).
- registry-ui-rancher.xxx.ru — используя браузер и логин/пароль от Registry. Для внимательно читающих фишка: можно вместо стандартного docker login registry-rancher.xxx.ru использовать этот интерфейс — там встроено проксирование к Registry.
Проверка гипотез:
1. Исходя из предыдущего опыта, попробуем найти способ в интернете по таким запросам: apache authentification external basic via cert.
Нашлась более-менее адекватная статья про ldap. Вот таким образом:
RewriteEngine on
RewriteCond %{IS_SUBREQ} ^false$
RewriteCond %{LA-U:REMOTE_USER} (.+)
RewriteRule . - [E=RU:%1]
RequestHeader set REMOTE_USER %{RU}e
И далее прокинуть заголовок в дополнительных заголовках через конструкцию
RequestHeader add Authorization "expr=%{env:zt-auth-before}" "expr=%{env:zt-auth-before} =~/.{1,}/"
Но, к сожалению, эта конструкция не подразумевает создания идентичного заголовка, заголовку запроса пользователя, да и env формируется некорректно.
Поэтому способ на основе стандартных rewrite-ов оказался бесполезным и сложным.
2. Если не можем стандартно, значит, надо обратиться к lua, ранее видели, что есть блоки обработки запросов, которые выполняются до обработки сертификатов, снова посмотрим на блок-схему из статьи lua_load_resty_core и инструкцию module_lua_writinghooks c конструкцией early.
Получается, что мы можем задействовать тот же самый скрипт (Как мы в ZeroTech подружили Apple Safari и клиентские сертификаты с websocket-ами), чтобы сохранить заголовок Authorization до замены им на FakeBasicAuth.
LuaHookAccessChecker /usr/local/etc/apache24/sslincludes/websocket_token.lua handler early
В Lua это теперь выглядит так:
require 'apache2'
function handler(r)
local fmt = '%Y%m%d%H%M%S'
local timeout = 3600 -- 1 hour
local auth = r.headers_in['Authorization']
r.notes['zt-cert-timeout'] = timeout
r.notes['zt-cert-date-next'] = os.date(fmt,os.time()+timeout)
r.notes['zt-cert-date-halfnext'] = os.date(fmt,os.time()+ (timeout/2))
r.notes['zt-cert-date-now'] = os.date(fmt,os.time())
if auth ~= nil then
r.notes['zt-auth-before'] = auth
end
return apache2.OK
end
Примечание:
Жирным шрифтом отмечены новые конструкции. И раз мы знаем, что env, полученный из Lua, доступен только для expr-выражений, добавляем конструкцию рядом с шифрованием zt-cert токена:
#исходящие Cookie пользователю
Header set Set-Cookie "expr=zt-cert=%{sha1:...
#передаём заголовки проксируемому сервису
RequestHeader add Authorization "expr=%{env:zt-auth-before}" "expr=%{env:zt-auth-before} =~/.{1,}/"
Доступность данных для передачи сервису проверяли через передачу данных обратно пользователю в браузер:
Header add Authorization "expr=%{env:zt-auth-before}" "expr=%{env:zt-auth-before} =~/.{1,}/"
Самое интересное здесь — это способ проверки наличия данных, чтобы не передавать заголовок проксируемому сервису, если он не пришёл снаружи от браузера пользователя. За это отвечает вторая часть конструкции:
"expr=%{env:zt-auth-before} =~/.{1,}/"
Завершение:
Готовых решений в интернете на текущий момент нет, потрачено около трех часов на поиск и попытки проверить вариации, так как не хотелось «изобретать велосипед».
Добавлено 5 строк, 3 из которых можно смело удалить. Как вы думаете, какие? — Пишите ваши варианты ответов в комментариях к статье.
Я не хотел писать об этом опыте, так как по-сути — это всего 2 строки и заголовок Authorization дойдёт до адресата, но решил всё же поделиться информацией, так как здесь используется хороший багаж знаний предыдущих исследований о сертификатах (речь об этой статье). К тому же вряд ли найдутся смельчаки написать что-то своё и настолько простое на неизвестном языке.