brainstate

pouriyajamshidi/nginwho · source

Snapshot 0 · 2026-11-04T12:47:27Z · Nim · MIT

Content fingerprinta9106805c7fc2e0952d36b69e9f1d97d807ba668d00baea9e0d9d01d692063ef

GitHub description at capture

Nginx log parser and Cloudflare origin IP resolver written in Nim

README at capture

# nginwho

<div align="center" style="width: 100%;">
 <img alt="nginwho" src="https://github.com/pouriyajamshidi/nginwho/blob/master/artwork/nginwho.jpeg?raw=true" width="700">
</div>

---

**nginwho** is a lightweight, efficient and extremely fast program offering:

1. [nginx log parser](#nginx-log-parser): Stores nginx logs into a **sqlite3** database for further analysis and actions
2. [Restore Cloudflare](#restore-cloudflare-original-visitor-ip) original visitor IP: Continuously parses **Cloudflare CIDRs** (`IPv4` and `IPv6`) through their **API**s so that nginx can leverage it to restore the original IP address of visitors
3. [Block untrusted](#block-untrusted-requests) requests using **nftables** to prevent HTTP and HTTPS requests coming from unknown IP addresses
4. [Reporting](#reporting) on gathered data such as top visited URLs through an interactive menu

Table of contents:

- [nginwho](#nginwho)
  - [Installation](#installation)
    - [Binary release (Linux x86_64)](#binary-release-linux-x86_64)
    - [Nimble](#nimble)
    - [Build from source](#build-from-source)
    - [Run as a service](#run-as-a-service)
  - [Usage](#usage)
  - [Flags](#flags)
  - [How it works](#how-it-works)
    - [nginx Log Parser](#nginx-log-parser)
    - [Restore Cloudflare Original Visitor IP](#restore-cloudflare-original-visitor-ip)
    - [Block Untrusted Requests](#block-untrusted-requests)
    - [Reporting](#reporting)
    - [Migrating v1 database to v2](#migrating-v1-database-to-v2)

## Installation

### Binary release (Linux x86_64)

```bash
curl -Lo nginwho https://github.com/pouriyajamshidi/nginwho/releases/latest/download/nginwho
sudo install nginwho -D -t /usr/local/bin/
```

### Nimble

```bash
nimble install nginwho
```

### Build from source

Requires [Nimble](https://github.com/nim-lang/nimble). It downloads the latest stable Nim if needed:

```bash
git clone https://github.com/pouriyajamshidi/nginwho.git
cd nginwho
nimble install -y --depsOnly
nimble c -d:release --opt:speed -d:ssl -o:nginwho src/nginwho.nim
sudo install nginwho -D -t /usr/local/bin/
```

To run the tests, use `nimble test`. The nftables tests run the real `nft` in a throwaway network namespace, so they don't need root or touch your firewall. They are skipped if that is not possible.

### Run as a service

Use the [accompanying systemd service](https://github.com/pouriyajamshidi/nginwho/blob/master/nginwho.service) to run **nginwho** in the background and survive reboots. Check the flags in `ExecStart` before enabling it, since it turns on `--showRealIps` and `--blockUntrustedCidrs`:

```bash
curl -Lo nginwho.service https://raw.githubusercontent.com/pouriyajamshidi/nginwho/master/nginwho.service
sudo install -m 644 nginwho.service -D -t /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now nginwho.service
```

## Usage

> [!IMPORTANT]
> If you have been a user since version 1, please check out [this section](#migrating-v1-database-to-v2) to migrate your database scheme to version 2.

```bash
nginwho --logPath:/var/log/nginx/access.log --dbPath:/var/log/nginwho.db

# If you want to omit a certain referrer from being logged (replace thegraynode.io with your domain):
nginwho --logPath:/var/log/nginx/access.log \
        --dbPath:/var/log/nginwho.db \
        --omitReferrer:thegraynode.io

# If you want to get real IP addresses of the visitors coming from Cloudflare:
nginwho --logPath:/var/log/nginx/access.log \
        --dbPath:/var/log/nginwho.db \
        --showRealIps:true
```

> Please note that you can mix these flags. They operate independently.

## Flags

Here are the available flags:

```text
  --help, -h              : Show help
  --version, -v           : Display version and quit
  --dbPath                : Path to SQLite database to log reports (default: /var/log/nginwho.db)
  --logPath               : Path to nginx access logs (default: /var/log/nginx/access.log)
  --interval              : Refresh interval in seconds (default: 10)
  --omitReferrer          : Omit a specific referrer from being logged (default: none)
  --showRealIps           : Show real IP of visitors by getting Cloudflare CIDRs to include in nginx config.
                            Self-updates every six hours (default: false)
  --blockUntrustedCidrs   : Block untrusted IP addresses using nftables. Only allows Cloudflare CIDRs (default: false)
  --processNginxLogs      : Process nginx logs (default: true)
  --report                : Enter report mode and query the database for statistics

  --migrateV1ToV2Db       : Migrate V1 database to V2 and exit (default: false).
                            Use with '--v1DbPath' and '--v2DbPath' flags
  --v1DbPath              : Path and name of the V1 database (e.g: /var/log/nginwho_v1.db)
  --v2DbPath              : Path and name of the V2 database (e.g: /var/log/nginwho.db)

```

## How it works

Let's see how nginwho works in a somewhat detailed yet short fashion.

### nginx Log Parser

**nginwho** by default reads `nginx` logs from `/var/log/nginx/access.log` and stores the parsed results in a **sqlite3** database located in `/var/log/nginwho.db` unless overridden by the [available flags](#flags).

It only reads the lines added since the last read, picks up where it left off after a restart and handles log rotation. Requests for static files (`.js`, `.css` and `.woff2`) are not stored, so reports show fewer requests than the raw log.

> [!WARNING]
> nginwho only supports the default nginx log format or any application that logs in the same format for now

### Restore Cloudflare Original Visitor IP

The second feature, `--showRealIps` flag fetches **Cloudflare CIDRs** (`IPv4` and `IPv6`) every _six hours_ through their **API**s and writes the result to a file named `nginwho` located in `/etc/nginx/`.

It is worthwhile to mention that **nginwho** leverages the `etag` field in Cloudflare's API response, so, if the newly fetched `etag` is the same as the current one, the `/etc/nginx/nginwho` file will not be overwritten.

If the `/etc/nginx/nginwho` file has changed or this is a fresh run, **nginwho** tests the **nginx** config (`nginx -t`) and if it passes, soft reloads **nginx** (`nginx -s reload`) right away. A soft reload does not drop open connections.

> [!IMPORTANT]
> The `--showRealIps` flag requires **root privileges**.

For the `--showRealIps` flag to work, add this line to your **nginx** configuration to include the generated `/etc/nginx/nginwho` file:

```text
include /etc/nginx/nginwho;
```

So that nginx knows how to restore original visitor IP addresses.

### Block Untrusted Requests

The third feature, `--blockUntrustedCidrs` flag gets Cloudflare CIDRs, either through:

1. Cloudflare APIs every _six hours_ when used together with the `--showRealIps` flag
2. or the `/etc/nginx/nginwho` file, read once at start, when the `--showRealIps` flag is not specified

The fetched CIDRs will be checked against your existing **nftables** rules and if necessary, the required rules will be created and added through _nftable's JSON API_.

There will be a bunch of tests and pre-checks done before applying any policies. These checks include:

1. Existence of Cloudflare IPv4 CIDRs nftables _Set_ (`Cloudflare_IPv4`)
1. Existence of Cloudflare IPv6 CIDRs nftables _Set_ (`Cloudflare_IPv6`)
1. Existence of nftables `nginwho` chain (`prerouting` hook)
1. Existence of nftables `input` chain
1. Existence of **drop** policy inside `nginwho` chain for untrusted IP addresses on port **80** and **443**
1. Existence of **accept** policy inside `input` chain for trusted IP addresses on port **80** and **443**

**nginwho** only creates the necessary changes. Otherwise, no actions will be taken. For instance, if a CIDR gets added or removed, only that part of **nftables** configuration will be changed and the rest remain unchanged.

> [!IMPORTANT]
> The `--blockUntrustedCidrs` flag requires **root privileges**.

> [!IMPORTANT]
> Since playing with **nftables** could result in blocking yourself out, **nginwho** requires you to have some basic policies in place, in specific, having an `inet filter` table. If you do not have it, **nginwho** will detect that and shows you how to create one.

### Reporting

Running **nginwho** with the `--report` flag will launch an interactive menu, providing some options (top visited URLs, top visiting IP addresses, etc.) that you can select and specify how many records to be queried. Reports cover the last 30 days by default. Press `w` to switch to the last 24 hours, 7 days or all time.

The database file is only readable by the user that created it, so if nginwho runs as a service (root), use `sudo`:

```bash
sudo nginwho --report --dbPath:/var/log/nginwho.db
```

### Migrating v1 database to v2

Running the command below will first check your database for any errors and, if it detects any, will output what recovery command should be run. If everything is fine, it will read out the data from your source database, convert and write the data to version 2 so that nginwho can continue working as intended.

> Change the database file names according to your setup.

```bash
nginwho --migrateV1ToV2Db \
        --v1DbPath:nginwho_v1.db \
        --v2DbPath:nginwho.db
```