dns-server: Драйвер DNS-сервера Lua для nginx-module-lua
Установка
Если вы еще не настроили подписку на репозиторий RPM, зарегистрируйтесь. Затем вы можете продолжить с следующими шагами.
CentOS/RHEL 7 или Amazon Linux 2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-dns-server
CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-dns-server
Чтобы использовать эту библиотеку Lua с NGINX, убедитесь, что nginx-module-lua установлен.
Этот документ описывает lua-resty-dns-server v0.2, выпущенный 23 июля 2019 года.
Эта библиотека Lua предоставляет драйвер DNS-сервера для модуля ngx_lua nginx:
https://github.com/openresty/stream-lua-nginx-module/#readme
Синопсис
stream {
server {
listen 53 udp;
content_by_lua_block {
local server = require 'resty.dns.server'
local sock, err = ngx.req.socket()
if not sock then
ngx.log(ngx.ERR, "не удалось получить сокет запроса: ", err)
return ngx.exit(ngx.ERROR)
end
local req, err = sock:receive()
if not req then
ngx.log(ngx.ERR, "не удалось получить: ", err)
return ngx.exit(ngx.ERROR)
end
local dns = server:new()
local request, err = dns:decode_request(req)
if not request then
ngx.log(ngx.ERR, "не удалось декодировать запрос: ", err)
local resp = dns:encode_response()
local ok, err = sock:send(resp)
if not ok then
ngx.log(ngx.ERR, "не удалось отправить: ", err)
ngx.exit(ngx.ERROR)
end
return
end
local query = request.questions[1]
ngx.log(ngx.DEBUG, "qname: ", query.qname, " qtype: ", query.qtype)
local subnet = request.subnet[1]
if subnet then
ngx.log(ngx.DEBUG, "адрес подсети: ", subnet.address, " маска: ", subnet.mask, " семейство: ", subnet.family)
end
local cname = "sinacloud.com"
if query.qtype == server.TYPE_CNAME or
query.qtype == server.TYPE_AAAA or query.qtype == server.TYPE_A then
local err = dns:create_cname_answer(query.qname, 600, cname)
if err then
ngx.log(ngx.ERR, "не удалось создать ответ cname: ", err)
return
end
else
dns:create_soa_answer("test.com", 600, "a.root-test.com", "vislee.test.com", 1515161223, 1800, 900, 604800, 86400)
end
local resp = dns:encode_response()
local ok, err = sock:send(resp)
if not ok then
ngx.log(ngx.ERR, "не удалось отправить: ", err)
return
end
}
}
server {
listen 53;
content_by_lua_block {
local bit = require 'bit'
local lshift = bit.lshift
local rshift = bit.rshift
local band = bit.band
local byte = string.byte
local char = string.char
local server = require 'resty.dns.server'
local sock, err = ngx.req.socket()
if not sock then
ngx.log(ngx.ERR, "не удалось получить сокет запроса: ", err)
return ngx.exit(ngx.ERROR)
end
local buf, err = sock:receive(2)
if not buf then
ngx.log(ngx.ERR, "не удалось получить: ", err)
return ngx.exit(ngx.ERROR)
end
local len_hi = byte(buf, 1)
local len_lo = byte(buf, 2)
local len = lshift(len_hi, 8) + len_lo
local data, err = sock:receive(len)
if not data then
ngx.log(ngx.ERR, "не удалось получить: ", err)
return ngx.exit(ngx.ERROR)
end
local dns = server:new()
local request, err = dns:decode_request(data)
if not request then
ngx.log(ngx.ERR, "не удалось декодировать dns запрос: ", err)
return
end
local query = request.questions[1]
ngx.log(ngx.DEBUG, "qname: ", query.qname, " qtype: ", query.qtype)
local subnet = request.subnet[1]
if subnet then
ngx.log(ngx.DEBUG, "адрес подсети: ", subnet.address, " маска: ", subnet.mask, " семейство: ", subnet.family)
end
if query.qtype == server.TYPE_CNAME or query.qtype == server.TYPE_A then
dns:create_cname_answer(query.qname, 600, "sinacloud.com")
elseif query.qtype == server.TYPE_AAAA then
local resp_header, err = dns:create_response_header(server.RCODE_NOT_IMPLEMENTED)
resp_header.ra = 0
else
dns:create_soa_answer("test.com", 600, "a.root-test.com", "vislee.test.com", 1515161223, 1800, 900, 604800, 86400)
end
local resp = dns:encode_response()
local len = #resp
local len_hi = char(rshift(len, 8))
local len_lo = char(band(len, 0xff))
local ok, err = sock:send({len_hi, len_lo, resp})
if not ok then
ngx.log(ngx.ERR, "не удалось отправить: ", err)
return
end
return
}
}
}
Методы
new
syntax: s, err = class:new()
Создает объект dns.server. Возвращает nil и строку сообщения в случае ошибки.
decode_request
syntax: request, err = s:decode_request(buf)
Парсит DNS-запрос.
Запрос возвращает таблицу lua, которая содержит некоторые из следующих полей:
-
header:headerтакже является таблицей lua, которая обычно содержит некоторые из следующих полей:id: Идентификатор, назначенный программой, генерирующей любой вид запроса.qr: Поле указывает, является ли это сообщение запросом (0) или ответом (1).opcode: Поле указывает тип запроса в этом сообщении.tc: Поле указывает, что это сообщение было усечено из-за превышения допустимой длины на канале передачи.rd: Рекурсия желательна. ЕслиRDустановлен, он указывает серверу имен продолжать запрос рекурсивно.rcode: код ответа.qdcount: Поле, указывающее количество записей в секции вопросов.
-
questions: Каждая запись вquestionsтакже является таблицей lua, которая содержит некоторые из следующих полей:qname: Доменное имя запроса.qtype: Указывает тип запроса.qclass: Указывает класс запроса. Обычно это полеINдля Интернета.
create_a_answer
syntax: err = s:create_a_answer(name, ttl, ipv4)
Создает A-записи. Возвращает nil или строку сообщения в случае ошибки.
которые обычно содержат некоторые из следующих полей:
-
nameИмя ресурсной записи. *
ttlЗначение времени жизни (TTL) в секундах для текущей ресурсной записи. *
ipv4IPv4-адрес.
create_aaaa_answer
syntax: err = s:create_aaaa_answer(name, ttl, ipv6)
Создает AAAA-записи. Возвращает nil или строку сообщения в случае ошибки.
которые обычно содержат некоторые из следующих полей:
-
nameИмя ресурсной записи. *
ttlЗначение времени жизни (TTL) в секундах для текущей ресурсной записи. *
ipv6IPv6-адрес.
create_cname_answer
syntax: err = s:create_cname_answer(name, ttl, cname)
Создает CNAME-записи. Возвращает nil или строку сообщения в случае ошибки.
которые обычно содержат некоторые из следующих полей:
-
nameИмя ресурсной записи. *
ttlЗначение времени жизни (TTL) в секундах для текущей ресурсной записи. *
cnameИмя для псевдонима.
create_txt_answer
syntax: err = s:create_txt_answer(name, ttl, txt)
Создает txt-записи. Возвращает nil или строку сообщения в случае ошибки.
которые обычно содержат некоторые из следующих полей:
-
nameИмя ресурсной записи. *
ttlЗначение времени жизни (TTL) в секундах для текущей ресурсной записи. *
txtТекстовые строки.
create_ns_answer
syntax: err = s:create_ns_answer(name, ttl, nsdname)
Создает NS-записи. Возвращает nil или строку сообщения в случае ошибки.
которые обычно содержат некоторые из следующих полей:
-
nameИмя ресурсной записи. *
ttlЗначение времени жизни (TTL) в секундах для текущей ресурсной записи. *
nsdnameУказывает хост, который должен быть авторитетным для указанного класса и домена.
create_soa_answer
syntax: err = s:create_soa_answer(name, ttl, mname, rname, serial, refresh, retry, expire, minimum)
Создает SOA-записи. Возвращает nil или строку сообщения в случае ошибки.
которые обычно содержат некоторые из следующих полей:
-
nameИмя ресурсной записи. *
ttlЗначение времени жизни (TTL) в секундах для текущей ресурсной записи. *
mnameИмя сервера имен, который был оригинальным или основным источником данных для этой зоны. *
rnameПочтовый ящик лица, ответственного за эту зону. *
serialНеподписанный 32-битный номер версии оригинальной копии зоны. *
refresh32-битный временной интервал, через который зона должна быть обновлена. *
retry32-битный временной интервал, который должен пройти, прежде чем неудачное обновление должно быть повторено. *
expire32-битное значение времени, которое указывает верхний предел временного интервала, который может пройти, прежде чем зона перестанет быть авторитетной. *
minimumНеподписанное 32-битное минимальное поле TTL, которое должно экспортироваться с любой RR из этой зоны.
create_mx_answer
syntax: err = s:create_mx_answer(name, ttl, preference, exchange)
Создает MX-записи. Возвращает nil или строку сообщения в случае ошибки.
которые обычно содержат некоторые из следующих полей:
-
nameИмя ресурсной записи. *
ttlЗначение времени жизни (TTL) в секундах для текущей ресурсной записи. *
preferenceПредпочтение этого почтового обмена. *
exchangeПочтовый обмен.
create_srv_answer
syntax: err = s:create_srv_answer(name, ttl, priority, weight, port, target)
Создает SRV-записи. Возвращает nil или строку сообщения в случае ошибки.
которые обычно содержат некоторые из следующих полей:
-
nameИмя ресурсной записи. *
ttlЗначение времени жизни (TTL) в секундах для текущей ресурсной записи. *
priorityПриоритет этого целевого хоста. *
weightПоле веса указывает относительный вес для записей с одинаковым приоритетом. *
portПорт на этом целевом хосте для этой службы. *
targetДоменное имя целевого хоста.
create_response_header
syntax: resp_header, err = s:create_response_header(rcode)
encode_response
syntax: resp = s:encode_response()
Кодирует ответы DNS. Возвращает строку сообщения об ответе или nil.
Константы
TYPE_A
Тип ресурсной записи A, равный десятичному числу 1.
TYPE_NS
Тип ресурсной записи NS, равный десятичному числу 2.
TYPE_CNAME
Тип ресурсной записи CNAME, равный десятичному числу 5.
TYPE_SOA
Тип ресурсной записи SOA, равный десятичному числу 6.
TYPE_MX
Тип ресурсной записи MX, равный десятичному числу 15.
TYPE_TXT
Тип ресурсной записи TXT, равный десятичному числу 16.
TYPE_AAAA
syntax: typ = s.TYPE_AAAA
Тип ресурсной записи AAAA, равный десятичному числу 28.
TYPE_SRV
syntax: typ = s.TYPE_SRV
Тип ресурсной записи SRV, равный десятичному числу 33.
Смотрите RFC 2782 для получения дополнительных сведений.
TYPE_ANY
syntax: typ = s.TYPE_ANY
Тип всех ресурсных записей, равный десятичному числу 255.
RCODE_FORMAT_ERROR
RCODE_NOT_IMPLEMENTED
См. также
- модуль stream-lua-nginx: https://github.com/openresty/stream-lua-nginx-module/#readme
- библиотека lua-resty-dns.
- эта библиотека ngx_stream_ipdb_module может поддерживать разрешение по регионам.
GitHub
Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-dns-server.