fix: peer access to /api/services/active and unconditional Caddy startup regen
Unit Tests / test (push) Successful in 7m23s

- Add _PEER_READABLE_PATHS allowlist in enforce_auth so peer-role sessions
  can read /api/services/active; fixes My Services showing 'not installed'
  for cell members when services are installed
- Move Caddy regeneration before the early-return in reapply_on_startup so
  the Caddyfile is always rebuilt from current identity on startup, even when
  no store services are installed; fixes ERR_SSL_PROTOCOL_ERROR after a cell
  rename (Caddyfile retained old wildcard domain)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 15:58:27 -04:00
parent 9bdda6aaf8
commit 1607a2e86f
2 changed files with 22 additions and 11 deletions
+9
View File
@@ -187,6 +187,13 @@ def enforce_setup():
return jsonify({'error': 'Setup required', 'redirect': '/setup'}), 428
# Read-only endpoints accessible to peer-role sessions (not just admin).
# Add paths here when peers need to read shared cell state.
_PEER_READABLE_PATHS = frozenset({
'/api/services/active',
})
@app.before_request
def enforce_auth():
"""Enforce session-based authentication and role-based access control.
@@ -238,6 +245,8 @@ def enforce_auth():
if path.startswith('/api/peer/'):
if role != 'peer':
return jsonify({'error': 'Forbidden'}), 403
elif path in _PEER_READABLE_PATHS:
pass # both admin and peer may read these endpoints
else:
if role != 'admin':
return jsonify({'error': 'Forbidden'}), 403
+13 -11
View File
@@ -407,6 +407,19 @@ class ServiceStoreManager(BaseServiceManager):
from firewall_manager import apply_service_rules
installed = self.config_manager.get_installed_services()
# Always regenerate the Caddyfile so a cell rename or fresh install
# produces the correct domain even when no store services are installed.
try:
caddy_routes = [
r.get('caddy_route')
for r in (installed or {}).values()
if r.get('caddy_route')
]
self.caddy_manager.regenerate_with_installed(caddy_routes)
except Exception as e:
logger.warning(f'reapply_on_startup: caddy regenerate failed: {e}')
if not installed:
return
@@ -419,17 +432,6 @@ class ServiceStoreManager(BaseServiceManager):
except Exception as e:
logger.warning(f'reapply_on_startup: apply_service_rules({svc_id}) failed: {e}')
# Regenerate Caddyfile
try:
caddy_routes = [
r.get('caddy_route')
for r in installed.values()
if r.get('caddy_route')
]
self.caddy_manager.regenerate_with_installed(caddy_routes)
except Exception as e:
logger.warning(f'reapply_on_startup: caddy regenerate failed: {e}')
# Bring up per-service compose stacks
if self.service_composer is not None:
try: