feat: peer access config, DNS fix, real routing table, reinstall notifications

Peer creation/edit form now configures:
- Tunnel mode: full (0.0.0.0/0) or split (PIC only)
- Per-service access toggles (calendar, files, mail, webdav)
- Peer-to-peer communication toggle
- Optional calendar account creation
- Access capability badges in peer list

Bug fixes:
- DNS in client configs was 8.8.8.8 / 172.20.0.2 — now 172.20.0.3 (CoreDNS)
  This was why .cell domains didn't resolve on connected VPN peers
- get_peer_config API uses stored internet_access to set AllowedIPs
- New PUT /api/peers/<name> endpoint with config_changed detection
- POST /api/peers/<name>/clear-reinstall clears reinstall flag after download
- Routing page reads real host routes via /proc/1/net/route (pid: host)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-20 15:40:19 -04:00
parent 9d7d74f3f4
commit 8e41568964
4 changed files with 800 additions and 1029 deletions
+55 -4
View File
@@ -890,8 +890,8 @@ def get_peer_config():
# Look up peer details from registry if not supplied
peer_ip = data.get('ip', '')
peer_private_key = data.get('private_key', '')
registered = peer_registry.get_peer(peer_name) if peer_name else {}
if peer_name and (not peer_ip or not peer_private_key):
registered = peer_registry.get_peer(peer_name)
if registered:
peer_ip = peer_ip or registered.get('ip', '')
peer_private_key = peer_private_key or registered.get('private_key', '')
@@ -902,7 +902,12 @@ def get_peer_config():
srv = wireguard_manager.get_server_config()
server_endpoint = srv.get('endpoint') or '<SERVER_IP>'
# Determine AllowedIPs: explicit > peer's stored internet_access > default full tunnel
allowed_ips = data.get('allowed_ips') or None
if not allowed_ips and registered:
internet_access = registered.get('internet_access', True)
allowed_ips = wireguard_manager.FULL_TUNNEL_IPS if internet_access else wireguard_manager.SPLIT_TUNNEL_IPS
result = wireguard_manager.get_peer_config(
peer_name=peer_name,
peer_ip=peer_ip,
@@ -980,19 +985,65 @@ def add_peer():
'server_endpoint': data.get('server_endpoint'),
'allowed_ips': data.get('allowed_ips'),
'persistent_keepalive': data.get('persistent_keepalive'),
'description': data.get('description')
'description': data.get('description'),
'internet_access': data.get('internet_access', True),
'service_access': data.get('service_access', ['calendar', 'files', 'mail', 'webdav']),
'peer_access': data.get('peer_access', True),
'config_needs_reinstall': False,
}
success = peer_registry.add_peer(peer_info)
if success:
return jsonify({"message": f"Peer {data['name']} added successfully"}), 201
else:
return jsonify({"error": f"Peer {data['name']} already exists"}), 400
except Exception as e:
logger.error(f"Error adding peer: {e}")
return jsonify({"error": str(e)}), 500
@app.route('/api/peers/<peer_name>', methods=['PUT'])
def update_peer(peer_name):
"""Update peer settings. Marks config_needs_reinstall if VPN config changed."""
try:
data = request.get_json(silent=True) or {}
existing = peer_registry.get_peer(peer_name)
if not existing:
return jsonify({"error": "Peer not found"}), 404
# Detect changes that require client to reinstall tunnel config
config_changed = (
('internet_access' in data and data['internet_access'] != existing.get('internet_access', True)) or
('ip' in data and data['ip'] != existing.get('ip')) or
('persistent_keepalive' in data and data['persistent_keepalive'] != existing.get('persistent_keepalive'))
)
updates = {k: v for k, v in data.items()}
if config_changed:
updates['config_needs_reinstall'] = True
success = peer_registry.update_peer(peer_name, updates)
if success:
result = {"message": f"Peer {peer_name} updated", "config_changed": config_changed}
return jsonify(result)
else:
return jsonify({"error": "Update failed"}), 500
except Exception as e:
logger.error(f"Error updating peer {peer_name}: {e}")
return jsonify({"error": str(e)}), 500
@app.route('/api/peers/<peer_name>/clear-reinstall', methods=['POST'])
def clear_peer_reinstall(peer_name):
"""Clear the config_needs_reinstall flag once user has downloaded new config."""
try:
peer_registry.clear_reinstall_flag(peer_name)
return jsonify({"message": "Reinstall flag cleared"})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/peers/<peer_name>', methods=['DELETE'])
def remove_peer(peer_name):
"""Remove a peer."""