mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-04-06 10:11:58 -07:00
@@ -137,7 +137,7 @@ ENV LANG=C.UTF-8
|
|||||||
RUN apk add --no-cache bash mtr libbsd zip lsblk tzdata curl arp-scan iproute2 iproute2-ss nmap fping \
|
RUN apk add --no-cache bash mtr libbsd zip lsblk tzdata curl arp-scan iproute2 iproute2-ss nmap fping \
|
||||||
nmap-scripts traceroute nbtscan net-tools net-snmp-tools bind-tools awake ca-certificates \
|
nmap-scripts traceroute nbtscan net-tools net-snmp-tools bind-tools awake ca-certificates \
|
||||||
sqlite php83 php83-fpm php83-cgi php83-curl php83-sqlite3 php83-session python3 envsubst \
|
sqlite php83 php83-fpm php83-cgi php83-curl php83-sqlite3 php83-session python3 envsubst \
|
||||||
nginx supercronic shadow su-exec && \
|
nginx supercronic shadow su-exec jq && \
|
||||||
rm -Rf /var/cache/apk/* && \
|
rm -Rf /var/cache/apk/* && \
|
||||||
rm -Rf /etc/nginx && \
|
rm -Rf /etc/nginx && \
|
||||||
addgroup -g ${NETALERTX_GID} ${NETALERTX_GROUP} && \
|
addgroup -g ${NETALERTX_GID} ${NETALERTX_GROUP} && \
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ LOG_FILES=(
|
|||||||
LOG_DB_IS_LOCKED
|
LOG_DB_IS_LOCKED
|
||||||
LOG_NGINX_ERROR
|
LOG_NGINX_ERROR
|
||||||
)
|
)
|
||||||
|
|
||||||
sudo chmod 666 /var/run/docker.sock 2>/dev/null || true
|
sudo chmod 666 /var/run/docker.sock 2>/dev/null || true
|
||||||
sudo chown "$(id -u)":"$(id -g)" /workspaces
|
sudo chown "$(id -u)":"$(id -g)" /workspaces
|
||||||
sudo chmod 755 /workspaces
|
sudo chmod 755 /workspaces
|
||||||
@@ -55,6 +54,9 @@ sudo install -d -m 777 /tmp/log/plugins
|
|||||||
sudo rm -rf /entrypoint.d
|
sudo rm -rf /entrypoint.d
|
||||||
sudo ln -s "${SOURCE_DIR}/install/production-filesystem/entrypoint.d" /entrypoint.d
|
sudo ln -s "${SOURCE_DIR}/install/production-filesystem/entrypoint.d" /entrypoint.d
|
||||||
|
|
||||||
|
sudo rm -rf /services
|
||||||
|
sudo ln -s "${SOURCE_DIR}/install/production-filesystem/services" /services
|
||||||
|
|
||||||
sudo rm -rf "${NETALERTX_APP}"
|
sudo rm -rf "${NETALERTX_APP}"
|
||||||
sudo ln -s "${SOURCE_DIR}/" "${NETALERTX_APP}"
|
sudo ln -s "${SOURCE_DIR}/" "${NETALERTX_APP}"
|
||||||
|
|
||||||
@@ -88,8 +90,6 @@ sudo chmod 777 "${LOG_DB_IS_LOCKED}"
|
|||||||
|
|
||||||
sudo pkill -f python3 2>/dev/null || true
|
sudo pkill -f python3 2>/dev/null || true
|
||||||
|
|
||||||
sudo chmod -R 777 "${PY_SITE_PACKAGES}" "${NETALERTX_DATA}" 2>/dev/null || true
|
|
||||||
|
|
||||||
sudo chown -R "${NETALERTX_USER}:${NETALERTX_GROUP}" "${NETALERTX_APP}"
|
sudo chown -R "${NETALERTX_USER}:${NETALERTX_GROUP}" "${NETALERTX_APP}"
|
||||||
date +%s | sudo tee "${NETALERTX_FRONT}/buildtimestamp.txt" >/dev/null
|
date +%s | sudo tee "${NETALERTX_FRONT}/buildtimestamp.txt" >/dev/null
|
||||||
|
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ ENV LANG=C.UTF-8
|
|||||||
RUN apk add --no-cache bash mtr libbsd zip lsblk tzdata curl arp-scan iproute2 iproute2-ss nmap fping \
|
RUN apk add --no-cache bash mtr libbsd zip lsblk tzdata curl arp-scan iproute2 iproute2-ss nmap fping \
|
||||||
nmap-scripts traceroute nbtscan net-tools net-snmp-tools bind-tools awake ca-certificates \
|
nmap-scripts traceroute nbtscan net-tools net-snmp-tools bind-tools awake ca-certificates \
|
||||||
sqlite php83 php83-fpm php83-cgi php83-curl php83-sqlite3 php83-session python3 envsubst \
|
sqlite php83 php83-fpm php83-cgi php83-curl php83-sqlite3 php83-session python3 envsubst \
|
||||||
nginx supercronic shadow su-exec && \
|
nginx supercronic shadow su-exec jq && \
|
||||||
rm -Rf /var/cache/apk/* && \
|
rm -Rf /var/cache/apk/* && \
|
||||||
rm -Rf /etc/nginx && \
|
rm -Rf /etc/nginx && \
|
||||||
addgroup -g ${NETALERTX_GID} ${NETALERTX_GROUP} && \
|
addgroup -g ${NETALERTX_GID} ${NETALERTX_GROUP} && \
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
mtr \
|
mtr \
|
||||||
procps \
|
procps \
|
||||||
gosu \
|
gosu \
|
||||||
|
jq \
|
||||||
&& wget -qO /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg \
|
&& wget -qO /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg \
|
||||||
&& echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list \
|
&& echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
#
|
#
|
||||||
# Scan multiple interfaces (eth1 and eth0):
|
# Scan multiple interfaces (eth1 and eth0):
|
||||||
# SCAN_SUBNETS = [ '192.168.1.0/24 --interface=eth1', '192.168.1.0/24 --interface=eth0' ]
|
# SCAN_SUBNETS = [ '192.168.1.0/24 --interface=eth1', '192.168.1.0/24 --interface=eth0' ]
|
||||||
|
BACKEND_API_URL='/server'
|
||||||
DISCOVER_PLUGINS=True
|
DISCOVER_PLUGINS=True
|
||||||
SCAN_SUBNETS=['--localnet']
|
SCAN_SUBNETS=['--localnet']
|
||||||
TIMEZONE='Europe/Berlin'
|
TIMEZONE='Europe/Berlin'
|
||||||
@@ -100,6 +100,8 @@ MQTT_PASSWORD='passw0rd'
|
|||||||
MQTT_QOS=0
|
MQTT_QOS=0
|
||||||
MQTT_DELAY_SEC=2
|
MQTT_DELAY_SEC=2
|
||||||
|
|
||||||
|
GRAPHQL_PORT=20212
|
||||||
|
|
||||||
|
|
||||||
#-------------------IMPORTANT INFO-------------------#
|
#-------------------IMPORTANT INFO-------------------#
|
||||||
# This file is ingested by a python script, so if #
|
# This file is ingested by a python script, so if #
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ http://<server>:<GRAPHQL_PORT>/
|
|||||||
* [Sync](API_SYNC.md) – Synchronization between multiple NetAlertX instances
|
* [Sync](API_SYNC.md) – Synchronization between multiple NetAlertX instances
|
||||||
* [Logs](API_LOGS.md) – Purging of logs and adding to the event execution queue for user triggered events
|
* [Logs](API_LOGS.md) – Purging of logs and adding to the event execution queue for user triggered events
|
||||||
* [DB query](API_DBQUERY.md) (⚠ Internal) - Low level database access - use other endpoints if possible
|
* [DB query](API_DBQUERY.md) (⚠ Internal) - Low level database access - use other endpoints if possible
|
||||||
|
* `/server` (⚠ Internal) - Backend server endpoint for internal communication only - **do not use directly**
|
||||||
|
|
||||||
### MCP Server Bridge
|
### MCP Server Bridge
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,8 @@ function getApiBase()
|
|||||||
|
|
||||||
if(apiBase == "")
|
if(apiBase == "")
|
||||||
{
|
{
|
||||||
const protocol = window.location.protocol.replace(':', '');
|
// Default to the same-origin proxy bridge
|
||||||
const host = window.location.hostname;
|
apiBase = "/server";
|
||||||
const port = getSetting("GRAPHQL_PORT");
|
|
||||||
|
|
||||||
apiBase = `${protocol}://${host}:${port}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove trailing slash for consistency
|
// Remove trailing slash for consistency
|
||||||
|
|||||||
@@ -94,6 +94,19 @@ http {
|
|||||||
access_log /tmp/log/nginx-access.log main;
|
access_log /tmp/log/nginx-access.log main;
|
||||||
|
|
||||||
|
|
||||||
|
# Map 1: The Legacy Logic (Referer Match)
|
||||||
|
map "$http_referer|$http_host" $sec_legacy {
|
||||||
|
"~^https?://(?<ref_host>[^/:]+)(?::\d+)?/.*\|\k<ref_host>(?::\d+)?$" "TRUSTED";
|
||||||
|
default "UNTRUSTED";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Map 2: Strict Same-Origin Enforcement
|
||||||
|
map $http_sec_fetch_site $is_trusted {
|
||||||
|
"same-origin" "TRUSTED";
|
||||||
|
"" $sec_legacy; # Fallback only if header is missing
|
||||||
|
default "UNTRUSTED"; # Blocks 'same-site' and 'cross-site'
|
||||||
|
}
|
||||||
|
|
||||||
# Virtual host config
|
# Virtual host config
|
||||||
server {
|
server {
|
||||||
listen ${LISTEN_ADDR}:${PORT} default_server;
|
listen ${LISTEN_ADDR}:${PORT} default_server;
|
||||||
@@ -102,6 +115,30 @@ http {
|
|||||||
index index.php;
|
index index.php;
|
||||||
add_header X-Forwarded-Prefix "/app" always;
|
add_header X-Forwarded-Prefix "/app" always;
|
||||||
|
|
||||||
|
location /server/ {
|
||||||
|
# 1. Enforcement
|
||||||
|
if ($is_trusted != "TRUSTED") {
|
||||||
|
return 403;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Path Rewriting & Proxy
|
||||||
|
rewrite ^/server/(.*)$ /$1 break;
|
||||||
|
proxy_pass http://127.0.0.1:${BACKEND_PORT};
|
||||||
|
|
||||||
|
# 3. Performance & SSE (Per #1440)
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_cache off;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
client_max_body_size 50m;
|
||||||
|
proxy_read_timeout 3600s;
|
||||||
|
|
||||||
|
# 4. Standard Proxy Headers
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
location ~* \.php$ {
|
location ~* \.php$ {
|
||||||
# Set Cache-Control header to prevent caching on the first load
|
# Set Cache-Control header to prevent caching on the first load
|
||||||
|
|||||||
@@ -42,9 +42,32 @@ if [ "$(id -u)" -eq 0 ]; then
|
|||||||
NGINX_USER_DIRECTIVE="user root;"
|
NGINX_USER_DIRECTIVE="user root;"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# BACKEND_PORT RESOLUTION
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Priority 1: APP_CONF_OVERRIDE (parsed via jq)
|
||||||
|
# Priority 2: GRAPHQL_PORT env var
|
||||||
|
# Priority 3: Default 20212
|
||||||
|
|
||||||
|
# Default
|
||||||
|
export BACKEND_PORT=20212
|
||||||
|
|
||||||
|
# Check env var
|
||||||
|
if [ -n "${GRAPHQL_PORT:-}" ]; then
|
||||||
|
export BACKEND_PORT="${GRAPHQL_PORT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check override (highest priority)
|
||||||
|
if [ -n "${APP_CONF_OVERRIDE:-}" ]; then
|
||||||
|
override_port=$(echo "${APP_CONF_OVERRIDE}" | jq -r '.GRAPHQL_PORT // empty')
|
||||||
|
if [ -n "${override_port}" ]; then
|
||||||
|
export BACKEND_PORT="${override_port}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Shell check doesn't recognize envsubst variables
|
# Shell check doesn't recognize envsubst variables
|
||||||
# shellcheck disable=SC2016
|
# shellcheck disable=SC2016
|
||||||
if envsubst '${LISTEN_ADDR} ${PORT} ${NGINX_USER_DIRECTIVE}' < "${SYSTEM_NGINX_CONFIG_TEMPLATE}" > "${TEMP_CONFIG_FILE}" 2>/dev/null; then
|
if envsubst '${LISTEN_ADDR} ${PORT} ${NGINX_USER_DIRECTIVE} ${BACKEND_PORT}' < "${SYSTEM_NGINX_CONFIG_TEMPLATE}" > "${TEMP_CONFIG_FILE}" 2>/dev/null; then
|
||||||
mv "${TEMP_CONFIG_FILE}" "${SYSTEM_SERVICES_ACTIVE_CONFIG_FILE}"
|
mv "${TEMP_CONFIG_FILE}" "${SYSTEM_SERVICES_ACTIVE_CONFIG_FILE}"
|
||||||
else
|
else
|
||||||
echo "Note: Unable to write to ${SYSTEM_SERVICES_ACTIVE_CONFIG_FILE}. Using default configuration."
|
echo "Note: Unable to write to ${SYSTEM_SERVICES_ACTIVE_CONFIG_FILE}. Using default configuration."
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ def importConfigs(pm, db, all_plugins):
|
|||||||
)
|
)
|
||||||
conf.BACKEND_API_URL = ccd(
|
conf.BACKEND_API_URL = ccd(
|
||||||
"BACKEND_API_URL",
|
"BACKEND_API_URL",
|
||||||
"",
|
"/server",
|
||||||
c_d,
|
c_d,
|
||||||
"API URL",
|
"API URL",
|
||||||
'{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]}',
|
'{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]}',
|
||||||
|
|||||||
141
test/api_endpoints/test_nginx_proxy_security.py
Normal file
141
test/api_endpoints/test_nginx_proxy_security.py
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
import pytest
|
||||||
|
import requests
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Nginx listens on PORT, default 20211
|
||||||
|
PORT = os.environ.get("PORT", "20211")
|
||||||
|
BACKEND_PORT = os.environ.get("BACKEND_PORT", "20212")
|
||||||
|
BASE_URL = f"http://localhost:{PORT}/server/"
|
||||||
|
|
||||||
|
REQUEST_TIMEOUT = int(os.environ.get("REQUEST_TIMEOUT", 5))
|
||||||
|
|
||||||
|
def http_get(url, headers=None):
|
||||||
|
return requests.get(url, headers=headers, timeout=REQUEST_TIMEOUT)
|
||||||
|
|
||||||
|
|
||||||
|
def test_nginx_proxy_security_modern_check():
|
||||||
|
"""
|
||||||
|
Test that access is allowed when Sec-Fetch-Site is 'same-origin'.
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
"Sec-Fetch-Site": "same-origin"
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
response = http_get(BASE_URL, headers=headers)
|
||||||
|
# 200 (OK), 401 (Auth), 404 (Not Found on backend), or 502 (Bad Gateway) means Nginx let it through.
|
||||||
|
# 403 means Nginx blocked it.
|
||||||
|
assert response.status_code in [200, 401, 404, 502], f"Expected access allowed, got {response.status_code}"
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
pytest.fail("Could not connect to Nginx. Is it running?")
|
||||||
|
|
||||||
|
|
||||||
|
def test_nginx_proxy_security_legacy_check():
|
||||||
|
"""
|
||||||
|
Test that access is allowed when Sec-Fetch-Site is missing but Referer matches host.
|
||||||
|
This is for old tablets/phones which are not updated in the last few years.
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
# No Sec-Fetch-Site
|
||||||
|
"Referer": f"http://localhost:{PORT}/some/page"
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
response = http_get(BASE_URL, headers=headers)
|
||||||
|
assert response.status_code in [200, 401, 404, 502], f"Expected access allowed, got {response.status_code}"
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
pytest.fail("Could not connect to Nginx. Is it running?")
|
||||||
|
|
||||||
|
|
||||||
|
def test_nginx_proxy_security_block_cross_site():
|
||||||
|
"""
|
||||||
|
Test that access is BLOCKED when Sec-Fetch-Site is 'cross-site'.
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
"Sec-Fetch-Site": "cross-site"
|
||||||
|
}
|
||||||
|
response = http_get(BASE_URL, headers=headers)
|
||||||
|
assert response.status_code == 403, f"Expected 403 Forbidden, got {response.status_code}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_nginx_proxy_security_block_no_headers():
|
||||||
|
"""
|
||||||
|
Test that access is BLOCKED when no security headers are present.
|
||||||
|
"""
|
||||||
|
headers = {}
|
||||||
|
response = http_get(BASE_URL, headers=headers)
|
||||||
|
assert response.status_code == 403, f"Expected 403 Forbidden, got {response.status_code}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_nginx_proxy_security_block_same_site():
|
||||||
|
"""
|
||||||
|
Test that access is BLOCKED when Sec-Fetch-Site is 'same-site'.
|
||||||
|
(Strict same-origin enforcement)
|
||||||
|
"""
|
||||||
|
headers = {"Sec-Fetch-Site": "same-site"}
|
||||||
|
response = http_get(BASE_URL, headers=headers)
|
||||||
|
assert response.status_code == 403, f"Expected 403 for same-site, got {response.status_code}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_nginx_proxy_security_block_referer_suffix_spoof():
|
||||||
|
"""
|
||||||
|
Test that access is BLOCKED when Referer merely ends with the valid host.
|
||||||
|
"""
|
||||||
|
headers = {"Referer": f"http://attacker.com/path?target=localhost:{PORT}"}
|
||||||
|
response = http_get(BASE_URL, headers=headers)
|
||||||
|
assert response.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
def test_nginx_proxy_security_block_bad_referer():
|
||||||
|
"""
|
||||||
|
Test that access is BLOCKED when Sec-Fetch-Site is missing and Referer is external.
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
"Referer": "http://evil.com/page"
|
||||||
|
}
|
||||||
|
response = http_get(BASE_URL, headers=headers)
|
||||||
|
assert response.status_code == 403, f"Expected 403 Forbidden, got {response.status_code}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_nginx_proxy_security_block_subdomain_referer():
|
||||||
|
"""
|
||||||
|
Test that access is BLOCKED when Referer is a subdomain (same-site, not same-origin).
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
"Referer": f"http://subdomain.localhost:{PORT}/"
|
||||||
|
}
|
||||||
|
response = http_get(BASE_URL, headers=headers)
|
||||||
|
assert response.status_code == 403, f"Expected 403 for subdomain referer, got {response.status_code}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_nginx_proxy_security_legacy_protocol_agnostic():
|
||||||
|
"""
|
||||||
|
Test that the legacy check allows both http and https referers.
|
||||||
|
"""
|
||||||
|
headers = {"Referer": f"https://localhost:{PORT}/path"}
|
||||||
|
response = http_get(BASE_URL, headers=headers)
|
||||||
|
assert response.status_code in [200, 401, 404, 502]
|
||||||
|
|
||||||
|
|
||||||
|
def test_nginx_proxy_security_block_server_docs():
|
||||||
|
"""
|
||||||
|
Test that access to `/server/docs` is BLOCKED when navigating with browser (no referrer)
|
||||||
|
"""
|
||||||
|
url = f"http://localhost:{PORT}/server/docs"
|
||||||
|
try:
|
||||||
|
response = http_get(url)
|
||||||
|
# Backend may return 404 if it doesn't have the path; Nginx should never allow a 200 here.
|
||||||
|
assert response.status_code == 403, f"Expected 403 for /server/docs, got {response.status_code}"
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
pytest.fail("Could not connect to Nginx. Is it running?")
|
||||||
|
|
||||||
|
|
||||||
|
def test_nginx_proxy_security_allow_port():
|
||||||
|
"""
|
||||||
|
Test that access to `:20212/docs` is allowed by Nginx (should return 200).
|
||||||
|
"""
|
||||||
|
headers = {"Referer": f"https://localhost:{BACKEND_PORT}/path"}
|
||||||
|
url = f"http://localhost:{BACKEND_PORT}/docs"
|
||||||
|
try:
|
||||||
|
response = http_get(url, headers=headers)
|
||||||
|
assert response.status_code == 200, f"Expected 200 for /server/docs on allowed port, got {response.status_code}"
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
pytest.fail("Could not connect to Nginx. Is it running?")
|
||||||
@@ -17,6 +17,9 @@ else
|
|||||||
echo "ERROR: generate-configs.sh not found. Aborting."
|
echo "ERROR: generate-configs.sh not found. Aborting."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
echo "Development $(git rev-parse --short=8 HEAD)" | tee ".VERSION" >/dev/null
|
||||||
|
date +%s > front/buildtimestamp.txt
|
||||||
|
|
||||||
|
|
||||||
# --- 2. Build the Docker Image ---
|
# --- 2. Build the Docker Image ---
|
||||||
echo "--- Building 'netalertx-dev-test' image ---"
|
echo "--- Building 'netalertx-dev-test' image ---"
|
||||||
|
|||||||
@@ -749,7 +749,7 @@ def test_custom_port_with_unwritable_nginx_config_compose() -> None:
|
|||||||
# Container should exit due to inability to write nginx config and custom port.
|
# Container should exit due to inability to write nginx config and custom port.
|
||||||
assert result.returncode == 1
|
assert result.returncode == 1
|
||||||
assert "unable to write to /tmp/nginx/active-config/netalertx.conf" in lowered_output
|
assert "unable to write to /tmp/nginx/active-config/netalertx.conf" in lowered_output
|
||||||
assert "mv: can't create '/tmp/nginx/active-config/nginx.conf'" in lowered_output
|
|
||||||
|
|
||||||
|
|
||||||
def test_host_network_compose(tmp_path: pathlib.Path) -> None:
|
def test_host_network_compose(tmp_path: pathlib.Path) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user