9a800e3b6b
DNS A records now return the WireGuard server IP (10.0.0.1) instead of Docker bridge VIPs so cross-cell peers resolve service names correctly regardless of their bridge subnet. DNAT rules (wg0:53→cell-dns:53 and wg0:80→cell-caddy:80) are applied at startup. Caddy routes by Host header, eliminating the Docker bridge subnet conflict. Firewall cell rules allow DNS and service (Caddy) traffic from linked cell subnets. Split-tunnel AllowedIPs now dynamically includes connected-cell VPN subnets and drops the 172.20.0.0/16 range. Peers with route_via set now receive full-tunnel config (0.0.0.0/0) so all their traffic exits via the remote cell. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
118 lines
4.4 KiB
Python
118 lines
4.4 KiB
Python
import logging
|
|
from flask import Blueprint, jsonify, session
|
|
logger = logging.getLogger('picell')
|
|
bp = Blueprint('peer_dashboard', __name__)
|
|
|
|
@bp.route('/api/peer/dashboard', methods=['GET'])
|
|
def peer_dashboard():
|
|
try:
|
|
from app import peer_registry, wireguard_manager, _configured_domain
|
|
peer_name = session.get('peer_name')
|
|
peer = peer_registry.get_peer(peer_name) if peer_name else None
|
|
if not peer:
|
|
return jsonify({'error': 'Peer not found'}), 404
|
|
|
|
wg_stats = {'online': None, 'transfer_rx': 0, 'transfer_tx': 0, 'last_handshake': None}
|
|
public_key = peer.get('public_key')
|
|
if public_key:
|
|
try:
|
|
wg_stats = wireguard_manager.get_peer_status(public_key)
|
|
except Exception:
|
|
pass
|
|
|
|
peer_ip = peer.get('ip', '')
|
|
allowed_ips = f"{peer_ip.split('/')[0]}/32" if peer_ip else ''
|
|
domain = _configured_domain()
|
|
_svc_url_map = {
|
|
'calendar': f'http://calendar.{domain}',
|
|
'files': f'http://files.{domain}',
|
|
'mail': f'http://mail.{domain}',
|
|
'webdav': f'http://webdav.{domain}',
|
|
}
|
|
service_urls = {
|
|
svc: _svc_url_map[svc]
|
|
for svc in peer.get('service_access', [])
|
|
if svc in _svc_url_map
|
|
}
|
|
return jsonify({
|
|
'name': peer_name,
|
|
'ip': peer_ip,
|
|
'service_access': peer.get('service_access', []),
|
|
'service_urls': service_urls,
|
|
'online': wg_stats.get('online'),
|
|
'transfer_rx': wg_stats.get('transfer_rx', 0),
|
|
'transfer_tx': wg_stats.get('transfer_tx', 0),
|
|
'last_handshake': wg_stats.get('last_handshake'),
|
|
'allowed_ips': peer.get('allowed_ips', allowed_ips),
|
|
})
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@bp.route('/api/peer/services', methods=['GET'])
|
|
def peer_services():
|
|
try:
|
|
from app import peer_registry, wireguard_manager, config_manager, _configured_domain, _resolve_peer_dns
|
|
peer_name = session.get('peer_name')
|
|
peer = peer_registry.get_peer(peer_name) if peer_name else None
|
|
if not peer:
|
|
return jsonify({'error': 'Peer not found'}), 404
|
|
|
|
domain = _configured_domain()
|
|
peer_ip = peer.get('ip', '')
|
|
|
|
server_public_key = ''
|
|
wg_port = 51820
|
|
server_endpoint = ''
|
|
try:
|
|
server_public_key = wireguard_manager.get_keys().get('public_key', '')
|
|
wg_port = config_manager.configs.get('_identity', {}).get('wireguard_port', 51820)
|
|
srv = wireguard_manager.get_server_config()
|
|
server_endpoint = srv.get('endpoint') or '<SERVER_IP>'
|
|
except Exception:
|
|
pass
|
|
|
|
wg_config = ''
|
|
peer_private_key = peer.get('private_key', '')
|
|
if peer_private_key:
|
|
try:
|
|
internet_access = peer.get('internet_access', True)
|
|
route_via = peer.get('route_via')
|
|
use_full = internet_access or bool(route_via)
|
|
allowed_ips = wireguard_manager.FULL_TUNNEL_IPS if use_full else wireguard_manager.get_split_tunnel_ips()
|
|
wg_config = wireguard_manager.get_peer_config(
|
|
peer_name=peer_name,
|
|
peer_ip=peer_ip,
|
|
peer_private_key=peer_private_key,
|
|
server_endpoint=server_endpoint,
|
|
allowed_ips=allowed_ips,
|
|
)
|
|
except Exception:
|
|
pass
|
|
|
|
return jsonify({
|
|
'username': peer_name,
|
|
'wireguard': {
|
|
'ip': peer_ip,
|
|
'server_public_key': server_public_key,
|
|
'endpoint_port': wg_port,
|
|
'dns': _resolve_peer_dns(),
|
|
'config': wg_config,
|
|
},
|
|
'email': {
|
|
'address': f'{peer_name}@{domain}',
|
|
'smtp': {'host': f'mail.{domain}', 'port': 587},
|
|
'imap': {'host': f'mail.{domain}', 'port': 993},
|
|
},
|
|
'caldav': {
|
|
'url': f'http://calendar.{domain}',
|
|
'username': peer_name,
|
|
},
|
|
'files': {
|
|
'url': f'http://files.{domain}',
|
|
'username': peer_name,
|
|
},
|
|
})
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|