Merge pull request #188 from outis1one/claude/asterisk-digital-ocean-w22kk8
Claude/asterisk digital ocean w22kk8
This commit is contained in:
@@ -159,6 +159,52 @@ reloads Caddy. No-ops silently if Caddy isn't installed. The fourth argument
|
|||||||
is an optional string inserted verbatim inside the Caddy site block (use it
|
is an optional string inserted verbatim inside the Caddy site block (use it
|
||||||
for `import authelia` or custom matchers).
|
for `import authelia` or custom matchers).
|
||||||
|
|
||||||
|
Sets two out-params (not `local` — read them after the call returns) so the
|
||||||
|
caller can tell whether Caddy actually ended up fronting the service:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
CADDY_SERVICE_CONFIGURED # true/false
|
||||||
|
CADDY_SERVICE_MODE # "local" or "remote" (only meaningful if configured)
|
||||||
|
```
|
||||||
|
|
||||||
|
Use this to skip opening a host firewall port for a service Caddy already
|
||||||
|
fronts *locally* (it reaches the service over `host.docker.internal`, not
|
||||||
|
the network) — but still open it when `CADDY_SERVICE_MODE` is `"remote"`,
|
||||||
|
since a remote Caddy machine needs to reach this host over the network
|
||||||
|
instead. See `services/asterisk.sh` and `services/asterisk-digital-ocean.sh`
|
||||||
|
for the reference pattern: call `configure_caddy_for_service` *before*
|
||||||
|
building firewall rules, not after, so the decision is known in time.
|
||||||
|
|
||||||
|
### UFW enable
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ensure_ufw_enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
Call this **after** your service has already added its own `ufw allow`
|
||||||
|
rules — it only flips UFW from inactive to active, it doesn't add rules for
|
||||||
|
you. No-ops if UFW is already active or not installed. Always allows SSH
|
||||||
|
first (reading the real port from `sshd_config` in case it's non-default)
|
||||||
|
before enabling, so this can't lock out the session running the installer.
|
||||||
|
|
||||||
|
### Closing a port to the internet without also closing it to Caddy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ufw_allow_from_caddy_net PORT [PROTO] # PROTO defaults to tcp
|
||||||
|
```
|
||||||
|
|
||||||
|
When `CADDY_SERVICE_MODE` is `"local"` (see above) and you `ufw delete
|
||||||
|
allow` a port because Caddy fronts it now, don't stop there — UFW rules
|
||||||
|
apply to *all* interfaces unless scoped, and Caddy's own request to
|
||||||
|
`host.docker.internal:PORT` is ordinary INPUT-chain traffic arriving over
|
||||||
|
the `caddy_net` bridge, not the public internet. A bare `ufw delete allow`
|
||||||
|
blocks that too and silently breaks the service (confirmed live: closing
|
||||||
|
the web admin port outright took Caddy down with it). Call
|
||||||
|
`ufw_allow_from_caddy_net` right after the `delete` to re-open the port
|
||||||
|
scoped to just `caddy_net`'s subnet — reachable from Caddy, not from the
|
||||||
|
internet. See `services/asterisk-digital-ocean.sh` and
|
||||||
|
`services/asterisk.sh` for the pattern.
|
||||||
|
|
||||||
### README generation
|
### README generation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
+67
-1
@@ -244,6 +244,55 @@ ensure_caddy_network() {
|
|||||||
&& log_info "Created Docker network ${_net} (needed by Caddy-fronted services)"
|
&& log_info "Created Docker network ${_net} (needed by Caddy-fronted services)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Enables UFW if it isn't already active. Call this AFTER the caller has
|
||||||
|
# already added its own `ufw allow` rules for whatever it needs — this only
|
||||||
|
# flips UFW from inactive to active, it doesn't add rules for the calling
|
||||||
|
# service itself.
|
||||||
|
#
|
||||||
|
# Always allows SSH first, using the sshd_config port if it's non-default —
|
||||||
|
# getting this wrong and then enabling UFW would lock out the very SSH
|
||||||
|
# session most people are running this script from. If UFW is already
|
||||||
|
# active, this is a no-op (assumed already handled correctly).
|
||||||
|
ensure_ufw_enabled() {
|
||||||
|
command -v ufw &>/dev/null || return 0
|
||||||
|
[ "$DRY_RUN" = true ] && return 0
|
||||||
|
ufw status 2>/dev/null | grep -q "Status: active" && return 0
|
||||||
|
|
||||||
|
local _ssh_port
|
||||||
|
_ssh_port="$(grep -iE '^[[:space:]]*Port[[:space:]]+[0-9]+' /etc/ssh/sshd_config 2>/dev/null \
|
||||||
|
| tail -1 | awk '{print $2}')"
|
||||||
|
_ssh_port="${_ssh_port:-22}"
|
||||||
|
|
||||||
|
ufw allow "${_ssh_port}/tcp" comment 'SSH' >/dev/null 2>&1
|
||||||
|
ufw --force enable >/dev/null 2>&1
|
||||||
|
log_success "UFW enabled (SSH on port ${_ssh_port} allowed first, so this won't lock you out)."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Scopes a UFW allow rule to just the caddy_net bridge subnet instead of
|
||||||
|
# every interface. Needed for any port that only needs to be reachable from
|
||||||
|
# a *locally* Caddy-fronted service (via host.docker.internal) — a plain
|
||||||
|
# `ufw delete allow <port>` closes it everywhere, but Caddy's own request to
|
||||||
|
# host.docker.internal is still ordinary INPUT-chain traffic as far as UFW
|
||||||
|
# is concerned, arriving over the caddy_net bridge, not the internet. UFW
|
||||||
|
# rules apply to all interfaces unless scoped like this, so closing the
|
||||||
|
# port outright also silently breaks Caddy.
|
||||||
|
ufw_allow_from_caddy_net() {
|
||||||
|
local _port="$1" _proto="${2:-tcp}"
|
||||||
|
command -v ufw &>/dev/null || return 0
|
||||||
|
[ "$DRY_RUN" = true ] && return 0
|
||||||
|
|
||||||
|
local _subnet
|
||||||
|
_subnet="$(docker network inspect "${SITE_CADDY_NET:-caddy_net}" \
|
||||||
|
--format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' 2>/dev/null)"
|
||||||
|
if [ -n "$_subnet" ]; then
|
||||||
|
ufw allow from "$_subnet" to any port "$_port" proto "$_proto" comment 'Caddy internal only' >/dev/null 2>&1
|
||||||
|
log_info "Port ${_port}/${_proto} reachable only from Caddy's internal network (${_subnet}), not the public internet."
|
||||||
|
else
|
||||||
|
log_warning "Could not determine ${SITE_CADDY_NET:-caddy_net}'s subnet — port ${_port}/${_proto} stays closed."
|
||||||
|
log_warning "If Caddy can't reach it: ufw allow from <caddy_net-subnet> to any port ${_port} proto ${_proto}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# ── SSH client config (~/.ssh/config) Host aliases ────────────────────────────
|
# ── SSH client config (~/.ssh/config) Host aliases ────────────────────────────
|
||||||
# Lets "ssh <alias>" connect directly to user@host without typing it out each
|
# Lets "ssh <alias>" connect directly to user@host without typing it out each
|
||||||
# time — handy for VPN/NetBird peers with unmemorable IPs. Operates on the
|
# time — handy for VPN/NetBird peers with unmemorable IPs. Operates on the
|
||||||
@@ -435,6 +484,16 @@ write_readme() {
|
|||||||
configure_caddy_for_service() {
|
configure_caddy_for_service() {
|
||||||
local SERVICE_NAME="$1" SERVICE_UPSTREAM="$2" DEFAULT_SUBDOMAIN="$3" EXTRA_CONFIG="${4:-}"
|
local SERVICE_NAME="$1" SERVICE_UPSTREAM="$2" DEFAULT_SUBDOMAIN="$3" EXTRA_CONFIG="${4:-}"
|
||||||
|
|
||||||
|
# Out-params (not `local` — callers read these after the call returns) so
|
||||||
|
# a caller can tell whether Caddy actually ended up fronting the service
|
||||||
|
# and, if so, whether that's a local container (reachable only over the
|
||||||
|
# host's internal network) or a remote machine (needs to reach this host
|
||||||
|
# over the network — usually its public IP). Services that also open a
|
||||||
|
# host firewall for the same port use this to skip that when Caddy is
|
||||||
|
# already the only intended way in, instead of leaving both routes open.
|
||||||
|
CADDY_SERVICE_CONFIGURED=false
|
||||||
|
CADDY_SERVICE_MODE=""
|
||||||
|
|
||||||
# Derive the proxy upstream and a port number for display messages.
|
# Derive the proxy upstream and a port number for display messages.
|
||||||
# Plain number → host.docker.internal:PORT (host-network or legacy
|
# Plain number → host.docker.internal:PORT (host-network or legacy
|
||||||
# services — Caddy itself runs in its own container on
|
# services — Caddy itself runs in its own container on
|
||||||
@@ -552,11 +611,16 @@ CADDY_BLOCK
|
|||||||
local OVERWRITE=""
|
local OVERWRITE=""
|
||||||
prompt_yn "Overwrite existing configuration? (y/n):" "n" OVERWRITE
|
prompt_yn "Overwrite existing configuration? (y/n):" "n" OVERWRITE
|
||||||
if [ "$OVERWRITE" != "y" ] && [ "$OVERWRITE" != "Y" ]; then
|
if [ "$OVERWRITE" != "y" ] && [ "$OVERWRITE" != "Y" ]; then
|
||||||
echo " Keeping existing configuration."; return 0
|
echo " Keeping existing configuration."
|
||||||
|
CADDY_SERVICE_CONFIGURED=true
|
||||||
|
CADDY_SERVICE_MODE="local"
|
||||||
|
return 0
|
||||||
fi
|
fi
|
||||||
sed -i "/^${SERVICE_DOMAIN}/,/^}/d" "$CADDYFILE"
|
sed -i "/^${SERVICE_DOMAIN}/,/^}/d" "$CADDYFILE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
CADDY_SERVICE_CONFIGURED=true
|
||||||
|
CADDY_SERVICE_MODE="local"
|
||||||
echo " Adding $SERVICE_NAME configuration to Caddyfile..."
|
echo " Adding $SERVICE_NAME configuration to Caddyfile..."
|
||||||
printf '%s\n' "$_SITE_BLOCK" >> "$CADDYFILE"
|
printf '%s\n' "$_SITE_BLOCK" >> "$CADDYFILE"
|
||||||
|
|
||||||
@@ -582,6 +646,8 @@ CADDY_BLOCK
|
|||||||
|
|
||||||
# ── Remote Caddy: write snippet file ─────────────────────────────────────
|
# ── Remote Caddy: write snippet file ─────────────────────────────────────
|
||||||
else
|
else
|
||||||
|
CADDY_SERVICE_CONFIGURED=true
|
||||||
|
CADDY_SERVICE_MODE="remote"
|
||||||
local SNIPPET_DIR="$DOCKER_DIR/caddy-snippets"
|
local SNIPPET_DIR="$DOCKER_DIR/caddy-snippets"
|
||||||
local SNIPPET_FILE="$SNIPPET_DIR/${DEFAULT_SUBDOMAIN}.caddy"
|
local SNIPPET_FILE="$SNIPPET_DIR/${DEFAULT_SUBDOMAIN}.caddy"
|
||||||
mkdir -p "$SNIPPET_DIR"
|
mkdir -p "$SNIPPET_DIR"
|
||||||
|
|||||||
@@ -699,7 +699,7 @@ CADDY_BLOCK
|
|||||||
ufw allow "${WEB_ADMIN_PORT_VAL}/tcp"
|
ufw allow "${WEB_ADMIN_PORT_VAL}/tcp"
|
||||||
else
|
else
|
||||||
ufw delete allow "${WEB_ADMIN_PORT_VAL}/tcp" 2>/dev/null || true
|
ufw delete allow "${WEB_ADMIN_PORT_VAL}/tcp" 2>/dev/null || true
|
||||||
log_info "Web admin port ${WEB_ADMIN_PORT_VAL} kept closed to the internet — Caddy fronts it locally."
|
ufw_allow_from_caddy_net "${WEB_ADMIN_PORT_VAL}"
|
||||||
fi
|
fi
|
||||||
ufw allow 8088/tcp
|
ufw allow 8088/tcp
|
||||||
ufw allow 8089/tcp
|
ufw allow 8089/tcp
|
||||||
@@ -707,6 +707,7 @@ CADDY_BLOCK
|
|||||||
ufw allow 3478/tcp
|
ufw allow 3478/tcp
|
||||||
ufw allow 10000:20000/udp
|
ufw allow 10000:20000/udp
|
||||||
ufw allow 49152:49252/udp
|
ufw allow 49152:49252/udp
|
||||||
|
ensure_ufw_enabled
|
||||||
log_success "UFW rules added."
|
log_success "UFW rules added."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
+26
-16
@@ -503,23 +503,11 @@ WEB_ADMIN_AUTH_DISABLED=false
|
|||||||
ENV
|
ENV
|
||||||
chmod 600 .env
|
chmod 600 .env
|
||||||
|
|
||||||
# ── UFW firewall rules ────────────────────────────────────────────────────
|
|
||||||
if command -v ufw &>/dev/null; then
|
|
||||||
log_info "Opening UFW ports for Asterisk + coturn..."
|
|
||||||
ufw allow 5060/udp
|
|
||||||
ufw allow 5060/tcp
|
|
||||||
ufw allow 5061/tcp
|
|
||||||
ufw allow "${WEB_ADMIN_PORT_VAL}/tcp"
|
|
||||||
ufw allow 8088/tcp
|
|
||||||
ufw allow 8089/tcp
|
|
||||||
ufw allow 3478/udp
|
|
||||||
ufw allow 3478/tcp
|
|
||||||
ufw allow 10000:20000/udp
|
|
||||||
ufw allow 49152:49252/udp
|
|
||||||
log_success "UFW rules added."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── Caddy reverse proxy for web admin ─────────────────────────────────────
|
# ── Caddy reverse proxy for web admin ─────────────────────────────────────
|
||||||
|
# Decided before the firewall rules below so they can be scoped
|
||||||
|
# correctly: if a local Caddy ends up fronting the web admin, there's no
|
||||||
|
# reason to also expose it on the LAN — Caddy already reaches it over
|
||||||
|
# the host's internal network (host.docker.internal).
|
||||||
local EXTRA_BLOCK=""
|
local EXTRA_BLOCK=""
|
||||||
if [ -d "$DOCKER_DIR/authelia" ]; then
|
if [ -d "$DOCKER_DIR/authelia" ]; then
|
||||||
local _use_auth=""
|
local _use_auth=""
|
||||||
@@ -532,6 +520,28 @@ ENV
|
|||||||
fi
|
fi
|
||||||
configure_caddy_for_service "Asterisk Web Admin" "${WEB_ADMIN_PORT_VAL}" "asterisk" "$EXTRA_BLOCK"
|
configure_caddy_for_service "Asterisk Web Admin" "${WEB_ADMIN_PORT_VAL}" "asterisk" "$EXTRA_BLOCK"
|
||||||
|
|
||||||
|
# ── UFW firewall rules ────────────────────────────────────────────────────
|
||||||
|
if command -v ufw &>/dev/null; then
|
||||||
|
log_info "Opening UFW ports for Asterisk + coturn..."
|
||||||
|
ufw allow 5060/udp
|
||||||
|
ufw allow 5060/tcp
|
||||||
|
ufw allow 5061/tcp
|
||||||
|
if [[ "$CADDY_SERVICE_CONFIGURED" == true && "$CADDY_SERVICE_MODE" == "local" ]]; then
|
||||||
|
ufw delete allow "${WEB_ADMIN_PORT_VAL}/tcp" 2>/dev/null || true
|
||||||
|
ufw_allow_from_caddy_net "${WEB_ADMIN_PORT_VAL}"
|
||||||
|
else
|
||||||
|
ufw allow "${WEB_ADMIN_PORT_VAL}/tcp"
|
||||||
|
fi
|
||||||
|
ufw allow 8088/tcp
|
||||||
|
ufw allow 8089/tcp
|
||||||
|
ufw allow 3478/udp
|
||||||
|
ufw allow 3478/tcp
|
||||||
|
ufw allow 10000:20000/udp
|
||||||
|
ufw allow 49152:49252/udp
|
||||||
|
ensure_ufw_enabled
|
||||||
|
log_success "UFW rules added."
|
||||||
|
fi
|
||||||
|
|
||||||
# ── README ────────────────────────────────────────────────────────────────
|
# ── README ────────────────────────────────────────────────────────────────
|
||||||
write_readme "$EA_DIR" << 'MD'
|
write_readme "$EA_DIR" << 'MD'
|
||||||
# Easy Asterisk PBX + coturn
|
# Easy Asterisk PBX + coturn
|
||||||
|
|||||||
+6
-4
@@ -5076,8 +5076,8 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Connection Type</label>
|
<label>Connection Type</label>
|
||||||
<select name="conn_type" class="form-control">
|
<select name="conn_type" class="form-control">
|
||||||
|
<option value="fqdn" selected>FQDN/Internet (TLS)</option>
|
||||||
<option value="lan">LAN/VPN (UDP)</option>
|
<option value="lan">LAN/VPN (UDP)</option>
|
||||||
<option value="fqdn">FQDN/Internet (TLS)</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -5314,9 +5314,11 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
|
|||||||
devicesCache = devices;
|
devicesCache = devices;
|
||||||
renderDevices();
|
renderDevices();
|
||||||
|
|
||||||
// Update category select in add device form
|
// Update category select in add device form. Most devices
|
||||||
|
// added day-to-day are phones, not fixed kiosks, so default
|
||||||
|
// to the "mobile" category instead of whichever sorts first.
|
||||||
document.getElementById('category-select').innerHTML = categoriesCache.map(c =>
|
document.getElementById('category-select').innerHTML = categoriesCache.map(c =>
|
||||||
`<option value="${c.id}">${c.name}</option>`
|
`<option value="${c.id}" ${c.id === 'mobile' ? 'selected' : ''}>${c.name}</option>`
|
||||||
).join('');
|
).join('');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showAlert('Failed to load devices', 'error');
|
showAlert('Failed to load devices', 'error');
|
||||||
@@ -5455,7 +5457,7 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
|
|||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
document.getElementById('category-select').innerHTML = categories.map(c =>
|
document.getElementById('category-select').innerHTML = categories.map(c =>
|
||||||
`<option value="${c.id}">${c.name}</option>`
|
`<option value="${c.id}" ${c.id === 'mobile' ? 'selected' : ''}>${c.name}</option>`
|
||||||
).join('');
|
).join('');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showAlert('Failed to load categories', 'error');
|
showAlert('Failed to load categories', 'error');
|
||||||
|
|||||||
Reference in New Issue
Block a user