/data and /tmp standarization

This commit is contained in:
Adam Outler
2025-11-04 22:26:35 +00:00
parent 90a07c61eb
commit 5b871865db
250 changed files with 7462 additions and 4940 deletions

View File

@@ -46,14 +46,16 @@ ARG INSTALL_DIR=/app
# NetAlertX app directories # NetAlertX app directories
ENV NETALERTX_APP=${INSTALL_DIR} ENV NETALERTX_APP=${INSTALL_DIR}
ENV NETALERTX_CONFIG=${NETALERTX_APP}/config ENV NETALERTX_DATA=/data
ENV NETALERTX_CONFIG=${NETALERTX_DATA}/config
ENV NETALERTX_FRONT=${NETALERTX_APP}/front ENV NETALERTX_FRONT=${NETALERTX_APP}/front
ENV NETALERTX_PLUGINS=${NETALERTX_FRONT}/plugins
ENV NETALERTX_SERVER=${NETALERTX_APP}/server ENV NETALERTX_SERVER=${NETALERTX_APP}/server
ENV NETALERTX_API=${NETALERTX_APP}/api ENV NETALERTX_API=/tmp/api
ENV NETALERTX_DB=${NETALERTX_APP}/db ENV NETALERTX_DB=${NETALERTX_DATA}/db
ENV NETALERTX_DB_FILE=${NETALERTX_DB}/app.db ENV NETALERTX_DB_FILE=${NETALERTX_DB}/app.db
ENV NETALERTX_BACK=${NETALERTX_APP}/back ENV NETALERTX_BACK=${NETALERTX_APP}/back
ENV NETALERTX_LOG=${NETALERTX_APP}/log ENV NETALERTX_LOG=/tmp/log
ENV NETALERTX_PLUGINS_LOG=${NETALERTX_LOG}/plugins ENV NETALERTX_PLUGINS_LOG=${NETALERTX_LOG}/plugins
ENV NETALERTX_CONFIG_FILE=${NETALERTX_CONFIG}/app.conf ENV NETALERTX_CONFIG_FILE=${NETALERTX_CONFIG}/app.conf
@@ -70,6 +72,7 @@ ENV LOG_EXECUTION_QUEUE=${NETALERTX_LOG}/execution_queue.log
ENV LOG_REPORT_OUTPUT_JSON=${NETALERTX_LOG}/report_output.json ENV LOG_REPORT_OUTPUT_JSON=${NETALERTX_LOG}/report_output.json
ENV LOG_STDOUT=${NETALERTX_LOG}/stdout.log ENV LOG_STDOUT=${NETALERTX_LOG}/stdout.log
ENV LOG_CROND=${NETALERTX_LOG}/crond.log ENV LOG_CROND=${NETALERTX_LOG}/crond.log
ENV LOG_NGINX_ERROR=${NETALERTX_LOG}/nginx-error.log
# System Services configuration files # System Services configuration files
ENV ENTRYPOINT_CHECKS=/entrypoint.d ENV ENTRYPOINT_CHECKS=/entrypoint.d
@@ -78,25 +81,26 @@ ENV SYSTEM_SERVICES_SCRIPTS=${SYSTEM_SERVICES}/scripts
ENV SYSTEM_SERVICES_CONFIG=${SYSTEM_SERVICES}/config ENV SYSTEM_SERVICES_CONFIG=${SYSTEM_SERVICES}/config
ENV SYSTEM_NGINX_CONFIG=${SYSTEM_SERVICES_CONFIG}/nginx ENV SYSTEM_NGINX_CONFIG=${SYSTEM_SERVICES_CONFIG}/nginx
ENV SYSTEM_NGINX_CONFIG_FILE=${SYSTEM_NGINX_CONFIG}/nginx.conf ENV SYSTEM_NGINX_CONFIG_FILE=${SYSTEM_NGINX_CONFIG}/nginx.conf
ENV SYSTEM_SERVICES_ACTIVE_CONFIG=${SYSTEM_NGINX_CONFIG}/conf.active ENV SYSTEM_SERVICES_ACTIVE_CONFIG=/tmp/nginx/active-config
ENV SYSTEM_SERVICES_PHP_FOLDER=${SYSTEM_SERVICES_CONFIG}/php ENV SYSTEM_SERVICES_PHP_FOLDER=${SYSTEM_SERVICES_CONFIG}/php
ENV SYSTEM_SERVICES_PHP_FPM_D=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.d ENV SYSTEM_SERVICES_PHP_FPM_D=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.d
ENV SYSTEM_SERVICES_CROND=${SYSTEM_SERVICES_CONFIG}/crond ENV SYSTEM_SERVICES_CROND=${SYSTEM_SERVICES_CONFIG}/crond
ENV SYSTEM_SERVICES_RUN=${SYSTEM_SERVICES}/run ENV SYSTEM_SERVICES_RUN=/tmp/run
ENV SYSTEM_SERVICES_RUN_TMP=${SYSTEM_SERVICES_RUN}/tmp ENV SYSTEM_SERVICES_RUN_TMP=${SYSTEM_SERVICES_RUN}/tmp
ENV SYSTEM_SERVICES_RUN_LOG=${SYSTEM_SERVICES_RUN}/logs ENV SYSTEM_SERVICES_RUN_LOG=${SYSTEM_SERVICES_RUN}/logs
ENV PHP_FPM_CONFIG_FILE=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.conf ENV PHP_FPM_CONFIG_FILE=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.conf
ENV READ_ONLY_FOLDERS="${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} ${SYSTEM_SERVICES} \ ENV READ_ONLY_FOLDERS="${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} ${SYSTEM_SERVICES} \
${SYSTEM_SERVICES_CONFIG} ${ENTRYPOINT_CHECKS}" ${SYSTEM_SERVICES_CONFIG} ${ENTRYPOINT_CHECKS}"
ENV READ_WRITE_FOLDERS="${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} ${NETALERTX_LOG} \ ENV READ_WRITE_FOLDERS="${NETALERTX_DATA} ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} \
${NETALERTX_PLUGINS_LOG} ${SYSTEM_SERVICES_RUN} ${SYSTEM_SERVICES_RUN_TMP} \ ${NETALERTX_LOG} ${NETALERTX_PLUGINS_LOG} ${SYSTEM_SERVICES_RUN} \
${SYSTEM_SERVICES_RUN_LOG}" ${SYSTEM_SERVICES_RUN_TMP} ${SYSTEM_SERVICES_RUN_LOG} \
${SYSTEM_SERVICES_ACTIVE_CONFIG}"
#Python environment #Python environment
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
ENV VIRTUAL_ENV=/opt/venv ENV VIRTUAL_ENV=/opt/venv
ENV VIRTUAL_ENV_BIN=/opt/venv/bin ENV VIRTUAL_ENV_BIN=/opt/venv/bin
ENV PYTHONPATH=${NETALERTX_APP}:${NETALERTX_SERVER}:${VIRTUAL_ENV}/lib/python3.12/site-packages ENV PYTHONPATH=${NETALERTX_APP}:${NETALERTX_SERVER}:${NETALERTX_PLUGINS}:${VIRTUAL_ENV}/lib/python3.12/site-packages
ENV PATH="${SYSTEM_SERVICES}:${VIRTUAL_ENV_BIN}:$PATH" ENV PATH="${SYSTEM_SERVICES}:${VIRTUAL_ENV_BIN}:$PATH"
# App Environment # App Environment
@@ -104,7 +108,7 @@ ENV LISTEN_ADDR=0.0.0.0
ENV PORT=20211 ENV PORT=20211
ENV NETALERTX_DEBUG=0 ENV NETALERTX_DEBUG=0
ENV VENDORSPATH=/app/back/ieee-oui.txt ENV VENDORSPATH=/app/back/ieee-oui.txt
ENV VENDORSPATH_NEWEST=/services/run/tmp/ieee-oui.txt ENV VENDORSPATH_NEWEST=${SYSTEM_SERVICES_RUN_TMP}/ieee-oui.txt
ENV ENVIRONMENT=alpine ENV ENVIRONMENT=alpine
ENV READ_ONLY_USER=readonly READ_ONLY_GROUP=readonly ENV READ_ONLY_USER=readonly READ_ONLY_GROUP=readonly
ENV NETALERTX_USER=netalertx NETALERTX_GROUP=netalertx ENV NETALERTX_USER=netalertx NETALERTX_GROUP=netalertx
@@ -128,8 +132,9 @@ COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} install/production-filesystem/
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 back ${NETALERTX_BACK} COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 back ${NETALERTX_BACK}
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 front ${NETALERTX_FRONT} COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 front ${NETALERTX_FRONT}
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 server ${NETALERTX_SERVER} COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 server ${NETALERTX_SERVER}
RUN install -d -o ${NETALERTX_USER} -g ${NETALERTX_GROUP} -m 755 ${NETALERTX_API} \
${NETALERTX_LOG} ${SYSTEM_SERVICES_RUN_TMP} ${SYSTEM_SERVICES_RUN_LOG} && \ # Create required folders with correct ownership and permissions
RUN install -d -o ${NETALERTX_USER} -g ${NETALERTX_GROUP} -m 700 ${READ_WRITE_FOLDERS} && \
sh -c "find ${NETALERTX_APP} -type f \( -name '*.sh' -o -name 'speedtest-cli' \) \ sh -c "find ${NETALERTX_APP} -type f \( -name '*.sh' -o -name 'speedtest-cli' \) \
-exec chmod 750 {} \;" -exec chmod 750 {} \;"
@@ -211,11 +216,14 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
# .devcontainer/scripts/generate-configs.sh # .devcontainer/scripts/generate-configs.sh
# The generator appends this stage to produce .devcontainer/Dockerfile. # The generator appends this stage to produce .devcontainer/Dockerfile.
# Prefer to place dev-only setup here; use setup.sh only for runtime fixes. # Prefer to place dev-only setup here; use setup.sh only for runtime fixes.
# Permissions in devcontainer should be of a brutalist nature. They will be
# Open and wide to avoid permission issues during development allowing max
# flexibility.
FROM runner AS netalertx-devcontainer FROM runner AS netalertx-devcontainer
ENV INSTALL_DIR=/app ENV INSTALL_DIR=/app
ENV PYTHONPATH=/workspaces/NetAlertX/test:/workspaces/NetAlertX/server:/app:/app/server:/opt/venv/lib/python3.12/site-packages:/usr/lib/python3.12/site-packages ENV PYTHONPATH=${PYTHONPATH}:/workspaces/NetAlertX/test:/workspaces/NetAlertX/server:/usr/lib/python3.12/site-packages
ENV PATH=/services:${PATH} ENV PATH=/services:${PATH}
ENV PHP_INI_SCAN_DIR=/services/config/php/conf.d:/etc/php83/conf.d ENV PHP_INI_SCAN_DIR=/services/config/php/conf.d:/etc/php83/conf.d
ENV LISTEN_ADDR=0.0.0.0 ENV LISTEN_ADDR=0.0.0.0
@@ -226,16 +234,28 @@ COPY .devcontainer/resources/devcontainer-overlay/ /
USER root USER root
# Install common tools, create user, and set up sudo # Install common tools, create user, and set up sudo
RUN apk add --no-cache git nano vim jq php83-pecl-xdebug py3-pip nodejs sudo gpgconf pytest \ RUN apk add --no-cache git nano vim jq php83-pecl-xdebug py3-pip nodejs sudo gpgconf pytest \
pytest-cov fish shfmt github-cli py3-yaml py3-docker-py docker-cli docker-cli-buildx \ pytest-cov zsh alpine-zsh-config shfmt github-cli py3-yaml py3-docker-py docker-cli docker-cli-buildx \
docker-cli-compose docker-cli-compose
RUN install -d -o netalertx -g netalertx -m 755 /services/php/modules && \ RUN install -d -o netalertx -g netalertx -m 755 /services/php/modules && \
cp -a /usr/lib/php83/modules/. /services/php/modules/ && \ cp -a /usr/lib/php83/modules/. /services/php/modules/ && \
echo "${NETALERTX_USER} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers echo "${NETALERTX_USER} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
RUN mkdir /workspaces && \ ENV SHELL=/bin/zsh
install -d -o netalertx -g netalertx -m 777 /services/run/logs && \
install -d -o netalertx -g netalertx -m 777 /app/run/tmp/client_body && \ RUN mkdir -p /workspaces && \
sed -i -e 's|:/app:|:/workspaces:|' /etc/passwd && \ install -d -m 777 /data /data/config /data/db && \
install -d -m 777 /tmp/log /tmp/log/plugins /tmp/api /tmp/run /tmp/nginx && \
install -d -m 777 /tmp/nginx/active-config /tmp/nginx/client_body /tmp/nginx/config && \
install -d -m 777 /tmp/nginx/fastcgi /tmp/nginx/proxy /tmp/nginx/scgi /tmp/nginx/uwsgi && \
install -d -m 777 /tmp/run/tmp /tmp/run/logs && \
chmod 777 /workspaces && \
chown -R netalertx:netalertx /data && \
chmod 666 /data/config/app.conf /data/db/app.db && \
chmod 1777 /tmp && \
install -d -o root -g root -m 1777 /tmp/.X11-unix && \
mkdir -p /home/netalertx && \
chown netalertx:netalertx /home/netalertx && \
sed -i -e 's#/app:#/workspaces:#' /etc/passwd && \
find /opt/venv -type d -exec chmod o+rwx {} \; find /opt/venv -type d -exec chmod o+rwx {} \;
USER netalertx USER netalertx

View File

@@ -0,0 +1,37 @@
{
"folders": [
{
"name": "NetAlertX Source",
"path": "."
},
{
"name": "💾 NetAlertX Data",
"path": "/data"
},
{
"name": "🔍 Active NetAlertX log",
"path": "/tmp/log"
},
{
"name": "🌐 Active NetAlertX nginx",
"path": "/tmp/nginx"
},
{
"name": "📊 Active NetAlertX api",
"path": "/tmp/api"
},
{
"name": "⚙️ Active NetAlertX run",
"path": "/tmp/run"
}
],
"settings": {
"terminal.integrated.suggest.enabled": true,
"terminal.integrated.defaultProfile.linux": "zsh",
"terminal.integrated.profiles.linux": {
"zsh": {
"path": "/usr/bin/fish"
}
}
}
}

View File

@@ -19,6 +19,17 @@ Common workflows (F1->Tasks: Run Task)
- Backend (GraphQL/Flask): `.devcontainer/scripts/restart-backend.sh` starts it under debugpy and logs to `/app/log/app.log` - Backend (GraphQL/Flask): `.devcontainer/scripts/restart-backend.sh` starts it under debugpy and logs to `/app/log/app.log`
- Frontend (nginx + PHP-FPM): Started via setup.sh; can be restarted by the task "Start Frontend (nginx and PHP-FPM)". - Frontend (nginx + PHP-FPM): Started via setup.sh; can be restarted by the task "Start Frontend (nginx and PHP-FPM)".
Production Container Evaulation
1. F1 → Tasks: Shutdown services ([Dev Container] Stop Frontend & Backend Services)
2. F1 → Tasks: Docker system and build prune ([Any] Docker system and build Prune)
3. F1 → Remote: Close Unused Forwarded Ports (VS Code command)
4. F1 → Tasks: Build & Launch Production (Build & Launch Prodcution Docker
5. visit http://localhost:20211
Unit tests
1. F1 → Tasks: Rebuild test container ([Any] Build Unit Test Docker image)
2. F1 → Test: Run all tests
Testing Testing
- pytest is installed via Alpine packages (py3-pytest, py3-pytest-cov). - pytest is installed via Alpine packages (py3-pytest, py3-pytest-cov).
- PYTHONPATH includes workspace and venv site-packages so tests can import `server/*` modules and third-party libs. - PYTHONPATH includes workspace and venv site-packages so tests can import `server/*` modules and third-party libs.

View File

@@ -0,0 +1,26 @@
# NetAlertX Multi-Folder Workspace
This repository uses a multi-folder workspace configuration to provide easy access to runtime directories.
## Opening the Multi-Folder Workspace
After the devcontainer builds, open the workspace file to access all folders:
1. **File****Open Workspace from File**
2. Select `NetAlertX.code-workspace`
Or use Command Palette (Ctrl+Shift+P / Cmd+Shift+P):
- Type: `Workspaces: Open Workspace from File`
- Select `NetAlertX.code-workspace`
## Workspace Folders
The workspace includes:
- **NetAlertX** - Main source code
- **/tmp** - Runtime temporary files
- **/tmp/api** - API response cache (JSON files)
- **/tmp/log** - Application and plugin logs
## Testing Configuration
Pytest is configured to only discover tests in the main `test/` directory, not in `/tmp` folders.

View File

@@ -2,6 +2,8 @@
"name": "NetAlertX DevContainer", "name": "NetAlertX DevContainer",
"remoteUser": "netalertx", "remoteUser": "netalertx",
"workspaceFolder": "/workspaces/NetAlertX", "workspaceFolder": "/workspaces/NetAlertX",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/NetAlertX,type=bind,consistency=cached",
"onCreateCommand": "mkdir -p /tmp/api /tmp/log",
"build": { "build": {
"dockerfile": "./Dockerfile", // Dockerfile generated by script "dockerfile": "./Dockerfile", // Dockerfile generated by script
"context": "../", // Context is the root of the repository "context": "../", // Context is the root of the repository
@@ -44,7 +46,8 @@
}, },
"postCreateCommand": { "postCreateCommand": {
"Install Pip Requirements": "/opt/venv/bin/pip3 install pytest docker debugpy" "Install Pip Requirements": "/opt/venv/bin/pip3 install pytest docker debugpy",
"Workspace Instructions": "printf '\n\n<> DevContainer Ready!\n\n📁 To access /tmp folders in the workspace:\n File → Open Workspace from File → NetAlertX.code-workspace\n\n📖 See .devcontainer/WORKSPACE.md for details\n\n'"
}, },
"postStartCommand": { "postStartCommand": {
"Start Environment":"${containerWorkspaceFolder}/.devcontainer/scripts/setup.sh", "Start Environment":"${containerWorkspaceFolder}/.devcontainer/scripts/setup.sh",
@@ -70,15 +73,25 @@
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"eamodio.gitlens", "eamodio.gitlens",
"alexcvzz.vscode-sqlite", "alexcvzz.vscode-sqlite",
"yzhang.markdown-all-in-one", "mkhl.shfmt",
"mkhl.shfmt" "charliermarsh.ruff",
"ms-python.flake8"
], ],
"settings": { "settings": {
"terminal.integrated.cwd": "${containerWorkspaceFolder}", "terminal.integrated.cwd": "${containerWorkspaceFolder}",
"terminal.integrated.profiles.linux": {
"zsh": {
"path": "/bin/zsh",
"args": ["-l"]
}
},
"terminal.integrated.defaultProfile.linux": "zsh",
// Python testing configuration // Python testing configuration
"python.testing.pytestEnabled": true, "python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false, "python.testing.unittestEnabled": false,
"python.testing.pytestArgs": ["test"], "python.testing.pytestArgs": ["test"],
"python.testing.cwd": "${containerWorkspaceFolder}",
// Make sure we discover tests and import server correctly // Make sure we discover tests and import server correctly
"python.analysis.extraPaths": [ "python.analysis.extraPaths": [
"/workspaces/NetAlertX", "/workspaces/NetAlertX",

View File

@@ -3,11 +3,14 @@
# .devcontainer/scripts/generate-configs.sh # .devcontainer/scripts/generate-configs.sh
# The generator appends this stage to produce .devcontainer/Dockerfile. # The generator appends this stage to produce .devcontainer/Dockerfile.
# Prefer to place dev-only setup here; use setup.sh only for runtime fixes. # Prefer to place dev-only setup here; use setup.sh only for runtime fixes.
# Permissions in devcontainer should be of a brutalist nature. They will be
# Open and wide to avoid permission issues during development allowing max
# flexibility.
FROM runner AS netalertx-devcontainer FROM runner AS netalertx-devcontainer
ENV INSTALL_DIR=/app ENV INSTALL_DIR=/app
ENV PYTHONPATH=/workspaces/NetAlertX/test:/workspaces/NetAlertX/server:/app:/app/server:/opt/venv/lib/python3.12/site-packages:/usr/lib/python3.12/site-packages ENV PYTHONPATH=${PYTHONPATH}:/workspaces/NetAlertX/test:/workspaces/NetAlertX/server:/usr/lib/python3.12/site-packages
ENV PATH=/services:${PATH} ENV PATH=/services:${PATH}
ENV PHP_INI_SCAN_DIR=/services/config/php/conf.d:/etc/php83/conf.d ENV PHP_INI_SCAN_DIR=/services/config/php/conf.d:/etc/php83/conf.d
ENV LISTEN_ADDR=0.0.0.0 ENV LISTEN_ADDR=0.0.0.0
@@ -18,16 +21,28 @@ COPY .devcontainer/resources/devcontainer-overlay/ /
USER root USER root
# Install common tools, create user, and set up sudo # Install common tools, create user, and set up sudo
RUN apk add --no-cache git nano vim jq php83-pecl-xdebug py3-pip nodejs sudo gpgconf pytest \ RUN apk add --no-cache git nano vim jq php83-pecl-xdebug py3-pip nodejs sudo gpgconf pytest \
pytest-cov fish shfmt github-cli py3-yaml py3-docker-py docker-cli docker-cli-buildx \ pytest-cov zsh alpine-zsh-config shfmt github-cli py3-yaml py3-docker-py docker-cli docker-cli-buildx \
docker-cli-compose docker-cli-compose
RUN install -d -o netalertx -g netalertx -m 755 /services/php/modules && \ RUN install -d -o netalertx -g netalertx -m 755 /services/php/modules && \
cp -a /usr/lib/php83/modules/. /services/php/modules/ && \ cp -a /usr/lib/php83/modules/. /services/php/modules/ && \
echo "${NETALERTX_USER} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers echo "${NETALERTX_USER} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
RUN mkdir /workspaces && \ ENV SHELL=/bin/zsh
install -d -o netalertx -g netalertx -m 777 /services/run/logs && \
install -d -o netalertx -g netalertx -m 777 /app/run/tmp/client_body && \ RUN mkdir -p /workspaces && \
sed -i -e 's|:/app:|:/workspaces:|' /etc/passwd && \ install -d -m 777 /data /data/config /data/db && \
install -d -m 777 /tmp/log /tmp/log/plugins /tmp/api /tmp/run /tmp/nginx && \
install -d -m 777 /tmp/nginx/active-config /tmp/nginx/client_body /tmp/nginx/config && \
install -d -m 777 /tmp/nginx/fastcgi /tmp/nginx/proxy /tmp/nginx/scgi /tmp/nginx/uwsgi && \
install -d -m 777 /tmp/run/tmp /tmp/run/logs && \
chmod 777 /workspaces && \
chown -R netalertx:netalertx /data && \
chmod 666 /data/config/app.conf /data/db/app.db && \
chmod 1777 /tmp && \
install -d -o root -g root -m 1777 /tmp/.X11-unix && \
mkdir -p /home/netalertx && \
chown netalertx:netalertx /home/netalertx && \
sed -i -e 's#/app:#/workspaces:#' /etc/passwd && \
find /opt/venv -type d -exec chmod o+rwx {} \; find /opt/venv -type d -exec chmod o+rwx {} \;
USER netalertx USER netalertx

View File

@@ -8,7 +8,9 @@ worker_processes auto;
pcre_jit on; pcre_jit on;
# Configures default error logger. # Configures default error logger.
error_log /app/log/nginx-error.log warn; error_log /tmp/log/nginx-error.log warn;
pid /tmp/run/nginx.pid;
events { events {
# The maximum number of simultaneous connections that can be opened by # The maximum number of simultaneous connections that can be opened by
@@ -19,11 +21,11 @@ events {
http { http {
# Mapping of temp paths for various nginx modules. # Mapping of temp paths for various nginx modules.
client_body_temp_path /services/run/tmp/client_body; client_body_temp_path /tmp/nginx/client_body;
proxy_temp_path /services/run/tmp/proxy; proxy_temp_path /tmp/nginx/proxy;
fastcgi_temp_path /services/run/tmp/fastcgi; fastcgi_temp_path /tmp/nginx/fastcgi;
uwsgi_temp_path /services/run/tmp/uwsgi; uwsgi_temp_path /tmp/nginx/uwsgi;
scgi_temp_path /services/run/tmp/scgi; scgi_temp_path /tmp/nginx/scgi;
# Includes mapping of file name extensions to MIME types of responses # Includes mapping of file name extensions to MIME types of responses
# and defines the default type. # and defines the default type.
@@ -89,7 +91,7 @@ http {
'"$http_user_agent" "$http_x_forwarded_for"'; '"$http_user_agent" "$http_x_forwarded_for"';
# Sets the path, format, and configuration for a buffered log write. # Sets the path, format, and configuration for a buffered log write.
access_log /app/log/nginx-access.log main; access_log /tmp/log/nginx-access.log main;
# Virtual host config # Virtual host config
@@ -104,7 +106,7 @@ http {
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
add_header Cache-Control "no-store"; add_header Cache-Control "no-store";
fastcgi_pass unix:/services/run/php.sock; fastcgi_pass unix:/tmp/run/php.sock;
include /services/config/nginx/fastcgi_params; include /services/config/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param SCRIPT_NAME $fastcgi_script_name;

View File

@@ -0,0 +1,47 @@
# NetAlertX devcontainer zsh configuration
# Keep this lightweight and deterministic so shells behave consistently.
export PATH="$HOME/.local/bin:$PATH"
export EDITOR=vim
export SHELL=/bin/zsh
# Start inside the workspace if it exists
if [ -d "/workspaces/NetAlertX" ]; then
cd /workspaces/NetAlertX
fi
# Enable basic completion and prompt helpers
autoload -Uz compinit promptinit colors
colors
compinit -u
promptinit
# Friendly prompt with virtualenv awareness
setopt PROMPT_SUBST
_venv_segment() {
if [ -n "$VIRTUAL_ENV" ]; then
printf '(%s) ' "${VIRTUAL_ENV:t}"
fi
}
PROMPT='%F{green}$(_venv_segment)%f%F{cyan}%n@%m%f %F{yellow}%~%f %# '
RPROMPT='%F{magenta}$(git rev-parse --abbrev-ref HEAD 2>/dev/null)%f'
# Sensible defaults
setopt autocd
setopt correct
setopt extendedglob
HISTFILE="$HOME/.zsh_history"
HISTSIZE=5000
SAVEHIST=5000
alias ll='ls -alF'
alias la='ls -A'
alias gs='git status -sb'
alias gp='git pull --ff-only'
# Ensure pyenv/virtualenv activate hooks adjust the prompt cleanly
if [ -f "$HOME/.zshrc.local" ]; then
source "$HOME/.zshrc.local"
fi

View File

@@ -1,7 +1,11 @@
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
read -r -p "Are you sure you want to destroy your host docker containers and images? Type YES to continue: " reply if [[ -n "${CONFIRM_PRUNE:-}" && "${CONFIRM_PRUNE}" == "YES" ]]; then
reply="YES"
else
read -r -p "Are you sure you want to destroy your host docker containers and images? Type YES to continue: " reply
fi
if [[ "${reply}" == "YES" ]]; then if [[ "${reply}" == "YES" ]]; then
docker system prune -af docker system prune -af

View File

@@ -1,184 +1,110 @@
#!/bin/bash #!/bin/bash
# Runtime setup for devcontainer (executed after container starts). # NetAlertX Devcontainer Setup Script
# Prefer building setup into resources/devcontainer-Dockerfile when possible. #
# Use this script for runtime-only adjustments (permissions, sockets, ownership, # This script forcefully resets all runtime state for a single-user devcontainer.
# and services managed without init) that are difficult at build time. # It is intentionally idempotent: every run wipes and recreates all relevant folders,
id # symlinks, and files, so the environment is always fresh and predictable.
#
# Define variables (paths, ports, environment) # - No conditional logic: everything is (re)created, overwritten, or reset unconditionally.
# - No security hardening: this is for disposable, local dev use only.
export APP_DIR="/app" # - No checks for existing files, mounts, or processes—just do the work.
export APP_COMMAND="/workspaces/NetAlertX/.devcontainer/scripts/restart-backend.sh" #
export PHP_FPM_BIN="/usr/sbin/php-fpm83" # If you add new runtime files or folders, add them to the creation/reset section below.
export CROND_BIN="/usr/sbin/crond -f" #
# Do not add if-then logic or error handling for missing/existing files. Simplicity is the goal.
export ALWAYS_FRESH_INSTALL=false SOURCE_DIR=${SOURCE_DIR:-/workspaces/NetAlertX}
export INSTALL_DIR=/app PY_SITE_PACKAGES="${VIRTUAL_ENV:-/opt/venv}/lib/python3.12/site-packages"
export LOGS_LOCATION=/app/logs SOURCE_SERVICES_DIR="${SOURCE_DIR}/install/production-filesystem/services"
export CONF_FILE="app.conf"
export DB_FILE="app.db"
export FULL_FILEDB_PATH="${INSTALL_DIR}/db/${DB_FILE}"
export OUI_FILE="/usr/share/arp-scan/ieee-oui.txt" # Define the path to ieee-oui.txt and ieee-iab.txt
export TZ=Europe/Paris
export PORT=20211
export SOURCE_DIR="/workspaces/NetAlertX"
LOG_FILES=(
LOG_APP
LOG_APP_FRONT
LOG_STDOUT
LOG_STDERR
LOG_EXECUTION_QUEUE
LOG_APP_PHP_ERRORS
LOG_IP_CHANGES
LOG_CROND
LOG_REPORT_OUTPUT_TXT
LOG_REPORT_OUTPUT_HTML
LOG_REPORT_OUTPUT_JSON
LOG_DB_IS_LOCKED
LOG_NGINX_ERROR
)
ensure_docker_socket_access() { sudo chmod 666 /var/run/docker.sock 2>/dev/null || true
local socket="/var/run/docker.sock" sudo chown "$(id -u)":"$(id -g)" /workspaces
if [ ! -S "${socket}" ]; then sudo chmod 755 /workspaces
echo "docker socket not present; skipping docker group configuration"
return
fi
local sock_gid killall php-fpm83 nginx crond python3 2>/dev/null || true
sock_gid=$(stat -c '%g' "${socket}" 2>/dev/null || true)
if [ -z "${sock_gid}" ]; then
echo "unable to determine docker socket gid; skipping docker group configuration"
return
fi
local group_entry="" # Mount ramdisks for volatile data
if command -v getent >/dev/null 2>&1; then sudo mount -t tmpfs -o size=100m,mode=0777 tmpfs /tmp/log 2>/dev/null || true
group_entry=$(getent group "${sock_gid}" 2>/dev/null || true) sudo mount -t tmpfs -o size=50m,mode=0777 tmpfs /tmp/api 2>/dev/null || true
else sudo mount -t tmpfs -o size=50m,mode=0777 tmpfs /tmp/run 2>/dev/null || true
group_entry=$(grep -E ":${sock_gid}:" /etc/group 2>/dev/null || true) sudo mount -t tmpfs -o size=50m,mode=0777 tmpfs /tmp/nginx 2>/dev/null || true
fi
local group_name="" sudo chmod 777 /tmp/log /tmp/api /tmp/run /tmp/nginx
if [ -n "${group_entry}" ]; then
group_name=$(echo "${group_entry}" | cut -d: -f1)
else
group_name="docker-host"
sudo addgroup -g "${sock_gid}" "${group_name}" 2>/dev/null || group_name=$(grep -E ":${sock_gid}:" /etc/group | head -n1 | cut -d: -f1)
fi
if [ -z "${group_name}" ]; then
echo "failed to resolve group for docker socket gid ${sock_gid}; skipping docker group configuration"
return
fi
if ! id -nG netalertx | tr ' ' '\n' | grep -qx "${group_name}"; then
sudo addgroup netalertx "${group_name}" 2>/dev/null || true
fi
}
main() {
echo "=== NetAlertX Development Container Setup ==="
killall php-fpm83 nginx crond python3 2>/dev/null
sleep 1
echo "Setting up ${SOURCE_DIR}..."
ensure_docker_socket_access
sudo chown $(id -u):$(id -g) /workspaces
sudo chmod 755 /workspaces
configure_source
echo "--- Starting Development Services ---"
configure_php
start_services
}
isRamDisk() {
if [ -z "$1" ] || [ ! -d "$1" ]; then
echo "Usage: isRamDisk <directory>" >&2
return 2
fi
local fstype
fstype=$(df -T "$1" | awk 'NR==2 {print $2}')
if [ "$fstype" = "tmpfs" ] || [ "$fstype" = "ramfs" ]; then
return 0 # Success (is a ramdisk)
else
return 1 # Failure (is not a ramdisk)
fi
}
# Setup source directory
configure_source() {
echo "[1/4] Configuring System..."
echo " -> Setting up /services permissions"
sudo chown -R netalertx /services
echo "[2/4] Configuring Source..."
echo " -> Cleaning up previous instances"
test -e ${NETALERTX_LOG} && sudo umount "${NETALERTX_LOG}" 2>/dev/null || true
test -e ${NETALERTX_API} && sudo umount "${NETALERTX_API}" 2>/dev/null || true
test -e ${NETALERTX_APP} && sudo rm -Rf ${NETALERTX_APP}/
echo " -> Linking source to ${NETALERTX_APP}"
sudo ln -s ${SOURCE_DIR}/ ${NETALERTX_APP}
echo " -> Mounting ramdisks for /log and /api"
mkdir -p ${NETALERTX_LOG} ${NETALERTX_API}
sudo mount -o uid=$(id -u netalertx),gid=$(id -g netalertx),mode=775 -t tmpfs -o size=256M tmpfs "${NETALERTX_LOG}"
sudo mount -o uid=$(id -u netalertx),gid=$(id -g netalertx),mode=775 -t tmpfs -o size=256M tmpfs "${NETALERTX_API}"
mkdir -p ${NETALERTX_PLUGINS_LOG}
touch ${NETALERTX_PLUGINS_LOG}/.dockerignore ${NETALERTX_API}/.dockerignore
# tmpfs mounts configured with netalertx ownership and 775 permissions above
touch /app/log/nginx_error.log
echo " -> Empty log"|tee ${INSTALL_DIR}/log/app.log \
${INSTALL_DIR}/log/app_front.log \
${INSTALL_DIR}/log/stdout.log
touch ${INSTALL_DIR}/log/stderr.log \
${INSTALL_DIR}/log/execution_queue.log
echo 0 > ${INSTALL_DIR}/log/db_is_locked.log
for f in ${INSTALL_DIR}/log/*.log; do
sudo chown netalertx:www-data $f
sudo chmod 664 $f
echo "" > $f
done
mkdir -p /app/log/plugins
sudo chown -R netalertx:www-data ${INSTALL_DIR}
while ps ax | grep -v grep | grep python3 > /dev/null; do
killall python3 &>/dev/null
sleep 0.2
done
sudo chmod 777 /opt/venv/lib/python3.12/site-packages/ && \
sudo chmod 005 /opt/venv/lib/python3.12/site-packages/
sudo chmod 666 /var/run/docker.sock
echo " -> Updating build timestamp"
date +%s > ${NETALERTX_FRONT}/buildtimestamp.txt
}
# configure_php: configure PHP-FPM and enable dev debug options
configure_php() {
echo "[3/4] Configuring PHP-FPM..."
sudo chown -R netalertx:netalertx ${SYSTEM_SERVICES_RUN} 2>/dev/null || true
}
# start_services: start crond, PHP-FPM, nginx and the application
start_services() {
echo "[4/4] Starting services"
sudo chmod +x /entrypoint.sh
setsid bash /entrypoint.sh&
sleep 1
}
sudo chmod 755 /app/
echo "Development $(git rev-parse --short=8 HEAD)"| sudo tee /app/.VERSION
# Run the main function
main
# create a services readme file
echo "This folder is auto-generated by the container and devcontainer setup.sh script." > /services/README.md
echo "Any changes here will be lost on rebuild. To make permanent changes, edit files in .devcontainer or production filesystem and rebuild the container." >> /services/README.md
echo "Only make temporary/test changes in this folder, then perform a rebuild to reset." >> /services/README.md
sudo rm -rf "${SYSTEM_NGINX_CONFIG}/conf.active"
sudo ln -s "${SYSTEM_SERVICES_ACTIVE_CONFIG}" "${SYSTEM_NGINX_CONFIG}/conf.active"
sudo rm -rf /entrypoint.d
sudo ln -s "${SOURCE_DIR}/install/production-filesystem/entrypoint.d" /entrypoint.d
sudo rm -rf "${NETALERTX_APP}"
sudo ln -s "${SOURCE_DIR}/" "${NETALERTX_APP}"
for dir in "${NETALERTX_DATA}" "${NETALERTX_CONFIG}" "${NETALERTX_DB}"; do
sudo install -d -m 777 "${dir}"
done
for dir in \
"${SYSTEM_SERVICES_RUN_LOG}" \
"${SYSTEM_SERVICES_ACTIVE_CONFIG}" \
"${NETALERTX_PLUGINS_LOG}" \
"/tmp/nginx/client_body" \
"/tmp/nginx/proxy" \
"/tmp/nginx/fastcgi" \
"/tmp/nginx/uwsgi" \
"/tmp/nginx/scgi"; do
sudo install -d -m 777 "${dir}"
done
# Create nginx temp subdirs with permissions
sudo mkdir -p "${SYSTEM_SERVICES_RUN_TMP}/client_body" "${SYSTEM_SERVICES_RUN_TMP}/proxy" "${SYSTEM_SERVICES_RUN_TMP}/fastcgi" "${SYSTEM_SERVICES_RUN_TMP}/uwsgi" "${SYSTEM_SERVICES_RUN_TMP}/scgi"
sudo chmod -R 777 "${SYSTEM_SERVICES_RUN_TMP}"
for var in "${LOG_FILES[@]}"; do
path=${!var}
dir=$(dirname "${path}")
sudo install -d -m 777 "${dir}"
touch "${path}"
done
printf '0\n' | sudo tee "${LOG_DB_IS_LOCKED}" >/dev/null
sudo chmod 777 "${LOG_DB_IS_LOCKED}"
sudo pkill -f python3 2>/dev/null || true
sudo chmod 777 "${PY_SITE_PACKAGES}" "${NETALERTX_DATA}" "${NETALERTX_DATA}"/* 2>/dev/null || true
sudo chmod 005 "${PY_SITE_PACKAGES}" 2>/dev/null || true
sudo chown -R "${NETALERTX_USER}:${NETALERTX_GROUP}" "${NETALERTX_APP}"
date +%s | sudo tee "${NETALERTX_FRONT}/buildtimestamp.txt" >/dev/null
sudo chmod 755 "${NETALERTX_APP}"
sudo chmod +x /entrypoint.sh
setsid bash /entrypoint.sh &
sleep 1
echo "Development $(git rev-parse --short=8 HEAD)" | sudo tee "${NETALERTX_APP}/.VERSION" >/dev/null

View File

@@ -1,4 +1,5 @@
.dockerignore .dockerignore
**/.dockerignore
.env .env
.git .git
.github .github

3
.flake8 Normal file
View File

@@ -0,0 +1,3 @@
[flake8]
max-line-length = 180
ignore = E221,E222,E251,E203

View File

@@ -18,7 +18,7 @@ Backend loop phases (see `server/__main__.py` and `server/plugin.py`): `once`, `
## Plugin patterns that matter ## Plugin patterns that matter
- Manifest lives at `front/plugins/<code_name>/config.json`; `code_name` == folder, `unique_prefix` drives settings and filenames (e.g., `ARPSCAN`). - Manifest lives at `front/plugins/<code_name>/config.json`; `code_name` == folder, `unique_prefix` drives settings and filenames (e.g., `ARPSCAN`).
- Control via settings: `<PREF>_RUN` (phase), `<PREF>_RUN_SCHD` (cron-like), `<PREF>_CMD` (script path), `<PREF>_RUN_TIMEOUT`, `<PREF>_WATCH` (diff columns). - Control via settings: `<PREF>_RUN` (phase), `<PREF>_RUN_SCHD` (cron-like), `<PREF>_CMD` (script path), `<PREF>_RUN_TIMEOUT`, `<PREF>_WATCH` (diff columns).
- Data contract: scripts write `/app/log/plugins/last_result.<PREF>.log` (pipedelimited: 9 required cols + optional 4). Use `front/plugins/plugin_helper.py`s `Plugin_Objects` to sanitize text and normalize MACs, then `write_result_file()`. - Data contract: scripts write `/tmp/log/plugins/last_result.<PREF>.log` (pipedelimited: 9 required cols + optional 4). Use `front/plugins/plugin_helper.py`s `Plugin_Objects` to sanitize text and normalize MACs, then `write_result_file()`.
- Device import: define `database_column_definitions` when creating/updating devices; watched fields trigger notifications. - Device import: define `database_column_definitions` when creating/updating devices; watched fields trigger notifications.
### Standard Plugin Formats ### Standard Plugin Formats
@@ -30,6 +30,7 @@ Backend loop phases (see `server/__main__.py` and `server/plugin.py`): `once`, `
* other: Miscellaneous plugins. Runs at various times. Data source: self / Template. * other: Miscellaneous plugins. Runs at various times. Data source: self / Template.
### Plugin logging & outputs ### Plugin logging & outputs
- Always check relevant logs first.
- Use logging as shown in other plugins. - Use logging as shown in other plugins.
- Collect results with `Plugin_Objects.add_object(...)` during processing and call `plugin_objects.write_result_file()` exactly once at the end of the script. - Collect results with `Plugin_Objects.add_object(...)` during processing and call `plugin_objects.write_result_file()` exactly once at the end of the script.
- Prefer to log a brief summary before writing (e.g., total objects added) to aid troubleshooting; keep logs concise at `info` level and use `verbose` or `debug` for extra context. - Prefer to log a brief summary before writing (e.g., total objects added) to aid troubleshooting; keep logs concise at `info` level and use `verbose` or `debug` for extra context.
@@ -46,18 +47,28 @@ Backend loop phases (see `server/__main__.py` and `server/plugin.py`): `once`, `
- DB helpers: prefer `server/db/db_helper.py` functions (e.g., `get_table_json`, device condition helpers) over raw SQL in new paths. - DB helpers: prefer `server/db/db_helper.py` functions (e.g., `get_table_json`, device condition helpers) over raw SQL in new paths.
## Dev workflow (devcontainer) ## Dev workflow (devcontainer)
- **Devcontainer philosophy: brutal simplicity.** One user, everything writable, completely idempotent. No permission checks, no conditional logic, no sudo needed. If something doesn't work, tear down the wall and rebuild - don't patch. We unit test permissions in the hardened build.
- **Permissions:** Never `chmod` or `chown` during operations. Everything is already writable. If you need permissions, the devcontainer setup is broken - fix `.devcontainer/scripts/setup.sh` or `.devcontainer/resources/devcontainer-Dockerfile` instead.
- **Files & Paths:** Use environment variables (`NETALERTX_DB`, `NETALERTX_LOG`, etc.) everywhere. `/data` for persistent config/db, `/tmp` for runtime logs/api/nginx state. Never hardcode `/data/db` or relative paths.
- **Database reset:** Use the `[Dev Container] Wipe and Regenerate Database` task. Kills backend, deletes `/data/{db,config}/*`, runs first-time setup scripts. Clean slate, no questions.
- Services: use tasks to (re)start backend and nginx/PHP-FPM. Backend runs with debugpy on 5678; attach a Python debugger if needed. - Services: use tasks to (re)start backend and nginx/PHP-FPM. Backend runs with debugpy on 5678; attach a Python debugger if needed.
- Run a plugin manually: `python3 front/plugins/<code_name>/script.py` (ensure `sys.path` includes `/app/front/plugins` and `/app/server` like the template). - Run a plugin manually: `python3 front/plugins/<code_name>/script.py` (ensure `sys.path` includes `/app/front/plugins` and `/app/server` like the template).
- Testing: pytest available via Alpine packages. Tests live in `test/`; app code is under `server/`. PYTHONPATH is preconfigured to include workspace and `/opt/venv` sitepackages. - Testing: pytest available via Alpine packages. Tests live in `test/`; app code is under `server/`. PYTHONPATH is preconfigured to include workspace and `/opt/venv` sitepackages.
- **Subprocess calls:** ALWAYS set explicit timeouts. Default to 60s minimum unless plugin config specifies otherwise. Nested subprocess calls (e.g., plugins calling external tools) need their own timeout - outer plugin timeout won't save you.
## What “done right” looks like ## What “done right” looks like
- When adding a plugin, start from `front/plugins/__template`, implement with `plugin_helper`, define manifest settings, and wire phase via `<PREF>_RUN`. Verify logs in `/app/log/plugins/` and data in `api/*.json`. - When adding a plugin, start from `front/plugins/__template`, implement with `plugin_helper`, define manifest settings, and wire phase via `<PREF>_RUN`. Verify logs in `/tmp/log/plugins/` and data in `api/*.json`.
- When introducing new config, define it once (core `ccd()` or plugin manifest) and read it via helpers everywhere. - When introducing new config, define it once (core `ccd()` or plugin manifest) and read it via helpers everywhere.
- When exposing new server functionality, add endpoints in `server/api_server/*` and keep authorization consistent; update UI by reading/writing JSON cache rather than bypassing the pipeline. - When exposing new server functionality, add endpoints in `server/api_server/*` and keep authorization consistent; update UI by reading/writing JSON cache rather than bypassing the pipeline.
## Useful references ## Useful references
- Docs: `docs/PLUGINS_DEV.md`, `docs/SETTINGS_SYSTEM.md`, `docs/API_*.md`, `docs/DEBUG_*.md` - Docs: `docs/PLUGINS_DEV.md`, `docs/SETTINGS_SYSTEM.md`, `docs/API_*.md`, `docs/DEBUG_*.md`
- Logs: backend `/app/log/app.log`, plugin logs under `/app/log/plugins/`, nginx/php logs under `/var/log/*` - Logs: All logs are under `/tmp/log/`. Plugin logs are very shortly under `/tmp/log/plugins/` until picked up by the server.
- plugin logs: `/tmp/log/app.log`
- backend logs: `/tmp/log/stdout.log` and `/tmp/log/stderr.log`
- frontend commands logs: `/tmp/log/app_front.log`
- php errors: `/tmp/log/app.php_errors.log`
- nginx logs: `/tmp/log/nginx-access.log` and `/tmp/log/nginx-error.log`
## Assistant expectations: ## Assistant expectations:
- Be concise, opinionated, and biased toward security and simplicity. - Be concise, opinionated, and biased toward security and simplicity.

8
.vscode/launch.json vendored
View File

@@ -29,6 +29,14 @@
"pathMappings": { "pathMappings": {
"/app": "${workspaceFolder}" "/app": "${workspaceFolder}"
} }
},
{
"name": "Python: Current File",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": true
} }
] ]
} }

18
.vscode/settings.json vendored
View File

@@ -11,13 +11,23 @@
// Let the Python extension invoke pytest via the interpreter; avoid hardcoded paths // Let the Python extension invoke pytest via the interpreter; avoid hardcoded paths
// Removed python.testing.pytestPath and legacy pytest.command overrides // Removed python.testing.pytestPath and legacy pytest.command overrides
"terminal.integrated.defaultProfile.linux": "fish", "terminal.integrated.defaultProfile.linux": "zsh",
"terminal.integrated.profiles.linux": { "terminal.integrated.profiles.linux": {
"fish": { "zsh": {
"path": "/usr/bin/fish" "path": "/bin/zsh"
} }
} }
, ,
// Fallback for older VS Code versions or schema validators that don't accept custom profiles // Fallback for older VS Code versions or schema validators that don't accept custom profiles
"terminal.integrated.shell.linux": "/usr/bin/fish" "terminal.integrated.shell.linux": "/usr/bin/zsh"
,
"python.linting.flake8Enabled": true,
"python.linting.enabled": true,
"python.linting.flake8Args": [
"--config=.flake8"
],
"python.formatting.provider": "black",
"python.formatting.blackArgs": [
"--line-length=180"
]
} }

91
.vscode/tasks.json vendored
View File

@@ -1,16 +1,27 @@
{ {
"version": "2.0.0", "version": "2.0.0",
"inputs": [
{
"id": "confirmPrune",
"type": "promptString",
"description": "DANGER! Type YES to confirm pruning all unused Docker resources. This will destroy containers, images, volumes, and networks!",
"default": ""
}
],
"tasks": [ "tasks": [
{ {
"label": "[Any POSIX] Generate Devcontainer Configs", "label": "[Any POSIX] Generate Devcontainer Configs",
"type": "shell", "type": "shell",
"command": ".devcontainer/scripts/generate-configs.sh", "command": ".devcontainer/scripts/generate-configs.sh",
"detail": "Generates devcontainer configs from the template. This must be run after changes to devcontainer to combine/merge them into the final config used by VS Code. Note- this has no bearing on the production or test image.",
"presentation": { "presentation": {
"echo": true, "echo": true,
"reveal": "always", "reveal": "always",
"panel": "shared", "panel": "shared",
"showReuseMessage": false "showReuseMessage": false,
"group": "POSIX Tasks"
}, },
"problemMatcher": [], "problemMatcher": [],
"group": { "group": {
"kind": "build", "kind": "build",
@@ -25,11 +36,18 @@
"label": "[Any] Docker system and build Prune", "label": "[Any] Docker system and build Prune",
"type": "shell", "type": "shell",
"command": ".devcontainer/scripts/confirm-docker-prune.sh", "command": ".devcontainer/scripts/confirm-docker-prune.sh",
"detail": "DANGER! Prunes all unused Docker resources (images, containers, volumes, networks). Any stopped container will be wiped and data will be lost. Use with caution.",
"options": {
"env": {
"CONFIRM_PRUNE": "${input:confirmPrune}"
}
},
"presentation": { "presentation": {
"echo": true, "echo": true,
"reveal": "always", "reveal": "always",
"panel": "shared", "panel": "shared",
"showReuseMessage": false "showReuseMessage": false,
"group": "Any"
}, },
"problemMatcher": [], "problemMatcher": [],
"group": { "group": {
@@ -45,6 +63,7 @@
"label": "[Dev Container] Re-Run Startup Script", "label": "[Dev Container] Re-Run Startup Script",
"type": "shell", "type": "shell",
"command": "./isDevContainer.sh || exit 1;/workspaces/NetAlertX/.devcontainer/scripts/setup.sh", "command": "./isDevContainer.sh || exit 1;/workspaces/NetAlertX/.devcontainer/scripts/setup.sh",
"detail": "The startup script runs directly after the container is started. It reprovisions permissions, links folders, and performs other setup tasks. Run this if you have made changes to the setup script or need to reprovision the container.",
"options": { "options": {
"cwd": "/workspaces/NetAlertX/.devcontainer/scripts" "cwd": "/workspaces/NetAlertX/.devcontainer/scripts"
}, },
@@ -65,6 +84,7 @@
"label": "[Dev Container] Start Backend (Python)", "label": "[Dev Container] Start Backend (Python)",
"type": "shell", "type": "shell",
"command": "./isDevContainer.sh || exit 1; /services/start-backend.sh", "command": "./isDevContainer.sh || exit 1; /services/start-backend.sh",
"detail": "Restarts the NetAlertX backend (Python) service in the dev container. This may take 5 seconds to be completely ready.",
"options": { "options": {
"cwd": "/workspaces/NetAlertX/.devcontainer/scripts" "cwd": "/workspaces/NetAlertX/.devcontainer/scripts"
}, },
@@ -73,7 +93,8 @@
"reveal": "always", "reveal": "always",
"panel": "shared", "panel": "shared",
"showReuseMessage": false, "showReuseMessage": false,
"clear": false "clear": false,
"group": "Devcontainer"
}, },
"problemMatcher": [], "problemMatcher": [],
"icon": { "icon": {
@@ -85,6 +106,7 @@
"label": "[Dev Container] Start CronD (Scheduler)", "label": "[Dev Container] Start CronD (Scheduler)",
"type": "shell", "type": "shell",
"command": "./isDevContainer.sh || exit 1; /services/start-crond.sh", "command": "./isDevContainer.sh || exit 1; /services/start-crond.sh",
"detail": "Stops and restarts the crond service.",
"options": { "options": {
"cwd": "/workspaces/NetAlertX/.devcontainer/scripts" "cwd": "/workspaces/NetAlertX/.devcontainer/scripts"
}, },
@@ -93,7 +115,8 @@
"reveal": "always", "reveal": "always",
"panel": "shared", "panel": "shared",
"showReuseMessage": false, "showReuseMessage": false,
"clear": false "clear": false,
"group": "Devcontainer"
}, },
"problemMatcher": [], "problemMatcher": [],
"icon": { "icon": {
@@ -105,6 +128,7 @@
"label": "[Dev Container] Start Frontend (nginx and PHP-FPM)", "label": "[Dev Container] Start Frontend (nginx and PHP-FPM)",
"type": "shell", "type": "shell",
"command": "./isDevContainer.sh || exit 1; /services/start-php-fpm.sh & /services/start-nginx.sh &", "command": "./isDevContainer.sh || exit 1; /services/start-php-fpm.sh & /services/start-nginx.sh &",
"detail": "Stops and restarts the NetAlertX frontend services (nginx and PHP-FPM) in the dev container. This launches almost instantly.",
"options": { "options": {
"cwd": "/workspaces/NetAlertX/.devcontainer/scripts" "cwd": "/workspaces/NetAlertX/.devcontainer/scripts"
@@ -114,7 +138,8 @@
"reveal": "always", "reveal": "always",
"panel": "shared", "panel": "shared",
"showReuseMessage": false, "showReuseMessage": false,
"clear": false "clear": false,
"group": "Devcontainer"
}, },
"problemMatcher": [], "problemMatcher": [],
"icon": { "icon": {
@@ -126,6 +151,7 @@
"label": "[Dev Container] Stop Frontend & Backend Services", "label": "[Dev Container] Stop Frontend & Backend Services",
"type": "shell", "type": "shell",
"command": "./isDevContainer.sh || exit 1; pkill -f 'php-fpm83|nginx|crond|python3' || true", "command": "./isDevContainer.sh || exit 1; pkill -f 'php-fpm83|nginx|crond|python3' || true",
"detail": "Stops all NetAlertX services running in the dev container.",
"options": { "options": {
"cwd": "/workspaces/NetAlertX/.devcontainer/scripts" "cwd": "/workspaces/NetAlertX/.devcontainer/scripts"
}, },
@@ -133,7 +159,8 @@
"echo": true, "echo": true,
"reveal": "always", "reveal": "always",
"panel": "shared", "panel": "shared",
"showReuseMessage": false "showReuseMessage": false,
"group": "Devcontainer"
}, },
"problemMatcher": [], "problemMatcher": [],
"icon": { "icon": {
@@ -142,29 +169,55 @@
} }
}, },
{ {
"label": "[Dev Container] List NetAlertX Ports", "label": "[Any] Build Unit Test Docker image",
"type": "shell", "type": "shell",
"command": "list-ports.sh", "command": "docker buildx build -t netalertx-test . && echo '🧪 Unit Test Docker image built: netalertx-test'",
"options": { "detail": "This must be run after changes to the container. Unit testing will not register changes until after this image is rebuilt. It takes about 30 seconds to build unless changes to the venv stage are made. venv takes 90s alone.",
"cwd": "/workspaces/NetAlertX/.devcontainer/scripts"
},
"presentation": { "presentation": {
"echo": true, "echo": true,
"reveal": "always", "reveal": "always",
"panel": "shared", "panel": "shared",
"showReuseMessage": false "showReuseMessage": false,
"group": "Any"
},
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": false
},
"icon": {
"id": "beaker",
"color": "terminal.ansiBlue"
}
},
{
"label": "[Dev Container] Wipe and Regenerate Database",
"type": "shell",
"command": "killall 'python3' || true && sleep 1 && rm -rf /data/db/* /data/config/* && bash /entrypoint.d/15-first-run-config.sh && bash /entrypoint.d/20-first-run-db.sh && echo '✅ Database and config wiped and regenerated'",
"detail": "Wipes devcontainer db and config. Provides a fresh start in devcontainer, run this task, then run the Rerun Startup Task",
"options": {},
"presentation": {
"echo": true,
"reveal": "always",
"panel": "shared",
"showReuseMessage": false,
"group": "Devcontainer"
}, },
"problemMatcher": [], "problemMatcher": [],
"icon": { "icon": {
"id": "output", "id": "database",
"color": "terminal.ansiBlue" "color": "terminal.ansiRed"
} }
} },
,
{ {
"label": "[Any] Build Unit Test Docker image", "label": "Build & Launch Prodcution Docker Container",
"type": "shell", "type": "shell",
"command": "docker buildx build -t netalertx-test . && echo '🧪 Unit Test Docker image built: netalertx-test'", "command": "docker compose up -d --build --force-recreate",
"detail": "Before launching, ensure VSCode Ports are closed and services are stopped. Tasks: Stop Frontend & Backend Services & Remote: Close Unused Forwarded Ports to ensure proper operation of the new container.",
"options": {
"cwd": "/workspaces/NetAlertX"
},
"presentation": { "presentation": {
"echo": true, "echo": true,
"reveal": "always", "reveal": "always",
@@ -177,7 +230,7 @@
"isDefault": false "isDefault": false
}, },
"icon": { "icon": {
"id": "beaker", "id": "package",
"color": "terminal.ansiBlue" "color": "terminal.ansiBlue"
} }
} }

View File

@@ -43,14 +43,16 @@ ARG INSTALL_DIR=/app
# NetAlertX app directories # NetAlertX app directories
ENV NETALERTX_APP=${INSTALL_DIR} ENV NETALERTX_APP=${INSTALL_DIR}
ENV NETALERTX_CONFIG=${NETALERTX_APP}/config ENV NETALERTX_DATA=/data
ENV NETALERTX_CONFIG=${NETALERTX_DATA}/config
ENV NETALERTX_FRONT=${NETALERTX_APP}/front ENV NETALERTX_FRONT=${NETALERTX_APP}/front
ENV NETALERTX_PLUGINS=${NETALERTX_FRONT}/plugins
ENV NETALERTX_SERVER=${NETALERTX_APP}/server ENV NETALERTX_SERVER=${NETALERTX_APP}/server
ENV NETALERTX_API=${NETALERTX_APP}/api ENV NETALERTX_API=/tmp/api
ENV NETALERTX_DB=${NETALERTX_APP}/db ENV NETALERTX_DB=${NETALERTX_DATA}/db
ENV NETALERTX_DB_FILE=${NETALERTX_DB}/app.db ENV NETALERTX_DB_FILE=${NETALERTX_DB}/app.db
ENV NETALERTX_BACK=${NETALERTX_APP}/back ENV NETALERTX_BACK=${NETALERTX_APP}/back
ENV NETALERTX_LOG=${NETALERTX_APP}/log ENV NETALERTX_LOG=/tmp/log
ENV NETALERTX_PLUGINS_LOG=${NETALERTX_LOG}/plugins ENV NETALERTX_PLUGINS_LOG=${NETALERTX_LOG}/plugins
ENV NETALERTX_CONFIG_FILE=${NETALERTX_CONFIG}/app.conf ENV NETALERTX_CONFIG_FILE=${NETALERTX_CONFIG}/app.conf
@@ -67,6 +69,7 @@ ENV LOG_EXECUTION_QUEUE=${NETALERTX_LOG}/execution_queue.log
ENV LOG_REPORT_OUTPUT_JSON=${NETALERTX_LOG}/report_output.json ENV LOG_REPORT_OUTPUT_JSON=${NETALERTX_LOG}/report_output.json
ENV LOG_STDOUT=${NETALERTX_LOG}/stdout.log ENV LOG_STDOUT=${NETALERTX_LOG}/stdout.log
ENV LOG_CROND=${NETALERTX_LOG}/crond.log ENV LOG_CROND=${NETALERTX_LOG}/crond.log
ENV LOG_NGINX_ERROR=${NETALERTX_LOG}/nginx-error.log
# System Services configuration files # System Services configuration files
ENV ENTRYPOINT_CHECKS=/entrypoint.d ENV ENTRYPOINT_CHECKS=/entrypoint.d
@@ -75,25 +78,26 @@ ENV SYSTEM_SERVICES_SCRIPTS=${SYSTEM_SERVICES}/scripts
ENV SYSTEM_SERVICES_CONFIG=${SYSTEM_SERVICES}/config ENV SYSTEM_SERVICES_CONFIG=${SYSTEM_SERVICES}/config
ENV SYSTEM_NGINX_CONFIG=${SYSTEM_SERVICES_CONFIG}/nginx ENV SYSTEM_NGINX_CONFIG=${SYSTEM_SERVICES_CONFIG}/nginx
ENV SYSTEM_NGINX_CONFIG_FILE=${SYSTEM_NGINX_CONFIG}/nginx.conf ENV SYSTEM_NGINX_CONFIG_FILE=${SYSTEM_NGINX_CONFIG}/nginx.conf
ENV SYSTEM_SERVICES_ACTIVE_CONFIG=${SYSTEM_NGINX_CONFIG}/conf.active ENV SYSTEM_SERVICES_ACTIVE_CONFIG=/tmp/nginx/active-config
ENV SYSTEM_SERVICES_PHP_FOLDER=${SYSTEM_SERVICES_CONFIG}/php ENV SYSTEM_SERVICES_PHP_FOLDER=${SYSTEM_SERVICES_CONFIG}/php
ENV SYSTEM_SERVICES_PHP_FPM_D=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.d ENV SYSTEM_SERVICES_PHP_FPM_D=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.d
ENV SYSTEM_SERVICES_CROND=${SYSTEM_SERVICES_CONFIG}/crond ENV SYSTEM_SERVICES_CROND=${SYSTEM_SERVICES_CONFIG}/crond
ENV SYSTEM_SERVICES_RUN=${SYSTEM_SERVICES}/run ENV SYSTEM_SERVICES_RUN=/tmp/run
ENV SYSTEM_SERVICES_RUN_TMP=${SYSTEM_SERVICES_RUN}/tmp ENV SYSTEM_SERVICES_RUN_TMP=${SYSTEM_SERVICES_RUN}/tmp
ENV SYSTEM_SERVICES_RUN_LOG=${SYSTEM_SERVICES_RUN}/logs ENV SYSTEM_SERVICES_RUN_LOG=${SYSTEM_SERVICES_RUN}/logs
ENV PHP_FPM_CONFIG_FILE=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.conf ENV PHP_FPM_CONFIG_FILE=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.conf
ENV READ_ONLY_FOLDERS="${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} ${SYSTEM_SERVICES} \ ENV READ_ONLY_FOLDERS="${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} ${SYSTEM_SERVICES} \
${SYSTEM_SERVICES_CONFIG} ${ENTRYPOINT_CHECKS}" ${SYSTEM_SERVICES_CONFIG} ${ENTRYPOINT_CHECKS}"
ENV READ_WRITE_FOLDERS="${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} ${NETALERTX_LOG} \ ENV READ_WRITE_FOLDERS="${NETALERTX_DATA} ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} \
${NETALERTX_PLUGINS_LOG} ${SYSTEM_SERVICES_RUN} ${SYSTEM_SERVICES_RUN_TMP} \ ${NETALERTX_LOG} ${NETALERTX_PLUGINS_LOG} ${SYSTEM_SERVICES_RUN} \
${SYSTEM_SERVICES_RUN_LOG} ${SYSTEM_NGINX_CONFIG}" ${SYSTEM_SERVICES_RUN_TMP} ${SYSTEM_SERVICES_RUN_LOG} \
${SYSTEM_SERVICES_ACTIVE_CONFIG}"
#Python environment #Python environment
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
ENV VIRTUAL_ENV=/opt/venv ENV VIRTUAL_ENV=/opt/venv
ENV VIRTUAL_ENV_BIN=/opt/venv/bin ENV VIRTUAL_ENV_BIN=/opt/venv/bin
ENV PYTHONPATH=${NETALERTX_APP}:${NETALERTX_SERVER}:${VIRTUAL_ENV}/lib/python3.12/site-packages ENV PYTHONPATH=${NETALERTX_APP}:${NETALERTX_SERVER}:${NETALERTX_PLUGINS}:${VIRTUAL_ENV}/lib/python3.12/site-packages
ENV PATH="${SYSTEM_SERVICES}:${VIRTUAL_ENV_BIN}:$PATH" ENV PATH="${SYSTEM_SERVICES}:${VIRTUAL_ENV_BIN}:$PATH"
# App Environment # App Environment
@@ -101,7 +105,7 @@ ENV LISTEN_ADDR=0.0.0.0
ENV PORT=20211 ENV PORT=20211
ENV NETALERTX_DEBUG=0 ENV NETALERTX_DEBUG=0
ENV VENDORSPATH=/app/back/ieee-oui.txt ENV VENDORSPATH=/app/back/ieee-oui.txt
ENV VENDORSPATH_NEWEST=/services/run/tmp/ieee-oui.txt ENV VENDORSPATH_NEWEST=${SYSTEM_SERVICES_RUN_TMP}/ieee-oui.txt
ENV ENVIRONMENT=alpine ENV ENVIRONMENT=alpine
ENV READ_ONLY_USER=readonly READ_ONLY_GROUP=readonly ENV READ_ONLY_USER=readonly READ_ONLY_GROUP=readonly
ENV NETALERTX_USER=netalertx NETALERTX_GROUP=netalertx ENV NETALERTX_USER=netalertx NETALERTX_GROUP=netalertx
@@ -125,8 +129,9 @@ COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} install/production-filesystem/
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 back ${NETALERTX_BACK} COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 back ${NETALERTX_BACK}
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 front ${NETALERTX_FRONT} COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 front ${NETALERTX_FRONT}
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 server ${NETALERTX_SERVER} COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 server ${NETALERTX_SERVER}
RUN install -d -o ${NETALERTX_USER} -g ${NETALERTX_GROUP} -m 755 ${NETALERTX_API} \
${NETALERTX_LOG} ${SYSTEM_SERVICES_RUN_TMP} ${SYSTEM_SERVICES_RUN_LOG} && \ # Create required folders with correct ownership and permissions
RUN install -d -o ${NETALERTX_USER} -g ${NETALERTX_GROUP} -m 700 ${READ_WRITE_FOLDERS} && \
sh -c "find ${NETALERTX_APP} -type f \( -name '*.sh' -o -name 'speedtest-cli' \) \ sh -c "find ${NETALERTX_APP} -type f \( -name '*.sh' -o -name 'speedtest-cli' \) \
-exec chmod 750 {} \;" -exec chmod 750 {} \;"

View File

@@ -49,14 +49,15 @@ FROM debian:bookworm-slim
# NetAlertX app directories # NetAlertX app directories
ENV INSTALL_DIR=/app ENV INSTALL_DIR=/app
ENV NETALERTX_APP=${INSTALL_DIR} ENV NETALERTX_APP=${INSTALL_DIR}
ENV NETALERTX_CONFIG=${NETALERTX_APP}/config ENV NETALERTX_DATA=/data
ENV NETALERTX_CONFIG=${NETALERTX_DATA}/config
ENV NETALERTX_FRONT=${NETALERTX_APP}/front ENV NETALERTX_FRONT=${NETALERTX_APP}/front
ENV NETALERTX_SERVER=${NETALERTX_APP}/server ENV NETALERTX_SERVER=${NETALERTX_APP}/server
ENV NETALERTX_API=${NETALERTX_APP}/api ENV NETALERTX_API=/tmp/api
ENV NETALERTX_DB=${NETALERTX_APP}/db ENV NETALERTX_DB=${NETALERTX_DATA}/db
ENV NETALERTX_DB_FILE=${NETALERTX_DB}/app.db ENV NETALERTX_DB_FILE=${NETALERTX_DB}/app.db
ENV NETALERTX_BACK=${NETALERTX_APP}/back ENV NETALERTX_BACK=${NETALERTX_APP}/back
ENV NETALERTX_LOG=${NETALERTX_APP}/log ENV NETALERTX_LOG=/tmp/log
ENV NETALERTX_PLUGINS_LOG=${NETALERTX_LOG}/plugins ENV NETALERTX_PLUGINS_LOG=${NETALERTX_LOG}/plugins
# NetAlertX log files # NetAlertX log files
@@ -72,17 +73,19 @@ ENV LOG_EXECUTION_QUEUE=${NETALERTX_LOG}/execution_queue.log
ENV LOG_REPORT_OUTPUT_JSON=${NETALERTX_LOG}/report_output.json ENV LOG_REPORT_OUTPUT_JSON=${NETALERTX_LOG}/report_output.json
ENV LOG_STDOUT=${NETALERTX_LOG}/stdout.log ENV LOG_STDOUT=${NETALERTX_LOG}/stdout.log
ENV LOG_CROND=${NETALERTX_LOG}/crond.log ENV LOG_CROND=${NETALERTX_LOG}/crond.log
ENV LOG_NGINX_ERROR=${NETALERTX_LOG}/nginx-error.log
# System Services configuration files # System Services configuration files
ENV SYSTEM_SERVICES=/services ENV SYSTEM_SERVICES=/services
ENV SYSTEM_SERVICES_CONFIG=${SYSTEM_SERVICES}/config ENV SYSTEM_SERVICES_CONFIG=${SYSTEM_SERVICES}/config
ENV SYSTEM_NGINIX_CONFIG=${SYSTEM_SERVICES_CONFIG}/nginx ENV SYSTEM_NGINIX_CONFIG=${SYSTEM_SERVICES_CONFIG}/nginx
ENV SYSTEM_NGINX_CONFIG_FILE=${SYSTEM_NGINIX_CONFIG}/nginx.conf ENV SYSTEM_NGINX_CONFIG_FILE=${SYSTEM_NGINIX_CONFIG}/nginx.conf
ENV SYSTEM_SERVICES_ACTIVE_CONFIG=/tmp/nginx/active-config
ENV NETALERTX_CONFIG_FILE=${NETALERTX_CONFIG}/app.conf ENV NETALERTX_CONFIG_FILE=${NETALERTX_CONFIG}/app.conf
ENV SYSTEM_SERVICES_PHP_FOLDER=${SYSTEM_SERVICES_CONFIG}/php ENV SYSTEM_SERVICES_PHP_FOLDER=${SYSTEM_SERVICES_CONFIG}/php
ENV SYSTEM_SERVICES_PHP_FPM_D=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.d ENV SYSTEM_SERVICES_PHP_FPM_D=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.d
ENV SYSTEM_SERVICES_CROND=${SYSTEM_SERVICES_CONFIG}/crond ENV SYSTEM_SERVICES_CROND=${SYSTEM_SERVICES_CONFIG}/crond
ENV SYSTEM_SERVICES_RUN=${SYSTEM_SERVICES}/run ENV SYSTEM_SERVICES_RUN=/tmp/run
ENV SYSTEM_SERVICES_RUN_TMP=${SYSTEM_SERVICES_RUN}/tmp ENV SYSTEM_SERVICES_RUN_TMP=${SYSTEM_SERVICES_RUN}/tmp
ENV SYSTEM_SERVICES_RUN_LOG=${SYSTEM_SERVICES_RUN}/logs ENV SYSTEM_SERVICES_RUN_LOG=${SYSTEM_SERVICES_RUN}/logs
ENV PHP_FPM_CONFIG_FILE=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.conf ENV PHP_FPM_CONFIG_FILE=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.conf
@@ -94,7 +97,7 @@ ENV VIRTUAL_ENV=/opt/venv
ENV VIRTUAL_ENV_BIN=/opt/venv/bin ENV VIRTUAL_ENV_BIN=/opt/venv/bin
ENV PATH="${VIRTUAL_ENV}/bin:${PATH}:/services" ENV PATH="${VIRTUAL_ENV}/bin:${PATH}:/services"
ENV VENDORSPATH=/app/back/ieee-oui.txt ENV VENDORSPATH=/app/back/ieee-oui.txt
ENV VENDORSPATH_NEWEST=/services/run/tmp/ieee-oui.txt ENV VENDORSPATH_NEWEST=${SYSTEM_SERVICES_RUN_TMP}/ieee-oui.txt
# App Environment # App Environment

7
NetAlertX.code-workspace Normal file
View File

@@ -0,0 +1,7 @@
{
"folders": [
{
"path": "."
}
]
}

View File

@@ -37,9 +37,9 @@ Start NetAlertX in seconds with Docker:
```bash ```bash
docker run -d --rm --network=host \ docker run -d --rm --network=host \
-v local_path/config:/app/config \ -v local_path/config:/data/config \
-v local_path/db:/app/db \ -v local_path/db:/data/db \
--mount type=tmpfs,target=/app/api \ --mount type=tmpfs,target=/tmp/api \
-e PUID=200 -e PGID=300 \ -e PUID=200 -e PGID=300 \
-e TZ=Europe/Berlin \ -e TZ=Europe/Berlin \
-e PORT=20211 \ -e PORT=20211 \
@@ -140,7 +140,7 @@ A: No. All scans and data remain local, unless you set up cloud-based notificati
A: Yes! You can install it bare-metal. See the [bare metal installation guide](https://github.com/jokob-sk/NetAlertX/blob/main/docs/HW_INSTALL.md). A: Yes! You can install it bare-metal. See the [bare metal installation guide](https://github.com/jokob-sk/NetAlertX/blob/main/docs/HW_INSTALL.md).
**Q: Where is the data stored?** **Q: Where is the data stored?**
A: In the `/config` and `/db` folders, mapped in Docker. Back up these folders regularly. A: In the `/data/config` and `/data/db` folders. Back up these folders regularly.
## 🐞 Known Issues ## 🐞 Known Issues

1
api Symbolic link
View File

@@ -0,0 +1 @@
/tmp/api

2
db/.gitignore vendored
View File

@@ -1,2 +0,0 @@
*
!.gitignore

View File

@@ -17,23 +17,18 @@ services:
volumes: volumes:
- type: volume # Persistent Docker-managed Named Volume for storage of config files - type: volume # Persistent Docker-managed Named Volume for storage
source: netalertx_config # the default name of the volume is netalertx_config source: netalertx_data # the default name of the volume is netalertx_data
target: /app/config # inside the container mounted to /app/config target: /data # consolidated configuration and database storage
read_only: false # writable volume read_only: false # writable volume
# Example custom local folder called /home/user/netalertx_config # Example custom local folder called /home/user/netalertx_data
# - type: bind # - type: bind
# source: /home/user/netalertx_config # source: /home/user/netalertx_data
# target: /app/config # target: /data
# read_only: false # read_only: false
# ... or use the alternative format # ... or use the alternative format
# - /home/user/netalertx_config:/app/config:rw # - /home/user/netalertx_data:/data:rw
- type: volume
source: netalertx_db
target: /app/db
read_only: false
- type: bind # Bind mount for timezone consistency - type: bind # Bind mount for timezone consistency
source: /etc/localtime source: /etc/localtime
@@ -41,29 +36,19 @@ services:
read_only: true read_only: true
# Use a custom Enterprise-configured nginx config for ldap or other settings # Use a custom Enterprise-configured nginx config for ldap or other settings
# - /custom-enterprise.conf:/services/config/nginx/conf.active/netalertx.conf:ro # - /custom-enterprise.conf:/tmp/nginx/active-config/netalertx.conf:ro
# Test your plugin on the production container # Test your plugin on the production container
# - /path/on/host:/app/front/plugins/custom # - /path/on/host:/app/front/plugins/custom
# Retain logs - comment out tmpfs /app/log if you want to retain logs between container restarts # Retain logs - comment out tmpfs /tmp/log if you want to retain logs between container restarts
# - /path/on/host/log:/app/log # - /path/on/host/log:/tmp/log
# Tempfs mounts for writable directories in a read-only container and improve system performance # tmpfs mounts for writable directories in a read-only container and improve system performance
# All mounts have noexec,nosuid,nodev for security purposes no devices, no suid/sgid and no execution of binaries # All writes now live under /tmp/* subdirectories which are created dynamically by entrypoint.d scripts
# async where possible for performance, sync where required for correctness
# uid=20211 and gid=20211 is the netalertx user inside the container # uid=20211 and gid=20211 is the netalertx user inside the container
# mode=1700 gives rwx------ permissions to the netalertx user only # mode=1700 gives rwx------ permissions to the netalertx user only
tmpfs: tmpfs:
# Speed up logging. This can be commented out to retain logs between container restarts
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
# Speed up API access as frontend/backend API is very chatty
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,sync,noatime,nodiratime"
# Required for customization of the nginx listen addr/port without rebuilding the container
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
# /services/config/nginx/conf.d is required for nginx and php to start
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
# /tmp is required by php for session save this should be reworked to /services/run/tmp
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime" - "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
environment: environment:
LISTEN_ADDR: ${LISTEN_ADDR:-0.0.0.0} # Listen for connections on all interfaces LISTEN_ADDR: ${LISTEN_ADDR:-0.0.0.0} # Listen for connections on all interfaces
@@ -86,6 +71,5 @@ services:
# Always restart the container unless explicitly stopped # Always restart the container unless explicitly stopped
restart: unless-stopped restart: unless-stopped
volumes: # Persistent volumes for configuration and database storage volumes: # Persistent volume for configuration and database storage
netalertx_config: # Configuration files netalertx_data:
netalertx_db: # Database files

View File

@@ -141,7 +141,7 @@ The endpoints are updated when objects in the API endpoints are changed.
### Location of the endpoints ### Location of the endpoints
In the container, these files are located under the `/app/api/` folder. You can access them via the `/php/server/query_json.php?file=user_notifications.json` endpoint. In the container, these files are located under the API directory (default: `/tmp/api/`, configurable via `NETALERTX_API` environment variable). You can access them via the `/php/server/query_json.php?file=user_notifications.json` endpoint.
### Available endpoints ### Available endpoints
@@ -332,7 +332,7 @@ Grafana template sample: [Download json](./samples/API/Grafana_Dashboard.json)
## API Endpoint: /log files ## API Endpoint: /log files
This API endpoint retrieves files from the `/app/log` folder. This API endpoint retrieves files from the `/tmp/log` folder.
- Endpoint URL: `php/server/query_logs.php?file=<file name>` - Endpoint URL: `php/server/query_logs.php?file=<file name>`
- Host: `same as front end (web ui)` - Host: `same as front end (web ui)`
@@ -357,7 +357,7 @@ This API endpoint retrieves files from the `/app/log` folder.
## API Endpoint: /config files ## API Endpoint: /config files
To retrieve files from the `/app/config` folder. To retrieve files from the `/data/config` folder.
- Endpoint URL: `php/server/query_config.php?file=<file name>` - Endpoint URL: `php/server/query_config.php?file=<file name>`
- Host: `same as front end (web ui)` - Host: `same as front end (web ui)`

View File

@@ -1,7 +1,7 @@
# Backing Things Up # Backing Things Up
> [!NOTE] > [!NOTE]
> To back up 99% of your configuration, back up at least the `/app/config` folder. > To back up 99% of your configuration, back up at least the `/data/config` folder.
> Database definitions can change between releases, so the safest method is to restore backups using the **same app version** they were taken from, then upgrade incrementally. > Database definitions can change between releases, so the safest method is to restore backups using the **same app version** they were taken from, then upgrade incrementally.
--- ---
@@ -25,7 +25,7 @@ Understanding where your data is stored helps you plan your backup strategy.
### Core Configuration ### Core Configuration
Stored in `/app/config/app.conf`. Stored in `/data/config/app.conf`.
This includes settings for: This includes settings for:
* Notifications * Notifications
@@ -37,7 +37,7 @@ This includes settings for:
### Device Data ### Device Data
Stored in `/app/config/devices_<timestamp>.csv` or `/app/config/devices.csv`, created by the [CSV Backup `CSVBCKP` Plugin](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/csv_backup). Stored in `/data/config/devices_<timestamp>.csv` or `/data/config/devices.csv`, created by the [CSV Backup `CSVBCKP` Plugin](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/csv_backup).
Contains: Contains:
* Device names, icons, and categories * Device names, icons, and categories
@@ -46,7 +46,7 @@ Contains:
### Historical Data ### Historical Data
Stored in `/app/db/app.db` (see [Database Overview](./DATABASE.md)). Stored in `/data/db/app.db` (see [Database Overview](./DATABASE.md)).
Contains: Contains:
* Plugin data and historical entries * Plugin data and historical entries
@@ -77,9 +77,9 @@ You can also download the `app.conf` and `devices.csv` files from the **Maintena
### 💾 What to Back Up ### 💾 What to Back Up
* `/app/db/app.db` (uncorrupted) * `/data/db/app.db` (uncorrupted)
* `/app/config/app.conf` * `/data/config/app.conf`
* `/app/config/workflows.json` * `/data/config/workflows.json`
### 📥 How to Restore ### 📥 How to Restore
@@ -93,14 +93,14 @@ Map these files into your container as described in the [Setup documentation](ht
### 💾 What to Back Up ### 💾 What to Back Up
* `/app/config/app.conf` * `/data/config/app.conf`
* `/app/config/workflows.json` * `/data/config/workflows.json`
* `/app/config/devices_<timestamp>.csv` (rename to `devices.csv` during restore) * `/data/config/devices_<timestamp>.csv` (rename to `devices.csv` during restore)
### 📥 How to Restore ### 📥 How to Restore
1. Copy `app.conf` and `workflows.json` into `/app/config/` 1. Copy `app.conf` and `workflows.json` into `/data/config/`
2. Rename and place `devices_<timestamp>.csv``/app/config/devices.csv` 2. Rename and place `devices_<timestamp>.csv``/data/config/devices.csv`
3. Restore via the **Maintenance** section under *Devices → Bulk Editing* 3. Restore via the **Maintenance** section under *Devices → Bulk Editing*
This recovers nearly all configuration, workflows, and device metadata. This recovers nearly all configuration, workflows, and device metadata.
@@ -157,6 +157,6 @@ For users running NetAlertX via Docker, you can back up or restore directly from
## Summary ## Summary
* Back up `/app/config` for configuration and devices; `/app/db` for history * Back up `/data/config` for configuration and devices; `/data/db` for history
* Keep regular backups, especially before upgrades * Keep regular backups, especially before upgrades
* For Docker setups, use the lightweight `alpine`-based backup method for consistency and portability * For Docker setups, use the lightweight `alpine`-based backup method for consistency and portability

View File

@@ -14,9 +14,9 @@ The app uses the MAC address as an unique identifier for devices. If a new MAC i
Make sure you [File permissions](./FILE_PERMISSIONS.md) are set correctly. Make sure you [File permissions](./FILE_PERMISSIONS.md) are set correctly.
* If facing issues (AJAX errors, can't write to DB, empty screen, etc,) make sure permissions are set correctly, and check the logs under `/app/log`. * If facing issues (AJAX errors, can't write to DB, empty screen, etc,) make sure permissions are set correctly, and check the logs under `/tmp/log`.
* To solve permission issues you can try setting the owner and group of the `app.db` by executing the following on the host system: `docker exec netalertx chown -R www-data:www-data /app/db/app.db`. * To solve permission issues you can try setting the owner and group of the `app.db` by executing the following on the host system: `docker exec netalertx chown -R www-data:www-data /data/db/app.db`.
* If still facing issues, try to map the app.db file (⚠ not folder) to `:/app/db/app.db` (see [docker-compose Examples](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md#-docker-composeyml-examples) for details) * If still facing issues, try to map the app.db file (⚠ not folder) to `:/data/db/app.db` (see [docker-compose Examples](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md#-docker-composeyml-examples) for details)
### Container restarts / crashes ### Container restarts / crashes
@@ -49,7 +49,7 @@ Make sure that the subnet and interface in `SCAN_SUBNETS` are correct. If your d
### Losing my settings and devices after an update ### Losing my settings and devices after an update
If you lose your devices and/or settings after an update that means you don't have the `/app/db` and `/app/config` folders mapped to a permanent storage. That means every time you update these folders are re-created. Make sure you have the [volumes specified correctly](./DOCKER_COMPOSE.md) in your `docker-compose.yml` or run command. If you lose your devices and/or settings after an update that means you don't have the `/data/db` and `/data/config` folders mapped to a permanent storage. That means every time you update these folders are re-created. Make sure you have the [volumes specified correctly](./DOCKER_COMPOSE.md) in your `docker-compose.yml` or run command.
### The application is slow ### The application is slow

View File

@@ -27,7 +27,7 @@ Sometimes, the UI might not be accessible. In that case, you can access the logs
3. **Check the PHP application error log:** 3. **Check the PHP application error log:**
```bash ```bash
cat /app/log/app.php_errors.log cat /tmp/log/app.php_errors.log
``` ```
These logs will help identify syntax issues, fatal errors, or startup problems when the UI fails to load properly. These logs will help identify syntax issues, fatal errors, or startup problems when the UI fails to load properly.

View File

@@ -14,8 +14,8 @@ Start the container via the **terminal** with a command similar to this one:
```bash ```bash
docker run --rm --network=host \ docker run --rm --network=host \
-v local/path/netalertx/config:/app/config \ -v local/path/netalertx/config:/data/config \
-v local/path/netalertx/db:/app/db \ -v local/path/netalertx/db:/data/db \
-e TZ=Europe/Berlin \ -e TZ=Europe/Berlin \
-e PORT=20211 \ -e PORT=20211 \
ghcr.io/jokob-sk/netalertx:latest ghcr.io/jokob-sk/netalertx:latest

View File

@@ -31,61 +31,46 @@ services:
- NET_BIND_SERVICE # Required to bind to privileged ports (nbtscan) - NET_BIND_SERVICE # Required to bind to privileged ports (nbtscan)
volumes: volumes:
- type: volume # Persistent Docker-managed Named Volume for storage of config files - type: volume # Persistent Docker-managed named volume for config + database
source: netalertx_config # the default name of the volume is netalertx_config source: netalertx_data
target: /app/config # inside the container mounted to /app/config target: /data # `/data/config` and `/data/db` live inside this mount
read_only: false # writable volume
# Example custom local folder called /home/user/netalertx_config
# - type: bind
# source: /home/user/netalertx_config
# target: /app/config
# read_only: false
# ... or use the alternative format
# - /home/user/netalertx_config:/app/config:rw
- type: volume # NetAlertX Database partiton
source: netalertx_db
target: /app/db
read_only: false read_only: false
- type: volume # Future proof mount. During the migration to a # Example custom local folder called /home/user/netalertx_data
source: netalertx_data # future version, app and db will be migrated to # - type: bind
target: /data # the /data partition. This will reduce the # source: /home/user/netalertx_data
read_only: false # overhead and pain in the upcoming migration. # target: /data
# read_only: false
# ... or use the alternative format
# - /home/user/netalertx_data:/data:rw
- type: bind # Bind mount for timezone consistency - type: bind # Bind mount for timezone consistency
source: /etc/localtime source: /etc/localtime # Alternatively add environment TZ: America/New York
target: /etc/localtime target: /etc/localtime
read_only: true read_only: true
# Mount your DHCP server file into NetAlertX for a plugin to access # Mount your DHCP server file into NetAlertX for a plugin to access
# - path/on/host/to/dhcp.file:/resources/dhcp.file # - path/on/host/to/dhcp.file:/resources/dhcp.file
# Retain logs - comment out tmpfs /app/log if you want to retain logs between container restarts # tmpfs mount consolidates writable state for a read-only container and improves performance
# - /path/on/host/log:/app/log
# Tempfs mounts for writable directories in a read-only container and improve system performance
# All mounts have noexec,nosuid,nodev for security purposes no devices, no suid/sgid and no execution of binaries
# async where possible for performance, sync where required for correctness
# uid=20211 and gid=20211 is the netalertx user inside the container # uid=20211 and gid=20211 is the netalertx user inside the container
# mode=1700 gives rwx------ permissions to the netalertx user only # mode=1700 grants rwx------ permissions to the netalertx user only
tmpfs: tmpfs:
# Speed up logging. This can be commented out to retain logs between container restarts # Comment out to retain logs between container restarts - this has a server performance impact.
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime" - "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
# Speed up API access as frontend/backend API is very chatty
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,sync,noatime,nodiratime" # Retain logs - comment out tmpfs /tmp if you want to retain logs between container restarts
# Required for customization of the nginx listen addr/port without rebuilding the container # Please note if you remove the /tmp mount, you must create and maintain sub-folder mounts.
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime" # - /path/on/host/log:/tmp/log
# /services/config/nginx/conf.d is required for nginx and php to start # - "/tmp/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime" # - "/tmp/nginx:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
# /tmp is required by php for session save this should be reworked to /services/run/tmp # - "/tmp/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp:uid=2Key-Value Pairs: 20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
environment: environment:
LISTEN_ADDR: ${LISTEN_ADDR:-0.0.0.0} # Listen for connections on all interfaces LISTEN_ADDR: ${LISTEN_ADDR:-0.0.0.0} # Listen for connections on all interfaces
PORT: ${PORT:-20211} # Application port PORT: ${PORT:-20211} # Application port
GRAPHQL_PORT: ${GRAPHQL_PORT:-20212} # GraphQL API port (passed into APP_CONF_OVERRIDE at runtime) GRAPHQL_PORT: ${GRAPHQL_PORT:-20212} # GraphQL API port (passed into APP_CONF_OVERRIDE at runtime)
NETALERTX_DEBUG: ${NETALERTX_DEBUG:-0} # 0=kill all services and restart if any dies. 1 keeps running dead services. # NETALERTX_DEBUG: ${NETALERTX_DEBUG:-0} # 0=kill all services and restart if any dies. 1 keeps running dead services.
# Resource limits to prevent resource exhaustion # Resource limits to prevent resource exhaustion
mem_limit: 2048m # Maximum memory usage mem_limit: 2048m # Maximum memory usage
@@ -101,10 +86,8 @@ services:
# Always restart the container unless explicitly stopped # Always restart the container unless explicitly stopped
restart: unless-stopped restart: unless-stopped
volumes: # Persistent volumes for configuration and database storage volumes: # Persistent volume for configuration and database storage
netalertx_config: # Configuration files netalertx_data:
netalertx_db: # Database files
netalertx_data: # For future config/db upgrade
``` ```
Run or re-run it: Run or re-run it:
@@ -163,8 +146,8 @@ However, if you prefer to have direct, file-level access to your configuration f
```yaml ```yaml
... ...
volumes: volumes:
- netalertx_config:/app/config:rw #short-form volume (no /path is a short volume) - netalertx_config:/data/config:rw #short-form volume (no /path is a short volume)
- netalertx_db:/app/db:rw - netalertx_db:/data/db:rw
... ...
``` ```
@@ -174,14 +157,14 @@ Make sure to replace `/home/adam/netalertx-files` with your actual path. The for
```yaml ```yaml
... ...
volumes: volumes:
# - netalertx_config:/app/config:rw # - netalertx_config:/data/config:rw
# - netalertx_db:/app/db:rw # - netalertx_db:/data/db:rw
- /home/adam/netalertx-files/config:/app/config:rw - /home/adam/netalertx-files/config:/data/config:rw
- /home/adam/netalertx-files/db:/app/db:rw - /home/adam/netalertx-files/db:/data/db:rw
... ...
``` ```
Now, any files created by NetAlertX in `/app/config` will appear in your `/home/adam/netalertx-files/config` folder. Now, any files created by NetAlertX in `/data/config` will appear in your `/home/adam/netalertx-files/config` folder.
This same method works for mounting other things, like custom plugins or enterprise NGINX files, as shown in the commented-out examples in the baseline file. This same method works for mounting other things, like custom plugins or enterprise NGINX files, as shown in the commented-out examples in the baseline file.

View File

@@ -25,9 +25,9 @@ Head to [https://netalertx.com/](https://netalertx.com/) for more gifs and scree
```yaml ```yaml
docker run -d --rm --network=host \ docker run -d --rm --network=host \
-v local_path/config:/app/config \ -v local_path/config:/data/config \
-v local_path/db:/app/db \ -v local_path/db:/data/db \
--mount type=tmpfs,target=/app/api \ --mount type=tmpfs,target=/tmp/api \
-e PUID=200 -e PGID=300 \ -e PUID=200 -e PGID=300 \
-e TZ=Europe/Berlin \ -e TZ=Europe/Berlin \
-e PORT=20211 \ -e PORT=20211 \
@@ -58,10 +58,10 @@ See alternative [docked-compose examples](https://github.com/jokob-sk/NetAlertX/
| Required | Path | Description | | Required | Path | Description |
| :------------- | :------------- | :-------------| | :------------- | :------------- | :-------------|
| ✅ | `:/app/config` | Folder which will contain the `app.conf` & `devices.csv` ([read about devices.csv](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICES_BULK_EDITING.md)) files | | ✅ | `:/data/config` | Folder which will contain the `app.conf` & `devices.csv` ([read about devices.csv](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICES_BULK_EDITING.md)) files |
| ✅ | `:/app/db` | Folder which will contain the `app.db` database file | | ✅ | `:/data/db` | Folder which will contain the `app.db` database file |
| | `:/app/log` | Logs folder useful for debugging if you have issues setting up the container | | | `:/tmp/log` | Logs folder useful for debugging if you have issues setting up the container |
| | `:/app/api` | A simple [API endpoint](https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md) containing static (but regularly updated) json and other files. | | | `:/tmp/api` | A simple [API endpoint](https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md) containing static (but regularly updated) json and other files. Path configurable via `NETALERTX_API` environment variable. |
| | `:/app/front/plugins/<plugin>/ignore_plugin` | Map a file `ignore_plugin` to ignore a plugin. Plugins can be soft-disabled via settings. More in the [Plugin docs](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md). | | | `:/app/front/plugins/<plugin>/ignore_plugin` | Map a file `ignore_plugin` to ignore a plugin. Plugins can be soft-disabled via settings. More in the [Plugin docs](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md). |
| | `:/etc/resolv.conf` | Use a custom `resolv.conf` file for [better name resolution](https://github.com/jokob-sk/NetAlertX/blob/main/docs/REVERSE_DNS.md). | | | `:/etc/resolv.conf` | Use a custom `resolv.conf` file for [better name resolution](https://github.com/jokob-sk/NetAlertX/blob/main/docs/REVERSE_DNS.md). |
@@ -70,7 +70,7 @@ See alternative [docked-compose examples](https://github.com/jokob-sk/NetAlertX/
### Initial setup ### Initial setup
- If unavailable, the app generates a default `app.conf` and `app.db` file on the first run. - If unavailable, the app generates a default `app.conf` and `app.db` file on the first run.
- The preferred way is to manage the configuration via the Settings section in the UI, if UI is inaccessible you can modify [app.conf](https://github.com/jokob-sk/NetAlertX/tree/main/back) in the `/app/config/` folder directly - The preferred way is to manage the configuration via the Settings section in the UI, if UI is inaccessible you can modify [app.conf](https://github.com/jokob-sk/NetAlertX/tree/main/back) in the `/data/config/` folder directly
#### Setting up scanners #### Setting up scanners

View File

@@ -51,13 +51,13 @@ You want to edit your `app.conf` and other configuration files directly from you
volumes: volumes:
# - type: volume # - type: volume
# source: netalertx_config # source: netalertx_config
# target: /app/config # target: /data/config
# read_only: false # read_only: false
... ...
# Example custom local folder called /data/netalertx_config # Example custom local folder called /data/netalertx_config
- type: bind - type: bind
source: /data/netalertx_config source: /data/netalertx_config
target: /app/config target: /data/config
read_only: false read_only: false
... ...
``` ```
@@ -70,7 +70,7 @@ You want to edit your `app.conf` and other configuration files directly from you
### About This Method ### About This Method
This replaces the Docker-managed volume with a "bind mount." This is a direct mapping between a folder on your host computer (`/data/netalertx_config`) and a folder inside the container (`/app/config`), allowing you to edit the files directly. This replaces the Docker-managed volume with a "bind mount." This is a direct mapping between a folder on your host computer (`/data/netalertx_config`) and a folder inside the container (`/data/config`), allowing you to edit the files directly.
--- ---
@@ -97,13 +97,13 @@ You are currently using a local folder (bind mount) for your configuration (e.g.
volumes: volumes:
- type: volume - type: volume
source: netalertx_config source: netalertx_config
target: /app/config target: /data/config
read_only: false read_only: false
... ...
# Example custom local folder called /data/netalertx_config # Example custom local folder called /data/netalertx_config
# - type: bind # - type: bind
# source: /data/netalertx_config # source: /data/netalertx_config
# target: /app/config # target: /data/config
# read_only: false # read_only: false
... ...
``` ```
@@ -149,7 +149,7 @@ You need to override the default Nginx configuration to add features like LDAP,
```yaml ```yaml
... ...
# Use a custom Enterprise-configured nginx config for ldap or other settings # Use a custom Enterprise-configured nginx config for ldap or other settings
- /data/my-netalertx.conf:/services/config/nginx/conf.active/netalertx.conf:ro - /data/my-netalertx.conf:/tmp/nginx/active-config/netalertx.conf:ro
... ...
``` ```
4. Restart the container: 4. Restart the container:

View File

@@ -45,18 +45,18 @@ services:
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- ${APP_FOLDER}/netalertx/config:/app/config - ${APP_FOLDER}/netalertx/config:/data/config
- ${APP_FOLDER}/netalertx/db:/app/db - ${APP_FOLDER}/netalertx/db:/data/db
# Optional: logs (useful for debugging setup issues, comment out for performance) # Optional: logs (useful for debugging setup issues, comment out for performance)
- ${APP_FOLDER}/netalertx/log:/app/log - ${APP_FOLDER}/netalertx/log:/tmp/log
# API storage options: # API storage options:
# (Option 1) tmpfs (default, best performance) # (Option 1) tmpfs (default, best performance)
- type: tmpfs - type: tmpfs
target: /app/api target: /tmp/api
# (Option 2) bind mount (useful for debugging) # (Option 2) bind mount (useful for debugging)
# - ${APP_FOLDER}/netalertx/api:/app/api # - ${APP_FOLDER}/netalertx/api:/tmp/api
environment: environment:
- TZ=${TZ} - TZ=${TZ}

View File

@@ -44,9 +44,9 @@ services:
ports: ports:
- 20211:20211 - 20211:20211
volumes: volumes:
- /mnt/YOUR_SERVER/netalertx/config:/app/config:rw - /mnt/YOUR_SERVER/netalertx/config:/data/config:rw
- /mnt/YOUR_SERVER/netalertx/db:/netalertx/app/db:rw - /mnt/YOUR_SERVER/netalertx/db:/netalertx/data/db:rw
- /mnt/YOUR_SERVER/netalertx/logs:/netalertx/app/log:rw - /mnt/YOUR_SERVER/netalertx/logs:/netalertx/tmp/log:rw
environment: environment:
- TZ=Europe/London - TZ=Europe/London
- PORT=20211 - PORT=20211

View File

@@ -11,13 +11,15 @@ NetAlertX requires certain paths to be writable at runtime. These paths should b
| Path | Purpose | Notes | | Path | Purpose | Notes |
| ------------------------------------ | ----------------------------------- | ------------------------------------------------------ | | ------------------------------------ | ----------------------------------- | ------------------------------------------------------ |
| `/app/config` | Application configuration | Persistent volume recommended | | `/data/config` | Application configuration | Persistent volume recommended |
| `/app/db` | Database files | Persistent volume recommended | | `/data/db` | Database files | Persistent volume recommended |
| `/app/log` | Logs | Can be `tmpfs` for speed or host volume to retain logs | | `/tmp/log` | Logs | Lives under `/tmp`; optional host bind to retain logs |
| `/app/api` | API cache | Use `tmpfs` for faster access | | `/tmp/api` | API cache | Subdirectory of `/tmp` |
| `/services/config/nginx/conf.active` | Active nginx configuration override | `tmpfs` recommended or customized file mounted | | `/tmp/nginx/active-config` | Active nginx configuration override | Mount `/tmp` (or override specific file) |
| `/services/run` | Runtime directories for nginx & PHP | `tmpfs` required | | `/tmp/run` | Runtime directories for nginx & PHP | Subdirectory of `/tmp` |
| `/tmp` | PHP session save directory | `tmpfs` required | | `/tmp` | PHP session save directory | Backed by `tmpfs` for runtime writes |
> Mounting `/tmp` as `tmpfs` automatically covers all of its subdirectories (`log`, `api`, `run`, `nginx/active-config`, etc.).
> All these paths will have **UID 20211 / GID 20211** inside the container. Files on the host will appear owned by `20211:20211`. > All these paths will have **UID 20211 / GID 20211** inside the container. Files on the host will appear owned by `20211:20211`.
@@ -33,8 +35,8 @@ Sometimes, permission issues arise if your existing host directories were create
```bash ```bash
docker run -it --rm --name netalertx --user "0" \ docker run -it --rm --name netalertx --user "0" \
-v local/path/config:/app/config \ -v local/path/config:/data/config \
-v local/path/db:/app/db \ -v local/path/db:/data/db \
ghcr.io/jokob-sk/netalertx:latest ghcr.io/jokob-sk/netalertx:latest
``` ```
@@ -60,16 +62,12 @@ services:
- NET_BIND_SERVICE - NET_BIND_SERVICE
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- local/path/config:/app/config - local/path/config:/data/config
- local/path/db:/app/db - local/path/db:/data/db
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin
- PORT=20211 - PORT=20211
tmpfs: tmpfs:
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,sync,noatime,nodiratime"
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime" - "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
``` ```

View File

@@ -9,7 +9,7 @@ NetAlertX comes with several logs that help to identify application issues. Thes
You can find most of the logs exposed in the UI under _Maintenance -> Logs_. You can find most of the logs exposed in the UI under _Maintenance -> Logs_.
If the UI is inaccessible, you can access them under `/app/log`. If the UI is inaccessible, you can access them under `/tmp/log`.
![Logs](./img/LOGGING/maintenance_logs.png) ![Logs](./img/LOGGING/maintenance_logs.png)
@@ -52,18 +52,18 @@ The default logs are erased every time the container restarts because they are s
2. Edit your `docker-compose.yml` file: 2. Edit your `docker-compose.yml` file:
* **Comment out** the `/app/log` line under the `tmpfs:` section. * **Comment out** the `/tmp/log` line under the `tmpfs:` section.
* **Uncomment** the "Retain logs" line under the `volumes:` section and set your desired host path. * **Uncomment** the "Retain logs" line under the `volumes:` section and set your desired host path.
```yaml ```yaml
... ...
tmpfs: tmpfs:
# - "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime" # - "/tmp/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
... ...
volumes: volumes:
... ...
# Retain logs - comment out tmpfs /app/log if you want to retain logs between container restarts # Retain logs - comment out tmpfs /tmp/log if you want to retain logs between container restarts
- /home/adam/netalertx_logs:/app/log - /home/adam/netalertx_logs:/tmp/log
... ...
``` ```
3. Restart the container: 3. Restart the container:
@@ -72,4 +72,4 @@ The default logs are erased every time the container restarts because they are s
docker-compose up -d docker-compose up -d
``` ```
This change stops Docker from mounting a temporary in-memory volume at `/app/log`. Instead, it "bind mounts" a persistent folder from your host computer (e.g., `/data/netalertx_logs`) to that *same location* inside the container. This change stops Docker from mounting a temporary in-memory volume at `/tmp/log`. Instead, it "bind mounts" a persistent folder from your host computer (e.g., `/data/netalertx_logs`) to that *same location* inside the container.

View File

@@ -43,7 +43,7 @@ A banner message will appear at the top of the web UI reminding you to update yo
> [!TIP] > [!TIP]
> If you have trouble accessing past backups, config or database files you can copy them into the newly mapped directories, for example by running this command in the container: `cp -r /app/config /home/pi/pialert/config/old_backup_files`. This should create a folder in the `config` directory called `old_backup_files` containing all the files in that location. Another approach is to map the old location and the new one at the same time to copy things over. > If you have trouble accessing past backups, config or database files you can copy them into the newly mapped directories, for example by running this command in the container: `cp -r /data/config /home/pi/pialert/config/old_backup_files`. This should create a folder in the `config` directory called `old_backup_files` containing all the files in that location. Another approach is to map the old location and the new one at the same time to copy things over.
#### New Docker mount locations #### New Docker mount locations
@@ -51,8 +51,8 @@ The internal application path in the container has changed from `/home/pi/pialer
| Old mount point | New mount point | | Old mount point | New mount point |
|----------------------|---------------| |----------------------|---------------|
| `/home/pi/pialert/config` | `/app/config` | | `/home/pi/pialert/config` | `/data/config` |
| `/home/pi/pialert/db` | `/app/db` | | `/home/pi/pialert/db` | `/data/db` |
If you were mounting files directly, please note the file names have changed: If you were mounting files directly, please note the file names have changed:
@@ -104,10 +104,10 @@ services:
network_mode: "host" network_mode: "host"
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- local/path/config:/app/config # 🆕 This has changed - local/path/config:/data/config # 🆕 This has changed
- local/path/db:/app/db # 🆕 This has changed - local/path/db:/data/db # 🆕 This has changed
# (optional) useful for debugging if you have issues setting up the container # (optional) useful for debugging if you have issues setting up the container
- local/path/logs:/app/log # 🆕 This has changed - local/path/logs:/tmp/log # 🆕 This has changed
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin
- PORT=20211 - PORT=20211
@@ -150,10 +150,10 @@ services:
network_mode: "host" network_mode: "host"
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- local/path/config/app.conf:/app/config/app.conf # 🆕 This has changed - local/path/config/app.conf:/data/config/app.conf # 🆕 This has changed
- local/path/db/app.db:/app/db/app.db # 🆕 This has changed - local/path/db/app.db:/data/db/app.db # 🆕 This has changed
# (optional) useful for debugging if you have issues setting up the container # (optional) useful for debugging if you have issues setting up the container
- local/path/logs:/app/log # 🆕 This has changed - local/path/logs:/tmp/log # 🆕 This has changed
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin
- PORT=20211 - PORT=20211
@@ -190,10 +190,10 @@ services:
network_mode: "host" network_mode: "host"
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- local/path/config:/app/config - local/path/config:/data/config
- local/path/db:/app/db - local/path/db:/data/db
# (optional) useful for debugging if you have issues setting up the container # (optional) useful for debugging if you have issues setting up the container
- local/path/logs:/app/log - local/path/logs:/tmp/log
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin
- PORT=20211 - PORT=20211
@@ -207,10 +207,10 @@ services:
network_mode: "host" network_mode: "host"
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- local/path/config:/app/config - local/path/config:/data/config
- local/path/db:/app/db - local/path/db:/data/db
# (optional) useful for debugging if you have issues setting up the container # (optional) useful for debugging if you have issues setting up the container
- local/path/logs:/app/log - local/path/logs:/tmp/log
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin
- PORT=20211 - PORT=20211
@@ -234,10 +234,10 @@ services:
network_mode: "host" network_mode: "host"
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- local/path/config:/app/config - local/path/config:/data/config
- local/path/db:/app/db - local/path/db:/data/db
# (optional) useful for debugging if you have issues setting up the container # (optional) useful for debugging if you have issues setting up the container
- local/path/logs:/app/log - local/path/logs:/tmp/log
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin
- PORT=20211 - PORT=20211
@@ -253,8 +253,8 @@ services:
```sh ```sh
docker run -it --rm --name netalertx --user "0" \ docker run -it --rm --name netalertx --user "0" \
-v local/path/config:/app/config \ -v local/path/config:/data/config \
-v local/path/db:/app/db \ -v local/path/db:/data/db \
ghcr.io/jokob-sk/netalertx:latest ghcr.io/jokob-sk/netalertx:latest
``` ```
@@ -273,24 +273,16 @@ services:
- NET_BIND_SERVICE # 🆕 New line - NET_BIND_SERVICE # 🆕 New line
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- local/path/config:/app/config - local/path/config:/data/config
- local/path/db:/app/db - local/path/db:/data/db
# (optional) useful for debugging if you have issues setting up the container # (optional) useful for debugging if you have issues setting up the container
#- local/path/logs:/app/log #- local/path/logs:/tmp/log
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin
- PORT=20211 - PORT=20211
# 🆕 New "tmpfs" section START 🔽 # 🆕 New "tmpfs" section START 🔽
tmpfs: tmpfs:
# Speed up logging. This can be commented out to retain logs between container restarts # All writable runtime state resides under /tmp; comment out to persist logs between restarts
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
# Speed up API access as frontend/backend API is very chatty
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,sync,noatime,nodiratime"
# Required for customization of the nginx listen addr/port without rebuilding the container
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
# /services/config/nginx/conf.d is required for nginx and php to start
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
# /tmp is required by php for session save this should be reworked to /services/run/tmp
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime" - "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
# 🆕 New "tmpfs" section END 🔼 # 🆕 New "tmpfs" section END 🔼
``` ```

View File

@@ -62,7 +62,7 @@ For example, the **ICMP plugin** allows you to specify a regular expression to s
## Storing Temporary Files in Memory ## Storing Temporary Files in Memory
On systems with slower I/O speeds, you can optimize performance by storing temporary files in memory. This primarily applies to the `/app/api` and `/app/log` folders. On systems with slower I/O speeds, you can optimize performance by storing temporary files in memory. This primarily applies to the API directory (default: `/tmp/api`, configurable via `NETALERTX_API`) and `/tmp/log` folders.
Using `tmpfs` reduces disk writes and improves performance. However, it should be **disabled** if persistent logs or API data storage are required. Using `tmpfs` reduces disk writes and improves performance. However, it should be **disabled** if persistent logs or API data storage are required.
@@ -80,15 +80,15 @@ services:
network_mode: "host" network_mode: "host"
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- local/path/config:/app/config - local/path/config:/data/config
- local/path/db:/app/db - local/path/db:/data/db
# (Optional) Useful for debugging setup issues # (Optional) Useful for debugging setup issues
- local/path/logs:/app/log - local/path/logs:/tmp/log
# (API: OPTION 1) Store temporary files in memory (recommended for performance) # (API: OPTION 1) Store temporary files in memory (recommended for performance)
- type: tmpfs # ◀ 🔺 - type: tmpfs # ◀ 🔺
target: /app/api # ◀ 🔺 target: /tmp/api # ◀ 🔺
# (API: OPTION 2) Store API data on disk (useful for debugging) # (API: OPTION 2) Store API data on disk (useful for debugging)
# - local/path/api:/app/api # - local/path/api:/tmp/api
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin
- PORT=20211 - PORT=20211

View File

@@ -42,9 +42,9 @@ services:
image: "ghcr.io/jokob-sk/netalertx:latest" image: "ghcr.io/jokob-sk/netalertx:latest"
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- /home/netalertx/config:/app/config - /home/netalertx/config:/data/config
- /home/netalertx/db:/app/db - /home/netalertx/db:/data/db
- /home/netalertx/log:/app/log - /home/netalertx/log:/tmp/log
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin
- PORT=20211 - PORT=20211
@@ -68,9 +68,9 @@ services:
image: "ghcr.io/jokob-sk/netalertx:latest" image: "ghcr.io/jokob-sk/netalertx:latest"
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- ./config/app.conf:/app/config/app.conf - ./config/app.conf:/data/config/app.conf
- ./db:/app/db - ./db:/data/db
- ./log:/app/log - ./log:/tmp/log
- ./config/resolv.conf:/etc/resolv.conf # Mapping the /resolv.conf file for better name resolution - ./config/resolv.conf:/etc/resolv.conf # Mapping the /resolv.conf file for better name resolution
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin

View File

@@ -499,8 +499,8 @@ Mapping the updated file (on the local filesystem at `/appl/docker/netalertx/def
```bash ```bash
docker run -d --rm --network=host \ docker run -d --rm --network=host \
--name=netalertx \ --name=netalertx \
-v /appl/docker/netalertx/config:/app/config \ -v /appl/docker/netalertx/config:/data/config \
-v /appl/docker/netalertx/db:/app/db \ -v /appl/docker/netalertx/db:/data/db \
-v /appl/docker/netalertx/default:/etc/nginx/sites-available/default \ -v /appl/docker/netalertx/default:/etc/nginx/sites-available/default \
-e TZ=Europe/Amsterdam \ -e TZ=Europe/Amsterdam \
-e PORT=20211 \ -e PORT=20211 \

View File

@@ -48,7 +48,7 @@ Heres a breakdown of the defensive layers you get, right out of the box using
**Methodology:** All writable locations are treated as untrusted, temporary, and non-executable by default. **Methodology:** All writable locations are treated as untrusted, temporary, and non-executable by default.
* **In-Memory Volatile Storage:** The `docker-compose.yml` configuration maps all temporary directories (e.g., `/app/log`, `/app/api`, `/tmp`) to in-memory `tmpfs` filesystems. They do not exist on the host's disk. * **In-Memory Volatile Storage:** The `docker-compose.yml` configuration maps all temporary directories (e.g., `/tmp/log`, `/tmp/api`, `/tmp`) to in-memory `tmpfs` filesystems. They do not exist on the host's disk.
* **Volatile Data:** Because these locations exist only in RAM, their contents are **instantly and irrevocably erased** when the container is stopped. This provides a "self-cleaning" mechanism that purges any attacker-dropped files or payloads on every single restart. * **Volatile Data:** Because these locations exist only in RAM, their contents are **instantly and irrevocably erased** when the container is stopped. This provides a "self-cleaning" mechanism that purges any attacker-dropped files or payloads on every single restart.

View File

@@ -40,10 +40,10 @@ services:
network_mode: "host" network_mode: "host"
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- local/path/config:/app/config - local/path/config:/data/config
- local/path/db:/app/db - local/path/db:/data/db
# (optional) useful for debugging if you have issues setting up the container # (optional) useful for debugging if you have issues setting up the container
- local/path/logs:/app/log - local/path/logs:/tmp/log
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin
- PORT=20211 - PORT=20211
@@ -57,10 +57,10 @@ services:
```yaml ```yaml
volumes: volumes:
- /volume1/app_storage/netalertx/config:/app/config - /volume1/app_storage/netalertx/config:/data/config
- /volume1/app_storage/netalertx/db:/app/db - /volume1/app_storage/netalertx/db:/data/db
# (optional) useful for debugging if you have issues setting up the container # (optional) useful for debugging if you have issues setting up the container
# - local/path/logs:/app/log <- commented out with # ⚠ # - local/path/logs:/tmp/log <- commented out with # ⚠
``` ```
![Adjusting docker-compose](./img/SYNOLOGY/08_Adjust_docker_compose_volumes.png) ![Adjusting docker-compose](./img/SYNOLOGY/08_Adjust_docker_compose_volumes.png)

View File

@@ -62,11 +62,11 @@ In the container execute and investigate:
`cat /var/log/nginx/error.log` `cat /var/log/nginx/error.log`
`cat /app/log/app.php_errors.log` `cat /tmp/log/app.php_errors.log`
### 8. Make sure permissions are correct ### 8. Make sure permissions are correct
> [!TIP] > [!TIP]
> You can try to start the container without mapping the `/app/config` and `/app/db` dirs and if the UI shows up then the issue is most likely related to your file system permissions or file ownership. > You can try to start the container without mapping the `/data/config` and `/data/db` dirs and if the UI shows up then the issue is most likely related to your file system permissions or file ownership.
Please read the [Permissions troubleshooting guide](./FILE_PERMISSIONS.md) and provide a screesnhot of the permissions and ownership in the `/app/db` and `app/config` directories. Please read the [Permissions troubleshooting guide](./FILE_PERMISSIONS.md) and provide a screesnhot of the permissions and ownership in the `/data/db` and `app/config` directories.

View File

@@ -23,7 +23,7 @@ Limit capabilities to only those required:
- NET_ADMIN - NET_ADMIN
- NET_BIND_SERVICE - NET_BIND_SERVICE
``` ```
- Remove any unnecessary `--cap-add` flags from docker run commands - Remove any unnecessary `--cap-add` or `--privileged` flags from docker run commands
## Additional Resources ## Additional Resources

View File

@@ -24,9 +24,9 @@ Review and correct your volume mounts in docker-compose.yml:
Example volume configuration: Example volume configuration:
```yaml ```yaml
volumes: volumes:
- ./data/db:/app/db - ./data/db:/data/db
- ./data/config:/app/config - ./data/config:/data/config
- ./data/log:/app/log - ./data/log:/tmp/log
``` ```
## Additional Resources ## Additional Resources

View File

@@ -20,7 +20,7 @@ If you want to use a custom port, create a bind mount for the nginx configuratio
- Add to your docker-compose.yml: - Add to your docker-compose.yml:
```yaml ```yaml
volumes: volumes:
- /path/to/nginx-config:/app/system/services/active/config - /path/to/nginx-config:/tmp/nginx/active-config
environment: environment:
- PORT=your_custom_port - PORT=your_custom_port
``` ```

View File

@@ -15,9 +15,22 @@
<?php <?php
require 'php/templates/header.php'; require 'php/templates/header.php';
// check permissions // check permissions
$dbPath = "../db/app.db"; // Use environment-aware paths with fallback to legacy locations
$confPath = "../config/app.conf"; $dbFolderPath = rtrim(getenv('NETALERTX_DB') ?: '/data/db', '/');
$configFolderPath = rtrim(getenv('NETALERTX_CONFIG') ?: '/data/config', '/');
$dbPath = $dbFolderPath . '/app.db';
$confPath = $configFolderPath . '/app.conf';
// Fallback to legacy paths if new locations don't exist
if (!file_exists($dbPath) && file_exists('../db/app.db')) {
$dbPath = '../db/app.db';
}
if (!file_exists($confPath) && file_exists('../config/app.conf')) {
$confPath = '../config/app.conf';
}
checkPermissions([$dbPath, $confPath]); checkPermissions([$dbPath, $confPath]);
?> ?>

View File

@@ -17,11 +17,12 @@
// Size and last mod of DB ------------------------------------------------------ // Size and last mod of DB ------------------------------------------------------
$nax_db = str_replace('front', 'db', getcwd()).'/app.db'; $dbBasePath = rtrim(getenv('NETALERTX_DB') ?: '/data/db', '/');
$nax_wal = str_replace('front', 'db', getcwd()).'/app.db-wal'; $nax_db = $dbBasePath . '/app.db';
$nax_db_size = number_format((filesize($nax_db) / 1000000),2,",",".") . ' MB'; $nax_wal = $dbBasePath . '/app.db-wal';
$nax_wal_size = number_format((filesize($nax_wal) / 1000000),2,",",".") . ' MB'; $nax_db_size = file_exists($nax_db) ? number_format((filesize($nax_db) / 1000000),2,",",".") . ' MB' : '0 MB';
$nax_db_mod = date ("F d Y H:i:s", filemtime($nax_db)); $nax_wal_size = file_exists($nax_wal) ? number_format((filesize($nax_wal) / 1000000),2,",",".") . ' MB' : '0 MB';
$nax_db_mod = file_exists($nax_db) ? date ("F d Y H:i:s", filemtime($nax_db)) : 'N/A';
// Table sizes ----------------------------------------------------------------- // Table sizes -----------------------------------------------------------------
@@ -334,7 +335,7 @@ $db->close();
var emptyArr = ['undefined', "", undefined, null]; var emptyArr = ['undefined', "", undefined, null];
var selectedTab = 'tab_DBTools_id'; var selectedTab = 'tab_DBTools_id';
initializeTabs(); // initializeTabs() is called in window.onload
// ----------------------------------------------------------- // -----------------------------------------------------------
// delete devices with emty macs // delete devices with emty macs
@@ -704,7 +705,7 @@ function renderLogs(customData) {
window.onload = function asyncFooter() { window.onload = function asyncFooter() {
renderLogs(); renderLogs();
// initializeTabs(); initializeTabs();
try { try {
$("#lastCommit").append('<a href="https://github.com/jokob-sk/NetAlertX/commits" target="_blank"><img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/jokob-sk/netalertx/main?logo=github"></a>'); $("#lastCommit").append('<a href="https://github.com/jokob-sk/NetAlertX/commits" target="_blank"><img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/jokob-sk/netalertx/main?logo=github"></a>');

View File

@@ -2,30 +2,59 @@
require '../server/init.php'; require '../server/init.php';
$logBasePath = rtrim(getenv('NETALERTX_LOG') ?: '/tmp/log', '/');
function resolveLogPath($path)
{
global $logBasePath;
if ($path === null || $path === '') {
return $path;
}
$placeholder = '__NETALERTX_LOG__';
if (strpos($path, $placeholder) === 0) {
return $logBasePath . substr($path, strlen($placeholder));
}
return $path;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// check if authenticated // check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
// Function to render the log area component // Function to render the log area component
function renderLogArea($params) { function renderLogArea($params) {
global $logBasePath;
$fileName = isset($params['fileName']) ? $params['fileName'] : ''; $fileName = isset($params['fileName']) ? $params['fileName'] : '';
$filePath = isset($params['filePath']) ? $params['filePath'] : ''; $filePath = isset($params['filePath']) ? $params['filePath'] : '';
$textAreaCssClass = isset($params['textAreaCssClass']) ? $params['textAreaCssClass'] : ''; $textAreaCssClass = isset($params['textAreaCssClass']) ? $params['textAreaCssClass'] : '';
$buttons = isset($params['buttons']) ? $params['buttons'] : []; $buttons = isset($params['buttons']) ? $params['buttons'] : [];
$content = ""; $content = "";
if (filesize($filePath) > 2000000) { $filePath = resolveLogPath($filePath);
if (!is_file($filePath)) {
$content = "";
$fileSizeMb = 0.0;
} elseif (filesize($filePath) > 2000000) {
$content = file_get_contents($filePath, false, null, -2000000); $content = file_get_contents($filePath, false, null, -2000000);
$fileSizeMb = filesize($filePath) / 1000000;
} else { } else {
$content = file_get_contents($filePath); $content = file_get_contents($filePath);
$fileSizeMb = filesize($filePath) / 1000000;
} }
// Prepare the download button HTML if filePath starts with /app // Prepare the download button HTML if filePath resides under the active log base path
$downloadButtonHtml = ''; $downloadButtonHtml = '';
if (strpos($filePath, '/app') === 0) { $logPrefix = $logBasePath . '/';
if ($logPrefix !== '/' && strpos($filePath, $logPrefix) === 0) {
$downloadName = basename($filePath);
$downloadButtonHtml = ' $downloadButtonHtml = '
<span class="span-padding"> <span class="span-padding">
<a href="' . htmlspecialchars(str_replace('/app/log/', '/php/server/query_logs.php?file=', $filePath)) . '" target="_blank"> <a href="' . htmlspecialchars('/php/server/query_logs.php?file=' . rawurlencode($downloadName)) . '" target="_blank">
<i class="fa fa-download"></i> <i class="fa fa-download"></i>
</a> </a>
</span>'; </span>';
@@ -63,7 +92,7 @@ function renderLogArea($params) {
</div> </div>
<div class="row logs-row"> <div class="row logs-row">
<div class="log-file col-sm-6 col-xs-12">' . htmlspecialchars($filePath) . ' <div class="log-file col-sm-6 col-xs-12">' . htmlspecialchars($filePath) . '
<div class="logs-size">' . number_format((filesize($filePath) / 1000000), 2, ",", ".") . ' MB' <div class="logs-size">' . number_format($fileSizeMb, 2, ",", ".") . ' MB'
. $downloadButtonHtml . . $downloadButtonHtml .
'</div> '</div>
</div> </div>

View File

@@ -11,7 +11,7 @@
} }
], ],
"fileName": "app.log", "fileName": "app.log",
"filePath": "/app/log/app.log", "filePath": "__NETALERTX_LOG__/app.log",
"textAreaCssClass": "logs" "textAreaCssClass": "logs"
}, },
@@ -23,7 +23,7 @@
} }
], ],
"fileName": "app_front.log", "fileName": "app_front.log",
"filePath": "/app/log/app_front.log", "filePath": "__NETALERTX_LOG__/app_front.log",
"textAreaCssClass": "logs logs-small" "textAreaCssClass": "logs logs-small"
}, },
{ {
@@ -34,7 +34,7 @@
} }
], ],
"fileName": "app.php_errors.log", "fileName": "app.php_errors.log",
"filePath": "/app/log/app.php_errors.log", "filePath": "__NETALERTX_LOG__/app.php_errors.log",
"textAreaCssClass": "logs logs-small" "textAreaCssClass": "logs logs-small"
}, },
{ {
@@ -45,14 +45,18 @@
} }
], ],
"fileName": "execution_queue.log", "fileName": "execution_queue.log",
"filePath": "/app/log/execution_queue.log", "filePath": "__NETALERTX_LOG__/execution_queue.log",
"textAreaCssClass": "logs logs-small" "textAreaCssClass": "logs logs-small"
}, },
{ {
"buttons": [ "buttons": [
{
"labelStringCode": "Maint_PurgeLog",
"event": "logManage('nginx-error.log', 'cleanLog')"
}
], ],
"fileName": "nginx/error.log", "fileName": "nginx-error.log",
"filePath": "/var/log/nginx/error.log", "filePath": "__NETALERTX_LOG__/nginx-error.log",
"textAreaCssClass": "logs logs-small" "textAreaCssClass": "logs logs-small"
}, },
{ {
@@ -63,7 +67,7 @@
} }
], ],
"fileName": "db_is_locked.log", "fileName": "db_is_locked.log",
"filePath": "/app/log/db_is_locked.log", "filePath": "__NETALERTX_LOG__/db_is_locked.log",
"textAreaCssClass": "logs logs-small" "textAreaCssClass": "logs logs-small"
}, },
{ {
@@ -74,7 +78,7 @@
} }
], ],
"fileName": "stdout.log", "fileName": "stdout.log",
"filePath": "/app/log/stdout.log", "filePath": "__NETALERTX_LOG__/stdout.log",
"textAreaCssClass": "logs logs-small" "textAreaCssClass": "logs logs-small"
}, },
{ {
@@ -85,7 +89,29 @@
} }
], ],
"fileName": "stderr.log", "fileName": "stderr.log",
"filePath": "/app/log/stderr.log", "filePath": "__NETALERTX_LOG__/stderr.log",
"textAreaCssClass": "logs logs-small"
},
{
"buttons": [
{
"labelStringCode": "Maint_PurgeLog",
"event": "logManage('IP_changes.log', 'cleanLog')"
}
],
"fileName": "IP_changes.log",
"filePath": "__NETALERTX_LOG__/IP_changes.log",
"textAreaCssClass": "logs logs-small"
},
{
"buttons": [
{
"labelStringCode": "Maint_PurgeLog",
"event": "logManage('crond.log', 'cleanLog')"
}
],
"fileName": "crond.log",
"filePath": "__NETALERTX_LOG__/crond.log",
"textAreaCssClass": "logs logs-small" "textAreaCssClass": "logs logs-small"
} }
] ]

View File

@@ -13,8 +13,35 @@
// $DBFILE = dirname(__FILE__).'/../../../db/app.db'; // $DBFILE = dirname(__FILE__).'/../../../db/app.db';
// $DBFILE_LOCKED_FILE = dirname(__FILE__).'/../../../log/db_is_locked.log'; // $DBFILE_LOCKED_FILE = dirname(__FILE__).'/../../../log/db_is_locked.log';
$scriptDir = realpath(dirname(__FILE__)); // Resolves symlinks to the actual physical path $scriptDir = realpath(dirname(__FILE__)); // Resolves symlinks to the actual physical path
$DBFILE = $scriptDir . '/../../../db/app.db'; $legacyDbPath = $scriptDir . '/../../../db/app.db';
$DBFILE_LOCKED_FILE = $scriptDir . '/../../../log/db_is_locked.log'; $legacyLogDir = $scriptDir . '/../../../log';
$dbFolderPath = rtrim(getenv('NETALERTX_DB') ?: '/data/db', '/');
$logFolderPath = rtrim(getenv('NETALERTX_LOG') ?: '/tmp/log', '/');
// Fallback to legacy layout if the new location is missing but the legacy file still exists
if (!is_dir($dbFolderPath) && file_exists($legacyDbPath)) {
$dbFolderPath = dirname($legacyDbPath);
}
if (!is_dir($dbFolderPath)) {
@mkdir($dbFolderPath, 0775, true);
}
$DBFILE = rtrim($dbFolderPath, '/') . '/app.db';
if (!file_exists($DBFILE) && file_exists($legacyDbPath)) {
$DBFILE = $legacyDbPath;
}
if (!is_dir($logFolderPath) && is_dir($legacyLogDir)) {
$logFolderPath = $legacyLogDir;
}
if (!is_dir($logFolderPath)) {
@mkdir($logFolderPath, 0775, true);
}
$DBFILE_LOCKED_FILE = rtrim($logFolderPath, '/') . '/db_is_locked.log';
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -39,8 +66,10 @@ function SQLite3_connect($trytoreconnect = true, $retryCount = 0) {
if (!file_exists($DBFILE)) { if (!file_exists($DBFILE)) {
die("Database file not found: $DBFILE"); die("Database file not found: $DBFILE");
} }
if (!file_exists(dirname($DBFILE_LOCKED_FILE))) {
die("Log directory not found: " . dirname($DBFILE_LOCKED_FILE)); $lockDir = dirname($DBFILE_LOCKED_FILE);
if (!is_dir($lockDir) && !@mkdir($lockDir, 0775, true)) {
die("Log directory not found and could not be created: $lockDir");
} }
@@ -130,6 +159,7 @@ class CustomDatabaseWrapper {
$message = 'Error executing query (attempts: ' . $attempts . '), query: ' . $query; $message = 'Error executing query (attempts: ' . $attempts . '), query: ' . $query;
// write_notification($message); // write_notification($message);
error_log("Query failed after {$this->maxRetries} attempts: " . $this->sqlite->lastErrorMsg()); error_log("Query failed after {$this->maxRetries} attempts: " . $this->sqlite->lastErrorMsg());
return false;
} }
public function query_log_add($query) public function query_log_add($query)
@@ -187,7 +217,7 @@ function OpenDB($DBPath = null) {
if (strlen($DBFILE) == 0) { if (strlen($DBFILE) == 0) {
$message = 'Database not available'; $message = 'Database not available';
echo '<script>alert('.$message.')</script>'; echo '<script>alert("'.$message.'")</script>';
write_notification($message); write_notification($message);
die('<div style="padding-left:150px">'.$message.'</div>'); die('<div style="padding-left:150px">'.$message.'</div>');
@@ -197,7 +227,7 @@ function OpenDB($DBPath = null) {
$db = new CustomDatabaseWrapper($DBFILE); $db = new CustomDatabaseWrapper($DBFILE);
} catch (Exception $e) { } catch (Exception $e) {
$message = "Error connecting to the database"; $message = "Error connecting to the database";
echo '<script>alert('.$message.'": ' . $e->getMessage() . '")</script>'; echo '<script>alert("'.$message.': '.$e->getMessage().'")</script>';
write_notification($message); write_notification($message);
die('<div style="padding-left:150px">'.$message.'</div>'); die('<div style="padding-left:150px">'.$message.'</div>');
} }

View File

@@ -1,5 +1,6 @@
<?php <?php
ini_set('error_log', '../../log/app.php_errors.log'); // initializing the app.php_errors.log file for the maintenance section $logPath = rtrim(getenv('NETALERTX_LOG') ?: '/tmp/log', '/') . '/app.php_errors.log';
ini_set('error_log', $logPath); // initializing the app.php_errors.log file for the maintenance section
require dirname(__FILE__).'/../templates/globals.php'; require dirname(__FILE__).'/../templates/globals.php';
require dirname(__FILE__).'/db.php'; require dirname(__FILE__).'/db.php';
require dirname(__FILE__).'/util.php'; require dirname(__FILE__).'/util.php';

View File

@@ -18,7 +18,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
// Check if file parameter is provided // Check if file parameter is provided
if ($file) { if ($file) {
// Define the folder where files are located // Define the folder where files are located
$filePath = "/app/config/" . basename($file); $configRoot = getenv('NETALERTX_CONFIG') ?: '/data/config';
$filePath = rtrim($configRoot, '/') . "/" . basename($file);
// Check if the file exists and is readable // Check if the file exists and is readable
if (file_exists($filePath) && is_readable($filePath)) { if (file_exists($filePath) && is_readable($filePath)) {

View File

@@ -11,6 +11,8 @@ require dirname(__FILE__).'/../server/init.php';
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Handle incoming requests // Handle incoming requests
if ($_SERVER['REQUEST_METHOD'] === 'GET') { if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$configRoot = getenv('NETALERTX_CONFIG') ?: '/data/config';
$apiRoot = getenv('NETALERTX_API') ?: '/tmp/api';
// Get query string parameter ?file=settings_table.json // Get query string parameter ?file=settings_table.json
$file = isset($_GET['file']) ? $_GET['file'] : null; $file = isset($_GET['file']) ? $_GET['file'] : null;
@@ -19,10 +21,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
// Define the folder where files are located // Define the folder where files are located
if ($file == "workflows.json") if ($file == "workflows.json")
{ {
$filePath = "/app/config/" . basename($file); $filePath = rtrim($configRoot, '/') . "/" . basename($file);
} else } else
{ {
$filePath = "/app/api/" . basename($file); $filePath = rtrim($apiRoot, '/') . "/" . basename($file);
} }
// Check if the file exists // Check if the file exists
@@ -59,7 +61,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
} }
$file = $_GET['file']; $file = $_GET['file'];
$filePath = "/app/config/" . basename($file); $configRoot = getenv('NETALERTX_CONFIG') ?: '/data/config';
$filePath = rtrim($configRoot, '/') . "/" . basename($file);
// Save new workflows.json (replace existing content) // Save new workflows.json (replace existing content)
if (file_put_contents($filePath, json_encode($decodedData, JSON_PRETTY_PRINT))) { if (file_put_contents($filePath, json_encode($decodedData, JSON_PRETTY_PRINT))) {

View File

@@ -17,7 +17,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
// Check if file parameter is provided // Check if file parameter is provided
if ($file) { if ($file) {
// Define the folder where files are located // Define the folder where files are located
$filePath = "/app/log/" . basename($file); $logBasePath = rtrim(getenv('NETALERTX_LOG') ?: '/tmp/log', '/');
$filePath = $logBasePath . '/' . basename($file);
// Check if the file exists // Check if the file exists
if (file_exists($filePath)) { if (file_exists($filePath)) {

View File

@@ -11,6 +11,7 @@
require dirname(__FILE__).'/../templates/globals.php'; require dirname(__FILE__).'/../templates/globals.php';
require dirname(__FILE__).'/../templates/skinUI.php'; require dirname(__FILE__).'/../templates/skinUI.php';
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// check if authenticated // check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
@@ -263,7 +264,7 @@ function cleanLog($logFile)
$path = ""; $path = "";
$allowedFiles = ['app.log', 'app_front.log', 'IP_changes.log', 'stdout.log', 'stderr.log', 'app.php_errors.log', 'execution_queue.log', 'db_is_locked.log']; $allowedFiles = ['app.log', 'app_front.log', 'IP_changes.log', 'stdout.log', 'stderr.log', 'app.php_errors.log', 'execution_queue.log', 'db_is_locked.log', 'nginx-error.log', 'crond.log'];
if(in_array($logFile, $allowedFiles)) if(in_array($logFile, $allowedFiles))
{ {
@@ -321,7 +322,12 @@ function saveSettings()
// collect all groups // collect all groups
$decodedSettings = json_decode($SETTINGS, true); $decodedSettings = json_decode((string)$SETTINGS, true);
if ($decodedSettings === null) {
echo "Error: Invalid JSON in settings data.";
return;
}
foreach ($decodedSettings as $setting) { foreach ($decodedSettings as $setting) {
if( in_array($setting[0] , $groups) == false) { if( in_array($setting[0] , $groups) == false) {
@@ -432,7 +438,8 @@ function getString ($setKey, $default) {
// ------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------
function getSettingValue($setKey) { function getSettingValue($setKey) {
// Define the JSON endpoint URL // Define the JSON endpoint URL
$url = dirname(__FILE__).'/../../../api/table_settings.json'; $apiRoot = rtrim(getenv('NETALERTX_API') ?: '/tmp/api', '/');
$url = $apiRoot . '/table_settings.json';
// Fetch the JSON data // Fetch the JSON data
$json = file_get_contents($url); $json = file_get_contents($url);

View File

@@ -7,6 +7,11 @@
require dirname(__FILE__).'/../templates/globals.php'; require dirname(__FILE__).'/../templates/globals.php';
function get_notification_store_path(): string {
$apiRoot = getenv('NETALERTX_API') ?: '/tmp/api';
return rtrim($apiRoot, '/') . '/user_notifications.json';
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// check if authenticated // check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
@@ -69,7 +74,7 @@ function generate_guid() {
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
// Logs a notification in in-app notification system // Logs a notification in in-app notification system
function write_notification($content, $level = "interrupt") { function write_notification($content, $level = "interrupt") {
$NOTIFICATION_API_FILE = '/app/api/user_notifications.json'; $NOTIFICATION_API_FILE = get_notification_store_path();
// Generate GUID // Generate GUID
$guid = generate_guid(); $guid = generate_guid();
@@ -102,7 +107,7 @@ function write_notification($content, $level = "interrupt") {
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
// Removes a notification based on GUID // Removes a notification based on GUID
function remove_notification($guid) { function remove_notification($guid) {
$NOTIFICATION_API_FILE = '/app/api/user_notifications.json'; $NOTIFICATION_API_FILE = get_notification_store_path();
// Read existing notifications // Read existing notifications
$notifications = json_decode(file_get_contents($NOTIFICATION_API_FILE), true); $notifications = json_decode(file_get_contents($NOTIFICATION_API_FILE), true);
@@ -119,7 +124,7 @@ function remove_notification($guid) {
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
// Deletes all notifications // Deletes all notifications
function notifications_clear() { function notifications_clear() {
$NOTIFICATION_API_FILE = '/app/api/user_notifications.json'; $NOTIFICATION_API_FILE = get_notification_store_path();
// Clear notifications by writing an empty array to the file // Clear notifications by writing an empty array to the file
file_put_contents($NOTIFICATION_API_FILE, json_encode(array())); file_put_contents($NOTIFICATION_API_FILE, json_encode(array()));
@@ -128,7 +133,7 @@ function notifications_clear() {
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
// Mark a notification read based on GUID // Mark a notification read based on GUID
function mark_notification_as_read($guid) { function mark_notification_as_read($guid) {
$NOTIFICATION_API_FILE = '/app/api/user_notifications.json'; $NOTIFICATION_API_FILE = get_notification_store_path();
$max_attempts = 3; $max_attempts = 3;
$attempts = 0; $attempts = 0;
@@ -177,7 +182,7 @@ function notifications_mark_all_read() {
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
function get_unread_notifications() { function get_unread_notifications() {
$NOTIFICATION_API_FILE = '/app/api/user_notifications.json'; $NOTIFICATION_API_FILE = get_notification_store_path();
// Read existing notifications // Read existing notifications
if (file_exists($NOTIFICATION_API_FILE) && is_readable($NOTIFICATION_API_FILE)) { if (file_exists($NOTIFICATION_API_FILE) && is_readable($NOTIFICATION_API_FILE)) {

View File

@@ -15,7 +15,19 @@ if (isset($_SESSION["login"]) && $_SESSION["login"] == 1) {
// Check if a valid cookie is present // Check if a valid cookie is present
$CookieSaveLoginName = "NetAlertX_SaveLogin"; $CookieSaveLoginName = "NetAlertX_SaveLogin";
$config_file = "../../../config/app.conf"; // depends on where this file is called from
// Use environment-aware config path
$configFolderPath = rtrim(getenv('NETALERTX_CONFIG') ?: '/data/config', '/');
$config_file = $configFolderPath . '/app.conf';
// Fallback to legacy path if new location doesn't exist
if (!file_exists($config_file)) {
$legacyPath = "../../../config/app.conf";
if (file_exists($legacyPath)) {
$config_file = $legacyPath;
}
}
$config_file_lines = file($config_file); $config_file_lines = file($config_file);
$config_file_lines = array_values(preg_grep('/^SETPWD_password.*=/', $config_file_lines)); $config_file_lines = array_values(preg_grep('/^SETPWD_password.*=/', $config_file_lines));
$password_line = explode("'", $config_file_lines[0]); $password_line = explode("'", $config_file_lines[0]);

View File

@@ -4,8 +4,8 @@
// ## Global constants and TimeZone processing // ## Global constants and TimeZone processing
// ###################################################################### // ######################################################################
$configFolderPath = "/app/config/"; $configFolderPath = rtrim(getenv('NETALERTX_CONFIG') ?: '/data/config', '/') . '/';
$logFolderPath = "/app/log/"; $logFolderPath = rtrim(getenv('NETALERTX_LOG') ?: '/tmp/log', '/') . '/';
$config_file = "app.conf"; $config_file = "app.conf";
$workflows_file = "workflows.json"; $workflows_file = "workflows.json";

View File

@@ -674,7 +674,7 @@
"Systeminfo_System_Uptime": "Uptime:", "Systeminfo_System_Uptime": "Uptime:",
"Systeminfo_This_Client": "Aquest Client", "Systeminfo_This_Client": "Aquest Client",
"Systeminfo_USB_Devices": "Dispositius USB", "Systeminfo_USB_Devices": "Dispositius USB",
"TICKER_MIGRATE_TO_NETALERTX": "⚠ S'han detectat punts muntatge antics. Ves a <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">aquesta guia</a> per migrar les noves <code>/app/config</code> i <code>/app/db</code> carpetes i al <code>netalertx</code> contenidor.", "TICKER_MIGRATE_TO_NETALERTX": "⚠ S'han detectat punts muntatge antics. Ves a <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">aquesta guia</a> per migrar les noves <code>/data/config</code> i <code>/data/db</code> carpetes i al <code>netalertx</code> contenidor.",
"TIMEZONE_description": "Fus horari per mostrar les estadístiques correctament. <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">aquí</a>.", "TIMEZONE_description": "Fus horari per mostrar les estadístiques correctament. <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">aquí</a>.",
"TIMEZONE_name": "Fus horari", "TIMEZONE_name": "Fus horari",
"UI_DEV_SECTIONS_description": "Seleccioneu quins elements de la interfície d'usuari per ocultar a les pàgines de dispositius.", "UI_DEV_SECTIONS_description": "Seleccioneu quins elements de la interfície d'usuari per ocultar a les pàgines de dispositius.",

View File

@@ -674,7 +674,7 @@
"Systeminfo_System_Uptime": "Uptime:", "Systeminfo_System_Uptime": "Uptime:",
"Systeminfo_This_Client": "This Client", "Systeminfo_This_Client": "This Client",
"Systeminfo_USB_Devices": "USB devices", "Systeminfo_USB_Devices": "USB devices",
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Old mount locations detected. Follow <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">this guide</a> to migrate to the new <code>/app/config</code> and <code>/app/db</code> folders and the <code>netalertx</code> container.", "TICKER_MIGRATE_TO_NETALERTX": "⚠ Old mount locations detected. Follow <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">this guide</a> to migrate to the new <code>/data/config</code> and <code>/data/db</code> folders and the <code>netalertx</code> container.",
"TIMEZONE_description": "Time zone to display stats correctly. Find your time zone <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">here</a>.", "TIMEZONE_description": "Time zone to display stats correctly. Find your time zone <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">here</a>.",
"TIMEZONE_name": "Time zone", "TIMEZONE_name": "Time zone",
"UI_DEV_SECTIONS_description": "Select which UI elements to hide in the devices pages.", "UI_DEV_SECTIONS_description": "Select which UI elements to hide in the devices pages.",

View File

@@ -734,7 +734,7 @@
"Systeminfo_System_Uptime": "Tiempo de actividad:", "Systeminfo_System_Uptime": "Tiempo de actividad:",
"Systeminfo_This_Client": "Este cliente", "Systeminfo_This_Client": "Este cliente",
"Systeminfo_USB_Devices": "Dispositivos USB", "Systeminfo_USB_Devices": "Dispositivos USB",
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Ubicaciones de montaje antiguas detectadas. Siga <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">esta guía</a> para migrar a las nuevas carpetas <code>/app/config</code> y <code>/app/db</code> y el contenedor <code>netalertx</code>.", "TICKER_MIGRATE_TO_NETALERTX": "⚠ Ubicaciones de montaje antiguas detectadas. Siga <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">esta guía</a> para migrar a las nuevas carpetas <code>/data/config</code> y <code>/data/db</code> y el contenedor <code>netalertx</code>.",
"TIMEZONE_description": "La zona horaria para mostrar las estadísticas correctamente. Encuentra tu zona horaria <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">aquí</a>.", "TIMEZONE_description": "La zona horaria para mostrar las estadísticas correctamente. Encuentra tu zona horaria <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">aquí</a>.",
"TIMEZONE_name": "Zona horaria", "TIMEZONE_name": "Zona horaria",
"UI_DEV_SECTIONS_description": "Seleccione los elementos de la interfaz de usuario que desea ocultar en las páginas de dispositivos.", "UI_DEV_SECTIONS_description": "Seleccione los elementos de la interfaz de usuario que desea ocultar en las páginas de dispositivos.",

View File

@@ -674,7 +674,7 @@
"Systeminfo_System_Uptime": "Durée d'activité:", "Systeminfo_System_Uptime": "Durée d'activité:",
"Systeminfo_This_Client": "Ce client", "Systeminfo_This_Client": "Ce client",
"Systeminfo_USB_Devices": "Appareils USB", "Systeminfo_USB_Devices": "Appareils USB",
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Emplacement de point de montage obsolète détecté. Suivez <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">ce guide</a> pour migrer vers les nouveaux dossiers <code>/app/config</code> and <code>/app/db</code> et le container <code>netalertx</code>.", "TICKER_MIGRATE_TO_NETALERTX": "⚠ Emplacement de point de montage obsolète détecté. Suivez <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">ce guide</a> pour migrer vers les nouveaux dossiers <code>/data/config</code> and <code>/data/db</code> et le container <code>netalertx</code>.",
"TIMEZONE_description": "Fuseau horaire pour afficher correctement les statistiques. Trouvez votre fuseau horaire <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">ici</a>.", "TIMEZONE_description": "Fuseau horaire pour afficher correctement les statistiques. Trouvez votre fuseau horaire <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">ici</a>.",
"TIMEZONE_name": "Fuseau horaire", "TIMEZONE_name": "Fuseau horaire",
"UI_DEV_SECTIONS_description": "Slecetionnez quels éléments de l'interface graphique masquer dans les pages des appareils.", "UI_DEV_SECTIONS_description": "Slecetionnez quels éléments de l'interface graphique masquer dans les pages des appareils.",

View File

@@ -674,7 +674,7 @@
"Systeminfo_System_Uptime": "Tempo di attività:", "Systeminfo_System_Uptime": "Tempo di attività:",
"Systeminfo_This_Client": "Questo client", "Systeminfo_This_Client": "Questo client",
"Systeminfo_USB_Devices": "Dispositivi USB", "Systeminfo_USB_Devices": "Dispositivi USB",
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Rilevate vecchie posizioni di montaggio. Segui <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">questa guida</a> per migrare alle nuove cartelle <code> /app/config</code> e <code>/app/db</code> e al contenitore <code>netalertx</code>.", "TICKER_MIGRATE_TO_NETALERTX": "⚠ Rilevate vecchie posizioni di montaggio. Segui <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">questa guida</a> per migrare alle nuove cartelle <code> /data/config</code> e <code>/data/db</code> e al contenitore <code>netalertx</code>.",
"TIMEZONE_description": "Fuso orario per visualizzare correttamente le statistiche. Trova il tuo fuso orario <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">qui</a>.", "TIMEZONE_description": "Fuso orario per visualizzare correttamente le statistiche. Trova il tuo fuso orario <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">qui</a>.",
"TIMEZONE_name": "Fuso orario", "TIMEZONE_name": "Fuso orario",
"UI_DEV_SECTIONS_description": "Seleziona quali elementi della UI nascondere nella pagina dei dispositivi.", "UI_DEV_SECTIONS_description": "Seleziona quali elementi della UI nascondere nella pagina dei dispositivi.",

View File

@@ -674,7 +674,7 @@
"Systeminfo_System_Uptime": "Oppetid:", "Systeminfo_System_Uptime": "Oppetid:",
"Systeminfo_This_Client": "Denne klienten", "Systeminfo_This_Client": "Denne klienten",
"Systeminfo_USB_Devices": "USB-enheter", "Systeminfo_USB_Devices": "USB-enheter",
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Eldre Mount-lokasjoner oppdaget. Følg <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">denne guiden</a> for å migrere til den nye <code>/app/config</code> og <code>/app/db </code> mappene og <code>netalertx</code> containeren.", "TICKER_MIGRATE_TO_NETALERTX": "⚠ Eldre Mount-lokasjoner oppdaget. Følg <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">denne guiden</a> for å migrere til den nye <code>/data/config</code> og <code>/data/db </code> mappene og <code>netalertx</code> containeren.",
"TIMEZONE_description": "Tidssone for å vise statistikk riktig. Finn din tidssone <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">her</a>.", "TIMEZONE_description": "Tidssone for å vise statistikk riktig. Finn din tidssone <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">her</a>.",
"TIMEZONE_name": "Tidssone", "TIMEZONE_name": "Tidssone",
"UI_DEV_SECTIONS_description": "Velg hvilke UI -elementer du vil skjule på enhetssiden.", "UI_DEV_SECTIONS_description": "Velg hvilke UI -elementer du vil skjule på enhetssiden.",

View File

@@ -674,7 +674,7 @@
"Systeminfo_System_Uptime": "Czas pracy:", "Systeminfo_System_Uptime": "Czas pracy:",
"Systeminfo_This_Client": "Ten klient", "Systeminfo_This_Client": "Ten klient",
"Systeminfo_USB_Devices": "Urządzenia USB", "Systeminfo_USB_Devices": "Urządzenia USB",
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Wykryto stare lokalizacje montowania. Skorzystaj z <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">tego przewodnika</a>, aby przeprowadzić migrację do nowych folderów <code>/app/config</code> i <code>/app/db</code> oraz kontenera <code>netalertx</code>.", "TICKER_MIGRATE_TO_NETALERTX": "⚠ Wykryto stare lokalizacje montowania. Skorzystaj z <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">tego przewodnika</a>, aby przeprowadzić migrację do nowych folderów <code>/data/config</code> i <code>/data/db</code> oraz kontenera <code>netalertx</code>.",
"TIMEZONE_description": "Ustaw strefę czasową, aby statystyki były wyświetlane poprawnie. Znajdź swoją strefę czasową <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">tutaj</a>.", "TIMEZONE_description": "Ustaw strefę czasową, aby statystyki były wyświetlane poprawnie. Znajdź swoją strefę czasową <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">tutaj</a>.",
"TIMEZONE_name": "Strefa czasowa", "TIMEZONE_name": "Strefa czasowa",
"UI_DEV_SECTIONS_description": "Wybierz elementy interfejsu użytkownika (UI), które chcesz ukryć na stronach Urządzeń.", "UI_DEV_SECTIONS_description": "Wybierz elementy interfejsu użytkownika (UI), które chcesz ukryć na stronach Urządzeń.",

View File

@@ -674,7 +674,7 @@
"Systeminfo_System_Uptime": "Время работы:", "Systeminfo_System_Uptime": "Время работы:",
"Systeminfo_This_Client": "Этот клиент", "Systeminfo_This_Client": "Этот клиент",
"Systeminfo_USB_Devices": "USB-устройства", "Systeminfo_USB_Devices": "USB-устройства",
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Обнаружены устаревшие местоположения. Следуйте этому руководству <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\"></a>, чтобы перейти на новые <code>/app/config</code> и <code>/app/db</code> папки и контейнер <code>netalertx</code>.", "TICKER_MIGRATE_TO_NETALERTX": "⚠ Обнаружены устаревшие местоположения. Следуйте этому руководству <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\"></a>, чтобы перейти на новые <code>/data/config</code> и <code>/data/db</code> папки и контейнер <code>netalertx</code>.",
"TIMEZONE_description": "Часовой пояс для корректного отображения статистики. Найдите свой часовой пояс <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">здесь</a>.", "TIMEZONE_description": "Часовой пояс для корректного отображения статистики. Найдите свой часовой пояс <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">здесь</a>.",
"TIMEZONE_name": "Часовой пояс", "TIMEZONE_name": "Часовой пояс",
"UI_DEV_SECTIONS_description": "Выберите, какие элементы интерфейса нужно скрыть на страницах «Устройства».", "UI_DEV_SECTIONS_description": "Выберите, какие элементы интерфейса нужно скрыть на страницах «Устройства».",

View File

@@ -674,7 +674,7 @@
"Systeminfo_System_Uptime": "Час роботи:", "Systeminfo_System_Uptime": "Час роботи:",
"Systeminfo_This_Client": "Цей клієнт", "Systeminfo_This_Client": "Цей клієнт",
"Systeminfo_USB_Devices": "USB-пристрої", "Systeminfo_USB_Devices": "USB-пристрої",
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Виявлено старі місця монтування. Дотримуйтеся <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">цього посібника</a>, щоб перейти на новий <code> папки /app/config</code> і <code>/app/db</code> і контейнер <code>netalertx</code>.", "TICKER_MIGRATE_TO_NETALERTX": "⚠ Виявлено старі місця монтування. Дотримуйтеся <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">цього посібника</a>, щоб перейти на нові папки <code>/data/config</code> і <code>/data/db</code> та контейнер <code>netalertx</code>.",
"TIMEZONE_description": "Часовий пояс для правильного відображення статистики. Знайдіть свій часовий пояс <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">тут</a>.", "TIMEZONE_description": "Часовий пояс для правильного відображення статистики. Знайдіть свій часовий пояс <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">тут</a>.",
"TIMEZONE_name": "Часовий пояс", "TIMEZONE_name": "Часовий пояс",
"UI_DEV_SECTIONS_description": "Виберіть, які елементи інтерфейсу користувача приховати на сторінках пристроїв.", "UI_DEV_SECTIONS_description": "Виберіть, які елементи інтерфейсу користувача приховати на сторінках пристроїв.",

View File

@@ -674,7 +674,7 @@
"Systeminfo_System_Uptime": "正常运行时间:", "Systeminfo_System_Uptime": "正常运行时间:",
"Systeminfo_This_Client": "此客户", "Systeminfo_This_Client": "此客户",
"Systeminfo_USB_Devices": "USB 设备", "Systeminfo_USB_Devices": "USB 设备",
"TICKER_MIGRATE_TO_NETALERTX": "⚠ 检测到旧的挂载位置。请按照<a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">本指南</a>迁移到新的 <code>/app/config</code> 和 <code>/app/db</code> 文件夹以及 <code>netalertx</code> 容器。", "TICKER_MIGRATE_TO_NETALERTX": "⚠ 检测到旧的挂载位置。请按照<a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">本指南</a>迁移到新的 <code>/data/config</code> 和 <code>/data/db</code> 文件夹以及 <code>netalertx</code> 容器。",
"TIMEZONE_description": "时区可正确显示统计数据。在<a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">此处</a>查找您的时区。", "TIMEZONE_description": "时区可正确显示统计数据。在<a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">此处</a>查找您的时区。",
"TIMEZONE_name": "时区", "TIMEZONE_name": "时区",
"UI_DEV_SECTIONS_description": "选择在设备页面中隐藏哪些 UI 元素。", "UI_DEV_SECTIONS_description": "选择在设备页面中隐藏哪些 UI 元素。",

View File

@@ -1,7 +1,18 @@
<?php <?php
// Constants // Constants
define('CONFIG_PATH', $_SERVER['DOCUMENT_ROOT'] . "/../config/app.conf"); $configFolderPath = rtrim(getenv('NETALERTX_CONFIG') ?: '/data/config', '/');
$legacyConfigPath = $_SERVER['DOCUMENT_ROOT'] . "/../config/app.conf";
// Use environment variable path, fallback to legacy
if (file_exists($configFolderPath . '/app.conf')) {
define('CONFIG_PATH', $configFolderPath . '/app.conf');
} else if (file_exists($legacyConfigPath)) {
define('CONFIG_PATH', $legacyConfigPath);
} else {
define('CONFIG_PATH', $configFolderPath . '/app.conf'); // default to new location
}
define('COOKIE_SAVE_LOGIN_NAME', "NetAlertX_SaveLogin"); define('COOKIE_SAVE_LOGIN_NAME', "NetAlertX_SaveLogin");
// Utility Functions // Utility Functions
@@ -48,7 +59,7 @@ if (!empty($_REQUEST['action']) && $_REQUEST['action'] == 'logout') {
// Load configuration // Load configuration
if (!file_exists(CONFIG_PATH)) { if (!file_exists(CONFIG_PATH)) {
die("Configuration file not found in " . $_SERVER['DOCUMENT_ROOT'] . "/../config/app.conf"); die("Configuration file not found in " . CONFIG_PATH);
} }
$configLines = file(CONFIG_PATH); $configLines = file(CONFIG_PATH);

View File

@@ -1,23 +1,18 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
import pathlib
import sys import sys
import json
import sqlite3
from pytz import timezone from pytz import timezone
# Define the installation path and extend the system path for plugin imports # Define the installation path and extend the system path for plugin imports
INSTALL_PATH = "/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from plugin_helper import Plugin_Objects
from plugin_utils import get_plugins_configs
from logger import mylog, Logger from logger import mylog, Logger
from const import pluginsPath, fullDbPath, logPath from const import logPath
from helper import timeNowTZ, get_setting_value from helper import get_setting_value
from messaging.in_app import write_notification
import conf import conf
# Make sure the TIMEZONE for logging is correct # Make sure the TIMEZONE for logging is correct
@@ -54,10 +49,11 @@ def main():
# insert devices into the lats_result.log # insert devices into the lats_result.log
# make sure the below mapping is mapped in config.json, for example: # make sure the below mapping is mapped in config.json, for example:
#"database_column_definitions": [ # "database_column_definitions": [
# { # {
# "column": "Object_PrimaryID", <--------- the value I save into primaryId # "column": "Object_PrimaryID", <--------- the value I save into primaryId
# "mapped_to_column": "cur_MAC", <--------- gets inserted into the CurrentScan DB table column cur_MAC # "mapped_to_column": "cur_MAC", <--------- gets inserted into the CurrentScan DB
# table column cur_MAC
# #
for device in device_data: for device in device_data:
plugin_objects.add_object( plugin_objects.add_object(

View File

@@ -1,31 +1,20 @@
#!/usr/bin/env python #!/usr/bin/env python
# Just a testing library plugin for development purposes # Just a testing library plugin for development purposes
import json
import subprocess
import argparse
import os import os
import pathlib
import sys import sys
from datetime import datetime
import time
import re import re
import hashlib import hashlib
import sqlite3
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
# NetAlertX modules # NetAlertX modules
import conf from const import logPath
from const import apiPath, confFileName, logPath
from plugin_utils import getPluginObject
from plugin_helper import Plugin_Objects from plugin_helper import Plugin_Objects
from logger import mylog, Logger, append_line_to_file from logger import mylog
from helper import timeNowTZ, get_setting_value, bytes_to_string, sanitize_string, cleanDeviceName from helper import get_setting_value
from models.notification_instance import NotificationInstance
from database import DB, get_device_stats
pluginName = 'TESTONLY' pluginName = 'TESTONLY'

View File

@@ -2,45 +2,45 @@
import json import json
import subprocess import subprocess
import argparse
import os import os
import pathlib
import sys import sys
from datetime import datetime
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
import conf import conf
from const import confFileName, logPath from const import confFileName, logPath
from plugin_helper import Plugin_Objects from plugin_helper import Plugin_Objects
from logger import mylog, Logger, append_line_to_file from logger import mylog, Logger
from helper import timeNowTZ, get_setting_value from helper import timeNowTZ, get_setting_value
from models.notification_instance import NotificationInstance from models.notification_instance import NotificationInstance
from database import DB from database import DB
from pytz import timezone from pytz import timezone
# Make sure the TIMEZONE for logging is correct # Make sure the TIMEZONE for logging is correct
conf.tz = timezone(get_setting_value('TIMEZONE')) conf.tz = timezone(get_setting_value("TIMEZONE"))
# Make sure log level is initialized correctly # Make sure log level is initialized correctly
Logger(get_setting_value('LOG_LEVEL')) Logger(get_setting_value("LOG_LEVEL"))
pluginName = 'APPRISE' pluginName = "APPRISE"
LOG_PATH = logPath + '/plugins'
RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log')
LOG_PATH = logPath + "/plugins"
RESULT_FILE = os.path.join(LOG_PATH, f"last_result.{pluginName}.log")
def main(): def main():
mylog("verbose", [f"[{pluginName}](publisher) In script"])
mylog('verbose', [f'[{pluginName}](publisher) In script'])
# Check if basic config settings supplied # Check if basic config settings supplied
if check_config() == False: if check_config() == False:
mylog('none', [f'[{pluginName}] ⚠ ERROR: Publisher notification gateway not set up correctly. Check your {confFileName} {pluginName}_* variables.']) mylog(
"none",
[
f"[{pluginName}] ⚠ ERROR: Publisher notification gateway not set up correctly. Check your {confFileName} {pluginName}_* variables."
],
)
return return
# Create a database connection # Create a database connection
@@ -58,47 +58,50 @@ def main():
# Process the new notifications (see the Notifications DB table for structure or check the /php/server/query_json.php?file=table_notifications.json endpoint) # Process the new notifications (see the Notifications DB table for structure or check the /php/server/query_json.php?file=table_notifications.json endpoint)
for notification in new_notifications: for notification in new_notifications:
# Send notification # Send notification
result = send(notification["HTML"], notification["Text"]) result = send(notification["HTML"], notification["Text"])
# Log result # Log result
plugin_objects.add_object( plugin_objects.add_object(
primaryId = pluginName, primaryId=pluginName,
secondaryId = timeNowTZ(), secondaryId=timeNowTZ(),
watched1 = notification["GUID"], watched1=notification["GUID"],
watched2 = result, watched2=result,
watched3 = 'null', watched3="null",
watched4 = 'null', watched4="null",
extra = 'null', extra="null",
foreignKey = notification["GUID"] foreignKey=notification["GUID"],
) )
plugin_objects.write_result_file() plugin_objects.write_result_file()
#-------------------------------------------------------------------------------
# -------------------------------------------------------------------------------
def check_config(): def check_config():
if get_setting_value('APPRISE_HOST') == '' or (get_setting_value('APPRISE_URL') == '' and get_setting_value('APPRISE_TAG') == ''): if get_setting_value("APPRISE_HOST") == "" or (
get_setting_value("APPRISE_URL") == ""
and get_setting_value("APPRISE_TAG") == ""
):
return False return False
else: else:
return True return True
#-------------------------------------------------------------------------------
def send(html, text):
payloadData = '' # -------------------------------------------------------------------------------
result = '' def send(html, text):
payloadData = ""
result = ""
# limit = 1024 * 1024 # 1MB limit (1024 bytes * 1024 bytes = 1MB) # limit = 1024 * 1024 # 1MB limit (1024 bytes * 1024 bytes = 1MB)
limit = get_setting_value('APPRISE_SIZE') limit = get_setting_value("APPRISE_SIZE")
# truncate size # truncate size
if get_setting_value('APPRISE_PAYLOAD') == 'html': if get_setting_value("APPRISE_PAYLOAD") == "html":
if len(html) > limit: if len(html) > limit:
payloadData = html[:limit] + "<h1>(text was truncated)</h1>" payloadData = html[:limit] + "<h1>(text was truncated)</h1>"
else: else:
payloadData = html payloadData = html
if get_setting_value('APPRISE_PAYLOAD') == 'text': if get_setting_value("APPRISE_PAYLOAD") == "text":
if len(text) > limit: if len(text) > limit:
payloadData = text[:limit] + " (text was truncated)" payloadData = text[:limit] + " (text was truncated)"
else: else:
@@ -106,36 +109,55 @@ def send(html, text):
# Define Apprise compatible payload (https://github.com/caronc/apprise-api#stateless-solution) # Define Apprise compatible payload (https://github.com/caronc/apprise-api#stateless-solution)
target_key = "tag" if get_setting_value('APPRISE_TARGETTYPE') == 'tag' else "urls" target_key = "tag" if get_setting_value("APPRISE_TARGETTYPE") == "tag" else "urls"
target_value = get_setting_value('APPRISE_TAG') if target_key == 'tag' else get_setting_value('APPRISE_URL') target_value = (
get_setting_value("APPRISE_TAG")
if target_key == "tag"
else get_setting_value("APPRISE_URL")
)
_json_payload = { _json_payload = {
target_key: target_value, target_key: target_value,
"title": "NetAlertX Notifications", "title": "NetAlertX Notifications",
"format": get_setting_value('APPRISE_PAYLOAD'), "format": get_setting_value("APPRISE_PAYLOAD"),
"body": payloadData "body": payloadData,
} }
try: try:
# try runnning a subprocess # try runnning a subprocess
p = subprocess.Popen(["curl","-i","-X", "POST" ,"-H", "Content-Type:application/json" ,"-d", json.dumps(_json_payload), get_setting_value('APPRISE_HOST')], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p = subprocess.Popen(
[
"curl",
"-i",
"-X",
"POST",
"-H",
"Content-Type:application/json",
"-d",
json.dumps(_json_payload),
get_setting_value("APPRISE_HOST"),
],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
stdout, stderr = p.communicate() stdout, stderr = p.communicate()
# write stdout and stderr into .log files for debugging if needed # write stdout and stderr into .log files for debugging if needed
# Log the stdout and stderr # Log the stdout and stderr
mylog('debug', [stdout, stderr]) mylog("debug", [stdout, stderr])
# log result # log result
result = stdout result = stdout
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
# An error occurred, handle it # An error occurred, handle it
mylog('none', [e.output]) mylog("none", [e.output])
# log result # log result
result = e.output result = e.output
return result return result
if __name__ == '__main__':
if __name__ == "__main__":
sys.exit(main()) sys.exit(main())

View File

@@ -1,12 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
import json
import subprocess
import argparse
import os import os
import pathlib
import sys import sys
import re import re
from datetime import datetime
from email.mime.multipart import MIMEMultipart from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText from email.mime.text import MIMEText
from email.header import Header from email.header import Header
@@ -17,14 +12,14 @@ import socket
import ssl import ssl
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
# NetAlertX modules # NetAlertX modules
import conf import conf
from const import confFileName, logPath from const import confFileName, logPath
from plugin_helper import Plugin_Objects from plugin_helper import Plugin_Objects
from logger import mylog, Logger, append_line_to_file from logger import mylog, Logger
from helper import timeNowTZ, get_setting_value, hide_email from helper import timeNowTZ, get_setting_value, hide_email
from models.notification_instance import NotificationInstance from models.notification_instance import NotificationInstance
from database import DB from database import DB

View File

@@ -14,7 +14,7 @@ from pytz import timezone
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH = "/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
# NetAlertX modules # NetAlertX modules
@@ -364,7 +364,6 @@ def mqtt_create_client():
return return
except Exception as err: except Exception as err:
mylog('verbose', [f"[{pluginName}] {err} Reconnect failed. Retrying..."]) mylog('verbose', [f"[{pluginName}] {err} Reconnect failed. Retrying..."])
pass
reconnect_delay *= RECONNECT_RATE reconnect_delay *= RECONNECT_RATE
reconnect_delay = min(reconnect_delay, MAX_RECONNECT_DELAY) reconnect_delay = min(reconnect_delay, MAX_RECONNECT_DELAY)

View File

@@ -2,23 +2,19 @@
#!/usr/bin/env python #!/usr/bin/env python
import json import json
import subprocess
import argparse
import os import os
import pathlib
import sys import sys
import requests import requests
from datetime import datetime
from base64 import b64encode from base64 import b64encode
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
import conf import conf
from const import confFileName, logPath from const import confFileName, logPath
from plugin_helper import Plugin_Objects, handleEmpty from plugin_helper import Plugin_Objects, handleEmpty
from logger import mylog, Logger, append_line_to_file from logger import mylog, Logger
from helper import timeNowTZ, get_setting_value from helper import timeNowTZ, get_setting_value
from models.notification_instance import NotificationInstance from models.notification_instance import NotificationInstance
from database import DB from database import DB

View File

@@ -1,12 +1,15 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import conf
from const import confFileName, logPath
from pytz import timezone
import os import os
import pathlib
import sys import sys
import json import json
import requests import requests
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Objects, handleEmpty # noqa: E402 from plugin_helper import Plugin_Objects, handleEmpty # noqa: E402
@@ -14,20 +17,17 @@ from logger import mylog, Logger # noqa: E402
from helper import timeNowTZ, get_setting_value, hide_string # noqa: E402 from helper import timeNowTZ, get_setting_value, hide_string # noqa: E402
from models.notification_instance import NotificationInstance # noqa: E402 from models.notification_instance import NotificationInstance # noqa: E402
from database import DB # noqa: E402 from database import DB # noqa: E402
import conf
from const import confFileName, logPath
from pytz import timezone
# Make sure the TIMEZONE for logging is correct # Make sure the TIMEZONE for logging is correct
conf.tz = timezone(get_setting_value('TIMEZONE')) conf.tz = timezone(get_setting_value("TIMEZONE"))
# Make sure log level is initialized correctly # Make sure log level is initialized correctly
Logger(get_setting_value('LOG_LEVEL')) Logger(get_setting_value("LOG_LEVEL"))
pluginName = "PUSHOVER" pluginName = "PUSHOVER"
LOG_PATH = logPath + '/plugins' LOG_PATH = logPath + "/plugins"
RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log') RESULT_FILE = os.path.join(LOG_PATH, f"last_result.{pluginName}.log")
def main(): def main():

View File

@@ -2,23 +2,18 @@
#!/usr/bin/env python #!/usr/bin/env python
import json import json
import subprocess
import argparse
import os import os
import pathlib
import sys import sys
import requests import requests
from datetime import datetime
from base64 import b64encode
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
import conf import conf
from const import confFileName, logPath from const import confFileName, logPath
from plugin_helper import Plugin_Objects, handleEmpty from plugin_helper import Plugin_Objects, handleEmpty
from logger import mylog, Logger, append_line_to_file from logger import mylog, Logger
from helper import timeNowTZ, get_setting_value, hide_string from helper import timeNowTZ, get_setting_value, hide_string
from models.notification_instance import NotificationInstance from models.notification_instance import NotificationInstance
from database import DB from database import DB

View File

@@ -1,21 +1,17 @@
#!/usr/bin/env python #!/usr/bin/env python
import json
import subprocess import subprocess
import argparse
import os import os
import pathlib
import sys import sys
from datetime import datetime
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH = "/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
import conf import conf
from const import confFileName, logPath from const import confFileName, logPath
from plugin_helper import Plugin_Objects from plugin_helper import Plugin_Objects
from logger import mylog, Logger, append_line_to_file from logger import mylog, Logger
from helper import timeNowTZ, get_setting_value from helper import timeNowTZ, get_setting_value
from models.notification_instance import NotificationInstance from models.notification_instance import NotificationInstance
from database import DB from database import DB

View File

@@ -3,26 +3,21 @@
import json import json
import subprocess import subprocess
import argparse
import os import os
import pathlib
import sys import sys
import requests
from datetime import datetime
from base64 import b64encode
import hashlib import hashlib
import hmac import hmac
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
import conf import conf
from const import logPath, confFileName from const import logPath, confFileName
from plugin_helper import Plugin_Objects, handleEmpty from plugin_helper import Plugin_Objects, handleEmpty
from logger import mylog, Logger, append_line_to_file from logger import mylog, Logger
from helper import timeNowTZ, get_setting_value, hide_string, write_file from helper import timeNowTZ, get_setting_value, write_file
from models.notification_instance import NotificationInstance from models.notification_instance import NotificationInstance
from database import DB from database import DB
from pytz import timezone from pytz import timezone

View File

@@ -1,45 +1,36 @@
#!/usr/bin/env python #!/usr/bin/env python
import os
import time
import pathlib
import argparse
import sys
import re
import base64
import subprocess
from time import strftime
# Register NetAlertX directories
INSTALL_PATH="/app"
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from database import DB from database import DB
from plugin_helper import Plugin_Object, Plugin_Objects, handleEmpty from plugin_helper import Plugin_Objects, handleEmpty
from logger import mylog, Logger, append_line_to_file from logger import mylog, Logger
from helper import timeNowTZ, get_setting_value from helper import get_setting_value
from const import logPath, applicationPath from const import logPath
import conf import conf
from pytz import timezone from pytz import timezone
import os
import time
import argparse
import re
import base64
import subprocess
# Make sure the TIMEZONE for logging is correct # Make sure the TIMEZONE for logging is correct
conf.tz = timezone(get_setting_value('TIMEZONE')) conf.tz = timezone(get_setting_value("TIMEZONE"))
# Make sure log level is initialized correctly # Make sure log level is initialized correctly
Logger(get_setting_value('LOG_LEVEL')) Logger(get_setting_value("LOG_LEVEL"))
pluginName = 'ARPSCAN' pluginName = "ARPSCAN"
LOG_PATH = logPath + '/plugins'
LOG_FILE = os.path.join(LOG_PATH, f'script.{pluginName}.log')
RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log')
LOG_PATH = logPath + "/plugins"
LOG_FILE = os.path.join(LOG_PATH, f"script.{pluginName}.log")
RESULT_FILE = os.path.join(LOG_PATH, f"last_result.{pluginName}.log")
def main(): def main():
parser = argparse.ArgumentParser(description="Import devices from settings")
parser = argparse.ArgumentParser(description='Import devices from settings') parser.add_argument("userSubnets", nargs="+", help="list of subnets with options")
parser.add_argument('userSubnets', nargs='+', help="list of subnets with options")
values = parser.parse_args() values = parser.parse_args()
# Assuming Plugin_Objects is a class or function that reads data from the RESULT_FILE # Assuming Plugin_Objects is a class or function that reads data from the RESULT_FILE
@@ -47,35 +38,32 @@ def main():
plugin_objects = Plugin_Objects(RESULT_FILE) plugin_objects = Plugin_Objects(RESULT_FILE)
# Print a message to indicate that the script is starting. # Print a message to indicate that the script is starting.
mylog('verbose', [f'[{pluginName}] In script ']) mylog("verbose", [f"[{pluginName}] In script "])
# holds a list of user-submitted subnets. # holds a list of user-submitted subnets.
# mylog('verbose', ['[ARP Scan] values.userSubnets: ', values.userSubnets]) # mylog('verbose', ['[ARP Scan] values.userSubnets: ', values.userSubnets])
# Extract the base64-encoded subnet information from the first element of the userSubnets list. # Extract the base64-encoded subnet information from the first element of the userSubnets list.
# The format of the element is assumed to be like 'userSubnets=b<base64-encoded-data>'. # The format of the element is assumed to be like 'userSubnets=<base64-encoded-data>'.
userSubnetsParamBase64 = values.userSubnets[0].split('userSubnets=b')[1] userSubnetsParamBase64 = values.userSubnets[0].split("userSubnets=")[1]
# Printing the extracted base64-encoded subnet information. # Printing the extracted base64-encoded subnet information.
# mylog('verbose', ['[ARP Scan] userSubnetsParamBase64: ', userSubnetsParamBase64]) # mylog('verbose', ['[ARP Scan] userSubnetsParamBase64: ', userSubnetsParamBase64])
# Decode the base64-encoded subnet information to get the actual subnet information in ASCII format. # Decode the base64-encoded subnet information to get the actual subnet information in ASCII format.
userSubnetsParam = base64.b64decode(userSubnetsParamBase64).decode('ascii') userSubnetsParam = base64.b64decode(userSubnetsParamBase64).decode("ascii")
# Print the decoded subnet information. # Print the decoded subnet information.
mylog('verbose', [f'[{pluginName}] userSubnetsParam: ', userSubnetsParam]) mylog("verbose", [f"[{pluginName}] userSubnetsParam: ", userSubnetsParam])
# Check if the decoded subnet information contains multiple subnets separated by commas. # Check if the decoded subnet information contains multiple subnets separated by commas.
# If it does, split the string into a list of individual subnets. # If it does, split the string into a list of individual subnets.
# Otherwise, create a list with a single element containing the subnet information. # Otherwise, create a list with a single element containing the subnet information.
if ',' in userSubnetsParam: if "," in userSubnetsParam:
subnets_list = userSubnetsParam.split(',') subnets_list = userSubnetsParam.split(",")
else: else:
subnets_list = [userSubnetsParam] subnets_list = [userSubnetsParam]
# Create a database connection # Create a database connection
db = DB() # instance of class DB db = DB() # instance of class DB
db.open() db.open()
@@ -84,17 +72,19 @@ def main():
# The function 'execute_arpscan' is assumed to be defined elsewhere in the code. # The function 'execute_arpscan' is assumed to be defined elsewhere in the code.
unique_devices = execute_arpscan(subnets_list) unique_devices = execute_arpscan(subnets_list)
for device in unique_devices: for device in unique_devices:
plugin_objects.add_object( plugin_objects.add_object(
primaryId = handleEmpty(device['mac']), # MAC (Device Name) primaryId=handleEmpty(device["mac"]), # MAC (Device Name)
secondaryId = handleEmpty(device['ip']), # IP Address secondaryId=handleEmpty(device["ip"]), # IP Address
watched1 = handleEmpty(device['ip']), # Device Name watched1=handleEmpty(device["ip"]), # Device Name
watched2 = handleEmpty(device.get('hw', '')), # Vendor (assuming it's in the 'hw' field) watched2=handleEmpty(
watched3 = handleEmpty(device.get('interface', '')), # Add the interface device.get("hw", "")
watched4 = '', ), # Vendor (assuming it's in the 'hw' field)
extra = pluginName, watched3=handleEmpty(device.get("interface", "")), # Add the interface
foreignKey = "") watched4="",
extra=pluginName,
foreignKey="",
)
plugin_objects.write_result_file() plugin_objects.write_result_file()
@@ -108,16 +98,18 @@ def execute_arpscan(userSubnets):
# scan each interface # scan each interface
for interface in userSubnets : for interface in userSubnets:
arpscan_output = execute_arpscan_on_interface (interface) arpscan_output = execute_arpscan_on_interface(interface)
mylog('verbose', [f'[{pluginName}] arpscan_output: ', arpscan_output]) mylog("verbose", [f"[{pluginName}] arpscan_output: ", arpscan_output])
# Search IP + MAC + Vendor as regular expresion # Search IP + MAC + Vendor as regular expresion
re_ip = r'(?P<ip>((2[0-5]|1[0-9]|[0-9])?[0-9]\.){3}((2[0-5]|1[0-9]|[0-9])?[0-9]))' re_ip = (
re_mac = r'(?P<mac>([0-9a-fA-F]{2}[:-]){5}([0-9a-fA-F]{2}))' r"(?P<ip>((2[0-5]|1[0-9]|[0-9])?[0-9]\.){3}((2[0-5]|1[0-9]|[0-9])?[0-9]))"
re_hw = r'(?P<hw>.*)' )
re_mac = r"(?P<mac>([0-9a-fA-F]{2}[:-]){5}([0-9a-fA-F]{2}))"
re_hw = r"(?P<hw>.*)"
re_pattern = re.compile(rf"{re_ip}\s+{re_mac}\s{re_hw}") re_pattern = re.compile(rf"{re_ip}\s+{re_mac}\s{re_hw}")
devices_list_tmp = [ devices_list_tmp = [
@@ -133,38 +125,55 @@ def execute_arpscan(userSubnets):
unique_mac = [] unique_mac = []
unique_devices = [] unique_devices = []
for device in devices_list : for device in devices_list:
if device['mac'] not in unique_mac: if device["mac"] not in unique_mac:
unique_mac.append(device['mac']) unique_mac.append(device["mac"])
unique_devices.append(device) unique_devices.append(device)
# return list # return list
mylog('verbose', [f'[{pluginName}] All devices List len:', len(devices_list)]) mylog("verbose", [f"[{pluginName}] All devices List len:", len(devices_list)])
mylog('verbose', [f'[{pluginName}] Devices List:', devices_list]) mylog("verbose", [f"[{pluginName}] Devices List:", devices_list])
mylog('verbose', [f'[{pluginName}] Found: Devices without duplicates ', len(unique_devices) ]) mylog(
"verbose",
[f"[{pluginName}] Found: Devices without duplicates ", len(unique_devices)],
)
return unique_devices return unique_devices
def execute_arpscan_on_interface(interface): def execute_arpscan_on_interface(interface):
# Prepare command arguments # Prepare command arguments
arpscan_args = get_setting_value('ARPSCAN_ARGS').split() + interface.split() arpscan_args = get_setting_value("ARPSCAN_ARGS").split() + interface.split()
# Optional duration in seconds (0 = run once) # Optional duration in seconds (0 = run once)
try: try:
scan_duration = int(get_setting_value('ARPSCAN_DURATION')) scan_duration = int(get_setting_value("ARPSCAN_DURATION"))
except Exception: except Exception:
scan_duration = 0 # default: single run scan_duration = 0 # default: single run
# Get timeout from plugin settings (default 30 seconds if not set)
try:
timeout_seconds = int(get_setting_value("ARPSCAN_RUN_TIMEOUT"))
except Exception:
timeout_seconds = 30
results = [] results = []
start_time = time.time() start_time = time.time()
while True: while True:
try: try:
result = subprocess.check_output(arpscan_args, universal_newlines=True) result = subprocess.check_output(
arpscan_args, universal_newlines=True, timeout=timeout_seconds
)
results.append(result) results.append(result)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError:
result = ""
except subprocess.TimeoutExpired:
mylog(
"warning",
[f"[{pluginName}] arp-scan timed out after {timeout_seconds}s"],
)
result = "" result = ""
# stop looping if duration not set or expired # stop looping if duration not set or expired
if scan_duration == 0 or (time.time() - start_time) > scan_duration: if scan_duration == 0 or (time.time() - start_time) > scan_duration:
@@ -175,10 +184,10 @@ def execute_arpscan_on_interface(interface):
return "\n".join(results) return "\n".join(results)
# ===============================================================================
#===============================================================================
# BEGIN # BEGIN
#=============================================================================== # ===============================================================================
if __name__ == '__main__':
if __name__ == "__main__":
main() main()

View File

@@ -2,7 +2,7 @@
import os import os
import sys import sys
INSTALL_PATH = "/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
pluginName = "ASUSWRT" pluginName = "ASUSWRT"
@@ -15,8 +15,7 @@ from asusrouter.modules.connection import ConnectionState
from const import logPath from const import logPath
from helper import get_setting_value from helper import get_setting_value
from logger import Logger, mylog from logger import Logger, mylog
from plugin_helper import (Plugin_Object, Plugin_Objects, decodeBase64, from plugin_helper import (Plugin_Objects, handleEmpty)
handleEmpty)
from pytz import timezone from pytz import timezone
conf.tz = timezone(get_setting_value("TIMEZONE")) conf.tz = timezone(get_setting_value("TIMEZONE"))

View File

@@ -1,13 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import sys import sys
import json
import socket import socket
import ipaddress import ipaddress
from zeroconf import Zeroconf, ServiceBrowser, ServiceInfo, InterfaceChoice, IPVersion from zeroconf import Zeroconf
from zeroconf.asyncio import AsyncZeroconf
INSTALL_PATH = "/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Objects from plugin_helper import Plugin_Objects

View File

@@ -344,7 +344,7 @@
} }
] ]
}, },
"default_value": "/app/config", "default_value": "/data/config",
"options": [], "options": [],
"localized": [ "localized": [
"name", "name",
@@ -367,15 +367,15 @@
"description": [ "description": [
{ {
"language_code": "en_us", "language_code": "en_us",
"string": "Where the <code>devices.csv</code> file should be saved. For example <code>/app/config</code>." "string": "Where the <code>devices.csv</code> file should be saved. For example <code>/data/config</code>."
}, },
{ {
"language_code": "es_es", "language_code": "es_es",
"string": "Donde se debe guardar el archivo <code>devices.csv</code>. Por ejemplo <code>/app/config</code>." "string": "Donde se debe guardar el archivo <code>devices.csv</code>. Por ejemplo <code>/data/config</code>."
}, },
{ {
"language_code": "de_de", "language_code": "de_de",
"string": "Wo die Datei <code>devices.csv</code> gespeichert werden soll. Zum Beispiel <code>/app/config</code>." "string": "Wo die Datei <code>devices.csv</code> gespeichert werden soll. Zum Beispiel <code>/data/config</code>."
} }
] ]
} }

View File

@@ -1,23 +1,19 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
import pathlib
import argparse import argparse
import sys import sys
import hashlib
import csv import csv
import sqlite3 import sqlite3
from io import StringIO
from datetime import datetime from datetime import datetime
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from logger import mylog, Logger
from logger import mylog, Logger, append_line_to_file from helper import get_setting_value
from helper import timeNowTZ, get_setting_value from const import logPath, fullDbPath
from const import logPath, applicationPath, fullDbPath
import conf import conf
from pytz import timezone from pytz import timezone

View File

@@ -1,38 +1,30 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
import pathlib
import argparse
import sys import sys
import hashlib
import csv
import sqlite3 import sqlite3
from io import StringIO
from datetime import datetime
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from logger import mylog, Logger
from logger import mylog, Logger, append_line_to_file from helper import get_setting_value
from helper import timeNowTZ, get_setting_value from const import logPath, fullDbPath
from const import logPath, applicationPath, fullDbPath
import conf import conf
from pytz import timezone from pytz import timezone
# Make sure the TIMEZONE for logging is correct # Make sure the TIMEZONE for logging is correct
conf.tz = timezone(get_setting_value('TIMEZONE')) conf.tz = timezone(get_setting_value("TIMEZONE"))
# Make sure log level is initialized correctly # Make sure log level is initialized correctly
Logger(get_setting_value('LOG_LEVEL')) Logger(get_setting_value("LOG_LEVEL"))
pluginName = 'DBCLNP' pluginName = "DBCLNP"
LOG_PATH = logPath + '/plugins'
LOG_FILE = os.path.join(LOG_PATH, f'script.{pluginName}.log')
RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log')
LOG_PATH = logPath + "/plugins"
LOG_FILE = os.path.join(LOG_PATH, f"script.{pluginName}.log")
RESULT_FILE = os.path.join(LOG_PATH, f"last_result.{pluginName}.log")
def main(): def main():
@@ -43,25 +35,39 @@ def main():
DAYS_TO_KEEP_EVENTS = int(get_setting_value("DAYS_TO_KEEP_EVENTS")) DAYS_TO_KEEP_EVENTS = int(get_setting_value("DAYS_TO_KEEP_EVENTS"))
CLEAR_NEW_FLAG = get_setting_value("CLEAR_NEW_FLAG") CLEAR_NEW_FLAG = get_setting_value("CLEAR_NEW_FLAG")
mylog('verbose', [f'[{pluginName}] In script']) mylog("verbose", [f"[{pluginName}] In script"])
# Execute cleanup/upkeep # Execute cleanup/upkeep
cleanup_database(fullDbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KEEP_OFFDEV, PLUGINS_KEEP_HIST, CLEAR_NEW_FLAG) cleanup_database(
fullDbPath,
DAYS_TO_KEEP_EVENTS,
HRS_TO_KEEP_NEWDEV,
HRS_TO_KEEP_OFFDEV,
PLUGINS_KEEP_HIST,
CLEAR_NEW_FLAG,
)
mylog('verbose', [f'[{pluginName}] Cleanup complete']) mylog("verbose", [f"[{pluginName}] Cleanup complete"])
return 0 return 0
#===============================================================================
# ===============================================================================
# Cleanup / upkeep database # Cleanup / upkeep database
#=============================================================================== # ===============================================================================
def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KEEP_OFFDEV, PLUGINS_KEEP_HIST, CLEAR_NEW_FLAG): def cleanup_database(
dbPath,
DAYS_TO_KEEP_EVENTS,
HRS_TO_KEEP_NEWDEV,
HRS_TO_KEEP_OFFDEV,
PLUGINS_KEEP_HIST,
CLEAR_NEW_FLAG,
):
""" """
Cleaning out old records from the tables that don't need to keep all data. Cleaning out old records from the tables that don't need to keep all data.
""" """
mylog('verbose', [f'[{pluginName}] Upkeep Database:' ]) mylog("verbose", [f"[{pluginName}] Upkeep Database:"])
# Connect to the App database # Connect to the App database
conn = sqlite3.connect(dbPath, timeout=30) conn = sqlite3.connect(dbPath, timeout=30)
@@ -69,20 +75,36 @@ def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KE
# ----------------------------------------------------- # -----------------------------------------------------
# Cleanup Online History # Cleanup Online History
mylog('verbose', [f'[{pluginName}] Online_History: Delete all but keep latest 150 entries']) mylog(
cursor.execute ("""DELETE from Online_History where "Index" not in ( "verbose",
[f"[{pluginName}] Online_History: Delete all but keep latest 150 entries"],
)
cursor.execute(
"""DELETE from Online_History where "Index" not in (
SELECT "Index" from Online_History SELECT "Index" from Online_History
order by Scan_Date desc limit 150)""") order by Scan_Date desc limit 150)"""
)
# ----------------------------------------------------- # -----------------------------------------------------
# Cleanup Events # Cleanup Events
mylog('verbose', [f'[{pluginName}] Events: Delete all older than {str(DAYS_TO_KEEP_EVENTS)} days (DAYS_TO_KEEP_EVENTS setting)']) mylog(
cursor.execute (f"""DELETE FROM Events "verbose",
WHERE eve_DateTime <= date('now', '-{str(DAYS_TO_KEEP_EVENTS)} day')""") [
f"[{pluginName}] Events: Delete all older than {str(DAYS_TO_KEEP_EVENTS)} days (DAYS_TO_KEEP_EVENTS setting)"
],
)
cursor.execute(
f"""DELETE FROM Events
WHERE eve_DateTime <= date('now', '-{str(DAYS_TO_KEEP_EVENTS)} day')"""
)
# ----------------------------------------------------- # -----------------------------------------------------
# Trim Plugins_History entries to less than PLUGINS_KEEP_HIST setting per unique "Plugin" column entry # Trim Plugins_History entries to less than PLUGINS_KEEP_HIST setting per unique "Plugin" column entry
mylog('verbose', [f'[{pluginName}] Plugins_History: Trim Plugins_History entries to less than {str(PLUGINS_KEEP_HIST)} per Plugin (PLUGINS_KEEP_HIST setting)']) mylog(
"verbose",
[
f"[{pluginName}] Plugins_History: Trim Plugins_History entries to less than {str(PLUGINS_KEEP_HIST)} per Plugin (PLUGINS_KEEP_HIST setting)"
],
)
# Build the SQL query to delete entries that exceed the limit per unique "Plugin" column entry # Build the SQL query to delete entries that exceed the limit per unique "Plugin" column entry
delete_query = f"""DELETE FROM Plugins_History delete_query = f"""DELETE FROM Plugins_History
@@ -101,9 +123,14 @@ def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KE
# ----------------------------------------------------- # -----------------------------------------------------
# Trim Notifications entries to less than DBCLNP_NOTIFI_HIST setting # Trim Notifications entries to less than DBCLNP_NOTIFI_HIST setting
histCount = get_setting_value('DBCLNP_NOTIFI_HIST') histCount = get_setting_value("DBCLNP_NOTIFI_HIST")
mylog('verbose', [f'[{pluginName}] Plugins_History: Trim Notifications entries to less than {histCount}']) mylog(
"verbose",
[
f"[{pluginName}] Plugins_History: Trim Notifications entries to less than {histCount}"
],
)
# Build the SQL query to delete entries # Build the SQL query to delete entries
delete_query = f"""DELETE FROM Notifications delete_query = f"""DELETE FROM Notifications
@@ -119,12 +146,11 @@ def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KE
cursor.execute(delete_query) cursor.execute(delete_query)
# ----------------------------------------------------- # -----------------------------------------------------
# Trim Workflow entries to less than WORKFLOWS_AppEvents_hist setting # Trim Workflow entries to less than WORKFLOWS_AppEvents_hist setting
histCount = get_setting_value('WORKFLOWS_AppEvents_hist') histCount = get_setting_value("WORKFLOWS_AppEvents_hist")
mylog('verbose', [f'[{pluginName}] Trim AppEvents to less than {histCount}']) mylog("verbose", [f"[{pluginName}] Trim AppEvents to less than {histCount}"])
# Build the SQL query to delete entries # Build the SQL query to delete entries
delete_query = f"""DELETE FROM AppEvents delete_query = f"""DELETE FROM AppEvents
@@ -141,38 +167,52 @@ def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KE
cursor.execute(delete_query) cursor.execute(delete_query)
conn.commit() conn.commit()
# ----------------------------------------------------- # -----------------------------------------------------
# Cleanup New Devices # Cleanup New Devices
if HRS_TO_KEEP_NEWDEV != 0: if HRS_TO_KEEP_NEWDEV != 0:
mylog('verbose', [f'[{pluginName}] Devices: Delete all New Devices older than {str(HRS_TO_KEEP_NEWDEV)} hours (HRS_TO_KEEP_NEWDEV setting)']) mylog(
"verbose",
[
f"[{pluginName}] Devices: Delete all New Devices older than {str(HRS_TO_KEEP_NEWDEV)} hours (HRS_TO_KEEP_NEWDEV setting)"
],
)
query = f"""DELETE FROM Devices WHERE devIsNew = 1 AND devFirstConnection < date('now', '-{str(HRS_TO_KEEP_NEWDEV)} hour')""" query = f"""DELETE FROM Devices WHERE devIsNew = 1 AND devFirstConnection < date('now', '-{str(HRS_TO_KEEP_NEWDEV)} hour')"""
mylog('verbose', [f'[{pluginName}] Query: {query} ']) mylog("verbose", [f"[{pluginName}] Query: {query} "])
cursor.execute (query) cursor.execute(query)
# ----------------------------------------------------- # -----------------------------------------------------
# Cleanup Offline Devices # Cleanup Offline Devices
if HRS_TO_KEEP_OFFDEV != 0: if HRS_TO_KEEP_OFFDEV != 0:
mylog('verbose', [f'[{pluginName}] Devices: Delete all New Devices older than {str(HRS_TO_KEEP_OFFDEV)} hours (HRS_TO_KEEP_OFFDEV setting)']) mylog(
"verbose",
[
f"[{pluginName}] Devices: Delete all New Devices older than {str(HRS_TO_KEEP_OFFDEV)} hours (HRS_TO_KEEP_OFFDEV setting)"
],
)
query = f"""DELETE FROM Devices WHERE devPresentLastScan = 0 AND devLastConnection < date('now', '-{str(HRS_TO_KEEP_OFFDEV)} hour')""" query = f"""DELETE FROM Devices WHERE devPresentLastScan = 0 AND devLastConnection < date('now', '-{str(HRS_TO_KEEP_OFFDEV)} hour')"""
mylog('verbose', [f'[{pluginName}] Query: {query} ']) mylog("verbose", [f"[{pluginName}] Query: {query} "])
cursor.execute (query) cursor.execute(query)
# ----------------------------------------------------- # -----------------------------------------------------
# Clear New Flag # Clear New Flag
if CLEAR_NEW_FLAG != 0: if CLEAR_NEW_FLAG != 0:
mylog('verbose', [f'[{pluginName}] Devices: Clear "New Device" flag for all devices older than {str(CLEAR_NEW_FLAG)} hours (CLEAR_NEW_FLAG setting)']) mylog(
"verbose",
[
f'[{pluginName}] Devices: Clear "New Device" flag for all devices older than {str(CLEAR_NEW_FLAG)} hours (CLEAR_NEW_FLAG setting)'
],
)
query = f"""UPDATE Devices SET devIsNew = 0 WHERE devIsNew = 1 AND date(devFirstConnection, '+{str(CLEAR_NEW_FLAG)} hour') < date('now')""" query = f"""UPDATE Devices SET devIsNew = 0 WHERE devIsNew = 1 AND date(devFirstConnection, '+{str(CLEAR_NEW_FLAG)} hour') < date('now')"""
# select * from Devices where devIsNew = 1 AND date(devFirstConnection, '+3 hour' ) < date('now') # select * from Devices where devIsNew = 1 AND date(devFirstConnection, '+3 hour' ) < date('now')
mylog('verbose', [f'[{pluginName}] Query: {query} ']) mylog("verbose", [f"[{pluginName}] Query: {query} "])
cursor.execute(query) cursor.execute(query)
# ----------------------------------------------------- # -----------------------------------------------------
# De-dupe (de-duplicate) from the Plugins_Objects table # De-dupe (de-duplicate) from the Plugins_Objects table
# TODO This shouldn't be necessary - probably a concurrency bug somewhere in the code :( # TODO This shouldn't be necessary - probably a concurrency bug somewhere in the code :(
mylog('verbose', [f'[{pluginName}] Plugins_Objects: Delete all duplicates']) mylog("verbose", [f"[{pluginName}] Plugins_Objects: Delete all duplicates"])
cursor.execute(""" cursor.execute(
"""
DELETE FROM Plugins_Objects DELETE FROM Plugins_Objects
WHERE rowid > ( WHERE rowid > (
SELECT MIN(rowid) FROM Plugins_Objects p2 SELECT MIN(rowid) FROM Plugins_Objects p2
@@ -181,8 +221,8 @@ def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KE
AND Plugins_Objects.Object_SecondaryID = p2.Object_SecondaryID AND Plugins_Objects.Object_SecondaryID = p2.Object_SecondaryID
AND Plugins_Objects.UserData = p2.UserData AND Plugins_Objects.UserData = p2.UserData
) )
""") """
)
conn.commit() conn.commit()
@@ -190,18 +230,18 @@ def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KE
cursor.execute("PRAGMA wal_checkpoint(TRUNCATE);") cursor.execute("PRAGMA wal_checkpoint(TRUNCATE);")
cursor.execute("PRAGMA wal_checkpoint(FULL);") cursor.execute("PRAGMA wal_checkpoint(FULL);")
mylog('verbose', [f'[{pluginName}] WAL checkpoint executed to truncate file.']) mylog("verbose", [f"[{pluginName}] WAL checkpoint executed to truncate file."])
# Shrink DB # Shrink DB
mylog('verbose', [f'[{pluginName}] Shrink Database']) mylog("verbose", [f"[{pluginName}] Shrink Database"])
cursor.execute ("VACUUM;") cursor.execute("VACUUM;")
# Close the database connection # Close the database connection
conn.close() conn.close()
#=============================================================================== # ===============================================================================
# BEGIN # BEGIN
#=============================================================================== # ===============================================================================
if __name__ == '__main__': if __name__ == "__main__":
main() main()

View File

@@ -1,26 +1,17 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
import pathlib
import argparse import argparse
import sys import sys
import hashlib
import csv
import subprocess import subprocess
import re
import base64
import sqlite3
from io import StringIO
from datetime import datetime
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from logger import mylog, Logger
from logger import mylog, Logger, append_line_to_file from helper import get_setting_value, check_IP_format
from helper import timeNowTZ, get_setting_value, check_IP_format from const import logPath
from const import logPath, applicationPath, fullDbPath
import conf import conf
from pytz import timezone from pytz import timezone

View File

@@ -1,21 +1,19 @@
#!/usr/bin/env python #!/usr/bin/env python
from __future__ import unicode_literals from __future__ import unicode_literals
import pathlib
import subprocess
import argparse import argparse
import os import os
import sys import sys
import chardet import chardet
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Object, Plugin_Objects, handleEmpty, is_mac from plugin_helper import Plugin_Objects, handleEmpty, is_mac
from logger import mylog, Logger from logger import mylog, Logger
from dhcp_leases import DhcpLeases from dhcp_leases import DhcpLeases
from helper import timeNowTZ, get_setting_value from helper import get_setting_value
import conf import conf
from const import logPath from const import logPath
from pytz import timezone from pytz import timezone

View File

@@ -8,12 +8,12 @@ from datetime import datetime
import sys import sys
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Objects, Plugin_Object from plugin_helper import Plugin_Objects, Plugin_Object
from logger import mylog, Logger from logger import mylog, Logger
from helper import timeNowTZ, get_setting_value from helper import get_setting_value
import conf import conf
from pytz import timezone from pytz import timezone
from const import logPath from const import logPath

View File

@@ -1,22 +1,17 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
import pathlib
import sys import sys
import json
import sqlite3
import subprocess import subprocess
# Define the installation path and extend the system path for plugin imports # Define the installation path and extend the system path for plugin imports
INSTALL_PATH = "/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from plugin_helper import Plugin_Objects
from plugin_utils import get_plugins_configs
from logger import mylog, Logger from logger import mylog, Logger
from const import pluginsPath, fullDbPath, logPath from const import logPath
from helper import timeNowTZ, get_setting_value from helper import get_setting_value
from messaging.in_app import write_notification
from database import DB from database import DB
from models.device_instance import DeviceInstance from models.device_instance import DeviceInstance
import conf import conf
@@ -118,7 +113,7 @@ def execute_name_lookup (ip, timeout):
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
mylog('verbose', [f'[{pluginName}] ⚠ ERROR - {e.output}']) mylog('verbose', [f'[{pluginName}] ⚠ ERROR - {e.output}'])
except subprocess.TimeoutExpired as timeErr: except subprocess.TimeoutExpired:
mylog('verbose', [f'[{pluginName}] TIMEOUT - the process forcefully terminated as timeout reached']) mylog('verbose', [f'[{pluginName}] TIMEOUT - the process forcefully terminated as timeout reached'])
if output == "": # check if the subprocess failed if output == "": # check if the subprocess failed

View File

@@ -1,10 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
import pathlib
import sys import sys
import json
import sqlite3
from pytz import timezone from pytz import timezone
import asyncio import asyncio
from datetime import datetime from datetime import datetime
@@ -17,15 +14,13 @@ from aiofreepybox.api.lan import Lan
from aiofreepybox.exceptions import NotOpenError, AuthorizationError from aiofreepybox.exceptions import NotOpenError, AuthorizationError
# Define the installation path and extend the system path for plugin imports # Define the installation path and extend the system path for plugin imports
INSTALL_PATH = "/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from plugin_helper import Plugin_Objects
from plugin_utils import get_plugins_configs
from logger import mylog, Logger from logger import mylog, Logger
from const import pluginsPath, fullDbPath, logPath from const import logPath
from helper import timeNowTZ, get_setting_value from helper import get_setting_value
from messaging.in_app import write_notification
import conf import conf
# Make sure the TIMEZONE for logging is correct # Make sure the TIMEZONE for logging is correct
@@ -86,7 +81,8 @@ def map_device_type(type: str):
async def get_device_data(api_version: int, api_address: str, api_port: int): async def get_device_data(api_version: int, api_address: str, api_port: int):
# ensure existence of db path # ensure existence of db path
data_dir = Path("/app/config/freeboxdb") config_base = Path(os.getenv("NETALERTX_CONFIG", "/data/config"))
data_dir = config_base / "freeboxdb"
data_dir.mkdir(parents=True, exist_ok=True) data_dir.mkdir(parents=True, exist_ok=True)
# Instantiate Freepybox class using default application descriptor # Instantiate Freepybox class using default application descriptor

View File

@@ -3,25 +3,18 @@
# tbc # tbc
import os import os
import pathlib
import argparse
import subprocess import subprocess
import sys import sys
import hashlib
import csv
import sqlite3
import re import re
from io import StringIO
from datetime import datetime
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from plugin_helper import Plugin_Objects
from logger import mylog, Logger, append_line_to_file from logger import mylog, Logger
from helper import timeNowTZ, get_setting_value from helper import get_setting_value
from const import logPath, applicationPath, fullDbPath from const import logPath
from database import DB from database import DB
from models.device_instance import DeviceInstance from models.device_instance import DeviceInstance
import conf import conf
@@ -157,7 +150,7 @@ def execute_scan (ip, timeout, args):
return False, output return False, output
except subprocess.TimeoutExpired as timeErr: except subprocess.TimeoutExpired:
mylog('verbose', [f'[{pluginName}] TIMEOUT - the process forcefully terminated as timeout reached']) mylog('verbose', [f'[{pluginName}] TIMEOUT - the process forcefully terminated as timeout reached'])
return False, output return False, output

View File

@@ -2,26 +2,19 @@
import os import os
import time import time
import pathlib
import argparse import argparse
import sys import sys
import hashlib
import csv
import subprocess import subprocess
import re import re
import base64
import sqlite3
from io import StringIO
from datetime import datetime
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from plugin_helper import Plugin_Objects
from logger import mylog, Logger, append_line_to_file from logger import mylog, Logger, append_line_to_file
from helper import timeNowTZ, check_IP_format, get_setting_value from helper import timeNowTZ, check_IP_format, get_setting_value
from const import logPath, applicationPath, fullDbPath from const import logPath
import conf import conf
from pytz import timezone from pytz import timezone

View File

@@ -2,17 +2,15 @@
import argparse import argparse
import os import os
import pathlib
import sys import sys
from datetime import datetime
import speedtest import speedtest
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Objects from plugin_helper import Plugin_Objects
from logger import mylog, Logger, append_line_to_file from logger import mylog, Logger
from helper import timeNowTZ, get_setting_value from helper import timeNowTZ, get_setting_value
import conf import conf
from pytz import timezone from pytz import timezone

View File

@@ -1,25 +1,20 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
import pathlib
import sys import sys
import json
import sqlite3
import subprocess import subprocess
from datetime import datetime from datetime import datetime
from pytz import timezone from pytz import timezone
from functools import reduce from functools import reduce
# Define the installation path and extend the system path for plugin imports # Define the installation path and extend the system path for plugin imports
INSTALL_PATH = "/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64, handleEmpty from plugin_helper import Plugin_Objects
from plugin_utils import get_plugins_configs
from logger import mylog, Logger from logger import mylog, Logger
from const import pluginsPath, fullDbPath, logPath from const import logPath
from helper import timeNowTZ, get_setting_value from helper import get_setting_value
from messaging.in_app import write_notification
import conf import conf
# Make sure the TIMEZONE for logging is correct # Make sure the TIMEZONE for logging is correct

View File

@@ -3,14 +3,14 @@ import os
import sys import sys
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
pluginName = 'LUCIRPC' pluginName = 'LUCIRPC'
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from plugin_helper import Plugin_Objects
from logger import mylog, Logger, append_line_to_file from logger import mylog, Logger
from helper import timeNowTZ, get_setting_value from helper import get_setting_value
from const import logPath, applicationPath from const import logPath
import conf import conf
from pytz import timezone from pytz import timezone

View File

@@ -1,24 +1,16 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
import pathlib
import argparse
import sys import sys
import hashlib
import csv
import sqlite3
from io import StringIO
from datetime import datetime
from collections import deque from collections import deque
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from logger import mylog, Logger
from logger import mylog, Logger, append_line_to_file from helper import get_setting_value
from helper import timeNowTZ, get_setting_value from const import logPath
from const import logPath, applicationPath
from messaging.in_app import remove_old from messaging.in_app import remove_old
import conf import conf
from pytz import timezone from pytz import timezone

View File

@@ -4,7 +4,7 @@ import os
import sys import sys
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Objects from plugin_helper import Plugin_Objects

View File

@@ -1,22 +1,17 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
import pathlib
import sys import sys
import json
import sqlite3
import subprocess import subprocess
# Define the installation path and extend the system path for plugin imports # Define the installation path and extend the system path for plugin imports
INSTALL_PATH = "/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from plugin_helper import Plugin_Objects
from plugin_utils import get_plugins_configs
from logger import mylog, Logger from logger import mylog, Logger
from const import pluginsPath, fullDbPath, logPath from const import logPath
from helper import timeNowTZ, get_setting_value from helper import get_setting_value
from messaging.in_app import write_notification
from database import DB from database import DB
from models.device_instance import DeviceInstance from models.device_instance import DeviceInstance
import conf import conf
@@ -139,7 +134,7 @@ def execute_name_lookup (ip, timeout):
# else: # else:
mylog('verbose', [f'[{pluginName}] ⚠ ERROR - {e.output}']) mylog('verbose', [f'[{pluginName}] ⚠ ERROR - {e.output}'])
except subprocess.TimeoutExpired as timeErr: except subprocess.TimeoutExpired:
mylog('verbose', [f'[{pluginName}] TIMEOUT - the process forcefully terminated as timeout reached']) mylog('verbose', [f'[{pluginName}] TIMEOUT - the process forcefully terminated as timeout reached'])
if output == "": # check if the subprocess failed if output == "": # check if the subprocess failed

View File

@@ -3,28 +3,21 @@
# tbc # tbc
import os import os
import pathlib
import argparse
import subprocess import subprocess
import sys import sys
import hashlib import hashlib
import csv
import sqlite3
import re import re
import nmap import nmap
from io import StringIO
from datetime import datetime
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from plugin_helper import Plugin_Objects
from logger import mylog, Logger, append_line_to_file from logger import mylog, Logger
from helper import timeNowTZ, get_setting_value, extract_between_strings, extract_ip_addresses, extract_mac_addresses from helper import get_setting_value
from const import logPath, applicationPath, fullDbPath from const import logPath
from database import DB from database import DB
from models.device_instance import DeviceInstance
import conf import conf
from pytz import timezone from pytz import timezone

View File

@@ -1,22 +1,18 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
import pathlib
import argparse import argparse
import sys import sys
import re
import base64
import subprocess import subprocess
from time import strftime
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from plugin_helper import Plugin_Objects, decodeBase64
from logger import mylog, Logger, append_line_to_file from logger import mylog, Logger, append_line_to_file
from helper import timeNowTZ, get_setting_value from helper import timeNowTZ, get_setting_value
from const import logPath, applicationPath from const import logPath
import conf import conf
from pytz import timezone from pytz import timezone
@@ -128,7 +124,7 @@ def performNmapScan(deviceIPs, deviceMACs, timeoutSec, args):
# An error occured, handle it # An error occured, handle it
mylog('none', ["[NMAP Scan] " ,e.output]) mylog('none', ["[NMAP Scan] " ,e.output])
mylog('none', ["[NMAP Scan] ⚠ ERROR - Nmap Scan - check logs", progress]) mylog('none', ["[NMAP Scan] ⚠ ERROR - Nmap Scan - check logs", progress])
except subprocess.TimeoutExpired as timeErr: except subprocess.TimeoutExpired:
mylog('verbose', [f'[{pluginName}] Nmap TIMEOUT - the process forcefully terminated as timeout reached for ', ip, progress]) mylog('verbose', [f'[{pluginName}] Nmap TIMEOUT - the process forcefully terminated as timeout reached for ', ip, progress])
if output == "": # check if the subprocess failed if output == "": # check if the subprocess failed

Some files were not shown because too many files have changed in this diff Show More