Работа со Сценариями (lua-скриптами)
Основные возможности
С помощью Сценариев (lua-скриптов) можно:
-
выбирать Сервер Балансировки (Content Switching (cs));
-
изменять содержимое запросов/ответов (Rewrite);
-
отвечать на HTTP-запрос (Responder);
-
прерывать нежелательные соединения.
lua-скрипты применяются к:
-
Виртуальному Серверу для:
-
выбора Сервера Балансировки (Content Switching (cs));
-
ответа на HTTP-запрос (Responder);
-
изменения содержимого запросов (Rewrite);
-
прерывания нежелательного соединения;
-
-
Серверу балансировки для:
-
ответа на HTTP-запрос (Responder) (например, при условии получении ответа);
-
изменения содержимого ответов (Rewrite);
-
прерывания нежелательного соединения (например, при условии получении ответа).
-
Основные положения
lua-скрипты привязываются к объекту с порядковым номером, определяющим очередность выполнения скрипта.
Пример:
set vs HTTP Vs01 luarules 10 script first.lua
set vs HTTP Vs01 luarules 15 script second.lua
|
Важно учитывать:
|
Выражение 'client.action'
Выражение 'client.action' определяет следующие варианты действий:
-
client.action = "bs"– возвращает Сервер Балансировки, определенный вclient.bs = “<Имя_Сервера_Балансировки>”; -
client.action = "respond"– возвращает ответ пользователю, не передавая запрос на Реальный Сервер.Содержимое ответа описывается вclient.respond.status; -
client.action = "drop"– сбрасывает соединение; -
client.action = ”pass”– используется в скриптах для модификации ответов, после этого ответ передается клиенту; -
client.action = ”auth”– останавливает обработку для аутентификации.
Условия выполнения
Условия выполнения того или иного действия определяются:
-
логическими выражениями
if,elseif,else,end; -
любыми данными, полученными из запроса, а также их комбинацией. Например:
-
if client.http_req.host == "abc.ru” then– eсли имя хоста из запроса эквивалентно «abc.ru», то выполнить какое-либо действие; -
if (client.http_req.host == "abc.ru” and client.remote_p.ip == "10.140.0.200") then– eсли имя хоста из запроса эквивалентно «abc.ru», и запрос пришел с IP-адреса 10.140.0.200, то выполнить какое-либо действие.
-
Можно использовать возможности языка для работы с полученными данными, поскольку правила с четким соответствием не всегда применимы.
Пример для curl qwe.123.ru/abczxc (см. Пример выражений для работы с данными):
> GET /abczxc HTTP/1.1
> Host: qwe.123.ru
> User-Agent: curl/7.81.0
| Выражение | Результат | Комментарий |
|---|---|---|
|
|
Эквивалентно |
|
|
|
|
|
Содержит значение |
|
|
Вернет строку, значение ДО « .123.ru» |
Примеры для Content Switching
Пример 1. Передача любого запроса на Сервер Балансировки «lb1»:
client.bs = "lb1"
client.action = "bs"
Пример 2. Если имя хоста в запросе точно соответствует «abc.domain.ru», то направить запрос на Сервер Балансировки «lb1». В противном случае вернуть код ответа 403 (по умолчанию вернется ошибка 503):
if client.http_req.host == "abc.domain.ru" then
client.bs = "lb1"
client.action = "bs"
else
client.respond.status = 403
client.action = "respond"
end
Пример 3. Если имя хоста содержит текст «abc» и запрос пришел из сети 192.0.2.0/24, то вернуть Сервер Балансировки «lb1». В противном случае вернуть код ответа 403 (по умолчанию вернется ошибка 503):
if (client.http_req.host:find("abc") and client.remote_p:is_network("192.0.2.0/24"))
then
client.bs = "lb1"
client.action = "bs"
else
client.respond.status = 403
client.action = "respond"
end
Пример 4. Если путь содержит:
-
«red», то вернуть Сервер Балансировки «lb1»;
-
«green», то вернуть Сервер Балансировки «lb2».
Если не сработало ни одно из правил, то вернуть Сервер Балансировки «lb-default».
Код:
if client.http_req.path:find("red") then
client.bs = "lb1"
client.action = "bs"
elseif client.http_req.path:find("green") then
client.bs = "lb2"
client.action = "bs"
else
client.bs = "lb-default"
client.action = "bs"
end
Примеры для Rewrite
Пример 1. Если имя хоста содержит текст «abc» И запрос пришел из сети 192.0.2.0/24, то добавить заголовок «XFF: <IP-адрес_пользователя_при_подключении>», и перенаправить запрос на Сервер Балансировки «lb1».
Код:
if (client.http_req.host:find("abc") and client.remote_p:is_network("192.0.2.0/24")) then
client.http_req.header:field_set("XFF", client.remote_p.ip)
client.bs = "lb1"
client.action = "bs"
end
Результат:
WEB -- 03<p>Method GET</p><p>URL on server: /</p><p>REQ
Headers: </p>Host: abc.domain.ru
User-Agent: curl/7.81.0
Accept: */*
XFF: 192.0.2.5
Пример 2. Если имя хоста начинается с «abc», то добавить заголовок «XFF: <IP-адрес_пользователя_при_подключении>» и «Remote-port: <порт-источник_на_клиенте>», и перенаправить запрос на Сервер Балансировки «lb1».
Код:
function startswith(text, prefix)
return text:find(prefix, 1, true) == 1
end
if startswith(client.http_req.host, "abc") then
client.http_req.header:field_set("Remote-port", client.remote_p.port)
client.http_req.header:field_set("XFF", client.remote_p.ip)
client.bs = "lb1"
client.action = "bs"
end
Пример 3. Если:
-
путь запроса содержит «app», то при наличии заголовка X-Forwarded-For добавить IP-адрес клиента в конец запроса;
-
заголовка не было в запросе, то добавить новый.
Перенаправить запрос на Сервер Балансировки «lb1».
Код:
if client.http_req.path:find("abc") then
if client.http_req.header:field_count("X-Forwarded-For") > 0 then
xff, xffip = client.http_req.header:field_get("X-Forwarded-For")
client.http_req.header:field_set("X-Forwarded-For", xffip ..", "..client.remote_p.ip)
else
client.http_req.header:field_set("X-Forwarded-For", client.remote_p.ip)
end
client.bs = "lb1"
client.action = "bs"
end
Пример 4. Подменить домен «ru» на домен «local» (например, если запрос идет к app.domain.ru, то в сторону Реального Сервера должен прийти app.domain.local). В начале пути запроса добавить «/external» и перенаправить запрос на Сервер Балансировки «lb1».
Код:
client.http_req.host = client.http_req.host:match("(.*).ru") .. ".local"
client.http_req.path = "/external" .. client.http_req.path
client.bs = "lb1"
client.action = "bs"
Примеры для Responder
Пример 1. Если метод не GET или не HEAD, то сбросить соединение.
Код:
if (client.http_req.method ~= "GET" and client.http_req.method ~= "HEAD") then
client.action = "drop"
end
Пример 2. Перенаправить соединение с HTTP на HTTPS.
Код:
client.respond.status = 302
client.respond.header["Location"] = "https://" .. client.http_req.host .. client.http_req.path
client.respond.header["Connection"] = "close"
client.action = "respond"
Пример 3. Если запрос из сети 192.0.2.0/24, то ответить HTML-страницей и параметрами запроса.
Код:
if client.remote_p:is_network("192.0.2.0/24") then
client.respond.header["Connection"] = "close"
client.respond.header["Content-type"] = 'text/html
client.respond.body = [[<html>
<body>
<meta charset="UTF-8">
<h1>Lets goodbye!</h1>
<p>Доступ запрещен</p>
</body>
</html>]] .. "IP: " .. client.remote_p.ip .. "\n TRY: " .. client.http_req.host .. client.http_req.path .. "\n" .. " Vserver: " .. client.local_p.ip .. ":" .. client.local_p.port
client.action = 'respond'
Примеры для Ratio (F5)
Пример процентного разделение запросов:
-
примерно 80% запросов попадет на Сервер Балансировки «lb01»;
-
примерно 20% запросов попадет на Сервер Балансировки «lb02».
За каждым Сервером Балансировки может находиться свой пул серверов или один Реальный Сервер.
Код:
if math.random(1, 100) <= 20 then
client.action = "bs"
client.bs = "lb01"
else
client.action = "bs"
client.bs = "lb02"
end
Примеры для страницы обслуживания («Sorry page»)
Пример 1. Если Сервер Балансировки «lb01» находится в состоянии «Online», то он будет выбран для подключения. В противном случае будет показана страница sorry.html.
Файл sorry.html должен располагаться в директории /var/lib/tdc/www.
|
Код:
if client:bs_state("lb01") == "ONLINE" then
client.bs = ”lb01"
client.action = "bs"
else client.respond.header["Content-Type"] = "text/html"
client.action = 'respond’
local filename = storage:sub_path('www', 'sorry.html’)
local file, err = io.open(filename)
client.respond.body = file:read("*a")
file:close()
end
Пример 2. Страница обслуживания:
-
отправляется в интервале времени с 19:00 до 20:00, событие записывается в журнал;
-
в остальное время принимаются и балансируются запросы.
Код:
if client:bs_state("lb01") == "ONLINE" then
client.bs = ”lb01"
client.action = "bs"
else client.respond.header["Content-Type"] = "text/html"
client.action = 'respond’
local filename = storage:sub_path('www', 'sorry.html’)
local file, err = io.open(filename)
client.respond.body = file:read("*a")
file:close()
end
Примеры для Сервера Балансировки
Пример 1. Удаление заголовка с именем «ETag» из ответа Реального Сервера.
Код:
client.http_resp.header["ETag"] = nil
client.action = "pass"
Пример 2. Добавление заголовка со временем и именем «RESTIME».
Код:
client.http_resp.header["RESTIME"] = os.date()
client.action = "pass"
Пример 3. Добавление заголовка с Реальным Сервером (именем «RS») со значением IP-адреса и порта Реального Сервера, от которого получен ответ.
Код:
client.http_resp.header["RS"] = tostring(client.rs)
client.action = "pass"
Пример 4. Генерация ответа с условием. Если Реальный Сервер ответил с кодом 500, то Termidesk Connect сгенерирует ответ с кодом 201.
Код:
if client.http_resp.status == 500 then
client.respond.header["Connection"] = "close"
client.respond.header["Content-Type"] = "text/html"
client.respond.status = 201
client.action = ”respond”
end
Пример 5. Замена заголовка «Server» в ответе.
Код:
client.http_resp.header["Server"] = "MYServer"