wip: wireguard
This commit is contained in:
+43
-1
@@ -832,6 +832,44 @@ def update_peer_ip():
|
||||
logger.error(f"Error updating peer IP: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route('/api/wireguard/peers/status', methods=['POST'])
|
||||
def get_peer_status():
|
||||
"""Get WireGuard peer status."""
|
||||
try:
|
||||
data = request.get_json(silent=True)
|
||||
if data is None or 'public_key' not in data:
|
||||
return jsonify({"error": "Missing public key"}), 400
|
||||
|
||||
public_key = data['public_key']
|
||||
status = wireguard_manager.get_peer_status(public_key)
|
||||
return jsonify(status)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting peer status: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route('/api/wireguard/network/setup', methods=['POST'])
|
||||
def setup_network():
|
||||
"""Setup network configuration for internet access."""
|
||||
try:
|
||||
success = wireguard_manager.setup_network_configuration()
|
||||
if success:
|
||||
return jsonify({"message": "Network configuration setup completed successfully"})
|
||||
else:
|
||||
return jsonify({"error": "Failed to setup network configuration"}), 500
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting up network configuration: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route('/api/wireguard/network/status', methods=['GET'])
|
||||
def get_network_status():
|
||||
"""Get network configuration status."""
|
||||
try:
|
||||
status = wireguard_manager.get_network_status()
|
||||
return jsonify(status)
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting network status: {e}")
|
||||
return jsonify({"error": str(e)}), 500
|
||||
|
||||
@app.route('/api/wireguard/peers/config', methods=['POST'])
|
||||
def get_peer_config():
|
||||
try:
|
||||
@@ -849,10 +887,14 @@ def get_peer_config():
|
||||
# Get server configuration
|
||||
server_config = wireguard_manager.get_server_config()
|
||||
|
||||
# Check if IP already has a subnet mask, if not add /32
|
||||
peer_ip = peer.get('ip', '10.0.0.2')
|
||||
peer_address = peer_ip if '/' in peer_ip else f"{peer_ip}/32"
|
||||
|
||||
# Generate client configuration using peer registry data
|
||||
config = f"""[Interface]
|
||||
PrivateKey = {peer.get('private_key', 'YOUR_PRIVATE_KEY_HERE')}
|
||||
Address = {peer.get('ip', '10.0.0.2')}/32
|
||||
Address = {peer_address}
|
||||
DNS = 8.8.8.8, 1.1.1.1
|
||||
|
||||
[Peer]
|
||||
|
||||
+239
-6
@@ -331,13 +331,62 @@ AllowedIPs = {allowed_ips}
|
||||
raise
|
||||
|
||||
def _reload_wireguard_config(self):
|
||||
"""Reload WireGuard configuration"""
|
||||
"""Reload WireGuard configuration by updating the main config file"""
|
||||
try:
|
||||
# This would typically involve restarting the WireGuard service
|
||||
# or reloading the configuration
|
||||
logger.info("WireGuard configuration reloaded")
|
||||
# Read the main server configuration
|
||||
server_config_path = os.path.join(self.wg_config_dir, 'wg_confs', 'wg0.conf')
|
||||
if not os.path.exists(server_config_path):
|
||||
logger.error("Server configuration file not found")
|
||||
return False
|
||||
|
||||
with open(server_config_path, 'r') as f:
|
||||
server_content = f.read()
|
||||
|
||||
# Find the end of the [Interface] section
|
||||
lines = server_content.split('\n')
|
||||
interface_end = 0
|
||||
for i, line in enumerate(lines):
|
||||
if line.strip().startswith('[Peer]'):
|
||||
interface_end = i
|
||||
break
|
||||
else:
|
||||
interface_end = len(lines)
|
||||
|
||||
# Keep only the [Interface] section
|
||||
interface_lines = lines[:interface_end]
|
||||
|
||||
# Add all peer configurations
|
||||
peer_lines = []
|
||||
for filename in os.listdir(self.peers_dir):
|
||||
if filename.endswith('.conf') and not filename.endswith('_keys.json'):
|
||||
peer_file = os.path.join(self.peers_dir, filename)
|
||||
with open(peer_file, 'r') as f:
|
||||
peer_content = f.read().strip()
|
||||
if peer_content:
|
||||
peer_lines.append('') # Empty line before peer
|
||||
peer_lines.extend(peer_content.split('\n'))
|
||||
|
||||
# Combine interface and peer configurations
|
||||
new_content = '\n'.join(interface_lines + peer_lines)
|
||||
|
||||
# Write the updated configuration
|
||||
with open(server_config_path, 'w') as f:
|
||||
f.write(new_content)
|
||||
|
||||
# Restart WireGuard container to apply changes
|
||||
import subprocess
|
||||
result = subprocess.run(['docker', 'restart', 'cell-wireguard'],
|
||||
capture_output=True, text=True, timeout=30)
|
||||
if result.returncode == 0:
|
||||
logger.info("WireGuard configuration reloaded and container restarted")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"Failed to restart WireGuard container: {result.stderr}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to reload WireGuard configuration: {e}")
|
||||
return False
|
||||
|
||||
def get_metrics(self) -> Dict[str, Any]:
|
||||
"""Get WireGuard metrics"""
|
||||
@@ -528,9 +577,13 @@ AllowedIPs = {allowed_ips}
|
||||
# Get peer private key from peer data
|
||||
peer_private_key = peer_info.get('private_key', 'YOUR_PRIVATE_KEY_HERE')
|
||||
|
||||
# Check if IP already has a subnet mask, if not add /32
|
||||
peer_ip = peer_info.get('ip', '10.0.0.2')
|
||||
peer_address = peer_ip if '/' in peer_ip else f"{peer_ip}/32"
|
||||
|
||||
config = f"""[Interface]
|
||||
PrivateKey = {peer_private_key}
|
||||
Address = {peer_info.get('ip', '10.0.0.2')}/32
|
||||
Address = {peer_address}
|
||||
DNS = 8.8.8.8, 1.1.1.1
|
||||
|
||||
[Peer]
|
||||
@@ -660,4 +713,184 @@ PersistentKeepalive = {peer_info.get('persistent_keepalive', 25)}"""
|
||||
return {
|
||||
'public_key': 'SERVER_PUBLIC_KEY_PLACEHOLDER',
|
||||
'endpoint': 'YOUR_SERVER_IP:51820'
|
||||
}
|
||||
}
|
||||
|
||||
def get_peer_status(self, public_key: str) -> Dict[str, Any]:
|
||||
"""Get status for a specific peer"""
|
||||
try:
|
||||
# Get WireGuard interface status
|
||||
result = subprocess.run(['wg', 'show'], capture_output=True, text=True, check=True)
|
||||
wg_output = result.stdout
|
||||
|
||||
# Parse the output to find the specific peer
|
||||
lines = wg_output.strip().split('\n')
|
||||
peer_info = {}
|
||||
in_peer = False
|
||||
|
||||
for line in lines:
|
||||
if line.startswith('peer:') and public_key in line:
|
||||
in_peer = True
|
||||
peer_info['public_key'] = public_key
|
||||
elif line.startswith('peer:') and public_key not in line:
|
||||
in_peer = False
|
||||
elif in_peer and line.startswith(' allowed ips:'):
|
||||
peer_info['allowed_ips'] = line.split(':', 1)[1].strip()
|
||||
elif in_peer and line.startswith(' latest handshake:'):
|
||||
handshake_str = line.split(':', 1)[1].strip()
|
||||
if handshake_str and handshake_str != '(none)':
|
||||
peer_info['latest_handshake'] = handshake_str
|
||||
peer_info['online'] = True
|
||||
else:
|
||||
peer_info['online'] = False
|
||||
elif in_peer and line.startswith(' transfer:'):
|
||||
transfer_str = line.split(':', 1)[1].strip()
|
||||
if transfer_str and transfer_str != '(none)':
|
||||
# Parse transfer data (e.g., "1.2 KiB received, 3.4 KiB sent")
|
||||
parts = transfer_str.split(',')
|
||||
if len(parts) >= 2:
|
||||
rx_part = parts[0].strip()
|
||||
tx_part = parts[1].strip()
|
||||
|
||||
# Extract numbers from strings like "1.2 KiB received"
|
||||
import re
|
||||
rx_match = re.search(r'([\d.]+)\s+(\w+)', rx_part)
|
||||
tx_match = re.search(r'([\d.]+)\s+(\w+)', tx_part)
|
||||
|
||||
if rx_match and tx_match:
|
||||
rx_value = float(rx_match.group(1))
|
||||
rx_unit = rx_match.group(2)
|
||||
tx_value = float(tx_match.group(1))
|
||||
tx_unit = tx_match.group(2)
|
||||
|
||||
# Convert to bytes
|
||||
def convert_to_bytes(value, unit):
|
||||
multipliers = {'B': 1, 'KiB': 1024, 'MiB': 1024**2, 'GiB': 1024**3}
|
||||
return int(value * multipliers.get(unit, 1))
|
||||
|
||||
peer_info['transfer_rx'] = convert_to_bytes(rx_value, rx_unit)
|
||||
peer_info['transfer_tx'] = convert_to_bytes(tx_value, tx_unit)
|
||||
|
||||
# Set default values if not found
|
||||
if 'online' not in peer_info:
|
||||
peer_info['online'] = False
|
||||
if 'transfer_rx' not in peer_info:
|
||||
peer_info['transfer_rx'] = 0
|
||||
if 'transfer_tx' not in peer_info:
|
||||
peer_info['transfer_tx'] = 0
|
||||
if 'latest_handshake' not in peer_info:
|
||||
peer_info['latest_handshake'] = None
|
||||
|
||||
return peer_info
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get peer status for {public_key}: {e}")
|
||||
return {'online': False, 'transfer_rx': 0, 'transfer_tx': 0, 'latest_handshake': None}
|
||||
|
||||
def setup_network_configuration(self) -> bool:
|
||||
"""Setup network configuration for internet access"""
|
||||
try:
|
||||
logger.info("Setting up network configuration for internet access...")
|
||||
|
||||
# Enable IP forwarding
|
||||
self._enable_ip_forwarding()
|
||||
|
||||
# Configure NAT and routing
|
||||
self._configure_nat_routing()
|
||||
|
||||
logger.info("Network configuration completed successfully")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to setup network configuration: {e}")
|
||||
return False
|
||||
|
||||
def _enable_ip_forwarding(self):
|
||||
"""Enable IP forwarding"""
|
||||
try:
|
||||
# Enable IP forwarding in the container
|
||||
subprocess.run(['sh', '-c', 'echo 1 > /proc/sys/net/ipv4/ip_forward'], check=True)
|
||||
logger.info("IP forwarding enabled")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to enable IP forwarding: {e}")
|
||||
raise
|
||||
|
||||
def _configure_nat_routing(self):
|
||||
"""Configure NAT and routing for internet access"""
|
||||
try:
|
||||
# Get the main network interface
|
||||
result = subprocess.run(['ip', 'route', 'show', 'default'], capture_output=True, text=True, check=True)
|
||||
main_interface = result.stdout.split()[4] # Extract interface name
|
||||
|
||||
# Configure iptables rules
|
||||
rules = [
|
||||
# Allow forwarding for WireGuard interface
|
||||
f"iptables -A FORWARD -i wg0 -j ACCEPT",
|
||||
f"iptables -A FORWARD -o wg0 -j ACCEPT",
|
||||
|
||||
# NAT rule for internet access
|
||||
f"iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o {main_interface} -j MASQUERADE",
|
||||
|
||||
# Allow established and related connections
|
||||
"iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT"
|
||||
]
|
||||
|
||||
for rule in rules:
|
||||
try:
|
||||
subprocess.run(['sh', '-c', rule], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.warning(f"Rule may already exist: {rule} - {e}")
|
||||
|
||||
logger.info(f"NAT and routing configured for interface {main_interface}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to configure NAT routing: {e}")
|
||||
raise
|
||||
|
||||
def get_network_status(self) -> Dict[str, Any]:
|
||||
"""Get network configuration status"""
|
||||
try:
|
||||
status = {
|
||||
'ip_forwarding': self._check_ip_forwarding(),
|
||||
'nat_rules': self._check_nat_rules(),
|
||||
'forwarding_rules': self._check_forwarding_rules(),
|
||||
'interface_status': self._check_interface_status(),
|
||||
'timestamp': datetime.utcnow().isoformat()
|
||||
}
|
||||
return status
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get network status: {e}")
|
||||
return {'error': str(e)}
|
||||
|
||||
def _check_ip_forwarding(self) -> bool:
|
||||
"""Check if IP forwarding is enabled"""
|
||||
try:
|
||||
# Check in WireGuard container
|
||||
result = subprocess.run(['docker', 'exec', 'cell-wireguard', 'cat', '/proc/sys/net/ipv4/ip_forward'], capture_output=True, text=True, check=True)
|
||||
return result.stdout.strip() == '1'
|
||||
except:
|
||||
return False
|
||||
|
||||
def _check_nat_rules(self) -> bool:
|
||||
"""Check if NAT rules are configured"""
|
||||
try:
|
||||
# Check in WireGuard container
|
||||
result = subprocess.run(['docker', 'exec', 'cell-wireguard', 'iptables', '-t', 'nat', '-L', 'POSTROUTING', '-n'], capture_output=True, text=True, check=True)
|
||||
return 'MASQUERADE' in result.stdout
|
||||
except:
|
||||
return False
|
||||
|
||||
def _check_forwarding_rules(self) -> bool:
|
||||
"""Check if forwarding rules are configured"""
|
||||
try:
|
||||
# Check in WireGuard container
|
||||
result = subprocess.run(['docker', 'exec', 'cell-wireguard', 'iptables', '-L', 'FORWARD', '-n'], capture_output=True, text=True, check=True)
|
||||
# Check for ACCEPT rules (which indicate forwarding is allowed)
|
||||
return 'ACCEPT' in result.stdout and len(result.stdout.strip().split('\n')) > 2
|
||||
except:
|
||||
return False
|
||||
|
||||
def _check_interface_status(self) -> bool:
|
||||
"""Check if WireGuard interface is up"""
|
||||
try:
|
||||
# Check in WireGuard container
|
||||
result = subprocess.run(['docker', 'exec', 'cell-wireguard', 'ip', 'link', 'show', 'wg0'], capture_output=True, text=True, check=True)
|
||||
return 'UP' in result.stdout
|
||||
except:
|
||||
return False
|
||||
Reference in New Issue
Block a user