From f57ec74cc1062d6e71d4af3654696b62736ce4d7 Mon Sep 17 00:00:00 2001 From: Adam Outler Date: Thu, 16 Oct 2025 00:09:07 +0000 Subject: [PATCH] Minor alterations to ddevcontainer. --- .devcontainer/Dockerfile | 139 ++++++++++-------- .devcontainer/scripts/generate-configs.sh | 2 + docker-compose.yml | 6 +- install/production-filesystem/entrypoint.sh | 24 ++- .../services/check-first-run-config.sh | 8 +- .../services/check-first-run-db.sh | 16 +- .../services/cron_script.sh | 2 +- .../services/start-php-fpm.sh | 4 +- log/plugins/.git-placeholder | 0 9 files changed, 121 insertions(+), 80 deletions(-) delete mode 100644 log/plugins/.git-placeholder diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 42ab0eee..950b7997 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -6,16 +6,16 @@ FROM alpine:3.22 AS builder ARG INSTALL_DIR=/app ENV PYTHONUNBUFFERED=1 - -# Install build dependencies -RUN apk add --no-cache bash shadow python3 python3-dev gcc musl-dev libffi-dev openssl-dev git \ - && python -m venv /opt/venv - -# Enable venv ENV PATH="/opt/venv/bin:$PATH" -RUN pip install openwrt-luci-rpc asusrouter asyncio aiohttp graphene flask flask-cors unifi-sm-api tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros yattag zeroconf simplejson future six urllib3 httplib2 git+https://github.com/foreign-sub/aiofreepybox.git +# Install build dependencies +COPY requirements.txt /tmp/requirements.txt +RUN apk add --no-cache bash shadow python3 python3-dev gcc musl-dev libffi-dev openssl-dev git \ + && python -m venv /opt/venv +RUN pip install -r /tmp/requirements.txt +# From this point on the Venv is owned by root without any read/write/execute permissions for root or group. +# This makes it easy to copy into hardened stage without worrying about permissions and keeps image size small RUN chmod -R u-rwx,g-rwx /opt # second stage is the main runtime stage with just the minimum required to run the application @@ -24,8 +24,6 @@ FROM alpine:3.22 AS runner ARG INSTALL_DIR=/app -ENV PATH="/opt/venv/bin:/usr/bin:/sbin:/bin:$PATH" - # NetAlertX app directories ENV NETALERTX_APP=${INSTALL_DIR} ENV NETALERTX_CONFIG=${NETALERTX_APP}/config @@ -37,6 +35,7 @@ ENV NETALERTX_DB_FILE=${NETALERTX_DB}/app.db ENV NETALERTX_BACK=${NETALERTX_APP}/back ENV NETALERTX_LOG=${NETALERTX_APP}/log ENV NETALERTX_PLUGINS_LOG=${NETALERTX_LOG}/plugins +ENV NETALERTX_CONFIG_FILE=${NETALERTX_CONFIG}/app.conf # NetAlertX log files ENV LOG_IP_CHANGES=${NETALERTX_LOG}/IP_changes.log @@ -57,7 +56,6 @@ ENV SYSTEM_SERVICES=/services ENV SYSTEM_SERVICES_CONFIG=${SYSTEM_SERVICES}/config ENV SYSTEM_NGINIX_CONFIG=${SYSTEM_SERVICES_CONFIG}/nginx ENV SYSTEM_NGINX_CONFIG_FILE=${SYSTEM_NGINIX_CONFIG}/nginx.conf -ENV NETALERTX_CONFIG_FILE=${NETALERTX_CONFIG}/app.conf 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_CROND=${SYSTEM_SERVICES_CONFIG}/crond @@ -65,101 +63,119 @@ ENV SYSTEM_SERVICES_RUN=${SYSTEM_SERVICES}/run ENV SYSTEM_SERVICES_RUN_TMP=${SYSTEM_SERVICES_RUN}/tmp ENV SYSTEM_SERVICES_RUN_LOG=${SYSTEM_SERVICES_RUN}/logs ENV PHP_FPM_CONFIG_FILE=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.conf +ENV READ_ONLY_FOLDERS="${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} ${SYSTEM_SERVICES} \ + ${SYSTEM_SERVICES_CONFIG} /opt /opt/venv" +ENV READ_WRITE_FOLDERS="${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} ${NETALERTX_LOG} \ + ${NETALERTX_PLUGINS_LOG} ${SYSTEM_SERVICES_RUN} ${SYSTEM_SERVICES_RUN_TMP} \ + ${SYSTEM_SERVICES_RUN_LOG}" -ENV PYTHONPATH=${NETALERTX_SERVER} +#Python environment ENV PYTHONUNBUFFERED=1 +ENV VIRTUAL_ENV=/opt/venv +ENV VIRTUAL_ENV_BIN=/opt/venv/bin +ENV PYTHONPATH=${NETALERTX_APP}:${NETALERTX_SERVER}:${VIRTUAL_ENV}/lib/python3.12/site-packages +ENV PATH="${SYSTEM_SERVICES}:${VIRTUAL_ENV_BIN}:$PATH" + +# App Environment +ENV LISTEN_ADDR=0.0.0.0 +ENV PORT=20211 +ENV NETALERTX_DEBUG=0 +ENV VENDORSPATH=/app/back/ieee-oui.txt +ENV VENDORSPATH_NEWEST=/services/run/tmp/ieee-oui.txt +ENV PYTHONPATHPATH="${NETALERTX_APP}:${VIRTUAL_ENV}/bin:${PATH}" +ENV ENVIRONMENT=alpine +ENV READ_ONLY_USER=readonly READ_ONLY_GROUP=readonly +ENV NETALERTX_USER=netalertx NETALERTX_GROUP=netalertx +ENV LANG=C.UTF-8 -RUN apk add --no-cache bash mtr libbsd zip lsblk sudo tzdata curl arp-scan iproute2 \ - iproute2-ss nmap nmap-scripts traceroute nbtscan net-tools net-snmp-tools bind-tools awake \ - ca-certificates sqlite php83 php83-fpm php83-cgi php83-curl php83-sqlite3 php83-session python3 \ - envsubst nginx sudo shadow && \ +RUN apk add --no-cache bash mtr libbsd zip lsblk tzdata curl arp-scan iproute2 iproute2-ss nmap \ + nmap-scripts traceroute nbtscan net-tools net-snmp-tools bind-tools awake ca-certificates \ + sqlite php83 php83-fpm php83-cgi php83-curl php83-sqlite3 php83-session python3 envsubst \ + nginx shadow && \ rm -Rf /var/cache/apk/* && \ rm -Rf /etc/nginx && \ - addgroup -g 20211 netalertx && \ - adduser -u 20211 -D -h ${NETALERTX_APP} -G netalertx netalertx && \ + addgroup -g 20211 ${NETALERTX_GROUP} && \ + adduser -u 20211 -D -h ${NETALERTX_APP} -G ${NETALERTX_GROUP} ${NETALERTX_USER} && \ apk del shadow # Install application, copy files, set permissions -COPY --from=builder --chown=20212:20212 /opt/venv /opt/venv -COPY --from=builder /usr/sbin/usermod /usr/sbin/groupmod /usr/sbin/ -COPY --chown=netalertx:netalertx install/production-filesystem/ / -COPY --chown=netalertx:netalertx --chmod=755 back ${NETALERTX_BACK} -COPY --chown=netalertx:netalertx --chmod=755 front ${NETALERTX_FRONT} -COPY --chown=netalertx:netalertx --chmod=755 server ${NETALERTX_SERVER} -RUN install -d -o netalertx -g netalertx -m 755 ${NETALERTX_API} \ +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 front ${NETALERTX_FRONT} +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} && \ sh -c "find ${NETALERTX_APP} -type f \( -name '*.sh' -o -name 'speedtest-cli' \) \ -exec chmod 750 {} \;" +# Copy the virtualenv from the builder stage +COPY --from=builder --chown=20212:20212 ${VIRTUAL_ENV} ${VIRTUAL_ENV} + #initialize each service with the dockerfiles/init-*.sh scripts, once. RUN apk add libcap && \ setcap cap_net_raw,cap_net_admin+eip /usr/bin/nmap && \ setcap cap_net_raw,cap_net_admin+eip /usr/bin/arp-scan && \ setcap cap_net_raw,cap_net_admin+eip /usr/bin/traceroute && \ - setcap cap_net_raw,cap_net_admin+eip /opt/venv/bin/scapy && \ + setcap cap_net_raw,cap_net_admin+eip ${VIRTUAL_ENV_BIN}/scapy && \ /bin/sh /build/init-nginx.sh && \ /bin/sh /build/init-php-fpm.sh && \ /bin/sh /build/init-crond.sh && \ /bin/sh /build/init-backend.sh && \ - chmod 755 ${NETALERTX_BACK}/update_vendors.sh ${NETALERTX_BACK}/cron_script.sh ${NETALERTX_BACK}/speedtest-cli && \ rm -rf /build && \ apk del libcap -# set netalertx to allow sudoers for any command, no password -RUN echo "netalertx ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers -ENTRYPOINT ["/bin/sh","-c","sleep infinity"] + +ENTRYPOINT ["/bin/sh","/entrypoint.sh"] # Final hardened stage to improve security by setting least possible permissions and removing sudo access. # When complete, if the image is compromised, there's not much that can be done with it. # This stage is separate from Runner stage so that devcontainer can use the Runner stage. FROM runner AS hardened -# create readonly user and group with no shell access. +ENV UMASK=0077 + +# Create readonly user and group with no shell access. # Readonly user marks folders that are created by NetAlertX, but should not be modified. -RUN addgroup -g 20212 readonly && \ - adduser -u 20212 -G readonly -D -h /app readonly && \ - usermod -s /sbin/nologin readonly +# AI may claim this is stupid, but it's actually least possible permissions as +# read-only user cannot login, cannot sudo, has no write permission, and cannot even +# read the files it owns. The read-only user is ownership-as-a-lock hardening pattern. +RUN addgroup -g 20212 ${READ_ONLY_GROUP} && \ + adduser -u 20212 -G ${READ_ONLY_GROUP} -D -h /app ${READ_ONLY_USER} # reduce permissions to minimum necessary for all NetAlertX files and folders +# Permissions 005 and 004 are not typos, they enable read-only. Everyone can +# read the read-only files, and nobody can write to them, even the readonly user. +RUN chown -R ${READ_ONLY_USER}:${READ_ONLY_GROUP} ${READ_ONLY_FOLDERS} && \ + chmod -R 004 ${READ_ONLY_FOLDERS} && \ + find ${READ_ONLY_FOLDERS} -type d -exec chmod 005 {} + && \ + install -d -o ${NETALERTX_USER} -g ${NETALERTX_GROUP} -m 700 ${READ_WRITE_FOLDERS} && \ + chown -R ${NETALERTX_USER}:${NETALERTX_GROUP} ${READ_WRITE_FOLDERS} && \ + chmod -R 600 ${READ_WRITE_FOLDERS} && \ + find ${READ_WRITE_FOLDERS} -type d -exec chmod 700 {} + && \ + chown ${READ_ONLY_USER}:${READ_ONLY_GROUP} /entrypoint.sh && \ + chmod 005 /entrypoint.sh ${SYSTEM_SERVICES}/*.sh /app + -RUN chown -R readonly:readonly ${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} ${SYSTEM_SERVICES} ${SYSTEM_SERVICES} && \ - chmod -R 004 ${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} && \ - find ${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} -type d -exec chmod 005 {} + && \ - chmod -R 005 ${SYSTEM_SERVICES} ${SYSTEM_SERVICES}/* && \ - chown -R netalertx:netalertx ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} ${NETALERTX_LOG} && \ - chmod -R 600 ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} ${NETALERTX_LOG} && \ - chmod 700 ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} ${NETALERTX_LOG} ${NETALERTX_PLUGINS_LOG} ${SYSTEM_SERVICES_RUN_TMP} && \ - chown readonly:readonly /entrypoint.sh && \ - install -d -o netalertx -g netalertx -m 700 ${SYSTEM_SERVICES_RUN} ${SYSTEM_SERVICES_RUN_TMP} ${SYSTEM_SERVICES_RUN_LOG} && \ - chmod 005 /entrypoint.sh ${NETALERTX_BACK}/update_vendors.sh ${NETALERTX_BACK}/cron_script.sh ${NETALERTX_BACK}/speedtest-cli - -# -# remove sudo and alpine installers pacakges -RUN apk del sudo apk-tools && \ - rm -rf /var/cache/apk/* -# remove all users and groups except readonly and netalertx & remove all sudoers -RUN rm -Rf /etc/sudoers.d/* /etc/shadow /etc/gshadow /etc/sudoers \ - /lib/apk /lib/firmware /lib/modules-load.d /lib/sysctl.d /mnt /home/ /root \ +# remove sudoers, alpine installers pacakges, and all users and groups except +# readonly and netalertx +RUN apk del apk-tools && \ + rm -Rf /var /etc/sudoers.d/* /etc/shadow /etc/gshadow /etc/sudoers \ + /lib/apk /lib/firmware /lib/modules-load.d /lib/sysctl.d /mnt /home/ /root \ /srv /media && \ - sed -i -n -e '/^readonly:/p' -e '/^netalertx:/p' /etc/passwd && \ - sed -i -n -e '/^readonly:/p' -e '/^netalertx:/p' /etc/group && \ + sed -i "/^\(${READ_ONLY_USER}\|${NETALERTX_USER}\):/!d" /etc/passwd && \ + sed -i "/^\(${READ_ONLY_GROUP}\|${NETALERTX_GROUP}\):/!d" /etc/group && \ echo -ne '#!/bin/sh\n"$@"\n' > /usr/bin/sudo && chmod +x /usr/bin/sudo - - - USER netalertx HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ CMD /services/healthcheck.sh -#ENTRYPOINT [ "/bin/sh" ] -ENTRYPOINT [ "/bin/sh", "/entrypoint.sh" ] # ---/resources/devcontainer-Dockerfile--- @@ -171,6 +187,8 @@ ENTRYPOINT [ "/bin/sh", "/entrypoint.sh" ] FROM runner AS netalertx-devcontainer ENV INSTALL_DIR=/app + + ENV PYTHONPATH=/workspaces/NetAlertX/test:/workspaces/NetAlertX/server:/app:/app/server:/opt/venv/lib/python3.12/site-packages ENV PATH=/services:${PATH} ENV PHP_INI_SCAN_DIR=/services/config/php/conf.d:/etc/php83/conf.d @@ -180,10 +198,11 @@ ENV NETALERTX_DEBUG=1 COPY .devcontainer/resources/devcontainer-overlay/ / # 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 pytest-cov fish shfmt +RUN apk add --no-cache git nano vim jq php83-pecl-xdebug py3-pip nodejs sudo gpgconf pytest pytest-cov fish shfmt sudo 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 # Install debugpy in the virtualenv if present, otherwise into system python3 RUN /bin/sh -c '(/opt/venv/bin/python3 -m pip install --no-cache-dir debugpy) || (python3 -m pip install --no-cache-dir debugpy) || true' && \ mkdir /workspaces && \ diff --git a/.devcontainer/scripts/generate-configs.sh b/.devcontainer/scripts/generate-configs.sh index 519ccd0c..b3dca0bd 100755 --- a/.devcontainer/scripts/generate-configs.sh +++ b/.devcontainer/scripts/generate-configs.sh @@ -62,3 +62,5 @@ awk ' ' >> "$NGINX_OUT" echo "Generated $NGINX_OUT from $NGINX_TEMPLATE" >&2 + +echo "Done." \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 37a3582e..569b48e8 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,9 +15,9 @@ services: security_opt: # Security options for the container - no-new-privileges:true # Prevent privilege escalation volumes: - - netalertx_config:/app/config:rw # Store your NetAlertX config - - netalertx_db:/app/db:rw # Store your NetAlertX devices and settings - - /etc/localtime:/etc/localtime:ro # Use your system clock inside the container (read-only) + - netalertx_config:/app/config:rw # Store your NetAlertX config + - netalertx_db:/app/db:rw # Store your NetAlertX devices and settings + - /etc/localtime:/etc/localtime:ro # Use your system clock inside the container (read-only) # Additional Volume Examples below diff --git a/install/production-filesystem/entrypoint.sh b/install/production-filesystem/entrypoint.sh index 89d5c62b..9e120230 100644 --- a/install/production-filesystem/entrypoint.sh +++ b/install/production-filesystem/entrypoint.sh @@ -24,6 +24,26 @@ SERVICES="" FAILED_NAME="" FAILED_STATUS=0 +is_pid_active() { + pid="$1" + [ -z "${pid}" ] && return 1 + + if ! kill -0 "${pid}" 2>/dev/null; then + return 1 + fi + + if [ -r "/proc/${pid}/status" ]; then + state_line=$(grep '^State:' "/proc/${pid}/status" 2>/dev/null || true) + case "${state_line}" in + *"(zombie)"*|*"(dead)"*) + return 1 + ;; + esac + fi + + return 0 +} + add_service() { script="$1" name="$2" @@ -48,7 +68,7 @@ shutdown_services() { for entry in ${SERVICES}; do pid="${entry%%:*}" [ -z "${pid}" ] && continue - if kill -0 "${pid}" 2>/dev/null; then + if is_pid_active "${pid}"; then kill "${pid}" 2>/dev/null || true fi done @@ -108,7 +128,7 @@ while [ -n "${SERVICES}" ]; do pid="${entry%%:*}" name="${entry#*:}" [ -z "${pid}" ] && continue - if ! kill -0 "${pid}" 2>/dev/null; then + if ! is_pid_active "${pid}"; then wait "${pid}" 2>/dev/null status=$? FAILED_STATUS=$status diff --git a/install/production-filesystem/services/check-first-run-config.sh b/install/production-filesystem/services/check-first-run-config.sh index 44b4d2bc..44269df3 100644 --- a/install/production-filesystem/services/check-first-run-config.sh +++ b/install/production-filesystem/services/check-first-run-config.sh @@ -1,16 +1,10 @@ #!/bin/sh # first-run-check.sh - Checks and initializes configuration files on first run -# Check for app.conf +# Check for app.conf and deploy if required if [ ! -f /app/config/app.conf ]; then mkdir -p /app/config cp /app/back/app.conf /app/config/app.conf echo "🆕 First run detected: Default configuration initialized." >&2 fi -# Check for app.db -if [ ! -f /app/db/app.db ]; then - mkdir -p /app/db - cp /app/back/app.db /app/db/app.db - echo "🆕 First run detected: Fresh database created." >&2 -fi diff --git a/install/production-filesystem/services/check-first-run-db.sh b/install/production-filesystem/services/check-first-run-db.sh index 2d44f76d..842be90a 100644 --- a/install/production-filesystem/services/check-first-run-db.sh +++ b/install/production-filesystem/services/check-first-run-db.sh @@ -1,9 +1,14 @@ +#!/bin/sh +# This script checks if the database file exists, and if not, creates it with the initial schema. +# It is intended to be run at the first start of the application. -test -f "${NETALERTX_DB}/app.db" && exit 0 +# if the db exists, exit +test -f "${NETALERTX_DB_FILE}" && exit 0 -echo "First run detected, creating initial database schema in ${NETALERTX_DB}/app.db" +echo "First run detected, creating initial database schema in ${NETALERTX_DB_FILE}" -cat << end-of-database > ${NETALERTX_DB}/db.sql +# Write all text to db file until we see "end-of-database-schema" +cat << end-of-database-schema > ${NETALERTX_DB_FILE} CREATE TABLE sqlite_stat1(tbl,idx,stat); CREATE TABLE Events (eve_MAC STRING (50) NOT NULL COLLATE NOCASE, eve_IP STRING (50) NOT NULL COLLATE NOCASE, eve_DateTime DATETIME NOT NULL, eve_EventType STRING (30) NOT NULL COLLATE NOCASE, eve_AdditionalInfo STRING (250) DEFAULT (''), eve_PendingAlertEmail BOOLEAN NOT NULL CHECK (eve_PendingAlertEmail IN (0, 1)) DEFAULT (1), eve_PairEventRowid INTEGER); CREATE TABLE Sessions (ses_MAC STRING (50) COLLATE NOCASE, ses_IP STRING (50) COLLATE NOCASE, ses_EventTypeConnection STRING (30) COLLATE NOCASE, ses_DateTimeConnection DATETIME, ses_EventTypeDisconnection STRING (30) COLLATE NOCASE, ses_DateTimeDisconnection DATETIME, ses_StillConnected BOOLEAN, ses_AdditionalInfo STRING (250)); @@ -415,6 +420,7 @@ CREATE TRIGGER "trg_delete_devices" 'delete' ); END; -end-of-database +end-of-database-schema -sqlite3 ${NETALERTX_DB}/app.db < ${NETALERTX_DB}/db.sql \ No newline at end of file +# Import the database schema into the new database file +sqlite3 ${NETALERTX_DB_FILE} < ${NETALERTX_DB}/db.sql \ No newline at end of file diff --git a/install/production-filesystem/services/cron_script.sh b/install/production-filesystem/services/cron_script.sh index 186b489a..2cde46a3 100755 --- a/install/production-filesystem/services/cron_script.sh +++ b/install/production-filesystem/services/cron_script.sh @@ -7,8 +7,8 @@ export INSTALL_DIR=/app if grep -q "cron_restart_backend" "${LOG_EXECUTION_QUEUE}"; then # Restart python application using s6 killall python3 + sleep 2 /services/start-backend.sh & - echo 'done' # Remove all lines containing cron_restart_backend from the log file sed -i '/cron_restart_backend/d' "${LOG_EXECUTION_QUEUE}" diff --git a/install/production-filesystem/services/start-php-fpm.sh b/install/production-filesystem/services/start-php-fpm.sh index 99de494d..53e64501 100755 --- a/install/production-filesystem/services/start-php-fpm.sh +++ b/install/production-filesystem/services/start-php-fpm.sh @@ -24,8 +24,8 @@ done trap cleanup EXIT trap forward_signal INT TERM -echo "/usr/sbin/php-fpm83 -y \"${PHP_FPM_CONFIG_FILE}\" -F >>\"${LOG_APP_PHP_ERRORS}\" 2>&1 &" -/usr/sbin/php-fpm83 -y "${PHP_FPM_CONFIG_FILE}" -F >>"${LOG_APP_PHP_ERRORS}" 2>&1 & +echo "/usr/sbin/php-fpm83 -y \"${PHP_FPM_CONFIG_FILE}\" -F 2>&1 >>\"${LOG_APP_PHP_ERRORS}\" &" +/usr/sbin/php-fpm83 -y "${PHP_FPM_CONFIG_FILE}" -F 2>&1 >>"${LOG_APP_PHP_ERRORS}" & php_fpm_pid=$! wait "${php_fpm_pid}" diff --git a/log/plugins/.git-placeholder b/log/plugins/.git-placeholder deleted file mode 100644 index e69de29b..00000000