Nginx获取用户真实IP

首先强调的是,这里需要两层nginx,用户访问nginx1,转发到nginx2(192.168.1.111),nginx2到真实后端。

nginx1 配置

1
2
3
4
5
6
7
8
9
10
server{
...
listen 8888;
location /test {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forward-For $remote_addr;
proxy_pass http://nginx2:8888/test2;
}
}

nginx2 配置

1
2
3
4
5
6
7
8
9
server{
...
listen 8888;
location /test2 {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-real-ip $remote_addr;
default_type text/html;
return 200 'This is text!';
}

测试

用户访问

1
curl -i -H "X-Forwarded-For: 110.110.110.110" -H "X-real-ip: 110.110.110.110" -s nginx1:8888/test -v

nginx1 日志:

1
192.168.1.110 0.000 - [12/Sep/2018:11:01:51 +0800] "GET /test HTTP/1.1" 200 13 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2" 110.110.110.110 110.110.110.110

nginx2 日志:

1
192.168.1.111 0.000 - [12/Sep/2018:11:01:51 +0800] "GET /test2 HTTP/1.1" 200 13 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2" 192.168.1.110 110.110.110.110

其中192.168.1.111为nginx1的ip

可以看到,在nginx2中可以拿X-real-ip获取用户的真实ip,在后端中可以拿这个头信息。

注意!必须要规定好nginx是在架构的哪一层级,根据所处的层级配置,否则该方法无效。

使用Openresty将pastebin内容格式化显示

前情提要

这里用的 https://github.com/solusipse/fiche 的pastebin服务端,功能不多但是贴一些代码或者日志足够了

痛点

fiche将贴的内容保存为文件,然后用nginx显示,如下:

1
2
3
4
5
6
7
8
9
10
server {
listen 80;
server_name mysite.com www.mysite.com;
charset utf-8;

location / {
root /home/www/code/;
index index.txt index.html;
}
}

这样显示出来的文本在行数多的时候就看瞎眼了 @_@

解决痛点

我们可以用openresty将要显示的文本提前拼凑成html,html中用google的code prettify格式化处理。
对于用curl请求的我们不做处理,直接返回纯文本。

lua代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
local headers = ngx.req.get_headers()
local reqUri = ngx.var.request_uri
local uri = ngx.var.uri
local userAgent = headers["user-agent"]
local codePath = "/data/fiche/code"

if uri == "/" then
ngx.say("Welcome to p.qiyuos.cn")
ngx.exit(200)
end

if uri == "/favicon.ico" then
ngx.exit(200)
end

local function file_exists(path)
local file = io.open(path, "rb")
if file then file:close() end
return file ~= nil
end

--读取文件到内存
local function readFile2Mem(file)
local fp = io.open(file,"r")
if fp then
return fp:read("*all")
end
end

local codeBeauty1 = [[
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="https://cdn.bootcss.com/prettify/r298/run_prettify.min.js?autoload=true&amp;lang=html"></script>
<style type="text/css">
body {
background: #fff;
}
pre.prettyprint {
background-color: #eee;
}
.prettyprint ol.linenums > li { list-style-type: decimal; }
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.clo,.opn,.pun{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.kwd,.tag,.typ{font-weight:700}.str{color:#060}.kwd{color:#006}.com{color:#600;font-style:italic}.typ{color:#404}.lit{color:#044}.clo,.opn,.pun{color:#440}.tag{color:#006}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
</style>
</head>
<body>
<pre class="prettyprint linenums" id="quine">
]]
local codeBeauty2 = [[
</pre>
<script type="text/javascript">//<![CDATA[
(function () {
function htmlEscape(s) {
return s;
// .replace(/&/g, '&amp;')
// .replace(/</g, '&lt;')
// .replace(/>/g, '&gt;');
}
// this page's own source code
var quineHtml = htmlEscape(
document.getElementById("quine").innerHTML
);
// Highlight the operative parts:
quineHtml = quineHtml.replace(
/&lt;script src[\s\S]*?&gt;&lt;\/script&gt;|&lt;!--\?[\s\S]*?--&gt;|&lt;pre\b[\s\S]*?&lt;\/pre&gt;/g,
'<span class="operative">$&<\/span>');
// insert into PRE
document.getElementById("quine").innerHTML = quineHtml;
})();
//\]\]>
</script>
</body>
</html>
]]

if not file_exists(codePath .. uri .. "/index.txt") then
ngx.exit(404)
end

local paste = readFile2Mem(codePath .. uri .. "/index.txt")
if not userAgent or string.len(userAgent) < 20 or not string.match(userAgent, "Mozilla") then
ngx.say(paste)
ngx.exit(200)
else
ngx.say(codeBeauty1 .. paste .. codeBeauty2)
ngx.exit(200)
end

nginx 配置如下:

1
2
3
4
5
6
7
8
9
server {
listen 80;
server_name mysite.com www.mysite.com;
charset utf-8;
location / {
default_type text/html;
content_by_lua_file "/usr/local/openresty/scripts/fiche.lua";
}
}

最后

代码还不完善,可能存在一些bug,欢迎提出

使用openresty搭一个类似ip.cn的接口

Openresty是什么

OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

官网 https://openresty.org/cn/

Openresty解决了nginx不能很好的添加一些逻辑判断的痛点,而且又不失性能。

接口代码

代码简单到不能再简单了,就三行

1
2
3
local ip = ngx.var.remote_addr
ngx.header["Content-Type"] = "text/plain charset=utf-8"
ngx.say(ip)
nginx.conf配置:
1
2
3
4
5
6
7
...
http
{
...
lua_package_path "/usr/local/openresty/script/waf/?.lua;/usr/local/openresty/lualib/?.lua";
...

vhost配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
server
{
listen 80;
server_name ip.testing.cn;
index index.html index.htm index.php;
charset utf-8;
# 原生nginx的方式,貌似更简单...
# location / {
# default_type text/plain;
# return 200 $remote_addr;
# }
location /{
content_by_lua_block {
local ip = ngx.var.remote_addr
ngx.header["Content-Type"] = "text/plain charset=utf-8"
ngx.say(ip)
}
}

}