I have setup nginx as a proxy server. It should basically forward the HTTP URL to particular IP address. The following is my configuration
worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
server {
listen 8080;
location ~ ^/db/([-_a-zA-Z0-9/]+)/series {
set $token $1 ;
set $upstream1 " ";
content_by_lua 'length = string.len(ngx.var.token)
if length < 8 then
ngx.say("Invalid token (less than 8 characters)")
return
end
local count = 0
for i=1,8 do
count = count + string.byte(ngx.var.token,i)
end
in_server = {
[0] = "10.0.0.1:8086",
[1] = "10.0.0.2:8086",
[2] = "10.0.0.3:8086",
[3] = "10.0.0.4:8086",
[4] = "10.0.0.5:8086",
[5] = "10.0.0.6:8086",
[6] = "10.0.0.7:8086",
[7] = "10.0.0.8:8086"
}
ngx.var.upstream1 = in_server[count%7]
';
proxy_pass http://$upstream1;
}
}
}
The upstream variable is set to an IP address based on the type of token. The logic is sound, I have tested in lua separately. But everytime I query nginx server , I get the following error :
2016/05/09 17:20:20 [error] 32680#0: *1 no resolver defined to resolve , client: 127.0.0.1, server: , request: "GET /db/rustytoken/series?&q=select%20%2A%20from%20foo%20limit%201 HTTP/1.1", host: "localhost:8080"
I am not sure, why does it need a resolver If am sending a direct IP Address. Anyways, I added the following in location directive
resolver 127.0.0.1
And installed dnsmasq to resolve domain names. It still couldnt. I get the following error instead.
2016/05/09 17:14:22 [error] 32030#0: *1 could not be resolved (3: Host not found), client: 127.0.0.1, server: , request: "GET /db/rustytoken/series?q=select%20%2A%20from%20foo%20limit%201 HTTP/1.1", host: "localhost:8080"
You should use "rewrite_by_lua" instead of "content_by_lua"
because you set $upstream1 " " , so you have to rewrite it.
You shoud use https://github.com/openresty/lua-nginx-module#balancer_by_lua_block
instead of content_by_lua.
See some examples here https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/balancer.md
Nginx config is not executed linearly.
Here is the best tutorial I know - http://openresty.org/download/agentzh-nginx-tutorials-en.html
Update
Another possible solution:
set_by_lua_block $upstream1 {
length = string.len(ngx.var.token)
if length < 8 then
-- cannot use this API here, you should add error processing
--ngx.say("Invalid token (less than 8 characters)")
return
end
local count = 0
for i=1,8 do
count = count + string.byte(ngx.var.token,i)
end
in_server = {
[0] = "10.0.0.1:8086",
[1] = "10.0.0.2:8086",
[2] = "10.0.0.3:8086",
[3] = "10.0.0.4:8086",
[4] = "10.0.0.5:8086",
[5] = "10.0.0.6:8086",
[6] = "10.0.0.7:8086",
[7] = "10.0.0.8:8086"
}
return in_server[count%7]
}
Related
I am using OpenResty to generate SSL certificates dynamically.
I am trying to find out the user-agent of request before running ssl_certificate_by_lua_block and decide If I want to continue with the request or not.
I found out that ssl_client_hello_by_lua_block directive runs before ssl_certificate_by_lua_block but if I try to execute ngx.req.get_headers()["user-agent"] inside ssl_client_hello_by_lua_block I get the following error
2022/06/13 09:20:58 [error] 31918#31918: *18 lua entry thread aborted: runtime error: ssl_client_hello_by_lua:6: API disabled in the current context
stack traceback:
coroutine 0:
[C]: in function 'error'
/usr/local/openresty/lualib/resty/core/request.lua:140: in function 'get_headers'
ssl_client_hello_by_lua:6: in main chunk, context: ssl_client_hello_by_lua*, client: 1.2.3.4, server: 0.0.0.0:443
I tried rewrite_by_lua_block but it runs after ssl_certificate_by_lua_block
Are there any directive that can let me access ngx.req.get_headers()["user-agent"] and run before ssl_certificate_by_lua_block as well?
My Nginx conf for reference.
nginx.conf
# HTTPS server
server {
listen 443 ssl;
rewrite_by_lua_block {
local user_agent = ngx.req.get_headers()["user-agent"]
ngx.log(ngx.ERR, "rewrite_by_lua_block user_agent -- > ", user_agent)
}
ssl_client_hello_by_lua_block {
ngx.log(ngx.ERR, "I am from ssl_client_hello_by_lua_block")
local ssl_clt = require "ngx.ssl.clienthello"
local host, err = ssl_clt.get_client_hello_server_name()
ngx.log(ngx.ERR, "hosts -- > ", host)
-- local user_agent = ngx.req.get_headers()["user-agent"]
-- ngx.log(ngx.ERR, "user_agent -- > ", user_agent)
}
ssl_certificate_by_lua_block {
auto_ssl:ssl_certificate()
}
ssl_certificate /etc/ssl/resty-auto-ssl-fallback.crt;
ssl_certificate_key /etc/ssl/resty-auto-ssl-fallback.key;
location / {
proxy_pass http://backend_proxy$request_uri;
}
}
If someone is facing the same issue.
Here is the email group of OpenResty that helped me.
I was not thinking correctly. The certificate negotiation happens before a client send user-agent data(that comes in after the SYNACK reaches the client). So you cant save issuing the certificate in the process. Hard luck.
Once the handshake and the Client/Server Hello happens then the server has the user-agent, you can do the blocking under access_by_lua_block.
I am using nginx lua docker image firesh/nginx-lua:alpine-3.4. And i tried to use environment variable in nginx.config file. Below is the configuration in /etc/nginx/nginx.conf.
user nginx;
env ES_USERNAME;
env ES_PWD;
worker_processes 1;
events {
worker_connections 10240;
}
http {
server {
listen 8080;
server_name localhost;
set_by_lua $es_username os.getenv("ES_USERNAME");
set_by_lua $es_pwd os.getenv("ES_PWD");
location /health {
proxy_pass http://$es_username:$es_pwd#elk-es-http:9200/_cluster/health;
}
...
After launching the container, I see this error in the log:
2021/11/18 01:07:14 [error] 6#6: *6 failed to load inlined Lua code: set_by_lua:1: unexpected symbol near '"http://"', client: 10.0.4.122, server: localhost, request: "GET /health HTTP/1.1", host: "10.0.2.170:8080"
The problem is that the url after proxy_pass is not reading the variable from lua. It treats the ${es_username} as a string rather than read its value. What is the correct way to use that?
That sounds strange. I rather expect both $es_username and $es_pwd variables will have an empty value. set_by_lua expects a function that should return a value, and your returns nothing. The correct usage is
set_by_lua $es_username 'return os.getenv("ES_USERNAME")';
set_by_lua $es_pwd 'return os.getenv("ES_PWD")';
We are using this syntax in our nginx configuration:
set $logging 1;
if ( $bot_in_log = 0 ) {
set $logging 0;
}
if ( $ip_in_log = 0 ) {
set $logging 0;
}
access_log /var/log/nginx/access.log combined if=$logging;
error_log /var/log/nginx/error.log warn;
However, we receive these messages in our error.log from bad bot requests:
24946#24946: *26106 using uninitialized "logging" variable while logging request, client: 122.34.124.134, server: domain.com, request: "GET /index.html/bwd0GoD HTTP/1.1"
Do we need to set if=$logging; for the error_log as well to avoid these kind of errors from showing up?
I am using nginx as a proxy to a nodejs application. I have the same application running multiple times each on a different port. The request is directed to the correct application/port based on host name.
So
test1.domain.com would be proxied to 127.0.0.1:8000
test2.domain.com would be proxied to 127.0.0.1:8001
test3.domain.com would be proxied to 127.0.0.1:8002
When I hard code " proxy_pass http://127.0.0.1:8000;" Everything works fine.
Now I wrote a njs script to read a file in a users directory to get the port number based on the subdommain. Here is the script.
#inclusion of js file
js_include sites-available/port_assign.js;
js_set $myPort port;
function port(r) {
var host = r.headersIn.host;
var subdomain = host.split('.');
var fs = require('fs');
var filename = '/home/' + subdomain[0] + '/port';
var port = fs.readFileSync(filename);
port.trim();
return(port);
}
this does read the file and returns the port number correctly. I have verified this in the error logs, Because I get:
2020/01/21 04:26:46 [error] 2729#2729: *6 invalid port in upstream "127.0.0.1:8001
", client: 96.54.17.234, server: *.foundryserver.com, request: "GET / HTTP/1.1", host: "test1.foundryserver.com"
now when I tried to issue the directive: proxy_pass http://127.0.0.1:$myPort I get an internal server error and the error stated above.
Not sure what is the difference it the two. I can only think somehow using a variable $myPort is got weird characters or something.
There was some extra information in the port variable. I was able to store the port number in a json format and parse it in the js. {"port":"8000"} is stored in the file.
function port(r) {
var host = r.headersIn.host;
var subdomain = host.split('.');
var fs = require('fs');
var filename = '/home/' + subdomain[0] + '/myport';
var jport = fs.readFileSync(filename);
var port = JSON.parse(jport);
return(port.port);
}
by doing the json parsing it removed any unseen characters in the variable.
I am using Nginx to proxy request to some backend server.
For this, i am using proxy_pass module to redirect.
Config file:
location = /hello {
proxy_pass: abc.com
}
I want to execute the following workflow.
Request to nginx server --> change request parameters --> pass the request to abc.com --> change the response parameters --> send response back to client.
Is this possible with nginx ? Any help/pointers to this problem would be appreciated.
You should be able to change/set new parameters with this
location /hello {
proxy_pass: abc.com
if ($args ~* paramToChange=(.+)) {
set $args newParamName=$1;
}
set $args otherParam=value;
}
Update:
There is not a way in nginx out of the box to make a request to get params dynamically then apply them to another request before sending the client response.
You can do this by adding a module to nginx called lua.
This module can be recompiled into nginx by downloading it and adding it in the .configure options during installation. Also I like the openresty bundle that comes with it and other useful modules already available, like echo.
Once you have the lua module this server code will work:
server {
listen 8083;
location /proxy/one {
proxy_set_header testheader test1;
proxy_pass http://localhost:8081;
}
location /proxy/two {
proxy_pass http://localhost:8082;
}
location / {
default_type text/html;
content_by_lua '
local first = ngx.location.capture("/proxy/one",
{ args = { test = "test" } }
)
local testArg = first.body
local second = ngx.location.capture("/proxy/two",
{ args = { test = testArg } }
)
ngx.print(second.body)
';
}
}
I tested this configuration with a couple of node js servers like this:
var koa = require('koa');
var http = require('http');
startServerOne();
startServerTwo();
function startServerOne() {
var app = koa();
app.use(function *(next){
console.log('\n------ Server One ------');
console.log('this.request.headers.testheader: ' + JSON.stringify(this.request.headers.testheader));
console.log('this.request.query: ' + JSON.stringify(this.request.query));
if (this.request.query.test == 'test') {
this.body = 'First query worked!';
}else{
this.body = 'this.request.query: ' + JSON.stringify(this.request.query);
}
});
http.createServer(app.callback()).listen(8081);
console.log('Server 1 - 8081');
}
function startServerTwo(){
var app = koa();
app.use(function *(next){
console.log('\n------ Server Two ------');
console.log('this.request.query: ' + JSON.stringify(this.request.query));
if (this.request.query.test == 'First query worked!') {
this.body = 'It Worked!';
}else{
this.body = 'this.request.query: ' + JSON.stringify(this.request.query);
}
});
http.createServer(app.callback()).listen(8082);
console.log('Server 2 - 8082');
}
This was the output from the node console logs:
Server 1 - 8081
Server 2 - 8082
------ Server One ------
this.request.headers.testheader: "test1"
this.request.query: {"test":"test"}
------ Server Two ------
this.request.query: {"test":"First query worked!"}
Here's what happens:
Nginx sends server one a request query with the test parameter set.
Node server 1 sees the test parameter and responds with 'First query worked!'.
Nginx updates the query parameters with the body from the server one response.
Nginx sends server two a request with the new query parameters.
Node server 2 sees that the 'test' query parameter equals 'First query worked!' and responds to the request with response body 'It Worked!'.
And the curl response or visiting localhost:8083 in a browser shows 'It worked':
curl -i 'http://localhost:8083'
HTTP/1.1 200 OK
Server: openresty/1.9.3.2
Date: Thu, 17 Dec 2015 16:57:45 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
It Worked!