This is an automated email from the ASF dual-hosted git repository.

spacewander pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new 03f0912  feat(real-ip): implement the first version (#4813)
03f0912 is described below

commit 03f09127f301165ca98e6b887004a60058055fb0
Author: 罗泽轩 <[email protected]>
AuthorDate: Fri Aug 13 17:00:00 2021 +0800

    feat(real-ip): implement the first version (#4813)
---
 apisix/plugins/real-ip.lua                         | 100 ++++++++
 conf/config-default.yaml                           |   1 +
 docs/en/latest/config.json                         |   1 +
 docs/en/latest/plugins/client-control.md           |   2 +-
 docs/en/latest/plugins/gzip.md                     |   2 +-
 .../plugins/{client-control.md => real-ip.md}      |  42 ++--
 t/admin/plugins.t                                  |   2 +-
 t/core/config.t                                    |   2 +-
 t/debug/debug-mode.t                               |   1 +
 t/plugin/real-ip.t                                 | 267 +++++++++++++++++++++
 10 files changed, 396 insertions(+), 24 deletions(-)

diff --git a/apisix/plugins/real-ip.lua b/apisix/plugins/real-ip.lua
new file mode 100644
index 0000000..f0da0bb
--- /dev/null
+++ b/apisix/plugins/real-ip.lua
@@ -0,0 +1,100 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local core = require("apisix.core")
+local is_apisix_or, client = pcall(require, "resty.apisix.client")
+local str_byte = string.byte
+local str_sub = string.sub
+
+
+local schema = {
+    type = "object",
+    properties = {
+        source = {
+            type = "string",
+            minLength = 1
+        }
+    },
+    required = {"source"},
+}
+
+
+local plugin_name = "real-ip"
+
+
+local _M = {
+    version = 0.1,
+    priority = 23000,
+    name = plugin_name,
+    schema = schema,
+}
+
+
+function _M.check_schema(conf)
+    return core.schema.check(schema, conf)
+end
+
+
+local function get_addr(conf, ctx)
+    return ctx.var[conf.source]
+end
+
+
+function _M.rewrite(conf, ctx)
+    if not is_apisix_or then
+        core.log.error("need to build APISIX-OpenResty to support setting real 
ip")
+        return 501
+    end
+
+    local addr = get_addr(conf, ctx)
+    if not addr then
+        core.log.warn("missing real address")
+        return
+    end
+
+    local ip, port = core.utils.parse_addr(addr)
+    if not ip or (not core.utils.parse_ipv4(ip) and not 
core.utils.parse_ipv6(ip)) then
+        core.log.warn("bad address: ", addr)
+        return
+    end
+
+    if str_byte(ip, 1, 1) == str_byte("[") then
+        -- For IPv6, the `set_real_ip` accepts '::1' but not '[::1]'
+        ip = str_sub(ip, 2, #ip - 1)
+    end
+
+    if port ~= nil and (port < 1 or port > 65535) then
+        core.log.warn("bad port: ", port)
+        return
+    end
+
+    core.log.info("set real ip: ", ip, ", port: ", port)
+
+    local ok, err = client.set_real_ip(ip, port)
+    if not ok then
+        core.log.error("failed to set real ip: ", err)
+        return
+    end
+
+    -- flush cached vars in APISIX
+    ctx.var.remote_addr = nil
+    ctx.var.remote_port = nil
+    ctx.var.realip_remote_addr = nil
+    ctx.var.realip_remote_port = nil
+end
+
+
+return _M
diff --git a/conf/config-default.yaml b/conf/config-default.yaml
index 53930c3..80d0978 100644
--- a/conf/config-default.yaml
+++ b/conf/config-default.yaml
@@ -271,6 +271,7 @@ graphql:
   #cmd: ["ls", "-l"]
 
 plugins:                          # plugin list (sorted by priority)
+  - real-ip                        # priority: 23000
   - client-control                 # priority: 22000
   - ext-plugin-pre-req             # priority: 12000
   - zipkin                         # priority: 11011
diff --git a/docs/en/latest/config.json b/docs/en/latest/config.json
index 1e3094c..a7135c2 100644
--- a/docs/en/latest/config.json
+++ b/docs/en/latest/config.json
@@ -39,6 +39,7 @@
             "plugins/redirect",
             "plugins/echo",
             "plugins/gzip",
+            "plugins/real-ip",
             "plugins/server-info",
             "plugins/ext-plugin-pre-req",
             "plugins/ext-plugin-post-req"
diff --git a/docs/en/latest/plugins/client-control.md 
b/docs/en/latest/plugins/client-control.md
index f191323..80e5d04 100644
--- a/docs/en/latest/plugins/client-control.md
+++ b/docs/en/latest/plugins/client-control.md
@@ -34,7 +34,7 @@ title: client-control
 The `client-control` plugin dynamically controls the behavior of Nginx to
 handle the client request.
 
-This plugin requires APISIX to run on 
[APISIX-OpenResty](../how-to-build.md#step-6-build-openresty-for-apache-apisix).
+**This plugin requires APISIX to run on 
[APISIX-OpenResty](../how-to-build.md#step-6-build-openresty-for-apache-apisix).**
 
 ## Attributes
 
diff --git a/docs/en/latest/plugins/gzip.md b/docs/en/latest/plugins/gzip.md
index d947215..a7a6af9 100644
--- a/docs/en/latest/plugins/gzip.md
+++ b/docs/en/latest/plugins/gzip.md
@@ -33,7 +33,7 @@ title: gzip
 
 The `gzip` plugin dynamically set the gzip behavior of Nginx.
 
-This plugin requires APISIX to run on 
[APISIX-OpenResty](../how-to-build.md#step-6-build-openresty-for-apache-apisix).
+**This plugin requires APISIX to run on 
[APISIX-OpenResty](../how-to-build.md#step-6-build-openresty-for-apache-apisix).**
 
 ## Attributes
 
diff --git a/docs/en/latest/plugins/client-control.md 
b/docs/en/latest/plugins/real-ip.md
similarity index 67%
copy from docs/en/latest/plugins/client-control.md
copy to docs/en/latest/plugins/real-ip.md
index f191323..673d973 100644
--- a/docs/en/latest/plugins/client-control.md
+++ b/docs/en/latest/plugins/real-ip.md
@@ -1,5 +1,5 @@
 ---
-title: client-control
+title: real-ip
 ---
 
 <!--
@@ -31,16 +31,19 @@ title: client-control
 
 ## Name
 
-The `client-control` plugin dynamically controls the behavior of Nginx to
-handle the client request.
+The `real-ip` plugin dynamically changes the client's IP and port seen by 
APISIX.
 
-This plugin requires APISIX to run on 
[APISIX-OpenResty](../how-to-build.md#step-6-build-openresty-for-apache-apisix).
+It works like Nginx's `ngx_http_realip_module`, but is more flexible.
+
+**This plugin requires APISIX to run on 
[APISIX-OpenResty](../how-to-build.md#step-6-build-openresty-for-apache-apisix).**
 
 ## Attributes
 
 | Name      | Type          | Requirement | Default    | Valid                 
                                                   | Description                
                                                                                
                                         |
 | --------- | ------------- | ----------- | ---------- | 
------------------------------------------------------------------------ | 
---------------------------------------------------------------------------------------------------------------------------------------------------
 |
-| max_body_size | integer        | optional    |              | >= 0 | 
dynamically set the `client_max_body_size` directive |
+| source      | string        | required    |            | Any Nginx variable 
like `arg_realip` or `http_x_forwarded_for`| dynamically set the client's IP 
and port in APISIX's view, according to the value of variable. If the value 
doesn't contain a port, the client's port won't be changed. |
+
+If the remote address comes from `source` is missing or invalid, this plugin 
will just let it go and don't change the client address.
 
 ## How To Enable
 
@@ -51,14 +54,20 @@ curl -i http://127.0.0.1:9080/apisix/admin/routes/1  -H 
'X-API-KEY: edd1c9f03433
 {
     "uri": "/index.html",
     "plugins": {
-        "client-control": {
-            "max_body_size" : 1
+        "real-ip": {
+            "source": "arg_realip"
+        },
+        "response-rewrite": {
+            "headers": {
+                "remote_addr": "$remote_addr",
+                "remote_port": "$remote_port"
+            }
         }
     },
     "upstream": {
         "type": "roundrobin",
         "nodes": {
-            "39.97.63.215:80": 1
+            "127.0.0.1:1980": 1
         }
     }
 }'
@@ -69,23 +78,16 @@ curl -i http://127.0.0.1:9080/apisix/admin/routes/1  -H 
'X-API-KEY: edd1c9f03433
 Use curl to access:
 
 ```shell
-curl -i http://127.0.0.1:9080/index.html -d '123'
-
-HTTP/1.1 413 Request Entity Too Large
+curl 'http://127.0.0.1:9080/index.html?realip=1.2.3.4:9080' -I
 ...
-<html>
-<head><title>413 Request Entity Too Large</title></head>
-<body>
-<center><h1>413 Request Entity Too Large</h1></center>
-<hr><center>openresty</center>
-</body>
-</html>
+remote-addr: 1.2.3.4
+remote-port: 9080
 ```
 
 ## Disable Plugin
 
 When you want to disable this plugin, it is very simple,
-you can delete the corresponding json configuration in the plugin 
configuration,
+you can delete the corresponding JSON configuration in the plugin 
configuration,
 no need to restart the service, it will take effect immediately:
 
 ```shell
@@ -95,7 +97,7 @@ curl http://127.0.0.1:9080/apisix/admin/routes/1  -H 
'X-API-KEY: edd1c9f034335f1
     "upstream": {
         "type": "roundrobin",
         "nodes": {
-            "39.97.63.215:80": 1
+            "127.0.0.1:1980": 1
         }
     }
 }'
diff --git a/t/admin/plugins.t b/t/admin/plugins.t
index 11354e9..40fcd67 100644
--- a/t/admin/plugins.t
+++ b/t/admin/plugins.t
@@ -40,7 +40,7 @@ __DATA__
 --- request
 GET /apisix/admin/plugins/list
 --- response_body_like eval
-qr/\["client-control","ext-plugin-pre-req","zipkin","request-id","fault-injection","serverless-pre-function","batch-requests","cors","ip-restriction","ua-restriction","referer-restriction","uri-blocker","request-validation","openid-connect","authz-casbin","wolf-rbac","hmac-auth","basic-auth","jwt-auth","key-auth","consumer-restriction","authz-keycloak","proxy-mirror","proxy-cache","proxy-rewrite","api-breaker","limit-conn","limit-count","limit-req","gzip","server-info","traffic-split","r
 [...]
+qr/\["real-ip","client-control","ext-plugin-pre-req","zipkin","request-id","fault-injection","serverless-pre-function","batch-requests","cors","ip-restriction","ua-restriction","referer-restriction","uri-blocker","request-validation","openid-connect","authz-casbin","wolf-rbac","hmac-auth","basic-auth","jwt-auth","key-auth","consumer-restriction","authz-keycloak","proxy-mirror","proxy-cache","proxy-rewrite","api-breaker","limit-conn","limit-count","limit-req","gzip","server-info","traffic
 [...]
 --- no_error_log
 [error]
 
diff --git a/t/core/config.t b/t/core/config.t
index 2ffd45c..b87fe12 100644
--- a/t/core/config.t
+++ b/t/core/config.t
@@ -38,7 +38,7 @@ __DATA__
 GET /t
 --- response_body
 etcd host: http://127.0.0.1:2379
-first plugin: "client-control"
+first plugin: "real-ip"
 
 
 
diff --git a/t/debug/debug-mode.t b/t/debug/debug-mode.t
index ea84728..f13e68c 100644
--- a/t/debug/debug-mode.t
+++ b/t/debug/debug-mode.t
@@ -44,6 +44,7 @@ GET /t
 --- response_body
 done
 --- error_log
+loaded plugin and sort by priority: 23000 name: real-ip
 loaded plugin and sort by priority: 22000 name: client-control
 loaded plugin and sort by priority: 12000 name: ext-plugin-pre-req
 loaded plugin and sort by priority: 11011 name: zipkin
diff --git a/t/plugin/real-ip.t b/t/plugin/real-ip.t
new file mode 100644
index 0000000..23badde
--- /dev/null
+++ b/t/plugin/real-ip.t
@@ -0,0 +1,267 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+use t::APISIX;
+
+my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';
+my $version = eval { `$nginx_binary -V 2>&1` };
+
+if ($version !~ m/\/apisix-nginx-module/) {
+    plan(skip_all => "apisix-nginx-module not installed");
+} else {
+    plan('no_plan');
+}
+
+repeat_each(1);
+log_level('info');
+no_root_location();
+no_shuffle();
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if (!$block->request) {
+        $block->set_value("request", "GET /t");
+    }
+
+    if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
+        $block->set_value("no_error_log", "[error]");
+    }
+});
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: schema check
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "uri": "/hello",
+                    "upstream": {
+                        "type": "roundrobin",
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        }
+                    },
+                    "plugins": {
+                        "real-ip": {
+                        }
+                    }
+            }]]
+            )
+
+        if code >= 300 then
+            ngx.status = code
+        end
+        ngx.print(body)
+    }
+}
+--- error_code: 400
+--- response_body
+{"error_msg":"failed to check the configuration of plugin real-ip err: 
property \"source\" is required"}
+
+
+
+=== TEST 2: sanity
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "uri": "/hello",
+                    "upstream": {
+                        "type": "roundrobin",
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        }
+                    },
+                    "plugins": {
+                        "real-ip": {
+                            "source": "http_xff"
+                        },
+                        "ip-restriction": {
+                            "whitelist": ["1.1.1.1"]
+                        }
+                    }
+            }]]
+            )
+
+        if code >= 300 then
+            ngx.status = code
+        end
+        ngx.say(body)
+    }
+}
+--- response_body
+passed
+
+
+
+=== TEST 3: hit
+--- request
+GET /hello
+--- more_headers
+XFF: 1.1.1.1
+
+
+
+=== TEST 4: with port
+--- request
+GET /hello
+--- more_headers
+XFF: 1.1.1.1:80
+
+
+
+=== TEST 5: miss address
+--- request
+GET /hello
+--- error_code: 403
+
+
+
+=== TEST 6: bad address
+--- request
+GET /hello
+--- more_headers
+XFF: 1.1.1.1.1
+--- error_code: 403
+
+
+
+=== TEST 7: bad port
+--- request
+GET /hello
+--- more_headers
+XFF: 1.1.1.1:65536
+--- error_code: 403
+
+
+
+=== TEST 8: ipv6
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "uri": "/hello",
+                    "upstream": {
+                        "type": "roundrobin",
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        }
+                    },
+                    "plugins": {
+                        "real-ip": {
+                            "source": "http_xff"
+                        },
+                        "ip-restriction": {
+                            "whitelist": ["::2"]
+                        }
+                    }
+            }]]
+            )
+
+        if code >= 300 then
+            ngx.status = code
+        end
+        ngx.say(body)
+    }
+}
+--- response_body
+passed
+
+
+
+=== TEST 9: hit
+--- request
+GET /hello
+--- more_headers
+XFF: ::2
+
+
+
+=== TEST 10: with port
+--- request
+GET /hello
+--- more_headers
+XFF: [::2]:80
+
+
+
+=== TEST 11: with bracket
+--- request
+GET /hello
+--- more_headers
+XFF: [::2]
+
+
+
+=== TEST 12: check port
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "uri": "/hello",
+                    "upstream": {
+                        "type": "roundrobin",
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        }
+                    },
+                    "plugins": {
+                        "real-ip": {
+                            "source": "http_xff"
+                        },
+                        "response-rewrite": {
+                            "headers": {
+                                "remote_port": "$remote_port"
+                            }
+                        }
+                    }
+            }]]
+            )
+
+        if code >= 300 then
+            ngx.status = code
+        end
+        ngx.say(body)
+    }
+}
+--- response_body
+passed
+
+
+
+=== TEST 13: hit
+--- request
+GET /hello
+--- more_headers
+XFF: 1.1.1.1:7090
+--- response_headers
+remote_port: 7090

Reply via email to