Sử dụng NGINX với Lua để thực hiện kiểm soát phức tạp và biến nội bộ của ngx_lua

Cài đặt module lua_nginx_module

Module lua_nginx_module có thể được cài đặt từng bước, hoặc sử dụng trực tiếp OpenResty của Alibaba

Đối với Centos và Debian việc cài đặt rất đơn giản...

Dưới đây là hướng dẫn cài đặt trên FreeBSD:

fetch http://www.lua.org/ftp/lua-5.1.4.tar.gz
tar zxvf lua-5.1.4.tar.gz
cd lua-5.1.4
make freebsd
make install
cd ..
fetch https://github.com/chaoslawful/lua-nginx-module/zipball/v0.1.6rc2
fetch https://github.com/simpl/ngx_devel_kit/zipball/v0.2.17rc2
tar zxvf v0.1.6rc2
mv chaoslawful-lua-nginx-module-ccaf132 lua_nginx_module
tar zxvf v0.2.17rc2
mv simpl-ngx_devel_kit-bc97eea ngx_devel_kit
tar zxvf pcre-8.12.tar.gz
tar zxvf nginx-1.0.3.tar.gz
cd nginx-1.0.3
./configure --prefix=/data/soft/nginx --with-pcre=../pcre-8.12 --add-module=../ngx_devel_kit --add-module=../lua_nginx_module
make && make install

Sau khi cài đặt hoàn tất, chúng ta sẽ trải nghiệm thử với Lua

Script Lua đầu tiên

ngx.say có nghĩa là in ra đầu ra...

location /echo {
    default_type text/plain;
    echo hello lua;
}
location /lua {
    default_type text/plain;
    content_by_lua 'ngx.say("hello world")';
}

Sử dụng script Lua để hạn chế truy cập nginx...

location @backend{
    proxy_pass http://www.example.com;
}
location ~ /test {
    default_type text/html;
    content_by_lua 'ngx.say("this is example.com!")';
    access_by_lua '
    if ngx.var.remote_addr == "10.2.20.110" then
        ngx.exit(ngx.HTTP_FORBIDDEN)
    end
    if ngx.var.remote_addr == "10.2.20.112" then
        ngx.exec("@backend")
    end
    ';
}

Trước khi truy cập cần phải qua xử lý kiểm tra

location / {
    access_by_lua '
    local result = ngx.location.capture("/validate")
    if result.status == ngx.HTTP_OK then
        return
    end
    if result.status == ngx.HTTP_FORBIDDEN then
        ngx.exit(result.status)
    end
    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
    ';

proxy_pass/fastcgi_pass/postgres_pass/...
}

Sử dụng Lua để chuyển hướng rewrite trong nginx

Đây là ví dụ kiểm tra nội dung trả về từ interface check-spam có phải là spam hay không, nếu đúng thì chuyển hướng đến trang khác

location / {
    rewrite_by_lua '
    local response = ngx.location.capture("/check-content")
    if response.body == "spam" then
        ngx.redirect("/terms-of-service.html")
    end
    '; 
    fastcgi_pass ...;
}

Phản hồi khác nhau dựa trên IP

location / {
    content_by_lua '
    local clientIP = ngx.req.get_headers()["X-Real-IP"]
    if clientIP == nil then
        clientIP = ngx.req.get_headers()["x_forwarded_for"]
    end
    if clientIP == nil then
        clientIP = ngx.var.remote_addr
    end
    if clientIP == "" then
        ngx.exec("@backend")
    else
        ngx.exec("@backend_test")
    end
    ';
}

Sử dụng redirect

return ngx.redirect("/target")
return ngx.redirect("http://localhost:1984/target", ngx.HTTP_MOVED_TEMPORARILY)
return ngx.redirect("/target", 301)

Trả về chuyển hướng tạm thời 302, thanh địa chỉ sẽ hiển thị URL sau khi chuyển hướng

rewrite ^ /target? redirect; # cấu hình nginx
return ngx.redirect('/target'); -- mã lua

Lọc tham số POST bằng Lua

location = /filter {
    content_by_lua '
    ngx.req.read_body()
    local params = ngx.req.get_post_args()
    for key, value in pairs(params) do
        if type(value) == "table" then
            ngx.say(key, ": ", table.concat(value, ", "))
        else
            ngx.say(key, ": ", value)
        end
    end
    ';
}

Một ví dụ về Lua:

#!/usr/bin/env lua
ngx.say('sample output ')
local path = ngx.var.uri
ngx.say('
',path,'
')
ngx.print('Header của lần truy cập này là ',ngx.req.raw_header())
ngx.print('')
ngx.print('

Đây là tiêu đề h1
=================

')
ngx.print('Lần truy cập này là GET hay POST ',ngx.req.get_method())
local parameters = ngx.req.get_uri_args()
ngx.print(parameters)
local response = ngx.location.capture("/")
ngx.print('
mã http
',response.status)

Ví dụ về kết nối MySQL với Lua

worker_processes 2;
error_log logs/error.log warn;
events {
    worker_connections 1024;
}
http {
    upstream database_backend {
        drizzle_server 127.0.0.1:3306 protocol=mysql
        dbname=test_db user=test_user password=test_password;
        drizzle_keepalive max=10 overflow=ignore mode=single;
    }
    
    server {
        listen 8080;
        
        location @fetch-by-name {
            set_unescape_uri item_name $arg_name;
            set_quote_sql_str item_name;
            drizzle_query 'select * from items where name=$item_name';
            drizzle_pass database_backend;
            rds_json on;
        }
        
        location @fetch-by-id {
            set_quote_sql_str item_id $arg_id;
            drizzle_query 'select * from items where id=$item_id';
            drizzle_pass database_backend;
            rds_json on;
        }
        
        location = /items {
            access_by_lua '
                if ngx.var.arg_name then
                    return ngx.exec("@fetch-by-name")
                end
                if ngx.var.arg_id then
                    return ngx.exec("@fetch-by-id")
                end
            ';
            rds_json_ret 400 "thiếu tham số \"name\" hoặc \"id\"";
        }
    }
}

Chỉ cần thay đổi mật khẩu là có thể sử dụng được rồi!

Lấy tham số từ URL bằng Lua

location = /calculate {
    set_by_lua total "
    local first_num = tonumber(ngx.arg[1])
    local second_num = tonumber(ngx.arg[2])
    return first_num + second_num
    " $arg_x $arg_y;

    echo $total;
}

Sử dụng ngx.req.set_uri

Cấu hình trong nginx là:

location /process {
    rewrite ^/process/(.*) /$1 break;
    proxy_pass http://upstream_server;
}

Cấu hình bằng Lua là:

location /process {
    rewrite_by_lua '
    local processed_uri = ngx.re.sub(ngx.var.uri, "^/process/(.*)", "$1", "o")
    ngx.req.set_uri(processed_uri)
    ';
    proxy_pass http://upstream_server;
}

Tôi nghĩ mọi người đã hiểu ý nghĩa thông qua so sánh này.

Thông qua Lua lấy các biến nội bộ của Nginx, sử dụng các biến này để xử lý logic...

Nginx cung cấp nhiều biến nội bộ như:

$arg_PARAMETER Biến này chứa giá trị của PARAMETER trong chuỗi truy vấn GET
$args Biến này bằng các tham số trong dòng yêu cầu
$binary_remote_addr Địa chỉ khách hàng ở dạng nhị phân
$body_bytes_sent Số byte truyền tải trang
$content_length Trường Content-length trong header yêu cầu
$content_type Trường Content-Type trong header yêu cầu
$cookie_COOKIE Giá trị cookie COOKIE
$document_root Giá trị được chỉ định trong lệnh root cho yêu cầu hiện tại
$document_uri Giống với $uri
$host Trường host trong yêu cầu, nếu trường host trong yêu cầu không khả dụng, thì là tên máy chủ xử lý yêu cầu
$is_args Nếu $args được thiết lập, giá trị là "?", ngược lại là ""
$limit_rate Biến này có thể giới hạn tốc độ kết nối
$nginx_version Phiên bản nginx đang chạy
$query_string Giống với $args
$remote_addr Địa chỉ IP khách hàng
$remote_port Cổng khách hàng
$remote_user Tên người dùng đã được xác thực bởi Auth Basic Module
$request_filename Đường dẫn tệp yêu cầu kết nối hiện tại, được tạo bởi lệnh root hoặc alias và URI yêu cầu
$request_body Biến này (0.7.58+) chứa thông tin chính trong yêu cầu
$request_body_file Tên tệp tạm thời chứa thông tin thân yêu cầu khách hàng
$request_completion Chưa rõ
$request_method Biến này là hành động yêu cầu của khách hàng, thường là GET hoặc POST
$request_uri Biến này bằng URI gốc chứa một số tham số yêu cầu khách hàng, không thể sửa đổi
$scheme Giao thức được sử dụng, ví dụ http hoặc https
$server_addr Địa chỉ máy chủ
$server_name Tên máy chủ
$server_port Cổng mà yêu cầu đến máy chủ
$server_protocol Giao thức yêu cầu sử dụng, thường là HTTP/1.0 hoặc HTTP/1.1
$uri URI hiện tại trong yêu cầu (không có tham số yêu cầu, tham số nằm trong $args)

Ngoài ra: HTTP_X_FORWARDED_FOR là để lấy IP thật của khách hàng thông qua máy chủ proxy, một số trường hợp đọc được vẫn là IP của máy chủ proxy. Một điểm cần lưu ý nữa là: nếu khách hàng không truy cập thông qua máy chủ proxy, thì giá trị nhận được từ HTTP_X_FORWARDED_FOR sẽ là rỗng.

Truy cập bằng hàm

location /script1 {
    default_type 'text/plain';
    content_by_lua 'ngx.say("xin chào, lua")';
}

Gọi URL khác

location /script2 {
    content_by_lua '
    local result = ngx.location.capture("/hello1")
    ngx.say("dữ liệu: " .. result.body)
    ';
}

Thẻ: nginx lua openresty web-server load-balancing

Đăng vào ngày 18 tháng 7 lúc 08:00