Auto-scan for a free web admin port instead of hardcoding one
The 8080->8081 fix from the last commit just moved the collision risk, not removed it — any hardcoded port can eventually collide with something else on a box running several services. Both services now scan for the first genuinely free port starting at 8081 (ss -tlnH "sport = :$PORT", capped at 100 ports checked) and use whatever they find — .env, UFW, the DO Cloud Firewall rule, and the Caddy proxy target all follow the actual chosen port, not a fixed number. asterisk-do.sh's self-contained Caddy block (unquoted heredoc) reads the port live. asterisk.sh's README heredoc is quoted (no expansion), so its generated docs keep the static "8081" default with an added note to check .env for the real value if it differed — the summary echo outside that heredoc still reports the live value correctly. Verified: normal case still lands on 8081; with 8081 deliberately occupied by another process, both services correctly detect the collision and fall through to 8082 instead, confirmed via the actual generated .env in each case.
This commit is contained in:
+35
-11
@@ -210,7 +210,8 @@ install_asterisk-do() {
|
||||
echo "[DRY-RUN] Would create $EA_DIR with Dockerfile, docker-compose.yml, .env"
|
||||
echo "[DRY-RUN] Would copy/download vendor files from easy-asterisk"
|
||||
echo "[DRY-RUN] Would detect droplet public IP via DO metadata service"
|
||||
echo "[DRY-RUN] Would open UFW ports: 5060, 5061, 8081, 8088, 8089, 3478, 10000-20000, 49152-49252"
|
||||
echo "[DRY-RUN] Would scan for a free web admin port starting at 8081 (avoids e.g. CrowdSec's 8080)"
|
||||
echo "[DRY-RUN] Would open UFW ports: 5060, 5061, <web admin port>, 8088, 8089, 3478, 10000-20000, 49152-49252"
|
||||
echo "[DRY-RUN] Would open 51820/udp (not 51821) if wg-easy was selected"
|
||||
echo "[DRY-RUN] Would offer to create a DigitalOcean Cloud Firewall via doctl"
|
||||
echo "[DRY-RUN] Would reverse-proxy the web admin on the SAME FQDN used for SIP (needed for cert sync)"
|
||||
@@ -487,6 +488,25 @@ EOF
|
||||
sed -i "/CADDY_VOLUME_PLACEHOLDER/d" docker-compose.yml
|
||||
fi
|
||||
|
||||
# ── Pick a free port for the web admin ─────────────────────────────────────
|
||||
# Hardcoding a single number gets fragile fast once several services share
|
||||
# a host — CrowdSec's own LAPI already collides with 8080 by default (its
|
||||
# own upstream default, confirmed against its real config.yaml). Scan
|
||||
# instead: start at 8081 and take the first port nothing is listening on,
|
||||
# capped so a pathological box can't spin this forever.
|
||||
local WEB_ADMIN_PORT_VAL=8081
|
||||
local _port_scan_limit=$((WEB_ADMIN_PORT_VAL + 100))
|
||||
while ss -tlnH "sport = :${WEB_ADMIN_PORT_VAL}" 2>/dev/null | grep -q . \
|
||||
&& [[ "$WEB_ADMIN_PORT_VAL" -lt "$_port_scan_limit" ]]; do
|
||||
WEB_ADMIN_PORT_VAL=$((WEB_ADMIN_PORT_VAL + 1))
|
||||
done
|
||||
if [[ "$WEB_ADMIN_PORT_VAL" -ge "$_port_scan_limit" ]]; then
|
||||
log_warning "No free port found in 8081-${_port_scan_limit} — falling back to 8081 anyway."
|
||||
WEB_ADMIN_PORT_VAL=8081
|
||||
elif [[ "$WEB_ADMIN_PORT_VAL" != 8081 ]]; then
|
||||
log_info "Port 8081 was already taken — web admin will use ${WEB_ADMIN_PORT_VAL} instead."
|
||||
fi
|
||||
|
||||
# ── .env ──────────────────────────────────────────────────────────────────
|
||||
cat > .env << ENV
|
||||
# ── Domain ────────────────────────────────────────────────────
|
||||
@@ -512,7 +532,11 @@ HAS_VLANS=n
|
||||
VLAN_SUBNETS=
|
||||
|
||||
# ── Web admin ─────────────────────────────────────────────────
|
||||
WEB_ADMIN_PORT=8081
|
||||
# Picked automatically at install time (first free port starting at 8081) —
|
||||
# see WEB_ADMIN_PORT_VAL in services/asterisk-do.sh if this ever needs to
|
||||
# change again; don't hand-edit without also updating Caddy's Caddyfile and
|
||||
# both firewall layers to match.
|
||||
WEB_ADMIN_PORT=${WEB_ADMIN_PORT_VAL}
|
||||
WEB_ADMIN_AUTH_DISABLED=false
|
||||
ENV
|
||||
chmod 600 .env
|
||||
@@ -523,7 +547,7 @@ ENV
|
||||
ufw allow 5060/udp
|
||||
ufw allow 5060/tcp
|
||||
ufw allow 5061/tcp
|
||||
ufw allow 8081/tcp
|
||||
ufw allow "${WEB_ADMIN_PORT_VAL}/tcp"
|
||||
ufw allow 8088/tcp
|
||||
ufw allow 8089/tcp
|
||||
ufw allow 3478/udp
|
||||
@@ -555,7 +579,7 @@ ENV
|
||||
"protocol:tcp,ports:5060,address:0.0.0.0/0,address:::/0"
|
||||
"protocol:udp,ports:5060,address:0.0.0.0/0,address:::/0"
|
||||
"protocol:tcp,ports:5061,address:0.0.0.0/0,address:::/0"
|
||||
"protocol:tcp,ports:8081,address:0.0.0.0/0,address:::/0"
|
||||
"protocol:tcp,ports:${WEB_ADMIN_PORT_VAL},address:0.0.0.0/0,address:::/0"
|
||||
"protocol:tcp,ports:8088-8089,address:0.0.0.0/0,address:::/0"
|
||||
"protocol:tcp,ports:3478,address:0.0.0.0/0,address:::/0"
|
||||
"protocol:udp,ports:3478,address:0.0.0.0/0,address:::/0"
|
||||
@@ -604,9 +628,9 @@ ENV
|
||||
# matching $DOMAIN_NAME, and SIP TLS would silently stay self-signed. So
|
||||
# there's no separate domain prompt: this always targets $DOMAIN_NAME.
|
||||
if [[ -z "$DOMAIN_NAME" ]]; then
|
||||
log_info "No FQDN set — web admin stays on http://${PUBLIC_IP:-localhost}:8081 (nothing for Caddy to do)."
|
||||
log_info "No FQDN set — web admin stays on http://${PUBLIC_IP:-localhost}:${WEB_ADMIN_PORT_VAL} (nothing for Caddy to do)."
|
||||
elif [[ ! -d "$DOCKER_DIR/caddy" ]] && [[ -z "${CADDY_REMOTE_HOST:-}" ]]; then
|
||||
log_info "Caddy not installed — web admin stays on http://${PUBLIC_IP:-localhost}:8081, SIP TLS stays self-signed."
|
||||
log_info "Caddy not installed — web admin stays on http://${PUBLIC_IP:-localhost}:${WEB_ADMIN_PORT_VAL}, SIP TLS stays self-signed."
|
||||
else
|
||||
local EXTRA_BLOCK=""
|
||||
if [ -d "$DOCKER_DIR/authelia" ]; then
|
||||
@@ -665,7 +689,7 @@ ENV
|
||||
|
||||
# Asterisk Web Admin
|
||||
${DOMAIN_NAME} {
|
||||
reverse_proxy localhost:8081
|
||||
reverse_proxy localhost:${WEB_ADMIN_PORT_VAL}
|
||||
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||
@@ -847,7 +871,7 @@ plan for the admin panel.
|
||||
| 22 | TCP | SSH (keep this open or you're locked out) |
|
||||
| 5060 | UDP/TCP | SIP signalling (unencrypted) |
|
||||
| 5061 | TCP | SIP over TLS |
|
||||
| 8081 | TCP | Easy Asterisk web admin |
|
||||
| ${WEB_ADMIN_PORT_VAL} | TCP | Easy Asterisk web admin (auto-picked — see \`.env\`) |
|
||||
| 8088/8089 | TCP | Asterisk HTTP/WS (ARI/AMI) |
|
||||
| 3478 | UDP/TCP | TURN/STUN (coturn) |
|
||||
| 10000–20000 | UDP | RTP media streams |
|
||||
@@ -881,7 +905,7 @@ be added later by running \`sudo ./setup.sh <name>\` from the repo.
|
||||
the public firewall; the web UI (51821) is deliberately **not** exposed —
|
||||
reach it via SSH tunnel: \`ssh -L 51821:localhost:51821 user@<droplet-ip>\`,
|
||||
then browse \`http://localhost:51821\`. A natural next step once it's
|
||||
installed: restrict the web admin (8081) to the VPN subnet only, on both
|
||||
installed: restrict the web admin (${WEB_ADMIN_PORT_VAL}) to the VPN subnet only, on both
|
||||
firewall layers, so reconfiguring the PBX requires being on the VPN —
|
||||
done manually, not automatically, since a firewall mistake there can lock
|
||||
you out.
|
||||
@@ -965,7 +989,7 @@ accept it manually).
|
||||
|
||||
## Web admin
|
||||
|
||||
Access the Easy Asterisk web interface at http://<droplet-ip>:8081
|
||||
Access the Easy Asterisk web interface at http://<droplet-ip>:${WEB_ADMIN_PORT_VAL}
|
||||
or via your configured reverse-proxy domain.
|
||||
|
||||
## Data directories (all inside ~/docker/asterisk-do/, included in backup)
|
||||
@@ -1001,7 +1025,7 @@ MD
|
||||
fi
|
||||
echo " Public IP: ${PUBLIC_IP:-unknown}"
|
||||
echo " SIP port: 5061 (TLS) / 5060 (UDP)"
|
||||
echo " Web admin: http://${PUBLIC_IP:-localhost}:8081"
|
||||
echo " Web admin: http://${PUBLIC_IP:-localhost}:${WEB_ADMIN_PORT_VAL}"
|
||||
echo " Manage: docker compose -f $EA_DIR/docker-compose.yml <up|down|logs>"
|
||||
echo " Script: docker exec -it easy-asterisk-do easy-asterisk --help"
|
||||
if [[ -n "$DOMAIN_NAME" ]] && [[ -d "$DOCKER_DIR/caddy" ]]; then
|
||||
|
||||
+34
-7
@@ -204,7 +204,8 @@ install_asterisk() {
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo "[DRY-RUN] Would create $EA_DIR with Dockerfile, docker-compose.yml, .env"
|
||||
echo "[DRY-RUN] Would copy/download vendor files from easy-asterisk"
|
||||
echo "[DRY-RUN] Would open UFW ports: 5060, 5061, 8081, 8088, 8089, 3478, 10000-20000, 49152-49252"
|
||||
echo "[DRY-RUN] Would scan for a free web admin port starting at 8081 (avoids e.g. CrowdSec's 8080)"
|
||||
echo "[DRY-RUN] Would open UFW ports: 5060, 5061, <web admin port>, 8088, 8089, 3478, 10000-20000, 49152-49252"
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -359,6 +360,25 @@ EOF
|
||||
sed -i "/CADDY_VOLUME_PLACEHOLDER/d" docker-compose.yml
|
||||
fi
|
||||
|
||||
# ── Pick a free port for the web admin ─────────────────────────────────────
|
||||
# Hardcoding a single number gets fragile fast once several services share
|
||||
# a host — CrowdSec's own LAPI already collides with 8080 by default (its
|
||||
# own upstream default, confirmed against its real config.yaml). Scan
|
||||
# instead: start at 8081 and take the first port nothing is listening on,
|
||||
# capped so a pathological box can't spin this forever.
|
||||
local WEB_ADMIN_PORT_VAL=8081
|
||||
local _port_scan_limit=$((WEB_ADMIN_PORT_VAL + 100))
|
||||
while ss -tlnH "sport = :${WEB_ADMIN_PORT_VAL}" 2>/dev/null | grep -q . \
|
||||
&& [[ "$WEB_ADMIN_PORT_VAL" -lt "$_port_scan_limit" ]]; do
|
||||
WEB_ADMIN_PORT_VAL=$((WEB_ADMIN_PORT_VAL + 1))
|
||||
done
|
||||
if [[ "$WEB_ADMIN_PORT_VAL" -ge "$_port_scan_limit" ]]; then
|
||||
log_warning "No free port found in 8081-${_port_scan_limit} — falling back to 8081 anyway."
|
||||
WEB_ADMIN_PORT_VAL=8081
|
||||
elif [[ "$WEB_ADMIN_PORT_VAL" != 8081 ]]; then
|
||||
log_info "Port 8081 was already taken — web admin will use ${WEB_ADMIN_PORT_VAL} instead."
|
||||
fi
|
||||
|
||||
# ── .env ──────────────────────────────────────────────────────────────────
|
||||
cat > .env << ENV
|
||||
# ── Domain ────────────────────────────────────────────────────
|
||||
@@ -383,7 +403,11 @@ HAS_VLANS=${HAS_VLANS_VAL}
|
||||
VLAN_SUBNETS=${VLAN_SUBNETS_VAL}
|
||||
|
||||
# ── Web admin ─────────────────────────────────────────────────
|
||||
WEB_ADMIN_PORT=8081
|
||||
# Picked automatically at install time (first free port starting at 8081) —
|
||||
# see WEB_ADMIN_PORT_VAL in services/asterisk.sh if this ever needs to
|
||||
# change again; don't hand-edit without also updating Caddy's Caddyfile and
|
||||
# any firewall rules to match.
|
||||
WEB_ADMIN_PORT=${WEB_ADMIN_PORT_VAL}
|
||||
WEB_ADMIN_AUTH_DISABLED=false
|
||||
ENV
|
||||
chmod 600 .env
|
||||
@@ -394,7 +418,7 @@ ENV
|
||||
ufw allow 5060/udp
|
||||
ufw allow 5060/tcp
|
||||
ufw allow 5061/tcp
|
||||
ufw allow 8081/tcp
|
||||
ufw allow "${WEB_ADMIN_PORT_VAL}/tcp"
|
||||
ufw allow 8088/tcp
|
||||
ufw allow 8089/tcp
|
||||
ufw allow 3478/udp
|
||||
@@ -415,7 +439,7 @@ ENV
|
||||
sed -i "s/^WEB_ADMIN_AUTH_DISABLED=.*/WEB_ADMIN_AUTH_DISABLED=true/" .env
|
||||
fi
|
||||
fi
|
||||
configure_caddy_for_service "Asterisk Web Admin" "8081" "asterisk" "$EXTRA_BLOCK"
|
||||
configure_caddy_for_service "Asterisk Web Admin" "${WEB_ADMIN_PORT_VAL}" "asterisk" "$EXTRA_BLOCK"
|
||||
|
||||
# ── README ────────────────────────────────────────────────────────────────
|
||||
write_readme "$EA_DIR" << 'MD'
|
||||
@@ -484,7 +508,10 @@ to accept it).
|
||||
## Web admin
|
||||
|
||||
Access the Easy Asterisk web interface at http://<host-ip>:8081
|
||||
or via your configured reverse-proxy domain.
|
||||
or via your configured reverse-proxy domain. (8081 is the default; if that
|
||||
port was already taken by something else on this box, the installer picked
|
||||
the next free one instead — check WEB_ADMIN_PORT in .env for the actual
|
||||
value.)
|
||||
|
||||
## Data directories (all inside ~/docker/asterisk/, included in backup)
|
||||
|
||||
@@ -502,7 +529,7 @@ or via your configured reverse-proxy domain.
|
||||
|---------------|----------|----------------------------------|
|
||||
| 5060 | UDP/TCP | SIP signalling (unencrypted) |
|
||||
| 5061 | TCP | SIP over TLS |
|
||||
| 8081 | TCP | Easy Asterisk web admin |
|
||||
| 8081 | TCP | Easy Asterisk web admin (default — see .env) |
|
||||
| 8088/8089 | TCP | Asterisk HTTP/WS (ARI/AMI) |
|
||||
| 3478 | UDP/TCP | TURN/STUN (coturn) |
|
||||
| 10000–20000 | UDP | RTP media streams |
|
||||
@@ -530,7 +557,7 @@ MD
|
||||
echo " TURN server: (none — LAN/VPN only)"
|
||||
fi
|
||||
echo " SIP port: 5061 (TLS) / 5060 (UDP)"
|
||||
echo " Web admin: http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo localhost):8081"
|
||||
echo " Web admin: http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo localhost):${WEB_ADMIN_PORT_VAL}"
|
||||
echo " Manage: docker compose -f $EA_DIR/docker-compose.yml <up|down|logs>"
|
||||
echo " Script: docker exec -it easy-asterisk easy-asterisk --help"
|
||||
echo ""
|
||||
|
||||
Reference in New Issue
Block a user