Enterprise Tor Hidden Service Deployment: Complete Security and Operations Guide
Tor Hidden Services (Onion Services) provide end-to-end encrypted, anonymized web services accessible exclusively through the Tor network. This comprehensive guide covers secure deployment, hardening practices, monitoring strategies, and enterprise operational considerations for running production Hidden Services on Linux systems.
Understanding Tor Hidden Services
Architecture and Security Model
Tor Hidden Services implement a sophisticated cryptographic architecture providing:
Core Security Features
- End-to-End Encryption: All traffic encrypted between client and service
- Location Privacy: Server IP addresses remain hidden from clients
- Censorship Resistance: Accessible even when normal internet connectivity is blocked
- Authentication: Optional client authentication for restricted access
- Perfect Forward Secrecy: Session keys prevent historical traffic decryption
Network Architecture
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Tor Client │────│ Guard Relay │────│Middle Relay │────│ Exit Relay │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│ │
│ ┌─────────────┐ ┌─────────────┐ │
└────────────│Intro Point │────│Rendezvous │─────────┘
│ │ │Point │
└─────────────┘ └─────────────┘
│ │
│ ┌─────────────┐
└────│Hidden Service│
│ Server │
└─────────────┘
Legal and Operational Considerations
Hosting Provider Compliance
- Most providers allow Hidden Services (non-exit nodes)
- Exit nodes may violate terms of service
- Hidden Services generate minimal external traffic
- No special port forwarding requirements
Traffic Analysis Protection
- Client traffic appears as
127.0.0.1
in web server logs - Host header contains
.onion
address - Request timing patterns may leak information
- Consider additional anonymization layers
Secure Installation and Configuration
System Preparation and Hardening
Initial System Security
# Update system packages
apt update && apt upgrade -y
# Install essential security tools
apt install -y ufw fail2ban apparmor-utils auditd
# Configure automatic security updates
apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
# Enable and configure firewall
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable
User and Permission Hardening
# Create dedicated tor service user
useradd --system --home /var/lib/tor --shell /bin/false tor-service
# Set strict permissions on tor directories
chmod 700 /var/lib/tor
chown -R debian-tor:debian-tor /var/lib/tor
# Create dedicated web service user
useradd --system --home /var/www --shell /bin/false www-hidden
# Configure sudo restrictions
cat > /etc/sudoers.d/tor-restrictions << 'EOF'
# Tor service management restrictions
%tor-admin ALL=(root) NOPASSWD: /bin/systemctl restart tor
%tor-admin ALL=(root) NOPASSWD: /bin/systemctl status tor
%tor-admin ALL=(root) NOPASSWD: /bin/systemctl reload tor
EOF
Tor Installation and Configuration
Official Tor Repository Setup
# Install prerequisite packages
apt install -y apt-transport-https gpg software-properties-common curl
# Import Tor Project GPG key with verification
curl -fsSL https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | \
gpg --dearmor -o /usr/share/keyrings/tor-archive-keyring.gpg
# Add Tor Project repository
echo "deb [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] https://deb.torproject.org/torproject.org $(lsb_release -cs) main" > \
/etc/apt/sources.list.d/tor.list
# Install Tor with verification
apt update
apt install -y tor deb.torproject.org-keyring
# Verify installation
tor --version
systemctl status tor
Advanced Tor Configuration
# Create comprehensive torrc configuration
cat > /etc/tor/torrc << 'EOF'
# Basic configuration
User debian-tor
DataDirectory /var/lib/tor
ControlPort 9051
HashedControlPassword 16:872860B76453A77D60CA2BB8C1A7042072093276A3D701AD684053EC4C
# Security enhancements
DisableDebuggerAttachment 1
SafeLogging 1
Log notice file /var/log/tor/notices.log
Log warn file /var/log/tor/warnings.log
# Hidden service configuration
HiddenServiceDir /var/lib/tor/hidden_service/
HiddenServicePort 80 127.0.0.1:8080
HiddenServicePort 443 127.0.0.1:8443
# Advanced security options
HiddenServiceMaxStreams 10
HiddenServiceMaxStreamsCloseCircuit 1
HiddenServiceNumIntroductionPoints 3
# Performance optimization
NumEntryGuards 8
NewCircuitPeriod 30
MaxCircuitDirtiness 600
# Additional security
ExitPolicy reject *:*
PublishServerDescriptor 0
EOF
# Set proper permissions
chmod 644 /etc/tor/torrc
chown root:root /etc/tor/torrc
# Create log directory
mkdir -p /var/log/tor
chown debian-tor:debian-tor /var/log/tor
chmod 750 /var/log/tor
Enhanced Hidden Service Configuration
# Create multiple hidden services for redundancy
cat >> /etc/tor/torrc << 'EOF'
# Primary web service
HiddenServiceDir /var/lib/tor/web_service/
HiddenServicePort 80 127.0.0.1:8080
HiddenServicePort 443 127.0.0.1:8443
HiddenServiceVersion 3
# API service (separate onion address)
HiddenServiceDir /var/lib/tor/api_service/
HiddenServicePort 80 127.0.0.1:8081
HiddenServicePort 443 127.0.0.1:8444
HiddenServiceVersion 3
# Admin interface with client authentication
HiddenServiceDir /var/lib/tor/admin_service/
HiddenServicePort 443 127.0.0.1:8445
HiddenServiceVersion 3
HiddenServiceAuthorizeClient stealth admin_client
EOF
# Restart Tor to generate onion addresses
systemctl restart tor
sleep 10
# Display generated onion addresses
echo "=== Generated Onion Addresses ==="
echo "Web Service:"
cat /var/lib/tor/web_service/hostname
echo "API Service:"
cat /var/lib/tor/api_service/hostname
echo "Admin Service:"
cat /var/lib/tor/admin_service/hostname
Web Server Configuration
Nginx Advanced Configuration
Primary Nginx Configuration
# /etc/nginx/sites-available/hidden-service
server {
listen 127.0.0.1:8080;
listen 127.0.0.1:8443 ssl http2;
# Server identification
server_name your-onion-address.onion;
# SSL configuration for internal encryption
ssl_certificate /etc/ssl/certs/hidden-service.crt;
ssl_certificate_key /etc/ssl/private/hidden-service.key;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
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 "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'" always;
# Onion-Location header for clearnet sites
add_header Onion-Location "http://your-onion-address.onion$request_uri" always;
# Root directory and index files
root /var/www/hidden-service;
index index.html index.htm index.php;
# Main location block
location / {
try_files $uri $uri/ =404;
# Rate limiting for Tor
limit_req zone=tor_limit burst=10 nodelay;
}
# API endpoint with additional security
location /api/ {
proxy_pass http://127.0.0.1:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# API-specific rate limiting
limit_req zone=api_limit burst=5 nodelay;
}
# Security-sensitive locations
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~ \.(log|txt|md)$ {
deny all;
access_log off;
log_not_found off;
}
# Logging configuration
access_log /var/log/nginx/hidden-service-access.log;
error_log /var/log/nginx/hidden-service-error.log warn;
}
Nginx Rate Limiting and Security
# /etc/nginx/conf.d/tor-security.conf
# Rate limiting zones for Tor traffic
limit_req_zone $binary_remote_addr zone=tor_limit:10m rate=1r/s;
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=5r/m;
# Connection limiting
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
# Map for detecting Tor traffic
map $remote_addr $is_tor {
default 0;
"127.0.0.1" 1;
"0.0.0.0" 1;
}
# Custom log format for Tor
log_format tor_combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'tor=$is_tor onion="$http_host"';
# Security configuration
server_tokens off;
client_max_body_size 1M;
client_body_timeout 10s;
client_header_timeout 10s;
keepalive_timeout 5s 5s;
send_timeout 10s;
Apache Configuration
Apache Virtual Host for Hidden Services
# /etc/apache2/sites-available/hidden-service.conf
<VirtualHost 127.0.0.1:8080>
ServerName your-onion-address.onion
DocumentRoot /var/www/hidden-service
# Logging
LogLevel warn
ErrorLog ${APACHE_LOG_DIR}/hidden-service-error.log
CustomLog ${APACHE_LOG_DIR}/hidden-service-access.log combined
# Security headers
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Content-Security-Policy "default-src 'self'"
# Onion-Location header
Header always set Onion-Location "http://your-onion-address.onion%{REQUEST_URI}e"
# Directory security
<Directory /var/www/hidden-service>
Options -Indexes -Includes -ExecCGI
AllowOverride None
Require all granted
# Rate limiting with mod_evasive
DOSHashTableSize 4096
DOSPageCount 3
DOSPageInterval 1
DOSSiteCount 50
DOSSiteInterval 1
DOSBlockingPeriod 600
</Directory>
# Deny access to sensitive files
<FilesMatch "\.(log|txt|md|conf)$">
Require all denied
</FilesMatch>
# Hide .htaccess files
<Files ~ "^\.">
Require all denied
</Files>
</VirtualHost>
<VirtualHost 127.0.0.1:8443>
ServerName your-onion-address.onion
DocumentRoot /var/www/hidden-service
# SSL Configuration
SSLEngine on
SSLCertificateFile /etc/ssl/certs/hidden-service.crt
SSLCertificateKeyFile /etc/ssl/private/hidden-service.key
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
# Include same configuration as HTTP vhost
Include /etc/apache2/sites-available/hidden-service-common.conf
</VirtualHost>
Advanced Security Hardening
Client Authentication
Stealth Authentication Setup
# Generate client authentication keys
cd /var/lib/tor/admin_service
tor --hash-password "admin_secret_password" > auth_password
# Configure client authentication in torrc
cat >> /etc/tor/torrc << 'EOF'
# Client authentication for admin service
HiddenServiceDir /var/lib/tor/admin_service/
HiddenServicePort 443 127.0.0.1:8445
HiddenServiceAuthorizeClient stealth admin_client,backup_admin
EOF
# Client-side authentication configuration
echo "HidServAuth your-admin-onion.onion admin_client_key" >> /etc/tor/torrc
Advanced Authentication with Onion Client Auth v3
#!/usr/bin/env python3
"""
Tor v3 Client Authentication Key Generator
"""
import base64
import nacl.public
import nacl.encoding
import os
from pathlib import Path
class TorClientAuth:
def __init__(self, service_dir="/var/lib/tor/authenticated_service"):
self.service_dir = Path(service_dir)
self.auth_dir = self.service_dir / "authorized_clients"
def generate_client_keypair(self, client_name: str):
"""Generate client authentication keypair"""
# Generate keypair
private_key = nacl.public.PrivateKey.generate()
public_key = private_key.public_key
# Create authorized_clients directory
self.auth_dir.mkdir(parents=True, exist_ok=True)
# Server-side: public key file
public_key_b32 = base64.b32encode(
public_key.encode(encoder=nacl.encoding.RawEncoder)
).decode('ascii').strip('=').lower()
auth_file = self.auth_dir / f"{client_name}.auth"
auth_file.write_text(f"descriptor:x25519:{public_key_b32}")
# Client-side: private key for torrc
private_key_b32 = base64.b32encode(
private_key.encode(encoder=nacl.encoding.RawEncoder)
).decode('ascii').strip('=').lower()
return {
'client_name': client_name,
'private_key': private_key_b32,
'public_key': public_key_b32,
'torrc_line': f"ClientOnionAuthDir /etc/tor/onion_auth"
}
def setup_client_auth_service(self, service_name: str, port: int = 443, target_port: int = 8445):
"""Setup authenticated hidden service"""
torrc_config = f"""
# Authenticated Hidden Service - {service_name}
HiddenServiceDir {self.service_dir}/
HiddenServicePort {port} 127.0.0.1:{target_port}
HiddenServiceVersion 3
"""
print(f"Add this to /etc/tor/torrc:")
print(torrc_config)
# Set proper permissions
os.chmod(self.service_dir, 0o700)
os.chmod(self.auth_dir, 0o700)
return torrc_config
# Generate client authentication
if __name__ == "__main__":
auth_manager = TorClientAuth()
# Generate keys for different clients
clients = ["admin_user", "backup_admin", "monitoring_system"]
for client in clients:
auth_info = auth_manager.generate_client_keypair(client)
print(f"\n=== Client: {client} ===")
print(f"Private Key: {auth_info['private_key']}")
print(f"Public Key: {auth_info['public_key']}")
print(f"Torrc Config: {auth_info['torrc_line']}")
# Create client-side auth file
client_auth_dir = Path(f"/etc/tor/onion_auth")
client_auth_dir.mkdir(parents=True, exist_ok=True)
# Client auth file format for v3
client_auth_file = client_auth_dir / f"{client}.auth_private"
client_auth_file.write_text(f"{auth_info['private_key']}")
os.chmod(client_auth_file, 0o600)
System-Level Security
AppArmor Profile for Tor
# Create AppArmor profile for enhanced Tor security
cat > /etc/apparmor.d/usr.bin.tor << 'EOF'
#include <tunables/global>
/usr/bin/tor {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/openssl>
capability setuid,
capability setgid,
capability sys_resource,
# Tor executable
/usr/bin/tor mr,
# Configuration files
/etc/tor/ r,
/etc/tor/** r,
/usr/share/tor/** r,
# Data directory
/var/lib/tor/ rw,
/var/lib/tor/** rw,
# Log files
/var/log/tor/ rw,
/var/log/tor/** rw,
# Network access
network inet stream,
network inet dgram,
# Proc filesystem
@{PROC}/sys/kernel/random/uuid r,
@{PROC}/sys/net/core/somaxconn r,
# Deny everything else
deny /home/** rw,
deny /root/** rw,
deny /tmp/** rw,
deny /var/tmp/** rw,
}
EOF
# Enable AppArmor profile
apparmor_parser -r /etc/apparmor.d/usr.bin.tor
systemctl restart tor
Systemd Security Hardening
# /etc/systemd/system/tor.service.d/security.conf
[Service]
# Security restrictions
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
ProtectHome=true
ProtectSystem=strict
ReadWritePaths=/var/lib/tor /var/log/tor
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictRealtime=true
RestrictNamespaces=true
LockPersonality=true
MemoryDenyWriteExecute=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
# Resource limits
LimitNOFILE=32768
LimitNPROC=64
# User/Group isolation
User=debian-tor
Group=debian-tor
SupplementaryGroups=
# Working directory
WorkingDirectory=/var/lib/tor
Monitoring and Operations
Comprehensive Monitoring System
Tor Metrics Collection
#!/usr/bin/env python3
"""
Tor Hidden Service Monitoring System
"""
import subprocess
import json
import time
import requests
import socket
from datetime import datetime
from pathlib import Path
import logging
class TorMonitor:
def __init__(self, control_port=9051, control_password=None):
self.control_port = control_port
self.control_password = control_password
self.logger = logging.getLogger(__name__)
def check_tor_service(self):
"""Check if Tor service is running"""
try:
result = subprocess.run(['systemctl', 'is-active', 'tor'],
capture_output=True, text=True)
return result.stdout.strip() == 'active'
except Exception as e:
self.logger.error(f"Failed to check Tor service: {e}")
return False
def get_hidden_service_status(self, service_dir):
"""Check hidden service status"""
service_path = Path(service_dir)
hostname_file = service_path / "hostname"
if not hostname_file.exists():
return {"status": "not_configured", "onion_address": None}
try:
onion_address = hostname_file.read_text().strip()
# Test connectivity to hidden service
connectivity_status = self.test_hidden_service_connectivity(onion_address)
return {
"status": "active" if connectivity_status else "unreachable",
"onion_address": onion_address,
"service_dir": str(service_path),
"connectivity": connectivity_status
}
except Exception as e:
self.logger.error(f"Failed to get hidden service status: {e}")
return {"status": "error", "error": str(e)}
def test_hidden_service_connectivity(self, onion_address, port=80, timeout=30):
"""Test if hidden service is reachable"""
try:
# Use subprocess to test with torify
result = subprocess.run([
'timeout', str(timeout), 'torify', 'curl', '-s', '-o', '/dev/null',
'-w', '%{http_code}', f'http://{onion_address}/'
], capture_output=True, text=True, timeout=timeout+5)
http_code = result.stdout.strip()
return http_code in ['200', '301', '302', '403']
except Exception as e:
self.logger.warning(f"Connectivity test failed for {onion_address}: {e}")
return False
def get_tor_metrics(self):
"""Collect comprehensive Tor metrics"""
metrics = {
"timestamp": datetime.now().isoformat(),
"tor_service_active": self.check_tor_service(),
"hidden_services": {},
"circuit_info": self.get_circuit_info(),
"bandwidth_usage": self.get_bandwidth_stats(),
"log_analysis": self.analyze_tor_logs()
}
# Check all configured hidden services
hidden_service_dirs = [
"/var/lib/tor/web_service",
"/var/lib/tor/api_service",
"/var/lib/tor/admin_service"
]
for service_dir in hidden_service_dirs:
service_name = Path(service_dir).name
metrics["hidden_services"][service_name] = self.get_hidden_service_status(service_dir)
return metrics
def get_circuit_info(self):
"""Get Tor circuit information via control port"""
try:
# Connect to Tor control port
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('127.0.0.1', self.control_port))
if self.control_password:
auth_cmd = f'AUTHENTICATE "{self.control_password}"\r\n'
sock.send(auth_cmd.encode())
sock.recv(1024)
# Get circuit information
sock.send(b'GETINFO circuit-status\r\n')
response = sock.recv(4096).decode()
sock.close()
circuits = []
for line in response.split('\n'):
if line.startswith('circuit-status='):
circuit_data = line.split('=', 1)[1]
circuits.append(circuit_data)
return {"circuit_count": len(circuits), "circuits": circuits}
except Exception as e:
self.logger.error(f"Failed to get circuit info: {e}")
return {"circuit_count": 0, "error": str(e)}
def get_bandwidth_stats(self):
"""Get bandwidth usage statistics"""
try:
# Read from /proc/net/dev for interface statistics
with open('/proc/net/dev', 'r') as f:
content = f.read()
# Parse for overall network stats (simplified)
lines = content.strip().split('\n')[2:] # Skip headers
total_rx = 0
total_tx = 0
for line in lines:
parts = line.split()
if len(parts) >= 10:
total_rx += int(parts[1]) # RX bytes
total_tx += int(parts[9]) # TX bytes
return {
"total_rx_bytes": total_rx,
"total_tx_bytes": total_tx,
"total_bytes": total_rx + total_tx
}
except Exception as e:
self.logger.error(f"Failed to get bandwidth stats: {e}")
return {"error": str(e)}
def analyze_tor_logs(self):
"""Analyze Tor logs for important events"""
log_analysis = {
"warnings": 0,
"errors": 0,
"circuit_build_failures": 0,
"hidden_service_events": 0
}
try:
# Analyze recent log entries
log_files = ['/var/log/tor/notices.log', '/var/log/tor/warnings.log']
for log_file in log_files:
if Path(log_file).exists():
with open(log_file, 'r') as f:
# Read last 100 lines
lines = f.readlines()[-100:]
for line in lines:
if '[warn]' in line.lower():
log_analysis["warnings"] += 1
elif '[err]' in line.lower():
log_analysis["errors"] += 1
elif 'circuit build' in line.lower() and 'failed' in line.lower():
log_analysis["circuit_build_failures"] += 1
elif 'hidden service' in line.lower():
log_analysis["hidden_service_events"] += 1
except Exception as e:
self.logger.error(f"Failed to analyze logs: {e}")
log_analysis["error"] = str(e)
return log_analysis
def generate_status_report(self):
"""Generate comprehensive status report"""
metrics = self.get_tor_metrics()
# Create readable status report
report = f"""
=== Tor Hidden Service Status Report ===
Generated: {metrics['timestamp']}
Tor Service Status: {'✓ Active' if metrics['tor_service_active'] else '✗ Inactive'}
Hidden Services:
"""
for service_name, service_info in metrics['hidden_services'].items():
status_icon = "✓" if service_info['status'] == 'active' else "✗"
report += f" {status_icon} {service_name}: {service_info['status']}\n"
if service_info.get('onion_address'):
report += f" Address: {service_info['onion_address']}\n"
report += f"""
Circuit Information:
Active Circuits: {metrics['circuit_info'].get('circuit_count', 'Unknown')}
Bandwidth Usage:
Total RX: {metrics['bandwidth_usage'].get('total_rx_bytes', 'Unknown')} bytes
Total TX: {metrics['bandwidth_usage'].get('total_tx_bytes', 'Unknown')} bytes
Log Analysis:
Warnings: {metrics['log_analysis']['warnings']}
Errors: {metrics['log_analysis']['errors']}
Circuit Build Failures: {metrics['log_analysis']['circuit_build_failures']}
Hidden Service Events: {metrics['log_analysis']['hidden_service_events']}
"""
return report
# Automated monitoring script
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
monitor = TorMonitor()
# Generate and display status report
status_report = monitor.generate_status_report()
print(status_report)
# Save metrics to file
metrics = monitor.get_tor_metrics()
with open('/var/log/tor/metrics.json', 'w') as f:
json.dump(metrics, f, indent=2)
Prometheus Integration
#!/usr/bin/env python3
"""
Tor Hidden Service Prometheus Exporter
"""
from prometheus_client import start_http_server, Gauge, Counter, Info
import time
import subprocess
from pathlib import Path
class TorPrometheusExporter:
def __init__(self, port=9150):
self.port = port
# Define metrics
self.tor_service_up = Gauge('tor_service_up', 'Tor service status')
self.hidden_service_up = Gauge('tor_hidden_service_up',
'Hidden service status', ['service_name', 'onion_address'])
self.tor_circuits = Gauge('tor_circuits_total', 'Number of active Tor circuits')
self.tor_bandwidth_rx = Counter('tor_bandwidth_rx_bytes_total', 'Total RX bandwidth')
self.tor_bandwidth_tx = Counter('tor_bandwidth_tx_bytes_total', 'Total TX bandwidth')
self.tor_log_warnings = Counter('tor_log_warnings_total', 'Total log warnings')
self.tor_log_errors = Counter('tor_log_errors_total', 'Total log errors')
self.tor_info = Info('tor_info', 'Tor service information')
def collect_metrics(self):
"""Collect all Tor metrics"""
# Tor service status
tor_active = self.check_tor_service()
self.tor_service_up.set(1 if tor_active else 0)
# Hidden service status
self.collect_hidden_service_metrics()
# Tor info
self.collect_tor_info()
def check_tor_service(self):
"""Check if Tor service is running"""
try:
result = subprocess.run(['systemctl', 'is-active', 'tor'],
capture_output=True, text=True)
return result.stdout.strip() == 'active'
except:
return False
def collect_hidden_service_metrics(self):
"""Collect hidden service metrics"""
services = {
'web': '/var/lib/tor/web_service',
'api': '/var/lib/tor/api_service',
'admin': '/var/lib/tor/admin_service'
}
for service_name, service_dir in services.items():
hostname_file = Path(service_dir) / 'hostname'
if hostname_file.exists():
try:
onion_address = hostname_file.read_text().strip()
# Test connectivity (simplified check)
is_reachable = self.test_service_reachability(onion_address)
self.hidden_service_up.labels(
service_name=service_name,
onion_address=onion_address
).set(1 if is_reachable else 0)
except Exception:
self.hidden_service_up.labels(
service_name=service_name,
onion_address='unknown'
).set(0)
def test_service_reachability(self, onion_address):
"""Test if onion service is reachable"""
try:
# Simple reachability test
result = subprocess.run([
'timeout', '10', 'torify', 'curl', '-s', '-o', '/dev/null',
'-w', '%{http_code}', f'http://{onion_address}/'
], capture_output=True, text=True, timeout=15)
return result.stdout.strip() in ['200', '301', '302', '403']
except:
return False
def collect_tor_info(self):
"""Collect Tor version and configuration info"""
try:
result = subprocess.run(['tor', '--version'], capture_output=True, text=True)
version = result.stdout.split('\n')[0] if result.returncode == 0 else 'unknown'
self.tor_info.info({
'version': version,
'config_file': '/etc/tor/torrc',
'data_directory': '/var/lib/tor'
})
except:
pass
def start_server(self):
"""Start Prometheus metrics server"""
start_http_server(self.port)
print(f"Tor metrics server started on port {self.port}")
while True:
self.collect_metrics()
time.sleep(30)
if __name__ == "__main__":
exporter = TorPrometheusExporter()
exporter.start_server()
Automated Backup and Recovery
Configuration Backup System
#!/bin/bash
# Tor Hidden Service Backup and Recovery System
BACKUP_DIR="/backup/tor-services"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=30
backup_tor_configuration() {
echo "Starting Tor configuration backup..."
# Create backup directory
mkdir -p "$BACKUP_DIR/$DATE"
# Backup Tor configuration
cp -p /etc/tor/torrc "$BACKUP_DIR/$DATE/"
# Backup hidden service directories (keys and hostnames)
if [ -d /var/lib/tor ]; then
tar -czf "$BACKUP_DIR/$DATE/hidden_services.tar.gz" \
-C /var/lib/tor \
--exclude='cached-*' \
--exclude='state' \
--exclude='lock' \
.
fi
# Backup web server configuration
if [ -d /etc/nginx/sites-available ]; then
cp -r /etc/nginx/sites-available "$BACKUP_DIR/$DATE/nginx-sites"
fi
if [ -d /etc/apache2/sites-available ]; then
cp -r /etc/apache2/sites-available "$BACKUP_DIR/$DATE/apache-sites"
fi
# Create backup manifest
cat > "$BACKUP_DIR/$DATE/manifest.txt" << EOF
Tor Hidden Service Backup
Date: $(date)
Hostname: $(hostname)
Tor Version: $(tor --version | head -1)
Backup Contents:
- torrc configuration
- Hidden service keys and hostnames
- Web server configurations
EOF
# Set proper permissions
chmod -R 600 "$BACKUP_DIR/$DATE"
echo "Backup completed: $BACKUP_DIR/$DATE"
}
restore_tor_configuration() {
local backup_date="$1"
local backup_path="$BACKUP_DIR/$backup_date"
if [[ -z "$backup_date" || ! -d "$backup_path" ]]; then
echo "Usage: restore_tor_configuration <backup_date>"
echo "Available backups:"
ls -1 "$BACKUP_DIR" | head -10
return 1
fi
echo "Restoring Tor configuration from $backup_date..."
# Stop services
systemctl stop tor nginx apache2 2>/dev/null
# Restore torrc
if [ -f "$backup_path/torrc" ]; then
cp "$backup_path/torrc" /etc/tor/torrc
echo "Restored torrc configuration"
fi
# Restore hidden service directories
if [ -f "$backup_path/hidden_services.tar.gz" ]; then
cd /var/lib/tor
tar -xzf "$backup_path/hidden_services.tar.gz"
chown -R debian-tor:debian-tor /var/lib/tor
echo "Restored hidden service keys"
fi
# Restore web server configurations
if [ -d "$backup_path/nginx-sites" ]; then
cp -r "$backup_path/nginx-sites"/* /etc/nginx/sites-available/
echo "Restored Nginx configurations"
fi
if [ -d "$backup_path/apache-sites" ]; then
cp -r "$backup_path/apache-sites"/* /etc/apache2/sites-available/
echo "Restored Apache configurations"
fi
# Restart services
systemctl start tor
sleep 5
systemctl start nginx apache2 2>/dev/null
echo "Restoration completed from backup: $backup_date"
}
cleanup_old_backups() {
find "$BACKUP_DIR" -type d -name "20*" -mtime +$RETENTION_DAYS -exec rm -rf {} \;
echo "Cleaned up backups older than $RETENTION_DAYS days"
}
# Main execution
case "${1:-backup}" in
backup)
backup_tor_configuration
cleanup_old_backups
;;
restore)
restore_tor_configuration "$2"
;;
cleanup)
cleanup_old_backups
;;
*)
echo "Usage: $0 {backup|restore <date>|cleanup}"
exit 1
;;
esac
Enterprise Deployment Considerations
High Availability Architecture
Load Balanced Hidden Services
#!/bin/bash
# Multi-server Hidden Service Load Balancing Setup
setup_ha_hidden_service() {
local service_name="$1"
local backend_servers=("$@")
backend_servers=("${backend_servers[@]:1}") # Remove first element (service_name)
echo "Setting up HA Hidden Service: $service_name"
echo "Backend servers: ${backend_servers[*]}"
# Create shared hidden service directory
mkdir -p "/var/lib/tor/ha_$service_name"
# Generate hidden service configuration
cat >> /etc/tor/torrc << EOF
# HA Hidden Service - $service_name
HiddenServiceDir /var/lib/tor/ha_$service_name/
HiddenServicePort 80 127.0.0.1:8080
HiddenServicePort 443 127.0.0.1:8443
HiddenServiceVersion 3
HiddenServiceMaxStreams 100
HiddenServiceMaxStreamsCloseCircuit 1
EOF
# Setup HAProxy for load balancing
cat > "/etc/haproxy/conf.d/$service_name.cfg" << EOF
# HA configuration for $service_name
frontend tor_frontend_$service_name
bind 127.0.0.1:8080
bind 127.0.0.1:8443 ssl crt /etc/ssl/certs/hidden-service.pem
mode http
default_backend tor_backend_$service_name
backend tor_backend_$service_name
mode http
balance roundrobin
option httpchk GET /health
http-check expect status 200
EOF
# Add backend servers
local server_id=1
for server in "${backend_servers[@]}"; do
echo " server backend$server_id $server:80 check" >> "/etc/haproxy/conf.d/$service_name.cfg"
((server_id++))
done
# Restart services
systemctl restart tor haproxy
echo "HA Hidden Service setup completed for $service_name"
}
# Setup monitoring for HA service
setup_ha_monitoring() {
cat > /usr/local/bin/tor-ha-monitor << 'EOF'
#!/bin/bash
# HA Hidden Service Health Monitor
check_backend_health() {
local backend="$1"
local timeout=10
if timeout $timeout curl -s -o /dev/null -w "%{http_code}" "http://$backend/health" | grep -q "200"; then
return 0
else
return 1
fi
}
monitor_ha_service() {
local service_name="$1"
local config_file="/etc/haproxy/conf.d/$service_name.cfg"
if [[ ! -f "$config_file" ]]; then
echo "Configuration file not found: $config_file"
return 1
fi
# Extract backend servers from HAProxy config
local backends=$(grep "server backend" "$config_file" | awk '{print $3}' | cut -d: -f1)
echo "Monitoring HA service: $service_name"
for backend in $backends; do
if check_backend_health "$backend"; then
echo "✓ Backend $backend is healthy"
else
echo "✗ Backend $backend is unhealthy"
# Could trigger alerts here
fi
done
}
# Monitor all configured services
for config in /etc/haproxy/conf.d/*.cfg; do
if [[ -f "$config" ]]; then
service_name=$(basename "$config" .cfg)
monitor_ha_service "$service_name"
fi
done
EOF
chmod +x /usr/local/bin/tor-ha-monitor
# Create systemd timer for monitoring
cat > /etc/systemd/system/tor-ha-monitor.service << 'EOF'
[Unit]
Description=Tor HA Service Monitor
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/tor-ha-monitor
User=root
EOF
cat > /etc/systemd/system/tor-ha-monitor.timer << 'EOF'
[Unit]
Description=Run Tor HA Monitor every 5 minutes
Requires=tor-ha-monitor.service
[Timer]
OnCalendar=*:0/5
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable tor-ha-monitor.timer
systemctl start tor-ha-monitor.timer
}
# Usage example
# setup_ha_hidden_service "webapp" "192.168.1.10" "192.168.1.11" "192.168.1.12"
# setup_ha_monitoring
Container Deployment
Docker Configuration
# Dockerfile for Tor Hidden Service
FROM debian:bullseye-slim
# Install dependencies
RUN apt-get update && apt-get install -y \
tor \
nginx \
curl \
gpg \
apt-transport-https \
&& rm -rf /var/lib/apt/lists/*
# Create tor user and directories
RUN useradd --system --home /var/lib/tor --shell /bin/false debian-tor \
&& mkdir -p /var/lib/tor /var/log/tor \
&& chown -R debian-tor:debian-tor /var/lib/tor /var/log/tor \
&& chmod 700 /var/lib/tor
# Copy configurations
COPY torrc /etc/tor/torrc
COPY nginx.conf /etc/nginx/sites-available/default
COPY entrypoint.sh /entrypoint.sh
# Set permissions
RUN chmod +x /entrypoint.sh \
&& chmod 644 /etc/tor/torrc
# Expose ports (internal only)
EXPOSE 8080 8443
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# Run as non-root user
USER debian-tor
ENTRYPOINT ["/entrypoint.sh"]
#!/bin/bash
# entrypoint.sh for Tor Hidden Service container
set -e
# Initialize hidden service directories if they don't exist
if [ ! -f /var/lib/tor/hidden_service/hostname ]; then
echo "Initializing hidden service..."
tor --DataDirectory /var/lib/tor --RunAsDaemon 0 --quiet &
TOR_PID=$!
# Wait for hidden service to be created
while [ ! -f /var/lib/tor/hidden_service/hostname ]; do
sleep 1
done
echo "Hidden service initialized: $(cat /var/lib/tor/hidden_service/hostname)"
kill $TOR_PID
wait $TOR_PID 2>/dev/null || true
fi
# Start nginx in background
nginx -g "daemon off;" &
NGINX_PID=$!
# Start tor in foreground
exec tor --DataDirectory /var/lib/tor --RunAsDaemon 0
Kubernetes Deployment
# tor-hidden-service-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: tor-hidden-service
labels:
app: tor-hidden-service
spec:
replicas: 2
selector:
matchLabels:
app: tor-hidden-service
template:
metadata:
labels:
app: tor-hidden-service
spec:
securityContext:
runAsNonRoot: true
runAsUser: 999
fsGroup: 999
containers:
- name: tor-service
image: tor-hidden-service:latest
ports:
- containerPort: 8080
name: http
- containerPort: 8443
name: https
env:
- name: TOR_HIDDEN_SERVICE_DIR
value: "/var/lib/tor/hidden_service"
volumeMounts:
- name: tor-data
mountPath: /var/lib/tor
- name: tor-config
mountPath: /etc/tor/torrc
subPath: torrc
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
volumes:
- name: tor-data
persistentVolumeClaim:
claimName: tor-data-pvc
- name: tor-config
configMap:
name: tor-config
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: tor-data-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: ConfigMap
metadata:
name: tor-config
data:
torrc: |
User debian-tor
DataDirectory /var/lib/tor
Log notice file /var/log/tor/notices.log
HiddenServiceDir /var/lib/tor/hidden_service/
HiddenServicePort 80 127.0.0.1:8080
HiddenServicePort 443 127.0.0.1:8443
HiddenServiceVersion 3
DisableDebuggerAttachment 1
SafeLogging 1
---
apiVersion: v1
kind: Service
metadata:
name: tor-hidden-service
spec:
selector:
app: tor-hidden-service
ports:
- name: http
port: 80
targetPort: 8080
- name: https
port: 443
targetPort: 8443
type: ClusterIP
This comprehensive guide provides enterprise-grade Tor Hidden Service deployment capabilities, ensuring robust security, monitoring, and operational excellence for production environments. The combination of security hardening, automated monitoring, and high availability configurations enables reliable anonymous web services across diverse infrastructure requirements.