# ip2nginx
<p align="center">
  <a href="https://safe-cap.com/" target="_blank">
    <img src="img/ip2nginx-small.png">
  </a>
	<br><br>
	<img src="https://img.shields.io/badge/version-0.0.1-green.svg?style=for-the-badge">
	<a href="https://www.paypal.com/donate/?hosted_button_id=JNFS79EFEM7C6" target="_blank">
		<img src="https://img.shields.io/badge/Donate-PayPal-blue.svg?style=for-the-badge">
	</a>
</p>

---

## 💡 Project Overview

**ip2nginx** is a lightweight and secure system for dynamically updating NGINX reverse proxy configurations based on public IP address changes, typically reported by edge devices like **pfSense**. It ensures that NGINX always routes traffic through the correct IP, even in dynamic environments.

---

<!-- TOC -->
## 📚 Table of Contents

- [💡 Project Overview](#-project-overview)
- [🏠 What is ip2nginx?](#-what-is-ip2nginx)
- [💡 Example Use Case](#-example-use-case)
- [⚙️ Features](#-features)
- [📁 Project Structure](#-project-structure)
- [🚀 Update Process: update.php](#-remote-update-api-updatephp)
- [🚀 Update Process: updater.php](#-update-process-updaterphp)
- [🛠 Environment Setup: check_env.php](#-check_envphp-environment-setup)
- [📅 Cron Setup with run.sh](#-cron-setup-runsh)
- [🔄 Usage Example from pfSense](#-pfsense-shell-example)
- [✅ Example Entry in token.json](#-example-tokenjson)
- [✅ Example Entry in meta.json](#-example-metajson)
- [🛡 Security](#-security-highlights)
- [✅ Requirements](#-requirements)
- [📜 License](#-license)
- [🤝 Author](#-author)
<!-- /TOC -->

---

🏠 What is ip2nginx?

ip2nginx is a self-hosted system that allows you to securely expose services running on your private home network (e.g. NAS, internal web interfaces, or a self-hosted app) under your own domain — without using third-party dynamic DNS services.

Instead of changing DNS records, it updates the reverse proxy (NGINX) configuration on your public server — making it the only point that needs to be publicly reachable.
🔐 Why is this more secure?

Your home server does not need to be exposed to the entire internet. Only your external server (running ip2nginx) needs access. This allows you to:

    Restrict incoming firewall access at home to just one remote IP (your public server).

    Avoid direct exposure of your internal services to DDOS or scanning attempts.

    Offload all public traffic to the external server, preventing your home bandwidth from being overwhelmed.

⚙️ How it works:

    Your home gateway (e.g. pfSense or another device) periodically sends its current public IP to your external server using an authenticated API call.

    The external server updates only the proxy_pass directive inside a specific location block in the NGINX config for your domain.

    NGINX is automatically reloaded to apply the changes.

    From the outside world, visitors reach your external server, which transparently proxies to your home server — using your latest IP.

This gives you full control, avoids third-party dependencies, and increases the security of your home infrastructure.

<p align="center">
  <img src="https://safe-cap.com/git/ip2nginx-server-small.png" alt="Diagram of a secure home-server access flow using an external NGINX proxy and API updates from the home server.">
  <br><br>
</p>

---

## 💡 Example Use Case
  You want to host https://home.example.com and route it to a web interface at your home (like pfSense or a Raspberry Pi), but your IP changes regularly due to your ISP.

  With ip2nginx, the server automatically updates the NGINX reverse proxy so your domain continues working — securely and without dynamic DNS services.

---

## ⚙️ Features

- Accepts remote updates via `update.php` using **token-authenticated** requests.
- Supports both `POST` and `GET`, though **POST is preferred** to avoid token caching.
- Updates only the `proxy_pass` line in the relevant `location` block of `nginx.conf`.
- Automatically marks entries in `meta.json` as `"changed": 1` when input changes.
- Logs all changes to `log.json` with timestamps.
- Automatically reloads NGINX: `nginx -t && systemctl reload nginx` (requires root).
- Built-in abuse protection: failed requests tracked and blocked.
- `.htaccess` ensures that only `update.php` is externally accessible.

---

## 📁 Project Structure

```
ip2nginx/
├── index.php             # Shared configuration and fallback error handler
├── update.php            # Receives incoming remote IP update requests
├── updater.php           # CLI-only: applies changes to nginx.conf if marked
├── run.sh                # Wrapper script for cron automation
├── check_env.php         # Environment validator and bootstrapper
├── .htaccess             # Blocks unauthorized access, routes traffic
├── data/
│   ├── meta.json         # Stores current configuration state per domain
│   ├── token.json        # Stores allowed tokens (auth)
│   ├── log.json          # Stores audit log of changes
│   ├── blocklist.json    # Temporarily blocked IPs (48h ban)
│   └── failures.json     # Tracks failed attempts per IP
```

---

## 🌐 Remote Update API: `update.php`

Supports **POST** (preferred) and **GET** methods.

| Parameter  | Required | Description                                                                 |
|------------|----------|-----------------------------------------------------------------------------|
| `name`     | ✅       | Identifier (e.g. `domain1.to.com`)                                          |
| `token`    | ✅       | Secret token assigned for this name                                         |
| `ip`       | ❌       | New public IP (default: auto-detected from request)                         |
| `domain`   | ❌       | Backend domain to proxy to (default: same as IP)                            |
| `port`     | ❌       | Port number (default: 443 for https, 80 for http)                           |
| `protocol` | ❌       | One of `http` or `https` (default: `https`)                                  |

Any change in `ip`, `domain`, `port`, or `protocol` triggers `"changed": 1` in `meta.json`.

If **any parameter is received via GET**, then `ip` and `domain` will be overridden with the **client’s real IP** for security.

---

## 🧩 Update Process: `updater.php`

To apply updates made via `update.php`:

1. Load all entries from `meta.json`
2. Check for entries marked `"changed": 1`
3. Find `/var/www/vhosts/system/<domain>/conf/nginx.conf`
4. Modify the appropriate `location` block’s `proxy_pass` directive only
5. Validate and reload NGINX
6. Reset `"changed": 0`

---

## 🛠 `check_env.php`: Environment Setup

This CLI script validates:

- Config files and permissions
- JSON structure of each config file
- Auto-creates missing files (with defaults)
- Token file includes example if missing

---

## ⏱ Cron Setup: `run.sh`

To automate updates, add `run.sh` to your crontab as root:

```sh
sudo crontab -e
```

Then add:

```cron
*/5 * * * * /path/to/ip2nginx/run.sh
```

This ensures automatic application of proxy changes to NGINX config and reloads.

---

## 🔄 pfSense Shell Example

Add the following script to `pfSense` via `System > Advanced > Cron`:

```sh
#!/bin/sh

SERVER="https://your-server.com/ip2nginx"
NAME="domain1.to.com"
DOMAIN="domain.from.com"
TOKEN="YOUR_SECRET_TOKEN"

curl -s -X POST "$SERVER/update.php"   -d "name=$NAME"   -d "domain=$DOMAIN"   -d "protocol=https"   -d "port=443"   -d "token=$TOKEN"
```

---

## ✅ Example: `token.json`

```json
{
  "domain1.to.com": "SECRET_TOKEN_8v73jDKsdLzAq9DkeUz1",
  "domain2.to.com": "SECRET_TOKEN_3im83jUj28mjo2mI23un"
}
```

---

## ✅ Example: `meta.json`

```json
{
  "domain1.to.com": {
    "domain": "domain.from.com",
    "ip": "192.0.2.4",
    "port": "443",
    "protocol": "https",
    "location": "/",
    "time": "2025-05-16T09:00:00+00:00",
    "changed": 1
  }
}
```

---

## 🔒 Security Highlights

- `.htaccess` denies access to all files except `update.php`.
- Only HTTPS connections should be used.
- All tokens are stored securely and verified per name.
- After 3 failed attempts, IP is banned for 48 hours.
- Generic error messages avoid leaking details to attackers.

---

## ✅ Requirements

- PHP 7.4 or newer
- NGINX with reload access (`sudo systemctl reload nginx`)
- `curl` on the client side
- Token definitions in `token.json`

---

## 📜 License

MIT (or similar): Open-source, free for use and modification.

---

## 🤝 Author
<p align="center">
  <a href="https://safe-cap.com" target="_blank">
    <img src="img/safe-cap-logo-small.png" width="100" height="100" alt="SAFE-CAP">
  </a>
  <br><br>
  **Maintained by SAFE-CAP / Alexander Schiemann / https://safe-cap.com**
</p>