Restructure in-progress

This commit is contained in:
n0mad1k
2025-07-04 23:48:04 -04:00
parent 32aad50820
commit 36de65ba60
114 changed files with 670 additions and 1041 deletions
@@ -0,0 +1,214 @@
#!/usr/bin/env python3
"""
Simple Email Tracking Server
A minimal Flask application that serves transparent tracking pixels
and logs email opens with metadata.
"""
import os
import json
import time
import logging
from datetime import datetime
from flask import Flask, request, send_file, render_template_string
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('/root/Tools/tracker/data/tracker.log'),
logging.StreamHandler()
]
)
# Create Flask app
app = Flask(__name__)
# Directory to store tracking data
DATA_DIR = '/root/Tools/tracker/data'
os.makedirs(DATA_DIR, exist_ok=True)
# Path to 1x1 transparent pixel
PIXEL_PATH = os.path.join(DATA_DIR, 'pixel.png')
# Create 1x1 transparent PNG if it doesn't exist
if not os.path.exists(PIXEL_PATH):
from PIL import Image
img = Image.new('RGBA', (1, 1), color=(0, 0, 0, 0))
img.save(PIXEL_PATH)
# Path to tracking data
TRACKING_DATA_PATH = os.path.join(DATA_DIR, 'tracking_data.json')
def load_tracking_data():
"""Load existing tracking data from JSON file"""
if os.path.exists(TRACKING_DATA_PATH):
try:
with open(TRACKING_DATA_PATH, 'r') as f:
return json.load(f)
except json.JSONDecodeError:
logging.error("Error loading tracking data, starting fresh")
return {}
def save_tracking_data(data):
"""Save tracking data to JSON file"""
with open(TRACKING_DATA_PATH, 'w') as f:
json.dump(data, f, indent=2)
@app.route('/pixel/<tracking_id>.png')
def tracking_pixel(tracking_id):
"""Serve a tracking pixel and log the request"""
# Get client information
user_agent = request.headers.get('User-Agent', 'Unknown')
ip_address = request.remote_addr
timestamp = datetime.now().isoformat()
referer = request.headers.get('Referer', 'Unknown')
# Log the tracking event
logging.info(f"Pixel loaded - ID: {tracking_id}, IP: {ip_address}")
# Add tracking event to data
tracking_data = load_tracking_data()
if tracking_id not in tracking_data:
tracking_data[tracking_id] = []
tracking_data[tracking_id].append({
'timestamp': timestamp,
'ip_address': ip_address,
'user_agent': user_agent,
'referer': referer
})
save_tracking_data(tracking_data)
# Return the 1x1 transparent pixel
return send_file(PIXEL_PATH, mimetype='image/png')
@app.route('/')
def dashboard():
"""Display tracking statistics dashboard"""
tracking_data = load_tracking_data()
# Prepare data for the dashboard
stats = []
for tracking_id, events in tracking_data.items():
stats.append({
'id': tracking_id,
'views': len(events),
'last_view': events[-1]['timestamp'] if events else 'Never',
'unique_ips': len(set(e['ip_address'] for e in events))
})
# Sort by most views
stats.sort(key=lambda x: x['views'], reverse=True)
# Simple HTML dashboard template
template = """
<!DOCTYPE html>
<html>
<head>
<title>Email Tracking Dashboard</title>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
h1 { color: #333; }
table { border-collapse: collapse; width: 100%; }
th, td { text-align: left; padding: 8px; border-bottom: 1px solid #ddd; }
tr:hover { background-color: #f5f5f5; }
th { background-color: #4CAF50; color: white; }
.container { max-width: 800px; margin: 0 auto; }
</style>
</head>
<body>
<div class="container">
<h1>Email Tracking Dashboard</h1>
<p>To track email opens, add this HTML to your emails:</p>
<pre>&lt;img src="https://YOUR_DOMAIN/px/YOUR_TRACKING_ID.png" height="1" width="1" /&gt;</pre>
<h2>Tracking Statistics</h2>
<table>
<tr>
<th>Tracking ID</th>
<th>Views</th>
<th>Unique IPs</th>
<th>Last View</th>
<th>Details</th>
</tr>
{% for stat in stats %}
<tr>
<td>{{ stat.id }}</td>
<td>{{ stat.views }}</td>
<td>{{ stat.unique_ips }}</td>
<td>{{ stat.last_view }}</td>
<td><a href="/details/{{ stat.id }}">View Details</a></td>
</tr>
{% endfor %}
</table>
</div>
</body>
</html>
"""
return render_template_string(template, stats=stats)
@app.route('/details/<tracking_id>')
def tracking_details(tracking_id):
"""Display detailed tracking information for a specific ID"""
tracking_data = load_tracking_data()
if tracking_id not in tracking_data:
return f"No data found for tracking ID: {tracking_id}", 404
events = tracking_data[tracking_id]
# Simple HTML template for details
template = """
<!DOCTYPE html>
<html>
<head>
<title>Tracking Details: {{ tracking_id }}</title>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
h1, h2 { color: #333; }
table { border-collapse: collapse; width: 100%; }
th, td { text-align: left; padding: 8px; border-bottom: 1px solid #ddd; }
tr:hover { background-color: #f5f5f5; }
th { background-color: #4CAF50; color: white; }
.container { max-width: 800px; margin: 0 auto; }
.back { margin-bottom: 20px; }
</style>
</head>
<body>
<div class="container">
<div class="back"><a href="/">&laquo; Back to Dashboard</a></div>
<h1>Tracking Details: {{ tracking_id }}</h1>
<p>Total views: {{ events|length }}</p>
<h2>Events</h2>
<table>
<tr>
<th>Time</th>
<th>IP Address</th>
<th>User Agent</th>
<th>Referer</th>
</tr>
{% for event in events %}
<tr>
<td>{{ event.timestamp }}</td>
<td>{{ event.ip_address }}</td>
<td>{{ event.user_agent }}</td>
<td>{{ event.referer }}</td>
</tr>
{% endfor %}
</table>
</div>
</body>
</html>
"""
return render_template_string(template, tracking_id=tracking_id, events=events)
if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000, debug=False)
+47
View File
@@ -0,0 +1,47 @@
server {
listen 80;
listen [::]:80;
server_name {{ tracker_domain }};
# Redirect to HTTPS if SSL is enabled
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name {{ tracker_domain }};
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/{{ tracker_domain }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ tracker_domain }}/privkey.pem;
# Restrict dashboard to localhost only
location / {
allow 127.0.0.1;
deny all;
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# Allow public access only to pixel endpoints
location ~ ^/pixel/(.+)\.png$ {
# Public access allowed
proxy_pass http://127.0.0.1:5000/pixel/$1.png;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Cache control - don't cache tracking pixels
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
expires off;
}
# Security headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer" always;
}
+45
View File
@@ -0,0 +1,45 @@
#!/bin/bash
# CLI tool to view email tracking stats
DATA_FILE="/root/Tools/tracker/data/tracking_data.json"
function show_summary() {
if [ ! -f "$DATA_FILE" ]; then
echo "No tracking data found."
exit 1
fi
echo "Email Tracking Summary:"
echo "======================="
jq -r 'to_entries | sort_by(.value | length) | reverse | .[] | "\(.key): \(.value | length) views"' $DATA_FILE
}
function show_details() {
ID=$1
if [ ! -f "$DATA_FILE" ]; then
echo "No tracking data found."
exit 1
fi
echo "Details for tracking ID: $ID"
echo "=========================="
jq -r --arg id "$ID" '.[$id] | if . then .[] | "\(.timestamp) | \(.ip_address) | \(.user_agent)" else "No data found for this ID" end' $DATA_FILE
}
case "$1" in
"list")
show_summary
;;
"details")
if [ -z "$2" ]; then
echo "Usage: $0 details TRACKING_ID"
exit 1
fi
show_details "$2"
;;
*)
echo "Usage: $0 [list|details TRACKING_ID]"
echo " list - Show summary of all tracking IDs"
echo " details ID - Show details for specific tracking ID"
;;
esac
+19
View File
@@ -0,0 +1,19 @@
[Unit]
Description=Email Tracking Server
After=network.target
[Service]
User=tracker
Group=tracker
WorkingDirectory=/root/Tools/tracker
ExecStart=/root/Tools/tracker/venv/bin/python /root/Tools/tracker/simple_email_tracker.py
Restart=always
RestartSec=10
# Security settings
PrivateTmp=true
ProtectSystem=full
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target