Ребята с LF порой интересуются — а нахуя в nginx нужна поддержка lua? Можно какой-то пример, но только не Hello World?
ㅤ
Конечно можно!
Понадобилось мне как-то реализовать продвинутый basic auth, чтобы nginx сходил в mysql базу и сопоставил введенные учетные данные.
А как блядь это сделать?
Тут-то и пригодится Lua!
Для начала создаем базу (nginx_auth) и табличку users. Ну и в ней колонки username и password. В username храним имя пользователя, в password захэшированный пароль:
Эту команду выполняешь в mysql, в ответ он выплюнет тебе хэш, его и нужно будет заебенить в колонку password.
Дальше конфигуряем nginx:
Ну и сам скрипт кидаем в
Всё! Релоадим nginx и получаем прокаченный Basic Auth.
Теперь nginx лезет в базу и при успешном исходе кидает пользователя в локейшен
Да, скрипт работает на mariadb. Для всяких перкон и т.п. возможно нужно будет переделать SELECT.
Вот такие пироги, вот тебе и заебачее применение Lua!
➡️ Кстати, если увидел вектор атаки, поделись в комментах.
tags: #linux #devops #lua #nginx
—
🔔 @bashdays➡️ @gitgate
ㅤ
Конечно можно!
Понадобилось мне как-то реализовать продвинутый basic auth, чтобы nginx сходил в mysql базу и сопоставил введенные учетные данные.
А как блядь это сделать?
Тут-то и пригодится Lua!
Для начала создаем базу (nginx_auth) и табличку users. Ну и в ней колонки username и password. В username храним имя пользователя, в password захэшированный пароль:
SHA1('$UpperPa$$word')Эту команду выполняешь в mysql, в ответ он выплюнет тебе хэш, его и нужно будет заебенить в колонку password.
Дальше конфигуряем nginx:
location / {
content_by_lua_file /etc/nginx/lua/auth.lua;
}
location @authenticated {
root /var/www/bashdays/htdocs/site;
index index.html;
}В nginx само собой добавляем модуль Lua, Как его собрать можешь в гугле посмотреть, ну либо взять какойнить openresty.
Ну и сам скрипт кидаем в
/etc/nginx/lua/auth.lua;local mysql = require "resty.mysql"
local sha1 = require "resty.sha1"
local resty_string = require "resty.string"
local db, err = mysql:new()
-- Установка таймаута
if not db then
ngx.log(ngx.ERR, "failed to create mysql object: ", err)
return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
db:set_timeout(1000)
-- Подключение к базе данных
local res, err = db:connect{
host = "db",
port = 3306,
database = "nginx_auth",
user = "nginx_user",
password = "password",
charset = "utf8",
}
if not res then
ngx.log(ngx.ERR, "failed to connect to MySQL: ", err)
return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
-- Извлекаем данные из заголовков Authorization
local auth = ngx.var.http_authorization
if not auth or not auth:find("Basic ") then
ngx.header["WWW-Authenticate"] = 'Basic realm="Restricted Area"'
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
-- Парсим Basic Auth заголовок
local encoded = auth:sub(7) -- Убираем "Basic " из начала
local decoded = ngx.decode_base64(encoded)
if not decoded then
ngx.header["WWW-Authenticate"] = 'Basic realm="Restricted Area"'
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
-- Разделяем строку на имя пользователя и пароль
local username, password = decoded:match("([^:]+):(.+)")
if not username or not password then
ngx.header["WWW-Authenticate"] = 'Basic realm="Restricted Area"'
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
-- Хешируем пароль
local sha1_obj = sha1:new()
sha1_obj:update(password)
local password_hash = resty_string.to_hex(sha1_obj:final())
-- Проверяем пользователя и пароль в базе данных
local sql = string.format("SELECT * FROM users WHERE username = '%s' AND password = '%s'", username, password_hash)
local res, err = db:query(sql)
if not res or #res == 0 then
ngx.header["WWW-Authenticate"] = 'Basic realm="Restricted Area"'
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
-- Передаем управление Nginx для загрузки страницы
ngx.exec("@authenticated")
Всё! Релоадим nginx и получаем прокаченный Basic Auth.
Сам скрипт не претендует на совершенство и очень простой, объяснять его не вижу смысла. Пробегись глазами и всё сам поймешь.
Теперь nginx лезет в базу и при успешном исходе кидает пользователя в локейшен
@authenticated.Да, скрипт работает на mariadb. Для всяких перкон и т.п. возможно нужно будет переделать SELECT.
Вот такие пироги, вот тебе и заебачее применение Lua!
tags: #linux #devops #lua #nginx
—
Please open Telegram to view this post
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
Про яйца и балансировщик нагрузки
Когда нужно распределить запросы между серверами, возникает проблема — а как блядь понять, какой сервер сейчас менее нагружен?
ㅤ
Постоянно опрашивать сервера, это хуйня, информация будет не актуальной, все это медленно, пока мы собирали данные, ситуация уже поменялась.
На этом фоне возникает эффект «стада», когда много запросов дружно бегут туда, где вроде бы свободно и по итогу перегружают сервер еще больше.
Это можно решить проще — например перенаправлять запросы случайно.
Работает это вроде неплохо, но опять же всегда есть шанс, что нагрузка будет неравномерной. И снова получаем, что какой-то из серверов будет перегружен запросами.
Какой-то замкнутый круг, печаль и говнище.
А чё делать?
Представь: я в рандоме выбираю два сервера, смотрю какой из них менее загружен и отправляю туда запросы. Все!
Вроде мелочь, но работает этот подход прям заебись! И что интересно у этого подхода есть официальная формулировка — Power of Two Choices или «Сила двух выборов».
Почему это заебись?
На просторах есть задачка «яйца и корзины», в оригинале там конечно не «яйца», а шары.
Короче. Мы случайно кидаем яйца в корзины. Если класть каждое яйцо в случайную корзину, то в итоге некоторые корзины будут переполнены.
Но если каждый раз выбирать две корзины и класть яйцо туда, где их меньше — ситуация резко улучшается.
Максимальная перегрузка корзины падает в разы. И это при том, что мы по-прежнему делаем случайный выбор, просто из двух вариантов, а не из одного.
Все просто! Тоже самое и с серверами:
- Если мы выбираем сервер наугад, нагрузка на сервер может стать критической.
- Если выбираем между двумя, вероятность перегрузить сервер падает квадратично.
Давай на котиках:
Представь, что у тебя есть куча серверов, и на четверти из них пришло по 4 запроса.
- Если я выбираю сервер случайно, есть 25% шанс попасть именно на сервер, которому уже пезда.
- Если я выбираю два сервера и беру менее загруженный — вероятность того, что оба окажутся с нагрузкой 4, уже всего 6,25% (1/16).
Получается, что вероятность перегрузки падает очень быстро. А дальше — ещё быстрее: чтобы нагрузка выросла до 5, 6 и так далее, нужно каждый раз «удачно» попасть в редкие перегруженные сервера, и шанс становится ничтожным.
Видишь, математика нужна не только бекендерам, но и в девопсе можно применить.
Что получаем на практике:
На практике балансировка «выбором из двух» делает распределение нагрузки по серверам почти равномерной. При этом мы не тратим кучу ресурсов на мониторинг и не пытаемся каждый раз точно узнать состояние всех серверов.
Такой подход - простой и элегантный. Как говорится — без хуйни!
Формулы и расчеты визуально можешь глянуть тут. Там как раз разбирается способ с яйцами, но уже с углубленной математикой и формулами.
Как реализовать в nginx
Вместо случайного выбора
Мотай на ус, глядишь сгодится в хозяйстве.
🛠 #devops #nginx
—
✅ @bashdays ✅ @linuxfactory ✅ @blog
Когда нужно распределить запросы между серверами, возникает проблема — а как блядь понять, какой сервер сейчас менее нагружен?
ㅤ
Постоянно опрашивать сервера, это хуйня, информация будет не актуальной, все это медленно, пока мы собирали данные, ситуация уже поменялась.
На этом фоне возникает эффект «стада», когда много запросов дружно бегут туда, где вроде бы свободно и по итогу перегружают сервер еще больше.
Это можно решить проще — например перенаправлять запросы случайно.
Работает это вроде неплохо, но опять же всегда есть шанс, что нагрузка будет неравномерной. И снова получаем, что какой-то из серверов будет перегружен запросами.
Какой-то замкнутый круг, печаль и говнище.
А чё делать?
Представь: я в рандоме выбираю два сервера, смотрю какой из них менее загружен и отправляю туда запросы. Все!
Вроде мелочь, но работает этот подход прям заебись! И что интересно у этого подхода есть официальная формулировка — Power of Two Choices или «Сила двух выборов».
Почему это заебись?
На просторах есть задачка «яйца и корзины», в оригинале там конечно не «яйца», а шары.
Короче. Мы случайно кидаем яйца в корзины. Если класть каждое яйцо в случайную корзину, то в итоге некоторые корзины будут переполнены.
Но если каждый раз выбирать две корзины и класть яйцо туда, где их меньше — ситуация резко улучшается.
Максимальная перегрузка корзины падает в разы. И это при том, что мы по-прежнему делаем случайный выбор, просто из двух вариантов, а не из одного.
Все просто! Тоже самое и с серверами:
- Если мы выбираем сервер наугад, нагрузка на сервер может стать критической.
- Если выбираем между двумя, вероятность перегрузить сервер падает квадратично.
Давай на котиках:
Представь, что у тебя есть куча серверов, и на четверти из них пришло по 4 запроса.
- Если я выбираю сервер случайно, есть 25% шанс попасть именно на сервер, которому уже пезда.
- Если я выбираю два сервера и беру менее загруженный — вероятность того, что оба окажутся с нагрузкой 4, уже всего 6,25% (1/16).
Получается, что вероятность перегрузки падает очень быстро. А дальше — ещё быстрее: чтобы нагрузка выросла до 5, 6 и так далее, нужно каждый раз «удачно» попасть в редкие перегруженные сервера, и шанс становится ничтожным.
Видишь, математика нужна не только бекендерам, но и в девопсе можно применить.
Что получаем на практике:
На практике балансировка «выбором из двух» делает распределение нагрузки по серверам почти равномерной. При этом мы не тратим кучу ресурсов на мониторинг и не пытаемся каждый раз точно узнать состояние всех серверов.
Такой подход - простой и элегантный. Как говорится — без хуйни!
Формулы и расчеты визуально можешь глянуть тут. Там как раз разбирается способ с яйцами, но уже с углубленной математикой и формулами.
Этот же принцип используют, например, в cuckoo hashing (структура данных для хранения ключей).
Cuckoo hashing (кукушкино хеширование) — алгоритм разрешения коллизий значений хеш-функций в таблице с постоянным временем выборки в худшем случае.
Как реализовать в nginx
upstream backend {
random two; # Power of 2 Choices
server app1.bashdays.ru;
server app2.bashdays.ru;
server app3.bashdays.ru;
}Вместо случайного выбора
nginx берёт два случайных сервера и кидает запрос на тот, где меньше соединений.Этот же принцип используют, например, в cuckoo hashing (структура данных для хранения ключей).
Мотай на ус, глядишь сгодится в хозяйстве.
—
Please open Telegram to view this post
VIEW IN TELEGRAM
7 88
Чем глубже пропасть, в которую падаешь, тем больше шансов научиться летать.
На подходе новый закон о глобальном запрете мата в сети, поэтому к нему нужно подготовиться. Переебашивать каждый пост в блоге — проблематично, да и лень.
ㅤ
Поэтому, как обычно, будем изобретать велосипед.
Суть идеи — налету в DOM менять маты на синонимы. То есть в момент отдачи контента, контент будет насильно зацензурен.
А там уже можно будет добавить регулярки и гибко всё затюнить, мало ли, может можно будет звездочками разбавлять такой контент.
Реализация
За основу я возьму angie (форк nginx) с модулем LUA. Я давно на это решение пересел, так как всё из коробки работает, включая генерацию SSL. Не нужно ебстись с компиляций модулей и страдать.
Если LUA не установлен, ставим:
В
В
Ну и создаем файл
Проверяем:
Открываем сайт и видим что все маты зацензурились согласно шаблону в файле
Если всё осталось по-прежнему, скинь кеш, в 99% дело в нем.
Давай разберемся как это работает.
И получаем — блок берёт HTML-страницу, проходит по каждому чанку тела ответа, и заменяет в нём «запрещённые» слова из словаря на синонимы.
Ааа, еще в словаре
Любая буква, цифра или подчёркивание (
Поэтому лучше сделать как-то так:
LUA — сила! Рекомендую хоть раз потыкать, проникнешься и уже не сможешь без этого модуля жить. Костыли клепать милое дело.
Как вариант, позже еще запилю «специальный» заголовок, при передаче которого angie будет отключать цензуру и выводить посты в оригинале.
Ну заебись же! Осталось придумать, как зацензурить 1000 постов в телеге.
🛠 #angie #nginx #tricks
—
✅ @bashdays ✅ @linuxfactory ✅ @blog
На подходе новый закон о глобальном запрете мата в сети, поэтому к нему нужно подготовиться. Переебашивать каждый пост в блоге — проблематично, да и лень.
ㅤ
Поэтому, как обычно, будем изобретать велосипед.
Суть идеи — налету в DOM менять маты на синонимы. То есть в момент отдачи контента, контент будет насильно зацензурен.
Хуйня == Фигня, Пезда == 3.14зда ну и в таком духе.
А там уже можно будет добавить регулярки и гибко всё затюнить, мало ли, может можно будет звездочками разбавлять такой контент.
Реализация
За основу я возьму angie (форк nginx) с модулем LUA. Я давно на это решение пересел, так как всё из коробки работает, включая генерацию SSL. Не нужно ебстись с компиляций модулей и страдать.
Как настроить автополучение SSL в angie, писал в этом посте.
Если LUA не установлен, ставим:
apt install angie-module-lua
В
/etc/angie/angie.conf добавляем:load_module modules/ndk_http_module.so;
load_module modules/ngx_http_lua_module.so;
load_module modules/ngx_stream_lua_module.so;
В
/etc/angie/sites-available/bashdays.ru.conf добавляем в корневой локейшен:location / {
root /var/www/bashdays.ru/htdocs;
index index.html;
gzip off;
body_filter_by_lua_block {
local replacements = dofile("/etc/angie/wordmap.lua")
local chunk = ngx.arg[1]
if chunk then
for _, pair in ipairs(replacements) do
chunk = ngx.re.gsub(chunk, pair[1], pair[2], "ijo")
end
ngx.arg[1] = chunk
end
}
}Ну и создаем файл
/etc/angie/wordmap.lua который будет содержать шаблоны замены:return {
{"хуйня", "фигня"},
{"хуй", "писька"},
{"говн%w*", "ерунда"},
{"еба%w*", "плохой"},
}Проверяем:
angie -t и если всё ок, можно сделать systemctl restart angie.Открываем сайт и видим что все маты зацензурились согласно шаблону в файле
/etc/angie/wordmap.lua.Если всё осталось по-прежнему, скинь кеш, в 99% дело в нем.
Давай разберемся как это работает.
body_filter_by_lua_block — перед отправкой ответа клиенту, запустится Lua-скрипт, который изменит тело ответа.dofile("/etc/angie/wordmap.lua") — загружает Lua-файл со словарём замен.ngx.arg[1] — текущий кусок (chunk) ответа (HTML, JSON и т.п.), который angie собирается отправить клиенту. Ответ приходит потоками.for _, pair in ipairs(replacements) — перебор всех замен из словаря.ngx.re.gsub(chunk, pair[1], pair[2], "ijo") — регулярная замена, [1] что искать, [2] на что заменить."ijo" — флаги: i — без учёта регистра, j — dotall (точка матчится на \n), o — оптимизация.ngx.arg[1] = chunk — возвращаем изменённый кусок, который уйдёт клиенту.И получаем — блок берёт HTML-страницу, проходит по каждому чанку тела ответа, и заменяет в нём «запрещённые» слова из словаря на синонимы.
Ааа, еще в словаре
{"говн%w*", "ерунда"} есть символы %w* это регулярка. Любая буква, цифра или подчёркивание (
[0-9A-Za-z_]). В UTF-8 оно обычно ловит только латиницу, а кириллицу нет.Поэтому лучше сделать как-то так:
{"говн[А-Яа-яЁё]*", "ерунда"}. Короче тут тебе карты в руки, сам натыкай паттерны.LUA — сила! Рекомендую хоть раз потыкать, проникнешься и уже не сможешь без этого модуля жить. Костыли клепать милое дело.
Есть конечно нюансы, например — Если слово вдруг разорвётся между чанками (например, ху в одном чанке и йня в другом) — фильтр его не заменит. Для надёжности нужно буферизовать весь ответ.
Ну и с кириллицей надо вопрос дофиксить, но это я реализую чуть позже. Главное концепт. Всё остальное реализуемо.
Как вариант, позже еще запилю «специальный» заголовок, при передаче которого angie будет отключать цензуру и выводить посты в оригинале.
А можно разработчиков подъебать, пусть ищут в своем коде, почему у них слова странным образом заменяются.
Ну заебись же! Осталось придумать, как зацензурить 1000 постов в телеге.
—
Please open Telegram to view this post
VIEW IN TELEGRAM
13 53