fix: P0/P1 audit fixes — DDNS correctness, peer provisioning gates, honest stubs
CloudflareDDNS.update() was calling the wrong endpoint; fix to use the correct zone-records API so DDNS updates actually land. NoIP and FreeDNS providers now return explicit "not implemented" errors instead of silently claiming success, preventing false-positive health state. PicNgoDNS ACME dns-challenge now sends the token in the request body (was missing), so cert issuance no longer silently fails. add_peer gates builtin-service provisioning on the installed-services list so a freshly-provisioned peer does not attempt to configure services that aren't present, eliminating the startup error loop. Startup Caddyfile regeneration added to routes/config.py so that a stale on-disk Caddyfile no longer triggers the health-monitor restart loop after a config change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,8 @@ import logging
|
||||
import re
|
||||
import yaml
|
||||
|
||||
from constants import RESERVED_SUBDOMAINS
|
||||
|
||||
logger = logging.getLogger('picell')
|
||||
|
||||
_SUBDOMAIN_RE = re.compile(r'^[a-z][a-z0-9-]{0,30}$')
|
||||
@@ -21,12 +23,6 @@ _CAP_DENYLIST = frozenset({
|
||||
'ALL', 'SYS_ADMIN', 'SYS_MODULE', 'SYS_PTRACE', 'SYS_RAWIO',
|
||||
'SYS_BOOT', 'MAC_ADMIN', 'MAC_OVERRIDE', 'SYS_TIME', 'SYS_TTY_CONFIG',
|
||||
})
|
||||
_RESERVED_SUBDOMAINS = frozenset({
|
||||
# Core PIC infrastructure — never allow store services to hijack these
|
||||
'api', 'webui', 'admin', 'www', 'ns1', 'ns2', 'git', 'registry', 'install',
|
||||
# 'mail', 'calendar', 'files', 'webdav', 'webmail' are intentionally absent:
|
||||
# they belong to official PIC store services and must be claimable by them.
|
||||
})
|
||||
_BACKEND_DENYLIST = frozenset({
|
||||
'cell-api', 'cell-caddy', 'cell-coredns', 'cell-dnsmasq',
|
||||
'cell-wireguard', 'cell-vault', 'localhost', '127.0.0.1',
|
||||
@@ -172,9 +168,11 @@ def validate_rendered_compose(yaml_text: str, allowed_data_dir: str = None,
|
||||
|
||||
allow_host_network: when True, the compose file is permitted to use
|
||||
network_mode: host and devices: — required for connectivity services
|
||||
(wireguard-ext, openvpn-client, tor) that must share the host network
|
||||
namespace to create tun/wg interfaces. The external-network requirement
|
||||
is also waived since host-network containers reach the cell network directly.
|
||||
(wireguard-ext, openvpn-client, tor, sshuttle [cell-sshuttle],
|
||||
proxy [cell-redsocks]) that must share the host network namespace to
|
||||
create tun/wg interfaces or expose local transparent-proxy listeners.
|
||||
The external-network requirement is also waived since host-network
|
||||
containers reach the cell network directly.
|
||||
"""
|
||||
errors = []
|
||||
|
||||
@@ -330,7 +328,7 @@ def _check_subdomain(value, field_name: str, errors: list) -> None:
|
||||
if not isinstance(value, str):
|
||||
errors.append(f'{field_name} must be a string')
|
||||
return
|
||||
if value in _RESERVED_SUBDOMAINS:
|
||||
if value in RESERVED_SUBDOMAINS:
|
||||
errors.append(f'{field_name} is reserved: {value!r}')
|
||||
elif not _SUBDOMAIN_RE.match(value):
|
||||
errors.append(
|
||||
|
||||
Reference in New Issue
Block a user