Skip to content

Commit ccd2926

Browse files
committed
Add a fully working IP and CIDR extraction mechanism, Update README.md and demo folder
1 parent da3f2fd commit ccd2926

File tree

6 files changed

+93
-179
lines changed

6 files changed

+93
-179
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.vscode

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@ luarocks pack lua-resty-whitelist-*.rockspec
1818

1919
```sh
2020
cd demo
21-
docker-compose up # visit openresty at localhost:80
22-
# Run a local server to serve the IP lists to simulate a dynamic IP whitelist
21+
docker-compose up
22+
# Visit openresty at localhost:80, You should get a 503 error as the whitelist endpoint is not up yet
23+
24+
# Run a local mockup server to serve the dynamic IP whitelist
2325
# Depending on the configuration, the server is genereally accessible inside the openrest docker container at 172.18.0.1
2426
python -m http.server 9001
25-
# Edit the IP list files to experiment with the configuration
27+
# Now visiting openresty at localhost:80 should return a 403 error as the IP is not whitelisted
28+
29+
# Add to and remove from the mockup server your client IP address or a CIDR that matches it such as 172.19.0.1/16 to test the configuration
30+
# If your client IP is whitelisted, you should get the "Welcome to OpenResty!" page
2631
```

demo/default.conf

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ server {
1010
lua_code_cache on;
1111
access_by_lua_block {
1212
local whitelist = require "resty.whitelist"
13-
local url = "http://172.18.0.1:9001/list-cloudfront-ips.json"
14-
whitelist.new(url)
13+
14+
local whitelist_urls = {
15+
"http://172.18.0.1:9001/list-cloudfront-ips.json", "http://172.18.0.1:9001/list-cloudflare-ips.txt"
16+
}
17+
local whitelist = whitelist.new(whitelist_urls)
1518
}
1619
}
1720
}

demo/list-cloudflare-ips.txt

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,4 @@
11
173.245.48.0/20
2-
103.21.244.0/22
3-
103.22.200.0/22
4-
103.31.4.0/22
5-
141.101.64.0/18
6-
108.162.192.0/18
7-
190.93.240.0/20
8-
188.114.96.0/20
9-
197.234.240.0/22
10-
198.41.128.0/17
11-
162.158.0.0/15
12-
104.16.0.0/13
132
104.24.0.0/14
143
172.64.0.0/13
154
131.0.72.0/22

demo/list-cloudfront-ips.json

Lines changed: 3 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,138 +1,12 @@
11
{
2-
"CLOUDFRONT_GLOBAL_IP_LIST": [
3-
"120.52.22.96/27",
4-
"205.251.249.0/24",
5-
"180.163.57.128/26",
6-
"204.246.168.0/22",
7-
"18.160.0.0/15",
8-
"205.251.252.0/23",
9-
"54.192.0.0/16",
10-
"204.246.173.0/24",
11-
"54.230.200.0/21",
12-
"120.253.240.192/26",
13-
"116.129.226.128/26",
14-
"130.176.0.0/17",
15-
"108.156.0.0/14",
16-
"99.86.0.0/16",
17-
"205.251.200.0/21",
18-
"223.71.71.128/25",
19-
"13.32.0.0/15",
20-
"120.253.245.128/26",
21-
"13.224.0.0/14",
22-
"70.132.0.0/18",
23-
"15.158.0.0/16",
24-
"13.249.0.0/16",
25-
"18.238.0.0/15",
26-
"18.244.0.0/15",
27-
"205.251.208.0/20",
28-
"65.9.128.0/18",
29-
"130.176.128.0/18",
30-
"58.254.138.0/25",
31-
"54.230.208.0/20",
32-
"116.129.226.0/25",
33-
"52.222.128.0/17",
34-
"18.164.0.0/15",
35-
"64.252.128.0/18",
36-
"205.251.254.0/24",
37-
"54.230.224.0/19",
38-
"71.152.0.0/17",
39-
"216.137.32.0/19",
40-
"204.246.172.0/24",
41-
"18.172.0.0/15",
42-
"120.52.39.128/27",
43-
"118.193.97.64/26",
44-
"223.71.71.96/27",
45-
"18.154.0.0/15",
46-
"54.240.128.0/18",
47-
"205.251.250.0/23",
48-
"180.163.57.0/25",
49-
"52.46.0.0/18",
50-
"223.71.11.0/27",
51-
"52.82.128.0/19",
52-
"54.230.0.0/17",
53-
"54.230.128.0/18",
54-
"54.239.128.0/18",
55-
"130.176.224.0/20",
56-
"36.103.232.128/26",
57-
"52.84.0.0/15",
58-
"143.204.0.0/16",
59-
"144.220.0.0/16",
60-
"120.52.153.192/26",
61-
"119.147.182.0/25",
62-
"120.232.236.0/25",
63-
"54.182.0.0/16",
64-
"58.254.138.128/26",
65-
"120.253.245.192/27",
66-
"54.239.192.0/19",
67-
"18.68.0.0/16",
68-
"18.64.0.0/14",
69-
"120.52.12.64/26",
70-
"99.84.0.0/16",
71-
"130.176.192.0/19",
72-
"52.124.128.0/17",
73-
"204.246.164.0/22",
74-
"13.35.0.0/16",
75-
"204.246.174.0/23",
76-
"36.103.232.0/25",
77-
"119.147.182.128/26",
78-
"118.193.97.128/25",
79-
"120.232.236.128/26",
80-
"204.246.176.0/20",
81-
"65.8.0.0/16",
82-
"65.9.0.0/17",
83-
"108.138.0.0/15",
84-
"120.253.241.160/27",
85-
"64.252.64.0/18"
86-
],
2+
"CLOUDFRONT_GLOBAL_IP_LIST": ["120.52.22.96/27", "64.252.64.0/18"],
873
"CLOUDFRONT_REGIONAL_EDGE_IP_LIST": [
884
"13.113.196.64/26",
5+
"172.19.0.1/16",
896
"13.113.203.0/24",
7+
"52.199.0.10",
908
"52.199.127.192/26",
919
"13.124.199.0/24",
92-
"3.35.130.128/25",
93-
"52.78.247.128/26",
94-
"13.233.177.192/26",
95-
"15.207.13.128/25",
96-
"15.207.213.128/25",
97-
"52.66.194.128/26",
98-
"13.228.69.0/24",
99-
"52.220.191.0/26",
100-
"13.210.67.128/26",
101-
"13.54.63.128/26",
102-
"99.79.169.0/24",
103-
"18.192.142.0/23",
104-
"35.158.136.0/24",
105-
"52.57.254.0/24",
106-
"13.48.32.0/24",
107-
"18.200.212.0/23",
108-
"52.212.248.0/26",
109-
"3.10.17.128/25",
110-
"3.11.53.0/24",
111-
"52.56.127.0/25",
112-
"15.188.184.0/24",
113-
"52.47.139.0/24",
114-
"18.229.220.192/26",
115-
"54.233.255.128/26",
116-
"3.231.2.0/25",
117-
"3.234.232.224/27",
118-
"3.236.169.192/26",
119-
"3.236.48.0/23",
120-
"34.195.252.0/24",
121-
"34.226.14.0/24",
122-
"13.59.250.0/26",
123-
"18.216.170.128/25",
124-
"3.128.93.0/24",
125-
"3.134.215.0/24",
126-
"52.15.127.128/26",
127-
"3.101.158.0/23",
128-
"52.52.191.128/26",
129-
"34.216.51.0/25",
130-
"34.223.12.224/27",
131-
"34.223.80.192/26",
132-
"35.162.63.192/26",
133-
"35.167.191.128/26",
134-
"44.227.178.0/24",
135-
"44.234.108.128/25",
13610
"44.234.90.252/30"
13711
]
13812
}

lib/resty/whitelist.lua

Lines changed: 76 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -32,44 +32,69 @@ local function fetch_whitelist(url)
3232
}
3333
})
3434

35-
local status = res and res.status or nil
36-
local body = res and res.body or nil
37-
38-
if (not res) or (not body) or (status and (status < 200 or status >= 300)) or err then
39-
ngx.log(ngx.ERR, "[lua-resty-whitelist] failed to fetch whitelist => url: " .. url .. ", status: " .. status ..
40-
", body: " .. body, err)
41-
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
35+
if not res then
36+
ngx.log(ngx.ERR, "[lua-resty-whitelist] failed to request whitelist endpoint: response is empty, URL: ", url)
37+
ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE)
38+
end
39+
if is_empty(res.body) or res.status < 200 or res.status > 299 then
40+
ngx.log(ngx.ERR,
41+
"[lua-resty-whitelist] failed to request whitelist endpoint: status code is not success, URL: ", url)
42+
ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE)
4243
end
4344

44-
local whitelist = body .. " "
45-
local whitelist_array = {}
45+
local body = res.body
4646

47-
-- Extract CIRDs
48-
for ip in string.gmatch(whitelist, "((%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)/(%d%d?))") do
49-
table.insert(whitelist_array, ip)
50-
end
47+
local whitelist_body = " " .. body .. " "
5148

52-
-- local whitelist_concat = table.concat(whitelist_array, ", ")
53-
-- ngx.log(ngx.ERR, "[lua-resty-whitelist] CIRDs: " .. whitelist_concat)
49+
-- Extract all CIDRs from whitelist
50+
local cidrs_regex =
51+
"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\/(3[0-6]|[1-2][0-9]|[0-9])))"
52+
local whitelist_cidrs = {}
53+
for cidr in ngx.re.gmatch(whitelist_body, cidrs_regex, "o") do
54+
table.insert(whitelist_cidrs, cidr[0])
55+
end
5456

55-
-- TODO: Extract normal IPs as well
57+
-- Extract all IPs from whitelist
58+
local ip_regex =
59+
"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))"
60+
local whitelist_ips = {}
61+
for ip in ngx.re.gmatch(whitelist_body, ip_regex, "o") do
62+
table.insert(whitelist_ips, ip[0])
63+
end
5664

57-
-- -- Extract IPs
58-
-- for ip in string.gmatch(whitelist, "((%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)%.(%d%d?%d?)$)") do
59-
-- table.insert(whitelist_array, string.sub(ip, 1, -2))
60-
-- end
65+
-- Parse CIDRs
66+
local whitelist_cidrs_parsed = {}
67+
if istable(whitelist_cidrs) and #whitelist_ips > 0 then
68+
whitelist_cidrs_parsed, err = iputils.parse_cidrs(whitelist_cidrs)
69+
if err then
70+
ngx.log(ngx.ERR, "[lua-resty-whitelist] failed to parse CIDRs, URL: " .. url .. ", err: " .. err)
71+
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
72+
end
73+
end
6174

62-
-- whitelist_concat = table.concat(whitelist_array, ", ")
63-
-- ngx.log(ngx.ERR, "[lua-resty-whitelist] whitelist_concat: " .. whitelist_concat)
75+
-- For each IP verify that there is no CIDR that contains it (sort of a clean up)
76+
local clean_whitelist_ips = {}
77+
for _, ip in ipairs(whitelist_ips) do
78+
if not iputils.ip_in_cidrs(ip, whitelist_cidrs_parsed) then
79+
table.insert(clean_whitelist_ips, ip)
80+
end
81+
end
6482

65-
return whitelist_array
83+
return clean_whitelist_ips, whitelist_cidrs_parsed, whitelist_cidrs
6684
end
6785

68-
local function match_ip_whitelist(ip, whitelist_array)
69-
local whitelist = iputils.parse_cidrs(whitelist_array)
70-
if not iputils.ip_in_cidrs(ip, whitelist) then
71-
return ngx.exit(ngx.HTTP_FORBIDDEN)
86+
local function match_ip_whitelist(ip, full_whitelist_ips, full_whitelist_cidrs_parsed)
87+
if iputils.ip_in_cidrs(ip, full_whitelist_cidrs_parsed) then
88+
return true
7289
end
90+
91+
for _, v in ipairs(full_whitelist_ips) do
92+
if v == ip then
93+
return true
94+
end
95+
end
96+
97+
return ngx.exit(ngx.HTTP_FORBIDDEN)
7398
end
7499

75100
function whitelist_m.new(url)
@@ -79,24 +104,41 @@ function whitelist_m.new(url)
79104
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
80105
end
81106

82-
local whitelist_array = {}
107+
local full_whitelist_cidrs_parsed = {}
108+
local full_whitelist_cidrs_unparsed = {}
109+
local full_whitelist_ips = {}
83110

84111
if istable(url) then
85-
for _, u in ipairs(url) do
86-
if is_empty(u) then
112+
-- Iterate over the table url as it is a list of URLs
113+
for _, v in ipairs(url) do
114+
if is_empty(v) then
87115
ngx.log(ngx.ERR, validation_message)
88116
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
89117
end
90118

91-
for _, v in ipairs(fetch_whitelist(u)) do
92-
table.insert(whitelist_array, v)
119+
local whitelist_ips, whitelist_cidrs_parsed, whitelist_cidrs_unparsed = fetch_whitelist(v)
120+
121+
for _, ip in ipairs(whitelist_ips) do
122+
table.insert(full_whitelist_ips, ip)
123+
end
124+
125+
for _, cidr in ipairs(whitelist_cidrs_parsed) do
126+
table.insert(full_whitelist_cidrs_parsed, cidr)
127+
end
128+
129+
for _, cidr in ipairs(whitelist_cidrs_unparsed) do
130+
table.insert(full_whitelist_cidrs_unparsed, cidr)
93131
end
94132
end
95133
else
96-
whitelist_array = fetch_whitelist(url)
134+
-- Get clean_whitelist_ips, whitelist_cidrs_parsed from fetch_whitelist(url)
135+
full_whitelist_ips, full_whitelist_cidrs_parsed, full_whitelist_cidrs_unparsed = fetch_whitelist(url)
97136
end
98137

99-
match_ip_whitelist(ngx.var.remote_addr, whitelist_array)
138+
ngx.log(ngx.ALERT, "Whitelist IPs: " .. table.concat(full_whitelist_ips, ", "))
139+
ngx.log(ngx.ALERT, "Whitelist CIDRs: " .. table.concat(full_whitelist_cidrs_unparsed, ", "))
140+
141+
match_ip_whitelist(ngx.var.remote_addr, full_whitelist_ips, full_whitelist_cidrs_parsed)
100142
end
101143

102144
return whitelist_m

0 commit comments

Comments
 (0)