Nginx 与 Lua

These months, we have gradually integrated into the front end of the Lua Nginx configuration Mixlr in. Lua is a profile can be embedded in Nginx dynamic scripting language, which can request any stage of the various processing executed in Lua code Nginx. We just started using Lua route requests to the back-end server, but it is the role of architecture we exceeded our expectations. Here to talk about the work we do. Forced the search engine indexes only mixlr.com Google target domain name as a completely separate website, we do not want the crawler to fetch page subdomain, reduce our Page rank.
location / {
  header_filter_by_lua '
    if ngx.var.query_string and ngx.re.match( ngx.var.query_string, "^([0-9]{10})$" ) then
      ngx.header["Expires"] = ngx.http_time( ngx.time() + 31536000 ); 
      ngx.header["Cache-Control"] = "max-age=31536000";
    end
  ';
If the request is not mixlr.com robots.txt domain name, then the internal rewrite to robots_diallow.txt, although the standard rewrite instructions can also achieve this demand, but Lua implementation easier to understand and maintain. The head is provided in response to the program logic provides a default configuration rules Nginx than a more flexible arrangement Lua. In the following example, we want to ensure that the correct response to the first set, so that if the browser sends a request header is specified, you can indefinitely cache static files, the user is simply download once. The rewrite rule that any static document, if the request contains a timestamp parameter values, then set the appropriate response Expires and Cache-Control header.
location / {
  header_filter_by_lua '
    if ngx.var.query_string and ngx.re.match( ngx.var.query_string, "^([0-9]{10})$" ) then
      ngx.header["Expires"] = ngx.http_time( ngx.time() + 31536000 ); 
      ngx.header["Cache-Control"] = "max-age=31536000";
    end
  ';

  try_files $uri @dynamic;}
Delete jQuery JSONP request time stamp parameter when many external client requests JSONP interface will include a time stamp similar parameters, resulting in Nginx proxy cache can not hit (because they can not ignore the specified HTTP parameters). The following rules removed the time stamp parameters that can be cached upstream Nginx server response content, and reduce the load on back-end servers.
location / {
  rewrite_by_lua '
    if ngx.var.args ~= nil then
      -- /some_request?_=1346491660 becomes /some_request
      local fixed_args, count = ngx.re.sub( ngx.var.args, "&?_=[0-9]+", "" );
      if count > 0 then
        return ngx.exec(ngx.var.uri, fixed_args);
      end
    end
  ';}
The rear end of the slow log request to the error log of Nginx if the rear end of the request response is very slow, it can be recorded in the error log of Nginx, for subsequent tracing.
location / {
  log_by_lua '
    if tonumber(ngx.var.upstream_response_time) >= 1 then
      ngx.log(ngx.WARN, "[SLOW] Ngx upstream response time: " .. ngx.var.upstream_response_time .. "s from " .. ngx.var.upstream_addr);
    end
  ';}
Redis-based real-time IP banned some cases, the need to prevent rogue crawling reptiles, which can be done by specialized equipment banned, but by Lua, can achieve a simple version of the ban.
lua_shared_dict banned_ips 1m; 

location / {
  access_by_lua '
    local banned_ips = ngx.shared.banned_ips;
    local updated_at = banned_ips:get("updated_at");

    -- only update banned_ips from Redis once every ten seconds:
    if updated_at == nil or updated_at < ( ngx.now() - 10 ) then
      local redis = require "resty.redis";
      local red = redis:new();
      red:set_timeout(200);

      local ok, err = red:connect("your-redis-hostname", 6379);
      if not ok then
        ngx.log(ngx.WARN, "Redis connection error retrieving banned_ips: " .. err);
      else
        local updated_banned_ips, err = red:smembers("banned_ips");
        if err then
          ngx.log(ngx.WARN, "Redis read error retrieving banned_ips: " .. err);
        else
          -- replace the locally stored banned_ips with the updated values:
          banned_ips:flush_all();
          for index, banned_ip in ipairs(updated_banned_ips) do
            banned_ips:set(banned_ip, true);
          end
          banned_ips:set("updated_at", ngx.now());
        end
      end
    end

    if banned_ips:get(ngx.var.remote_addr) then
      ngx.log(ngx.WARN, "Banned IP detected and refused access: " .. ngx.var.remote_addr);
      return ngx.exit(ngx.HTTP_FORBIDDEN);
    end
  ';}
Now you can block specific IP access:
ruby> $redis.sadd("banned_ips", "200.1.35.4")
Nginx process once every 10 seconds to obtain the latest ban IP list from Redis. Note that, if the architecture used in Haproxy such a similar load balancing server, you need the $ remote_addr set to the correct remote IP address. This method can also be used to check the HTTP User-Agent field, required to satisfy the specified conditions. Use Nginx output CSRF (form_authenticity_token) a problem Mixlr extensive use of the page cache, thus introduced is how to give each page output session-level CSRF token. We sub Nginx request token acquired from the upstream web server, and then use the Nginx SSI (server-side include) function to output the page. This will not only solve the problem of CSRF attacks, but also to ensure that the cache can be normal use.
location /csrf_token_endpoint {
  internal;
   
  include /opt/nginx/conf/proxy.conf;
  proxy_pass "http://upstream";}

location @dynamic {
  ssi on;
  set $csrf_token '';

  rewrite_by_lua '
    -- Using a subrequest, we our upstream servers for the CSRF token for this session:
    local csrf_capture = ngx.location.capture("/csrf_token_endpoint");
    if csrf_capture.status == 200 then
      ngx.var.csrf_token = csrf_capture.body;

      -- if this is a new session, ensure it sticks by passing through the new session_id
      -- to both the subsequent upstream request, and the response:
      if not ngx.var.cookie_session then
        local match = ngx.re.match(csrf_capture.header["Set-Cookie"], "session=([a-zA-Z0-9_+=/+]+);");
        if match then
          ngx.req.set_header("Cookie", "session=" .. match[1]);
          ngx.header["Set-Cookie"] = csrf_capture.header["Set-Cookie"]; 
        end
      end
    else
      ngx.log(ngx.WARN, "No CSRF token returned from upstream, ignoring.");
    end
  ';

  try_files /maintenance.html /rails_cache$uri @thin;}
CSRF token生成 app/metal/csrf_token_endpoint.rb:
class CsrfTokenEndpoint
  def self.call(env)
    if env["PATH_INFO"] =~ /^\/csrf_token_endpoint/
      session = env["rack.session"] || {}

      token = session[:_csrf_token]
      if token.nil?
        token = SecureRandom.base64(32)
        session[:_csrf_token] = token
      end

      [ 200, { "Content-Type" => "text/plain" }, [ token ] ]
    else      
      [404, {"Content-Type" => "text/html"}, ["Not Found"]]
    end
  endend
我们的模版文件示例: <meta name="csrf-param" value="authenticity_token"/> <meta name="csrf-token" value="<!--# echo var="csrf_token" default="" encoding="none" -->"/> Again you could make use of lua_shared_dict to store in memory the CSRF token for a particular session. This minimises the number of trips made to /csrf_token_endpoint. 原文链接:http://devblog.mixlr.com/2012/09/01/nginx-lua/

Reproduced in: https: //my.oschina.net/766/blog/211004

Guess you like

Origin blog.csdn.net/weixin_34208185/article/details/91493207