Fix Node-RED container net: extra_hosts, CODE HTTPS, force-recreate

Detect host LAN IP and CODE URL so OCS/Collabora work unattended
on each VM. Avoid 127.0.1.1 Nextcloud DNS and HTTP-to-TLS CODE.
This commit is contained in:
root
2026-08-22 19:41:55 +02:00
parent 13bbeba60f
commit d6f795dee0
4 changed files with 172 additions and 24 deletions
+10 -2
View File
@@ -5,10 +5,16 @@
NEXTCLOUD_URL=https://cloud.hannesahlin.se NEXTCLOUD_URL=https://cloud.hannesahlin.se
NEXTCLOUD_PUBLIC_URL=https://cloud.hannesahlin.se NEXTCLOUD_PUBLIC_URL=https://cloud.hannesahlin.se
# Collabora CODE — bridge IP auto-detected if COLLABORA_IP is empty # Collabora CODE
# COLLABORA_URL: https://code:9980 när CODE delar Docker-nätverk med Node-RED
# (integrate-into-nodered.sh connectar CODE till nodernätet automatiskt).
# Fallback om 'code' inte kan resolvas: https://<CODE-IP>:9980.
# ALDRIG http://127.0.0.1:9980 eller http://host.docker.internal:9980 —
# CODE lyssnar bara på HTTPS internt och kör inte på Node-RED-värden.
# verifyTls=false sätts i nc-collab-config-noden (self-signed cert internt).
CODE_CONTAINER=code CODE_CONTAINER=code
COLLABORA_IP= COLLABORA_IP=
COLLABORA_URL= COLLABORA_URL=https://code:9980
# Existing Node-RED install (integrate mode) # Existing Node-RED install (integrate mode)
NODERED_DIR= NODERED_DIR=
@@ -18,6 +24,8 @@ INSTALL_USER=
# Sidecar mode (not default) # Sidecar mode (not default)
NC_HOST=cloud.hannesahlin.se NC_HOST=cloud.hannesahlin.se
# Host-LAN-IP för extra_hosts. Tomt = auto-detekteras av integrate-into-
# nodered.sh (hostname -I, 192.168.x/10.x), fallback 192.168.1.26.
NC_HOST_IP= NC_HOST_IP=
TZ=Europe/Stockholm TZ=Europe/Stockholm
NODERED_CREDENTIAL_SECRET=change-this-secret-in-production NODERED_CREDENTIAL_SECRET=change-this-secret-in-production
+2 -1
View File
@@ -13,8 +13,9 @@ services:
PORT: "1881" PORT: "1881"
TZ: ${TZ:-Europe/Stockholm} TZ: ${TZ:-Europe/Stockholm}
NEXTCLOUD_URL: ${NEXTCLOUD_URL:-https://cloud.hannesahlin.se} NEXTCLOUD_URL: ${NEXTCLOUD_URL:-https://cloud.hannesahlin.se}
COLLABORA_URL: ${COLLABORA_URL:-http://172.17.0.2:9980} COLLABORA_URL: ${COLLABORA_URL:-https://code:9980}
extra_hosts: extra_hosts:
- "host.docker.internal:host-gateway"
- "cloud.hannesahlin.se:${NC_HOST_IP:-192.168.1.26}" - "cloud.hannesahlin.se:${NC_HOST_IP:-192.168.1.26}"
volumes: volumes:
- nodered_collab_data:/data - nodered_collab_data:/data
+8
View File
@@ -35,6 +35,13 @@ NODERED_CONTAINER="${NODERED_CONTAINER:-nextcloud-node-red}"
NODERED_PORT="${NODERED_PORT:-1880}" NODERED_PORT="${NODERED_PORT:-1880}"
CODE_CONTAINER="${CODE_CONTAINER:-code}" CODE_CONTAINER="${CODE_CONTAINER:-code}"
COLLABORA_IP="${COLLABORA_IP:-}" COLLABORA_IP="${COLLABORA_IP:-}"
# Aldrig http://127.0.0.1:9980 eller http://host.docker.internal:9980
case "${COLLABORA_URL:-https://code:9980}" in
http://127.0.0.1:*|http://localhost:*|http://host.docker.internal:*|\
https://127.0.0.1:*|https://localhost:*|https://host.docker.internal:*|"")
COLLABORA_URL="https://code:9980" ;;
*) COLLABORA_URL="${COLLABORA_URL}" ;;
esac
NEXTCLOUD_URL="${NEXTCLOUD_URL:-https://cloud.hannesahlin.se}" NEXTCLOUD_URL="${NEXTCLOUD_URL:-https://cloud.hannesahlin.se}"
[ -n "${NEXTCLOUD_URL}" ] || NEXTCLOUD_URL="https://cloud.hannesahlin.se" [ -n "${NEXTCLOUD_URL}" ] || NEXTCLOUD_URL="https://cloud.hannesahlin.se"
@@ -69,6 +76,7 @@ PKG_SRC="${INSTALL_DIR}" \
NODERED_PORT="${NODERED_PORT}" \ NODERED_PORT="${NODERED_PORT}" \
CODE_CONTAINER="${CODE_CONTAINER}" \ CODE_CONTAINER="${CODE_CONTAINER}" \
COLLABORA_IP="${COLLABORA_IP}" \ COLLABORA_IP="${COLLABORA_IP}" \
COLLABORA_URL="${COLLABORA_URL}" \
NEXTCLOUD_URL="${NEXTCLOUD_URL}" \ NEXTCLOUD_URL="${NEXTCLOUD_URL}" \
INSTALL_USER="${INSTALL_USER:-}" \ INSTALL_USER="${INSTALL_USER:-}" \
bash "${SCRIPT_DIR}/scripts/integrate-into-nodered.sh" bash "${SCRIPT_DIR}/scripts/integrate-into-nodered.sh"
+152 -21
View File
@@ -146,6 +146,45 @@ code_bridge_ip() {
docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{if eq $k "bridge"}}{{$v.IPAddress}}{{end}}{{end}}' "${CODE_CONTAINER}" 2>/dev/null docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{if eq $k "bridge"}}{{$v.IPAddress}}{{end}}{{end}}' "${CODE_CONTAINER}" 2>/dev/null
} }
# Host-LAN-IP för extra_hosts (detektera; fallback 192.168.1.26)
detect_host_lan_ip() {
local ip=""
if command -v hostname >/dev/null 2>&1; then
ip="$(hostname -I 2>/dev/null | tr ' ' '\n' \
| grep -E '^(192\.168|10)\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1 || true)"
fi
if [ -z "${ip}" ] && command -v ip >/dev/null 2>&1; then
ip="$(ip -4 addr show scope global 2>/dev/null | awk '/inet /{print $2}' | cut -d/ -f1 \
| grep -E '^(192\.168|10)\.' | head -n1 || true)"
fi
if [ -z "${ip}" ]; then
warn "Kunde inte detektera host-LAN-IP — fallback 192.168.1.26"
ip="192.168.1.26"
fi
HOST_LAN_IP="${ip}"
log "Host-LAN-IP: ${HOST_LAN_IP}"
}
# Skriv NC_HOST_IP i nextcloud-node-red/.env så compose-substitutionen plockar upp den
write_nodered_env_ip() {
local envfile="${NODERED_DIR}/.env"
local existing=""
if [ -f "${envfile}" ]; then
existing="$(grep -E '^NC_HOST_IP=[[:alnum:].:]+' "${envfile}" 2>/dev/null | tail -n1 | cut -d= -f2 || true)"
fi
if [ -n "${existing}" ]; then
log "${envfile} har redan NC_HOST_IP=${existing} — behåller"
HOST_LAN_IP="${existing}"
return 0
fi
if [ -f "${envfile}" ] && grep -q '^NC_HOST_IP=' "${envfile}" 2>/dev/null; then
run_as_root sed -i "s|^NC_HOST_IP=.*|NC_HOST_IP=${HOST_LAN_IP}|" "${envfile}"
else
run_as_root bash -c "echo 'NC_HOST_IP=${HOST_LAN_IP}' >> '${envfile}'"
fi
log "NC_HOST_IP=${HOST_LAN_IP} → ${envfile}"
}
detect_code_ip() { detect_code_ip() {
local ip local ip
ip="$(code_bridge_ip)" ip="$(code_bridge_ip)"
@@ -202,6 +241,8 @@ resolve_paths() {
detect_nodered_dir detect_nodered_dir
detect_code_ip detect_code_ip
detect_host_lan_ip
write_nodered_env_ip
} }
stop_sidecar_if_any() { stop_sidecar_if_any() {
@@ -316,13 +357,85 @@ EOF
fi fi
} }
# Krav: docker network connect <nodered-nät> code — ignorerar already connected
connect_code_network() { connect_code_network() {
log "Connecting ${NODERED_CONTAINER} to Docker bridge (CODE)" local net
if docker network inspect bridge --format '{{range .Containers}}{{.Name}} {{end}}' 2>/dev/null | grep -q "${NODERED_CONTAINER}"; then net="$(docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{printf "%s\n" $k}}{{end}}' "${NODERED_CONTAINER}" 2>/dev/null \
log "Already connected to bridge" | grep -v '^bridge$' | head -n1)"
else [ -n "${net}" ] || die "No Node-RED compose network on ${NODERED_CONTAINER}"
docker network connect bridge "${NODERED_CONTAINER}" 2>/dev/null || warn "Could not connect bridge — check manually"
if docker network inspect "${net}" --format '{{range .Containers}}{{.Name}} {{end}}' 2>/dev/null | grep -qw "${CODE_CONTAINER}"; then
log "Already connected: ${CODE_CONTAINER} on ${net}"
return 0
fi fi
log "Connecting ${CODE_CONTAINER} → ${net}"
if ! docker network connect "${net}" "${CODE_CONTAINER}" 2>/dev/null; then
if docker network inspect "${net}" --format '{{range .Containers}}{{.Name}} {{end}}' 2>/dev/null | grep -qw "${CODE_CONTAINER}"; then
log "Already connected (race): ${CODE_CONTAINER} on ${net}"
else
warn "Could not connect ${CODE_CONTAINER} → ${net} — COLLABORA_URL fallar tillbaka till CODE-IP"
fi
fi
}
compose_service_name() {
local svc
svc="$(docker inspect -f '{{index .Config.Labels "com.docker.compose.service"}}' "${NODERED_CONTAINER}" 2>/dev/null || true)"
echo "${svc:-node-red}"
}
# Recreate utan 'docker compose down' — volymer (node-red-data) bevaras.
# ExtraHosts måste vara ifyllda efteråt.
recreate_nodered_service() {
local service extra
service="$(compose_service_name)"
log "Recreating ${service} (--no-deps --force-recreate, volymer bevaras)..."
if ! compose_run up -d --no-deps --force-recreate "${service}"; then
warn "force-recreate misslyckades — rm + up --no-deps (volymen node-red-data ligger kvar)"
run_as_root docker rm -f "${NODERED_CONTAINER}" 2>/dev/null || true
compose_run up -d --no-deps "${service}"
fi
for _ in $(seq 1 10); do
extra="$(docker inspect -f '{{len .HostConfig.ExtraHosts}}' "${NODERED_CONTAINER}" 2>/dev/null || echo 0)"
[ "${extra}" != "0" ] && break
sleep 1
done
if [ -z "${extra:-0}" ] || [ "${extra}" = "0" ]; then
die "ExtraHosts är [] på ${NODERED_CONTAINER} efter recreate — kontrollera extra_hosts i ${NODERED_DIR}/docker-compose.yml"
fi
log "ExtraHosts (${extra}): $(docker inspect -f '{{json .HostConfig.ExtraHosts}}' "${NODERED_CONTAINER}")"
}
# COLLABORA_URL: https://code:9980 primärt, fallback https://<CODE-IP>:9980.
# Aldrig http://127.0.0.1:9980 eller http://host.docker.internal:9980.
resolve_collabora_url() {
local url="${COLLABORA_URL:-https://code:9980}"
case "${url}" in
http://127.0.0.1:*|http://localhost:*|http://host.docker.internal:*|\
https://127.0.0.1:*|https://localhost:*|https://host.docker.internal:*)
warn "Otillåten COLLABORA_URL '${url}' — tvingar https://code:9980"
url="https://code:9980"
;;
esac
# Fallback till CODE-IP om 'code' inte kan resolvas från nod-containern
if ! docker exec "${NODERED_CONTAINER}" getent hosts code >/dev/null 2>&1; then
local ip
ip="$(docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{$v.IPAddress}} {{end}}' "${CODE_CONTAINER}" 2>/dev/null | awk '{print $1}')"
[ -n "${ip}" ] || ip="${COLLABORA_IP:-}"
if [ -n "${ip}" ]; then
warn "'code' inte resolvbart från ${NODERED_CONTAINER} — fallback https://${ip}:9980"
url="https://${ip}:9980"
else
die "Varken 'code'-DNS eller CODE-IP tillgänglig — kan inte sätta COLLABORA_URL"
fi
fi
COLLABORA_URL="${url}"
log "COLLABORA_URL=${COLLABORA_URL}"
} }
rebuild_nodered() { rebuild_nodered() {
@@ -334,7 +447,7 @@ rebuild_nodered() {
else else
compose_run build compose_run build
fi fi
compose_run up -d recreate_nodered_service
} }
wait_nodered() { wait_nodered() {
@@ -350,18 +463,35 @@ wait_nodered() {
} }
verify_code_from_nodered() { verify_code_from_nodered() {
local code_ip # Krav 7: getent i containern != 127.0.0.1/127.0.1.1 + curl mot CODE och Nextcloud
code_ip="$(code_bridge_ip)" local fail=0 h ip_out
[ -n "${code_ip}" ] || code_ip="${COLLABORA_IP}" for h in cloud.hannesahlin.se docs.hannesahlin.se host.docker.internal code; do
# CODE listens on HTTPS internally (HTTP returns empty reply) ip_out="$(docker exec "${NODERED_CONTAINER}" getent hosts "${h}" 2>/dev/null | awk '{print $1}' | head -n1 || true)"
local url="https://${code_ip}:9980/hosting/capabilities" if [ -z "${ip_out}" ]; then
log "Testing CODE from ${NODERED_CONTAINER}: ${url}" warn "getent ${h}: INGET SVAR"; fail=1
if docker exec "${NODERED_CONTAINER}" curl -sfk "${url}" >/dev/null 2>&1; then elif [ "${ip_out#127.}" != "${ip_out}" ] || [ "${ip_out}" = "::1" ]; then
log "OK: CODE reachable from Node-RED container" warn "getent ${h}: loopback (${ip_out}) — extra_hosts/translering trasig"; fail=1
else
log "getent ${h}: ${ip_out}"
fi
done
if docker exec "${NODERED_CONTAINER}" curl -sfk "${COLLABORA_URL}/hosting/capabilities" >/dev/null 2>&1; then
log "OK: CODE nås från Node-RED: ${COLLABORA_URL}/hosting/capabilities"
else else
warn "CODE not reachable — run: docker network connect bridge ${NODERED_CONTAINER}" warn "CODE inte nåbar: ${COLLABORA_URL}/hosting/capabilities"
warn "nc-collab-config collaboraUrl=https://${code_ip}:9980 (verifyTls=false)" warn "Kör: docker network connect \$(docker inspect -f '{{range \$k,\$_ := .NetworkSettings.Networks}}{{printf \"%s\\n\" \$k}}{{end}}' ${NODERED_CONTAINER} | head -n1) ${CODE_CONTAINER}"
fail=1
fi fi
if docker exec "${NODERED_CONTAINER}" curl -sfk https://cloud.hannesahlin.se/status.php >/dev/null 2>&1; then
log "OK: Nextcloud nås från Node-RED: https://cloud.hannesahlin.se/status.php"
else
warn "Nextcloud inte nåbar från Node-RED via https://cloud.hannesahlin.se/status.php"
fail=1
fi
return "${fail}"
} }
main() { main() {
@@ -377,13 +507,14 @@ main() {
patch_entrypoint patch_entrypoint
rebuild_nodered rebuild_nodered
connect_code_network connect_code_network
resolve_collabora_url
wait_nodered wait_nodered
verify_code_from_nodered if ! verify_code_from_nodered; then
warn "Verifiering hade fel — se varningarna ovan"
fi
local host_ip code_ip nc_url local host_ip nc_url
host_ip="$(hostname -I 2>/dev/null | awk '{print $1}' || echo "127.0.0.1")" host_ip="$(hostname -I 2>/dev/null | awk '{print $1}' || echo "127.0.0.1")"
code_ip="$(code_bridge_ip)"
[ -n "${code_ip}" ] || code_ip="${COLLABORA_IP}"
nc_url="${NEXTCLOUD_URL:-https://cloud.hannesahlin.se}" nc_url="${NEXTCLOUD_URL:-https://cloud.hannesahlin.se}"
cat <<EOF cat <<EOF
@@ -394,7 +525,7 @@ Integrating done — same Node-RED on port ${NODERED_PORT}
Create config first (gear → nc-collab-config): Create config first (gear → nc-collab-config):
Nextcloud: ${nc_url} Nextcloud: ${nc_url}
Collabora: https://${code_ip}:9980 (verifyTls=false) Collabora: ${COLLABORA_URL} (verifyTls=false — self-signed internt)
Public WOPI: ${nc_url} Public WOPI: ${nc_url}
Auth: username + app password Auth: username + app password