From 6b2aaee75606e6419b40762193f1577b97966690 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Jul 2026 18:32:40 +0000 Subject: [PATCH 1/2] wolf-pair: don't expose the pairing UI publicly when Caddy fronts it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wolf-pair has no login of its own — Authelia via Caddy is the only protection option offered for it — but UFW opened its port to the whole internet unconditionally, before the Caddy/Authelia prompt even ran. Same gap just fixed for the Asterisk web admin: reachable straight over the bare port regardless of Authelia. Reordered so the Caddy decision happens first, and scope the port to caddy_net's subnet via ufw_allow_from_caddy_net() instead of leaving it open to 0.0.0.0/0 when Caddy fronts it locally. Also enables UFW via ensure_ufw_enabled() like the Asterisk services. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_015X1jRGHwrvovz2qkhKfDZi --- services/wolf-pair.sh | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/services/wolf-pair.sh b/services/wolf-pair.sh index a7a94a1..a874b9a 100644 --- a/services/wolf-pair.sh +++ b/services/wolf-pair.sh @@ -422,13 +422,11 @@ COMPOSE chown -R "$ACTUAL_USER:$ACTUAL_USER" "$WOLFPAIR_DIR" - # ── 4. Firewall ─────────────────────────────────────────────────────────── - if command -v ufw &>/dev/null; then - ufw allow "${WOLFPAIR_PORT}/tcp" comment "wolf-pair pairing UI" >/dev/null 2>&1 || true - log_success "UFW: opened port $WOLFPAIR_PORT/tcp" - fi - - # ── 5. Caddy (optional) ─────────────────────────────────────────────────── + # ── 4. Caddy (optional) ─────────────────────────────────────────────────── + # Decided before the firewall rule below: wolf-pair has no login of its + # own (that's the whole reason to offer Authelia here), so if Caddy ends + # up fronting it locally, the UFW rule must not also leave the bare port + # open to the internet — that would bypass Authelia entirely. local WOLFPAIR_EXTRA_BLOCK="" if [ -d "$DOCKER_DIR/authelia" ]; then local _use_auth="" @@ -437,6 +435,18 @@ COMPOSE fi configure_caddy_for_service "wolf-pair" "$WOLFPAIR_PORT" "wolf-pair" "$WOLFPAIR_EXTRA_BLOCK" + # ── 5. Firewall ─────────────────────────────────────────────────────────── + if command -v ufw &>/dev/null; then + if [[ "$CADDY_SERVICE_CONFIGURED" == true && "$CADDY_SERVICE_MODE" == "local" ]]; then + ufw delete allow "${WOLFPAIR_PORT}/tcp" 2>/dev/null || true + ufw_allow_from_caddy_net "${WOLFPAIR_PORT}" + else + ufw allow "${WOLFPAIR_PORT}/tcp" comment "wolf-pair pairing UI" >/dev/null 2>&1 || true + log_success "UFW: opened port $WOLFPAIR_PORT/tcp" + fi + ensure_ufw_enabled + fi + # ── 6. README ───────────────────────────────────────────────────────────── write_readme "$WOLFPAIR_DIR" << 'MD' # wolf-pair From d9af3ecc3fa2b50fcaf4f0cef8287a253e7a6439 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Jul 2026 18:56:53 +0000 Subject: [PATCH 2/2] Fix invalid "pjsip reload" command used everywhere (11 call sites) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "pjsip reload" was never a valid Asterisk CLI command — confirmed live: `asterisk -rx "pjsip reload"` returns "No such command 'pjsip reload'". The real command is "module reload res_pjsip.so". Every reload-after-change call in the script used the invalid form, both in the interactive CLI (add/edit/delete device, transport setup, TLS cert sync) and in every web admin mutation (add_device, delete_device, rename_device, change_device_category) — all silently no-op'd, since `asterisk -rx` just prints its own "no such command" error to a discarded/redirected output and returns normally either way. Endpoints only ever picked up new pjsip.conf entries after a full container restart (which re-reads config from scratch at startup) or a manual `module reload res_pjsip.so` — never from the web admin's own reload call, live-confirmed: a device stayed Unavailable with zero registration attempts logged until a manual reload picked it up immediately. Global replace across all 11 occurrences. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_015X1jRGHwrvovz2qkhKfDZi --- vendor/easy-asterisk/easy-asterisk-v0.10.0.sh | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/vendor/easy-asterisk/easy-asterisk-v0.10.0.sh b/vendor/easy-asterisk/easy-asterisk-v0.10.0.sh index 4aaba9c..a3b1c55 100755 --- a/vendor/easy-asterisk/easy-asterisk-v0.10.0.sh +++ b/vendor/easy-asterisk/easy-asterisk-v0.10.0.sh @@ -549,7 +549,7 @@ manage_categories() { # Delete the category sed -i "/^${cid}|/d" "$CATEGORIES_FILE" - asterisk -rx "pjsip reload" 2>/dev/null + asterisk -rx "module reload res_pjsip.so" 2>/dev/null rebuild_dialplan print_success "Category and ${device_count} device(s) deleted" else @@ -867,7 +867,7 @@ qualify_frequency=30 EOF chown -R asterisk:asterisk /etc/asterisk 2>/dev/null || true - asterisk -rx "pjsip reload" >/dev/null 2>&1 + asterisk -rx "module reload res_pjsip.so" >/dev/null 2>&1 rebuild_dialplan # Prepare provisioning URLs if HTTP server is configured @@ -1019,7 +1019,7 @@ remove_device() { ' /etc/asterisk/pjsip.conf > /etc/asterisk/pjsip.conf.tmp mv /etc/asterisk/pjsip.conf.tmp /etc/asterisk/pjsip.conf chown asterisk:asterisk /etc/asterisk/pjsip.conf - asterisk -rx "pjsip reload" 2>/dev/null + asterisk -rx "module reload res_pjsip.so" 2>/dev/null rebuild_dialplan print_success "All devices deleted" else @@ -1066,7 +1066,7 @@ remove_device() { ' /etc/asterisk/pjsip.conf > /etc/asterisk/pjsip.conf.tmp mv /etc/asterisk/pjsip.conf.tmp /etc/asterisk/pjsip.conf chown asterisk:asterisk /etc/asterisk/pjsip.conf - asterisk -rx "pjsip reload" 2>/dev/null + asterisk -rx "module reload res_pjsip.so" 2>/dev/null rebuild_dialplan print_success "Removed extension $ext ($name)" fi @@ -1155,7 +1155,7 @@ rename_device() { chown asterisk:asterisk /etc/asterisk/pjsip.conf # Reload Asterisk - asterisk -rx "pjsip reload" 2>/dev/null + asterisk -rx "module reload res_pjsip.so" 2>/dev/null rebuild_dialplan quiet print_success "Device renamed: ${old_name} → ${new_name}" @@ -3943,7 +3943,7 @@ uninstall_menu() { awk '/^; === Device:/{exit} {print}' /etc/asterisk/pjsip.conf > "$temp" mv "$temp" /etc/asterisk/pjsip.conf chown asterisk:asterisk /etc/asterisk/pjsip.conf - asterisk -rx "pjsip reload" >/dev/null 2>&1 || true + asterisk -rx "module reload res_pjsip.so" >/dev/null 2>&1 || true fi print_success "All devices removed" ;; @@ -4339,7 +4339,7 @@ def delete_device(extension): if found: with open(PJSIP_CONF, 'w') as f: f.writelines(new_lines) - subprocess.run(['asterisk', '-rx', 'pjsip reload'], capture_output=True) + subprocess.run(['asterisk', '-rx', 'module reload res_pjsip.so'], capture_output=True) subprocess.run(['/usr/local/bin/easy-asterisk', '--rebuild-dialplan'], capture_output=True) return True, "Device deleted" return False, "Device not found" @@ -4413,7 +4413,7 @@ def rename_device(extension, new_name): if found: with open(PJSIP_CONF, 'w') as f: f.writelines(new_lines) - subprocess.run(['asterisk', '-rx', 'pjsip reload'], capture_output=True) + subprocess.run(['asterisk', '-rx', 'module reload res_pjsip.so'], capture_output=True) subprocess.run(['/usr/local/bin/easy-asterisk', '--rebuild-dialplan'], capture_output=True) return True, "Device renamed" return False, "Device not found" @@ -4526,7 +4526,7 @@ def change_device_category(extension, new_category): if found: with open(PJSIP_CONF, 'w') as f: f.writelines(new_lines) - subprocess.run(['asterisk', '-rx', 'pjsip reload'], capture_output=True) + subprocess.run(['asterisk', '-rx', 'module reload res_pjsip.so'], capture_output=True) subprocess.run(['/usr/local/bin/easy-asterisk', '--rebuild-dialplan'], capture_output=True) return True, "Category changed" return False, "Device not found" @@ -4775,7 +4775,7 @@ qualify_frequency=30 with open(PJSIP_CONF, 'a') as f: f.write(device_config) - subprocess.run(['asterisk', '-rx', 'pjsip reload'], capture_output=True) + subprocess.run(['asterisk', '-rx', 'module reload res_pjsip.so'], capture_output=True) subprocess.run(['chown', 'asterisk:asterisk', PJSIP_CONF], capture_output=True) subprocess.run(['/usr/local/bin/easy-asterisk', '--rebuild-dialplan'], capture_output=True) @@ -6891,7 +6891,7 @@ import_clients() { # Reload Asterisk echo "" echo "Reloading Asterisk configuration..." - asterisk -rx "pjsip reload" >/dev/null 2>&1 + asterisk -rx "module reload res_pjsip.so" >/dev/null 2>&1 rebuild_dialplan quiet print_success "Import completed successfully!"