This commit is contained in:
Eliezer Croitoru 2026-08-11 19:04:35 +03:00
commit 885ec7e678
7 changed files with 171 additions and 0 deletions

12
Dockerfile.bridge Normal file
View File

@ -0,0 +1,12 @@
FROM python:3.11-slim
WORKDIR /app
RUN pip install --no-cache-dir flask gunicorn
COPY app.py .
COPY mapping.json .
EXPOSE 3000
CMD ["gunicorn", "--bind", "0.0.0.0:3000", "app:app"]

67
app.py Normal file
View File

@ -0,0 +1,67 @@
import json
import os
import urllib.parse
import urllib.request
from flask import Flask, request, jsonify
app = Flask(__name__)
KUMA_BASE_URL = os.getenv("KUMA_BASE_URL", "http://localhost:3001/api/push")
MAPPING_FILE = os.getenv("MAPPING_FILE", "mapping.json")
# Shared secret for authentication
WEBHOOK_SECRET = os.getenv("WEBHOOK_SECRET", "super-secret-key-change-me")
def load_mapping():
if not os.path.exists(MAPPING_FILE):
return {}
try:
with open(MAPPING_FILE, "r") as f:
return json.load(f)
except Exception as e:
print(f"[ERROR] Failed to parse {MAPPING_FILE}: {e}")
return {}
@app.route("/webhook/ppp", methods=["POST"])
def ppp_webhook():
# 1. Validate Secret Token in Header
auth_header = request.headers.get("X-API-Key")
if not auth_header or auth_header != WEBHOOK_SECRET:
print(f"[WARN] Unauthorized webhook attempt from IP: {request.remote_addr}")
return jsonify({"status": "error", "message": "Unauthorized"}), 401
data = request.get_json(silent=True) or {}
user = data.get("user")
status = data.get("status")
ip = data.get("ip", "0.0.0.0")
msg = data.get("msg", "")
ping = data.get("ping", 0)
if not user or not status:
return jsonify({"status": "error", "message": "Missing required fields"}), 400
mapping = load_mapping()
token = mapping.get(user)
if not token:
print(f"[WARN] No token found for user: '{user}'")
return jsonify({"status": "ignored", "message": f"User '{user}' not mapped."}), 404
kuma_status = "up" if str(status).lower() == "up" else "down"
if not msg:
msg = f"PPP status is {kuma_status.upper()} for user '{user}' ({ip})"
encoded_msg = urllib.parse.quote(msg)
kuma_url = f"{KUMA_BASE_URL}/{token}?status={kuma_status}&msg={encoded_msg}&ping={ping}"
try:
req = urllib.request.Request(kuma_url, headers={"User-Agent": "MikroTik-Bridge/1.0"})
with urllib.request.urlopen(req, timeout=5) as resp:
print(f"[INFO] Forwarded push for '{user}' -> status: {kuma_status}")
return jsonify({"status": "success"}), 200
except Exception as e:
print(f"[ERROR] Failed to contact Uptime Kuma: {e}")
return jsonify({"status": "error", "message": str(e)}), 500
if __name__ == "__main__":
app.run(host="0.0.0.0", port=3000)

View File

@ -0,0 +1,42 @@
version: '3.8'
services:
# Uptime Kuma Monitoring Service
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
restart: always
ports:
- "3001:3001"
volumes:
- kuma-data:/app/data
networks:
- kuma-net
# MikroTik to Uptime Kuma Python Webhook Bridge
mikrotik-bridge:
build:
context: ./bridge
dockerfile: Dockerfile
container_name: mikrotik-kuma-bridge
restart: always
ports:
- "3000:3000"
environment:
# Internal Docker network URL pointing to the Uptime Kuma service container
- KUMA_BASE_URL=http://uptime-kuma:3001/api/push
- MAPPING_FILE=mapping.json
volumes:
# Mount mapping.json so you can edit tokens on the host without rebuilding the container
- ./bridge/mapping.json:/app/mapping.json:ro
depends_on:
- uptime-kuma
networks:
- kuma-net
networks:
kuma-net:
driver: bridge
volumes:
kuma-data:

15
docker-compose.yml Normal file
View File

@ -0,0 +1,15 @@
services:
mikrotik-bridge:
build:
context: ./
dockerfile: Dockerfile.bridge
container_name: mikrotik-kuma-bridge
restart: always
ports:
- "3000:3000"
environment:
- KUMA_BASE_URL=http://uptime-kuma:3001/api/push
- MAPPING_FILE=mapping.json
- WEBHOOK_SECRET=MySuperSecretToken123!
volumes:
- ./bridge/mapping.json:/app/mapping.json:ro

6
mapping.json Normal file
View File

@ -0,0 +1,6 @@
{
"john_doe": "A1b2C3d4E5",
"branch_office_vpn": "F6g7H8i9J0",
"site2site_tunnel": "K1l2M3n4O5",
"admin_laptop": "P6q7R8s9T0"
}

View File

@ -0,0 +1,19 @@
# --- Define Local Variables ---
:local status "up"
:local serverUrl "http://localhost/webhook-test.php"
:local apiKey "MySuperSecretToken123!"
# --- Fetch PPP System Variables safely ---
:local u $"user"
:local lip $"local-address"
:local rip $"remote-address"
:local cid $"caller-id"
:local iface $"interface"
:local msg ("PPP session " . $status . " on interface " . $iface)
# --- Construct JSON Body ---
:local jsonBody "{\"user\":\"$u\", \"local_ip\":\"$lip\", \"ip\":\"$rip\", \"caller_id\":\"$cid\", \"interface\":\"$iface\", \"status\":\"$status\", \"msg\":\"$msg\"}"
# --- Send Webhook ---
/tool fetch url=$serverUrl http-method=post http-header-field="Content-Type: application/json,X-API-Key: $apiKey" http-data=$jsonBody keep-result=no

10
testing.sh Normal file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env bash
curl -X POST http://localhost:3000/webhook/ppp \
-H "Content-Type: application/json" \
-H "X-API-Key: MySuperSecretToken123!" \
-d '{
"user": "john_doe",
"ip": "10.0.0.5",
"status": "up"
}'