SigSci Nginx Lua module troubleshooting guide
Introduction
This troubleshooting guide is helpful to support engineers for diagnosing Nginx Lua module issues reported by the customers.
Nginx versions supported
- 1.10.0 and above
Review Nginx error logs
Agent online but module isn't registered or undetected
- Verify the module configuration
- Verify that agent is running
systemctl status sigsci-agent
Request data uploaded to the cloud have agentresponse -2
This occurs when the agent sees a response without seeing the original request. Agent not being able to pair up the SigSci PreRequest and PostRequest of a given request. This means that phases that normally run are skipped, such as the rewrite or access phase. This also means that later phases that are run regardless, e.g. log_by_lua, will not have access to information that is normally set in those phases.
-
Nginx is handling/erroring the request before it gets to the Lua module.
Requests are terminated early within customer's nginx.conf, i.e. with 4xx codes. This can cause NGINX to respond to the request before the Lua 'access_by_lua' phase runs, which contains 'sigsci.prerequest()' statement.
Nginx may terminate a request early with:
code message 400 Bad Request 405 Not Allowed 408 Request Timeout 413 Request Entity Too Large 414 Request URI Too Large 494 Request Headers Too Large 499 Client Closed Request 500 Internal Server Error 501 Not Implemented -
Nginx configuration contained {access,rewrite}_by_lua in
serverorlocationblock which override the SigSci {access,rewrite}_by_lua inhttpblock and didn't call the method 'sigsci.prerequest()'.Add method 'sigsci.prerequest()' as first statement to 'access_by_lua' of customer's existing {access,rewrite}_by_lua script to resolve the issue.
-
Nginx configuration contained a
returnordeny allstatment inlocationblock which returned response before calling SigSci PreRequest in {access,rewrite}_by_lua
Example with return statement
server {
listen 80;
server_name ironmq.dactbc.com;
location / {
# Calling the rewrite_by_lua_block directly allows us
# to force our module to run first and then do the
# return statement for nginx.
rewrite_by_lua_block {
sigsci.prerequest()
return ngx.exit(200)
}
# The return statements will stop all processing so
# normally with our access_by_lua 'sigsci.prerequest()',
# which is in the include /opt/sigsci/nginx/sigsci.conf,
# these requests are missed by us.
#return 200;
}
location /redirect {
# Same thing here but this includes a redirect to another
# page and/or server. All of the functionality of the return
# statement is available in the lua block
rewrite_by_lua_block {
sigsci.prerequest()
return ngx.redirect("https://cloud.dactbc.com")
}
#return 302 https://cloud.dactbc.com;
}
## Adding Additional Data to a Return statement
location /ping {
default_type application/json;
rewrite_by_lua_block {
sigsci.prerequest()
ngx.say('{"ping":"successful"}')
return ngx.exit(200)
}
}
}
Additional examples
# Return a 503 error if the maintenance page exists.
if (-f /var/www/ring-site/shared/public/system/maintenance.html) {
# Start of SigSci lua script
rewrite_by_lua_block {
sigsci.prerequest()
return ngx.exit(503)
}
# End of SigSci lua script
# return 503; # old return statement
}
#returning a 301 with a few variables
location ~ ^/oauth/authorize$ {
# Start of SigSci lua script
rewrite_by_lua_block {
sigsci.prerequest()
# this is calling nginx variables into lua variables
return ngx.redirect(ngx.var.oauth_upstream .. ngx.var.request_uri, 301);
}
# End of SigSci lua script
# return 301 $oauth_upstream$request_uri; # old return statement
}
# Listen to HTTP and redirect to HTTPS for additional server names
# Returning a 301 with a string and a few variables
server {
listen 81;
server_name site-stg.dev.ring.com site-stg.ring.com;
# Start of SigSci lua script
rewrite_by_lua_block {
sigsci.prerequest()
return ngx.redirect("https://" .. ngx.var.host .. ngx.var.request_uri, 301)
}
# End of SigSci lua script
# return 301 https://$host$request_uri;
}
In another case, the deny all directive could be used, which responds with a 403.
# Block access to "hidden" files and directories whose names begin with a
# period. This includes directories used by version control systems such
# as Subversion or Git to store control files.
location ~ /\.(.*)/ {
# Calling the rewrite_by_lua_block directly allows us to force our
# module to run first and then do the return statement for nginx.
# In this example, instead of calling "deny all" after the
# sigsci.prerequest(), we call "return ngx.exit(403)". It is possible
# that "deny all" could be used again, but not tested as of posting this example.
rewrite_by_lua_block {
sigsci.prerequest()
return ngx.exit(403)
}
#deny all;
}
After installing 'nginx110/111-lua-module' from the SigSci repo and trying to load into the nginx conf, I get the error "Module is binary incompatible" when trying to start nginx
The dynamic Lua modules we provide for nginx versions > 1.10 example are built for specific distributions of nginx, generally the distros provided directly by NGINX.org. Even when using dynamic modules, nginx tends to be very finicky about the compile options needing to match between the dynamic module and the nginx build itself, and when they are built with different compile flags this error can occur. Where applicable, we've also provided Lua packages for Amazon linux and EPEL distros of nginx.
If you run into this error you will likely need to explore either having the customer download a different distro of nginx (either NGINX.org to support our dynamic Lua modules, or a distro such as OpenResty that already has Lua pre-baked) or recompiling nginx themselves to include Lua support.
Request data uploaded to the cloud have agentresponse 406 but server response isn't 406
This occurs when the Nginx configuration mapped error code to non-existing custom error page.
# example
server {
error_page 406 /406.html
location / {
redirect /index.htm permanent;
}
#location /406.html {
# root /usr/share/nginx/html;
# break;
#}
}
Ability to exclude certain paths from being seen/processed by module
Update the existing access_by_lua and log_by_lua directives in /opt/sigsci/nginx/sigsci.conf file
It is safe to edit the sigsci.conf file as it is outside the module code.
Exclude module based on path using regexp
access_by_lua 'if not ngx.re.match(ngx.var.uri, [[^/mailbox.*]]) then
sigsci.prerequest()
end';
log_by_lua 'if not ngx.re.match(ngx.var.uri, [[^/mailbox.*]]) then
sigsci.postrequest()
end';
Exclude module based on IP
access_by_lua 'if not (( ngx.var.remote_addr == "132.5.72.3") or (ngx.var.remote_addr == "132.5.72.3")) then
sigsci.prerequest()
end';
log_by_lua 'if not (( ngx.var.remote_addr == "132.5.72.3") or (ngx.var.remote_addr == "132.5.72.3")) then
sigsci.postrequest()
end';
Module latency time can be way off
The nginx module's latency graphs can show very high, but the queue time is very low. This makes it look like there is a lot of module/network overhead, but it is probably a Lua feature (bug) with a cached ngx.now() value.
The latency time is measured from NowMillis until inspection is about to start. The queue time is a sub-set of the latency time - how long the call spent in the queue before a worker handled it. A high latency, low queue means there was a lot of overhead in the RPC call (module or network). The nginx module uses ngx.now() to calculate NowMillis, but it seems that this value is cached.
Use source IP in proxy protocol as RemoteAddress
AWS load balancer and Haproxy support PROXY protocol and can send the client remote address before HTTP request. Nginx variable ngx.var.remote_addr contains downstream proxy IP address and not the client remote address when the Nginx configued to use PROXY protocol. SigSci module sends the proxy IP address and not the real client remote address.
This issue can be resolved by replacing one line in SigSci NGINX Lua module file SignalSciences.lua
# replace
RemoteAddr = ngx.var.remote_addr,
# with
RemoteAddr = (ngx.var.proxy_protocol_addr ~= "" and ngx.var.proxy_protocol_addr or ngx.var.remote_addr)
This issue can also be resolved by adding these statements to nginx configuration
real_ip_header proxy_protocol;
# Replace subnet 172.31.19.0/24 or IP with the correct AWS Load balancer
# NGINX facing IP or subnet.
set_real_ip_from 172.31.19.0/24;
Make sure NGINX built with --with-http_realip_module, otherwise the above configuration may not work.
Following command should print 1
nginx -V 2>&1|grep -c realip_module
Conflicting with 'satisfy any' directive
HTTP basic auth along with IP restriction, the directive satisfy any won't behave as expected if sigsci nginx module is loaded. All traffic allowed when an auth dialogue should be shown for those who didn't satisfy IP rules. SigSci module bypassing the auth.
The directive satisfy all grants access if a client satisfies all conditions. The directive satisfy any grants access if a client satisfies at least one condition.
Lua hook access_by_lua satisfies any condition, so basic auth by-passed. This is why Lua hook rewrite_by_lua recommended to resolve the issue.
Example:
server {
satisfy any;
allow 1.1.1.1/32;
deny all;
auth_basic "login required";
auth_basic_user_file conf/htpasswd;
}
Escalating issues to engineering
Add the output of the following commands to the JIRA ticket
nginx -v
nginx -T > nginx.config
uname -a
cat /etc/os-release