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:
+55
-4
@@ -890,8 +890,8 @@ def get_peer_config():
|
|||||||
# Look up peer details from registry if not supplied
|
# Look up peer details from registry if not supplied
|
||||||
peer_ip = data.get('ip', '')
|
peer_ip = data.get('ip', '')
|
||||||
peer_private_key = data.get('private_key', '')
|
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):
|
if peer_name and (not peer_ip or not peer_private_key):
|
||||||
registered = peer_registry.get_peer(peer_name)
|
|
||||||
if registered:
|
if registered:
|
||||||
peer_ip = peer_ip or registered.get('ip', '')
|
peer_ip = peer_ip or registered.get('ip', '')
|
||||||
peer_private_key = peer_private_key or registered.get('private_key', '')
|
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()
|
srv = wireguard_manager.get_server_config()
|
||||||
server_endpoint = srv.get('endpoint') or '<SERVER_IP>'
|
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
|
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(
|
result = wireguard_manager.get_peer_config(
|
||||||
peer_name=peer_name,
|
peer_name=peer_name,
|
||||||
peer_ip=peer_ip,
|
peer_ip=peer_ip,
|
||||||
@@ -980,19 +985,65 @@ def add_peer():
|
|||||||
'server_endpoint': data.get('server_endpoint'),
|
'server_endpoint': data.get('server_endpoint'),
|
||||||
'allowed_ips': data.get('allowed_ips'),
|
'allowed_ips': data.get('allowed_ips'),
|
||||||
'persistent_keepalive': data.get('persistent_keepalive'),
|
'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)
|
success = peer_registry.add_peer(peer_info)
|
||||||
if success:
|
if success:
|
||||||
return jsonify({"message": f"Peer {data['name']} added successfully"}), 201
|
return jsonify({"message": f"Peer {data['name']} added successfully"}), 201
|
||||||
else:
|
else:
|
||||||
return jsonify({"error": f"Peer {data['name']} already exists"}), 400
|
return jsonify({"error": f"Peer {data['name']} already exists"}), 400
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error adding peer: {e}")
|
logger.error(f"Error adding peer: {e}")
|
||||||
return jsonify({"error": str(e)}), 500
|
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'])
|
@app.route('/api/peers/<peer_name>', methods=['DELETE'])
|
||||||
def remove_peer(peer_name):
|
def remove_peer(peer_name):
|
||||||
"""Remove a peer."""
|
"""Remove a peer."""
|
||||||
|
|||||||
@@ -266,6 +266,27 @@ class PeerRegistry(BaseServiceManager):
|
|||||||
self.logger.error(f"Error removing peer {name}: {e}")
|
self.logger.error(f"Error removing peer {name}: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def update_peer(self, name: str, fields: Dict[str, Any]) -> bool:
|
||||||
|
"""Update arbitrary fields on a peer."""
|
||||||
|
try:
|
||||||
|
with self.lock:
|
||||||
|
for peer in self.peers:
|
||||||
|
if peer.get('peer') == name:
|
||||||
|
peer.update(fields)
|
||||||
|
peer['updated_at'] = datetime.utcnow().isoformat()
|
||||||
|
self._save_peers()
|
||||||
|
self.logger.info(f"Updated peer {name}: {list(fields.keys())}")
|
||||||
|
return True
|
||||||
|
self.logger.warning(f"Peer {name} not found for update")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Error updating peer {name}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def clear_reinstall_flag(self, name: str) -> bool:
|
||||||
|
"""Clear the config_needs_reinstall flag after user downloads new config."""
|
||||||
|
return self.update_peer(name, {'config_needs_reinstall': False})
|
||||||
|
|
||||||
def update_peer_ip(self, name: str, new_ip: str) -> bool:
|
def update_peer_ip(self, name: str, new_ip: str) -> bool:
|
||||||
"""Update peer IP address"""
|
"""Update peer IP address"""
|
||||||
try:
|
try:
|
||||||
|
|||||||
+723
-1024
File diff suppressed because it is too large
Load Diff
@@ -181,7 +181,7 @@ function WireGuard() {
|
|||||||
return { public_key: '', endpoint: '<SERVER_IP>:51820' };
|
return { public_key: '', endpoint: '<SERVER_IP>:51820' };
|
||||||
};
|
};
|
||||||
|
|
||||||
const CELL_DNS = '172.20.0.2';
|
const CELL_DNS = '172.20.0.3';
|
||||||
const SPLIT_TUNNEL_IPS = '10.0.0.0/24, 172.20.0.0/16';
|
const SPLIT_TUNNEL_IPS = '10.0.0.0/24, 172.20.0.0/16';
|
||||||
const FULL_TUNNEL_IPS = '0.0.0.0/0, ::/0';
|
const FULL_TUNNEL_IPS = '0.0.0.0/0, ::/0';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user