Merge pull request #170 from outis1one/claude/asterisk-dns-filtering-vlan-5pmz12
asterisk: default to FQDN mode, auto-detect VLANs, auto-sync Caddy certs
This commit is contained in:
+74
-4
@@ -244,16 +244,42 @@ install_asterisk() {
|
|||||||
# ── Networking mode ───────────────────────────────────────────────────────
|
# ── Networking mode ───────────────────────────────────────────────────────
|
||||||
echo ""
|
echo ""
|
||||||
echo " Networking mode:"
|
echo " Networking mode:"
|
||||||
echo " 1) LAN-only — no domain, self-signed cert, works on local network/VPN only"
|
echo " 1) FQDN (recommended) — TLS + TURN relay, every phone connects the"
|
||||||
echo " 2) FQDN — TLS + TURN relay, works from anywhere (requires public domain)"
|
echo " same way regardless of LAN/VLAN/remote"
|
||||||
|
echo " 2) LAN-only — no domain, self-signed cert, local network/VPN only"
|
||||||
local HA_NETMODE=""
|
local HA_NETMODE=""
|
||||||
prompt_text "Choose [1]:" "1" HA_NETMODE
|
prompt_text "Choose [1]:" "1" HA_NETMODE
|
||||||
|
|
||||||
local DOMAIN_NAME=""
|
local DOMAIN_NAME=""
|
||||||
if [[ "$HA_NETMODE" == "2" ]]; then
|
if [[ "$HA_NETMODE" != "2" ]]; then
|
||||||
prompt_text "FQDN (e.g. asterisk.${SITE_DOMAIN:-example.com}) [blank=skip]:" "" DOMAIN_NAME
|
prompt_text "FQDN (e.g. asterisk.${SITE_DOMAIN:-example.com}) [blank=fall back to LAN-only]:" "" DOMAIN_NAME
|
||||||
|
[[ -z "$DOMAIN_NAME" ]] && log_warning "No FQDN entered — proceeding in LAN-only mode."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── Local networks / VLANs ────────────────────────────────────────────────
|
||||||
|
# Feeds HAS_VLANS/VLAN_SUBNETS into .env, which the entrypoint reads to add
|
||||||
|
# extra local_net= entries in pjsip.conf so phones on those subnets get
|
||||||
|
# correct NAT/SDP handling (this is what fixes the "no sound" symptom for
|
||||||
|
# devices on a VLAN the server isn't itself attached to).
|
||||||
|
echo ""
|
||||||
|
echo " Detecting networks this host can see..."
|
||||||
|
local DETECTED_NETS=""
|
||||||
|
DETECTED_NETS="$(ip -o -f inet addr show scope global 2>/dev/null \
|
||||||
|
| awk '{print $2, $4}' \
|
||||||
|
| grep -Ev '^(docker|br-|veth|tun|tap|wg)' \
|
||||||
|
| awk '{ split($2,a,"/"); split(a[1],o,"."); print o[1]"."o[2]"."o[3]".0/"a[2] }' \
|
||||||
|
| sort -u)"
|
||||||
|
if [[ -n "$DETECTED_NETS" ]]; then
|
||||||
|
echo " This host is directly attached to:"
|
||||||
|
echo "$DETECTED_NETS" | sed 's/^/ /'
|
||||||
|
fi
|
||||||
|
echo " Phones on OTHER VLANs (this server usually can't see those directly)"
|
||||||
|
echo " still need to be listed here so their media is treated as local/trusted."
|
||||||
|
local VLAN_SUBNETS_VAL=""
|
||||||
|
prompt_text "VLAN/VPN subnets, space-separated CIDRs [blank=none]:" "" VLAN_SUBNETS_VAL
|
||||||
|
local HAS_VLANS_VAL="n"
|
||||||
|
[[ -n "$VLAN_SUBNETS_VAL" ]] && HAS_VLANS_VAL="y"
|
||||||
|
|
||||||
# ── Secrets ───────────────────────────────────────────────────────────────
|
# ── Secrets ───────────────────────────────────────────────────────────────
|
||||||
local TURN_PASSWORD
|
local TURN_PASSWORD
|
||||||
TURN_PASSWORD="$(generate_password 24)"
|
TURN_PASSWORD="$(generate_password 24)"
|
||||||
@@ -280,6 +306,7 @@ services:
|
|||||||
- ./spool:/var/spool/asterisk
|
- ./spool:/var/spool/asterisk
|
||||||
- ./lib:/var/lib/asterisk
|
- ./lib:/var/lib/asterisk
|
||||||
- ./easy-asterisk.sh:/usr/local/bin/easy-asterisk:ro
|
- ./easy-asterisk.sh:/usr/local/bin/easy-asterisk:ro
|
||||||
|
CADDY_VOLUME_PLACEHOLDER
|
||||||
env_file: .env
|
env_file: .env
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
healthcheck:
|
healthcheck:
|
||||||
@@ -316,6 +343,15 @@ services:
|
|||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# Share Caddy's cert store (read-only) so the entrypoint can auto-sync a
|
||||||
|
# real Let's Encrypt cert for DOMAIN_NAME instead of falling back to
|
||||||
|
# self-signed. No-op if Caddy isn't installed on this box.
|
||||||
|
if [[ -d "$DOCKER_DIR/caddy/data" ]]; then
|
||||||
|
sed -i "s#CADDY_VOLUME_PLACEHOLDER# - ${DOCKER_DIR}/caddy/data:/caddy-data:ro#" docker-compose.yml
|
||||||
|
else
|
||||||
|
sed -i "/CADDY_VOLUME_PLACEHOLDER/d" docker-compose.yml
|
||||||
|
fi
|
||||||
|
|
||||||
# ── .env ──────────────────────────────────────────────────────────────────
|
# ── .env ──────────────────────────────────────────────────────────────────
|
||||||
cat > .env << ENV
|
cat > .env << ENV
|
||||||
# ── Domain ────────────────────────────────────────────────────
|
# ── Domain ────────────────────────────────────────────────────
|
||||||
@@ -333,6 +369,12 @@ TURN_SERVER=${TURN_SERVER_VAL}
|
|||||||
RTP_START=10000
|
RTP_START=10000
|
||||||
RTP_END=20000
|
RTP_END=20000
|
||||||
|
|
||||||
|
# ── VLAN/VPN subnets ──────────────────────────────────────────
|
||||||
|
# Extra local_net= entries for phones on networks this server isn't
|
||||||
|
# itself attached to. Space-separated CIDRs.
|
||||||
|
HAS_VLANS=${HAS_VLANS_VAL}
|
||||||
|
VLAN_SUBNETS=${VLAN_SUBNETS_VAL}
|
||||||
|
|
||||||
# ── Web admin ─────────────────────────────────────────────────
|
# ── Web admin ─────────────────────────────────────────────────
|
||||||
WEB_ADMIN_PORT=8080
|
WEB_ADMIN_PORT=8080
|
||||||
WEB_ADMIN_AUTH_DISABLED=false
|
WEB_ADMIN_AUTH_DISABLED=false
|
||||||
@@ -404,6 +446,34 @@ docker exec -it easy-asterisk easy-asterisk --help
|
|||||||
|
|
||||||
Recommended softphones: Linphone, Zoiper, Bria, Grandstream Wave.
|
Recommended softphones: Linphone, Zoiper, Bria, Grandstream Wave.
|
||||||
|
|
||||||
|
For a phone to work the same way regardless of network (LAN, VLAN, remote,
|
||||||
|
no VPN), register it against `<DOMAIN_NAME>:5061` over TLS — that's what
|
||||||
|
FQDN mode is for. Plain UDP/TCP on 5060 still works for LAN-only devices,
|
||||||
|
but only the FQDN+TLS path is location-independent.
|
||||||
|
|
||||||
|
## VLANs / other subnets
|
||||||
|
|
||||||
|
`.env` → `HAS_VLANS`/`VLAN_SUBNETS` lists extra networks (space-separated
|
||||||
|
CIDRs) this server isn't itself attached to but that phones live on. These
|
||||||
|
become `local_net=` entries in `pjsip.conf` so NAT/SDP handling is correct
|
||||||
|
for those devices (missing entries here is the most common cause of calls
|
||||||
|
connecting with no audio). To change this after install:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -it easy-asterisk easy-asterisk
|
||||||
|
# Server Settings → Configure VLAN/VPN Subnets
|
||||||
|
```
|
||||||
|
|
||||||
|
## TLS certificate
|
||||||
|
|
||||||
|
If Caddy is installed and already holds a Let's Encrypt cert for
|
||||||
|
`DOMAIN_NAME` (i.e. there's a Caddyfile site block for that exact hostname),
|
||||||
|
the container mounts Caddy's cert store read-only and the entrypoint syncs
|
||||||
|
it in automatically on every start — and re-checks every 12h so renewals
|
||||||
|
get picked up without a restart. No Caddyfile block for the domain, or no
|
||||||
|
Caddy at all, falls back to a self-signed cert (phones must be configured
|
||||||
|
to accept it).
|
||||||
|
|
||||||
## Web admin
|
## Web admin
|
||||||
|
|
||||||
Access the Easy Asterisk web interface at http://<host-ip>:8080
|
Access the Easy Asterisk web interface at http://<host-ip>:8080
|
||||||
|
|||||||
+76
@@ -73,8 +73,38 @@ if [[ "$raw_cidr" =~ \.([0-9]+)/([0-9]+)$ ]]; then
|
|||||||
default_cidr="${raw_cidr%.*}.0/${BASH_REMATCH[2]}"
|
default_cidr="${raw_cidr%.*}.0/${BASH_REMATCH[2]}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── 4b. Sync Caddy-issued TLS cert (if the compose file shared it) ───────
|
||||||
|
# If services/asterisk.sh mounted Caddy's data dir read-only at /caddy-data,
|
||||||
|
# and Caddy already holds a real Let's Encrypt cert for our own DOMAIN_NAME
|
||||||
|
# (e.g. because a matching site block exists in the Caddyfile), prefer that
|
||||||
|
# over a self-signed cert — phones then get a CA-trusted TLS connection.
|
||||||
|
CADDY_CERT_DIR="/caddy-data/caddy/certificates/acme-v02.api.letsencrypt.org-directory"
|
||||||
|
sync_caddy_cert() {
|
||||||
|
[[ -n "${DOMAIN_NAME:-}" ]] || return 1
|
||||||
|
local src_crt="${CADDY_CERT_DIR}/${DOMAIN_NAME}/${DOMAIN_NAME}.crt"
|
||||||
|
local src_key="${CADDY_CERT_DIR}/${DOMAIN_NAME}/${DOMAIN_NAME}.key"
|
||||||
|
[[ -f "$src_crt" && -f "$src_key" ]] || return 1
|
||||||
|
|
||||||
|
if [[ -f /etc/asterisk/certs/server.crt ]] && cmp -s "$src_crt" /etc/asterisk/certs/server.crt; then
|
||||||
|
return 1 # already in sync
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p /etc/asterisk/certs
|
||||||
|
cp "$src_crt" /etc/asterisk/certs/server.crt
|
||||||
|
cp "$src_key" /etc/asterisk/certs/server.key
|
||||||
|
chown asterisk:asterisk /etc/asterisk/certs/server.crt /etc/asterisk/certs/server.key
|
||||||
|
chmod 644 /etc/asterisk/certs/server.crt
|
||||||
|
chmod 600 /etc/asterisk/certs/server.key
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ -d "$CADDY_CERT_DIR" ]] && sync_caddy_cert; then
|
||||||
|
log_info "Synced Caddy-issued Let's Encrypt cert for ${DOMAIN_NAME} (CA-trusted, no self-signed warning)"
|
||||||
|
fi
|
||||||
|
|
||||||
# ── 5. Generate self-signed certs ──────────────────────────────
|
# ── 5. Generate self-signed certs ──────────────────────────────
|
||||||
# Regenerate if missing OR if existing cert lacks SANs (modern TLS clients require them)
|
# Regenerate if missing OR if existing cert lacks SANs (modern TLS clients require them)
|
||||||
|
# Skipped entirely if the Caddy sync above just installed a real cert.
|
||||||
regen_cert=false
|
regen_cert=false
|
||||||
if [[ ! -f /etc/asterisk/certs/server.crt ]]; then
|
if [[ ! -f /etc/asterisk/certs/server.crt ]]; then
|
||||||
regen_cert=true
|
regen_cert=true
|
||||||
@@ -279,6 +309,38 @@ EOF
|
|||||||
chown asterisk:asterisk /etc/asterisk/pjsip.conf
|
chown asterisk:asterisk /etc/asterisk/pjsip.conf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── Ensure transport-udp/transport-tcp exist (same migration path as TLS) ──
|
||||||
|
# Same scenario as above: a preserved/migrated pjsip.conf can be missing the
|
||||||
|
# plain SIP transports entirely, silently, with no bind error — LAN/VLAN
|
||||||
|
# devices registering without TLS then can never connect. Inject if absent.
|
||||||
|
if [[ -f /etc/asterisk/pjsip.conf ]] && ! grep -q "^\[transport-udp\]" /etc/asterisk/pjsip.conf; then
|
||||||
|
log_info "transport-udp missing from pjsip.conf — adding UDP transport..."
|
||||||
|
cat >> /etc/asterisk/pjsip.conf << EOF
|
||||||
|
|
||||||
|
[transport-udp]
|
||||||
|
type=transport
|
||||||
|
protocol=udp
|
||||||
|
bind=0.0.0.0:5060
|
||||||
|
${nat_settings}
|
||||||
|
|
||||||
|
EOF
|
||||||
|
chown asterisk:asterisk /etc/asterisk/pjsip.conf
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f /etc/asterisk/pjsip.conf ]] && ! grep -q "^\[transport-tcp\]" /etc/asterisk/pjsip.conf; then
|
||||||
|
log_info "transport-tcp missing from pjsip.conf — adding TCP transport..."
|
||||||
|
cat >> /etc/asterisk/pjsip.conf << EOF
|
||||||
|
|
||||||
|
[transport-tcp]
|
||||||
|
type=transport
|
||||||
|
protocol=tcp
|
||||||
|
bind=0.0.0.0:5060
|
||||||
|
${nat_settings}
|
||||||
|
|
||||||
|
EOF
|
||||||
|
chown asterisk:asterisk /etc/asterisk/pjsip.conf
|
||||||
|
fi
|
||||||
|
|
||||||
# ── rtp.conf (always regenerated) ──
|
# ── rtp.conf (always regenerated) ──
|
||||||
# ICE is enabled so Asterisk participates in ICE negotiation with clients.
|
# ICE is enabled so Asterisk participates in ICE negotiation with clients.
|
||||||
# stunaddr/turnaddr are NOT set here because:
|
# stunaddr/turnaddr are NOT set here because:
|
||||||
@@ -378,6 +440,20 @@ if [[ -f "$WEB_ADMIN_SCRIPT" ]]; then
|
|||||||
python3 "$WEB_ADMIN_SCRIPT" &
|
python3 "$WEB_ADMIN_SCRIPT" &
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── 10b. Keep the Caddy-issued cert fresh across renewals ────────────────
|
||||||
|
# Let's Encrypt certs renew every ~60-90 days without the container restarting.
|
||||||
|
# Re-check periodically and hot-reload Asterisk when Caddy's copy changes.
|
||||||
|
if [[ -d "$CADDY_CERT_DIR" ]]; then
|
||||||
|
(
|
||||||
|
while sleep 43200; do # every 12h
|
||||||
|
if sync_caddy_cert; then
|
||||||
|
log_info "Caddy cert renewed for ${DOMAIN_NAME} — reloading Asterisk"
|
||||||
|
asterisk -rx "core reload" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
) &
|
||||||
|
fi
|
||||||
|
|
||||||
# ── 11. Signal handling for clean shutdown ────────────────────
|
# ── 11. Signal handling for clean shutdown ────────────────────
|
||||||
cleanup() {
|
cleanup() {
|
||||||
log_info "Shutting down..."
|
log_info "Shutting down..."
|
||||||
|
|||||||
Reference in New Issue
Block a user