Skip to content
This repository was archived by the owner on Apr 14, 2025. It is now read-only.

Commit 5cab782

Browse files
authored
Merge pull request #20 from mozilla-services/ajvb/readme-revamp
Improve documentation
2 parents 92137ee + 9b27d59 commit 5cab782

File tree

2 files changed

+106
-57
lines changed

2 files changed

+106
-57
lines changed

README.md

+106-57
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,26 @@ and integrate it yourself.
88
*Note:* If nginx is behind a load balancer, make sure to use something like
99
[ngx_http_realip_module](https://nginx.org/en/docs/http/ngx_http_realip_module.html).
1010

11+
## What exactly does iprepd-nginx do?
12+
13+
By using the `iprepd` client in `iprepd-nginx`, you can configure nginx to check the reputation of an incoming client IP within `iprepd`. With
14+
this reputation, `iprepd-nginx` will attach up to three HTTP headers on the request that is then forwarded to your application and can reject
15+
requests that are below the configured threshold.
16+
17+
These three headers are:
18+
19+
| Header | Values | Description |
20+
|---|---|---|
21+
| X-Foxsec-IP-Reputation | int (0-100) | Reputation score returned by iprepd |
22+
| X-Foxsec-IP-Reputation-Below-Threshold | boolean ('true'/'false') | Whether the reputation is below the configured threshold |
23+
| X-Foxsec-Block | boolean ('true'/'false') | High-level whether the request should be blocked (subject to change on what this means) |
24+
25+
As well, `iprepd-nginx` is designed to fail open and prefer performance to accuracy. The preference of performance to accuracy can be changed a bit as an
26+
operator, but only to a certain extent (discussed further below).
27+
28+
## (Mozilla-specific) Architecture Diagram
29+
30+
![Architecture Diagram](docs/moz-architecture-diagram.png)
1131

1232
## Installation
1333

@@ -17,68 +37,23 @@ Install using [opm](https://github.com/openresty/opm)
1737
opm get mozilla-services/iprepd-nginx
1838
```
1939

20-
## Example
21-
22-
Note: Check out `/etc` in this repo for a working example.
40+
## Operators Guide
2341

24-
```
25-
init_by_lua_block {
26-
client = require("resty.iprepd").new({
27-
url = os.getenv("IPREPD_URL"),
28-
api_key = os.getenv("IPREPD_API_KEY"),
29-
threshold = tonumber(os.getenv("IPREPD_REPUTATION_THRESHOLD")),
30-
cache_ttl = os.getenv("IPREPD_CACHE_TTL"),
31-
timeout = tonumber(os.getenv("IPREPD_TIMEOUT")) or 10,
32-
cache_errors = tonumber(os.getenv("IPREPD_CACHE_ERRORS")),
33-
statsd_host = os.getenv("STATSD_HOST") or nil,
34-
statsd_port = tonumber(os.getenv("STATSD_PORT")) or 8125,
35-
statsd_max_buffer_count = tonumber(os.getenv("STATSD_MAX_BUFFER_COUNT")) or 100,
36-
statsd_flush_timer = tonumber(os.getenv("STATSD_FLUSH_TIMER")) or 5,
37-
dont_block = tonumber(os.getenv("DONT_BLOCK")) or 0,
38-
verbose = tonumber(os.getenv("VERBOSE")) or 0,
39-
whitelist = {},
40-
})
41-
}
42+
### Prerequisites
4243

43-
init_worker_by_lua_block {
44-
client:config_flush_timer()
45-
}
44+
* [iprepd](https://github.com/mozilla-services/iprepd), preferably near your `iprepd-nginx` servers (e.g. within the same region in AWS or GCP)
45+
* A mechanism for updating iprepd. At Mozilla, this is done by feeding logs from your load balancer, application server, and potentially other locations into our [fraud detection pipeline](https://github.com/mozilla-services/foxsec-pipeline).
46+
* (optional) A mechanism for collecting statsd metrics.
4647

47-
server {
48-
listen 80;
49-
root /dev/null;
50-
error_page 500 502 503 504 /50x.html;
48+
### Note on Performance
5149

52-
location = /50x.html {
53-
root /usr/local/openresty/nginx/html/;
54-
}
50+
A core requirement for iprepd-nginx is that it will add no more than 10ms of latency to requests. Of the mechanisms in place to accomplish this, as an operator there are a few you should be aware of:
5551

56-
location = /health {
57-
return 200;
58-
access_log off;
59-
}
52+
#### Heavy use of caching of responses from iprepd
53+
By default, iprepd-nginx will cache all non-error responses from iprepd for 30 seconds. It is a good idea to cache errors in production, which is done by enabling `cache_errors` (discussed further below). As well, you may want to lengthen the cache ttl.
6054

61-
set_by_lua_block $backend { return os.getenv("backend") }
62-
63-
location / {
64-
proxy_set_header "X-Forwarded-Port" $server_port;
65-
proxy_set_header "X-Forwarded-For" $proxy_add_x_forwarded_for;
66-
proxy_set_header "X-Real-IP" $remote_addr;
67-
proxy_set_header "Host" $host;
68-
proxy_pass $backend;
69-
70-
access_by_lua_block {
71-
client:check(ngx.var.remote_addr)
72-
}
73-
74-
log_by_lua_block {
75-
if client.statsd then
76-
client.statsd.set("iprepd.ips_seen", ngx.var.remote_addr)
77-
end
78-
}
79-
}
80-
}
81-
```
55+
#### Strict timeouts to iprepd
56+
By default, iprepd-nginx’s request to iprepd will timeout after 10ms. This should not be increased in production, and may be worth reducing if the network design can support it.
8257

8358
### Configuration of the client
8459

@@ -142,9 +117,83 @@ client = require("resty.iprepd").new({
142117
})
143118
```
144119

120+
### Metrics (statsd)
121+
122+
#### Metrics that are collected
123+
124+
| name | type | description |
125+
|---|---|---|
126+
| iprepd.status.below_threshold | count | The reputation for the client ip is below the configured threshold. |
127+
| iprepd.status.rejected | count | The request was blocked (won’t be sent if `dont_block` is enabled). |
128+
| iprepd.status.accepted | count | The reputation for the client ip is above the configured threshold and was accepted. |
129+
| iprepd.err.timeout | count | Request to iprepd timed out |
130+
| iprepd.err.500 | count | Got a 500 response from iprepd |
131+
| iprepd.err.401 | count | Got a 401 response from iprepd, usually means the API key in use is invalid or being sent incorrectly by nginx. |
132+
| iprepd.err.* | count | Got an error while sending a request to iprepd. This could be other 4xx or 5xx status codes for example. |
133+
134+
135+
#### Setting up custom metrics
136+
137+
You can use `client.statsd` (where `client = require("resty.iprepd").new({...})`) to submit your
138+
own custom metrics. Do note that there is no prefix, so it will act as any other statsd client.
139+
140+
##### Available statsd functions
141+
142+
```lua
143+
client = require("resty.iprepd").new({...})
144+
145+
client.statsd.count(name, value)
146+
client.statsd.incr(name) # Increments a count by 1
147+
client.statsd.time(name, value)
148+
client.statsd.set(name, value)
149+
```
150+
151+
##### Example within nginx config
152+
```
153+
init_by_lua_block {
154+
client = require("resty.iprepd").new({
155+
url = os.getenv("IPREPD_URL"),
156+
api_key = os.getenv("IPREPD_API_KEY"),
157+
statsd_host = os.getenv("STATSD_HOST"),
158+
})
159+
}
160+
161+
init_worker_by_lua_block {
162+
# async flushing of metrics
163+
client:config_flush_timer()
164+
}
165+
166+
server {
167+
...
168+
169+
location / {
170+
...
171+
172+
access_by_lua_block {
173+
client:check(ngx.var.remote_addr)
174+
}
175+
176+
log_by_lua_block {
177+
# This conditional is not required, but can be helpful to not cause problems
178+
# if you want to temporarily disable statsd. This will evaluate to false if
179+
# `statsd_host` is not set.
180+
if client.statsd then
181+
# Here is our custom metric
182+
client.statsd.set("iprepd.ips_seen", ngx.var.remote_addr)
183+
end
184+
}
185+
}
186+
}
187+
```
188+
189+
### Common Gotchas
190+
191+
* Make sure iprepd-nginx is seeing the real client IP. You will usually need to use something like [ngx_http_realip_module](https://nginx.org/en/docs/http/ngx_http_realip_module.html), and confirm that it is configured correctly.
192+
193+
145194
## Running locally
146195

147-
Create a `.env` file in this repo with the needed environment variables (documentaion below).
196+
Create a `.env` file in this repo with the needed environment variables (documentation below).
148197

149198
Then run:
150199
```

docs/moz-architecture-diagram.png

19.5 KB
Loading

0 commit comments

Comments
 (0)