# syntax=docker/dockerfile:1 FROM python:3.12-slim AS prod # Unbuffered so `docker logs` shows poll output as it happens rather than in # 4KB bursts -- stdout is block-buffered when it is not a TTY. ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 WORKDIR /app # Dependencies first so edits to the script do not invalidate the layer. # cryptography is required, not optional: without it pysnmp cannot do AES # privacy and every poll fails with "Ciphering services not available". COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY wlc_monitor.py . # Run unprivileged. The image needs no root at runtime: outbound UDP/161 and # TCP/SMTP only, plus one JSON file in /data. RUN useradd --system --uid 10001 --no-create-home --shell /usr/sbin/nologin wlcmon \ && mkdir -p /data \ && chown wlcmon:wlcmon /data USER wlcmon # State lives here; mount a named volume so alert de-duplication and the # "consecutive failures" counter survive a restart. Without persistence the # container starts with a blank comparison baseline after recreation. VOLUME ["/data"] ENV MONITOR_STATE_FILE=/data/state.json # Reports on the poll loop, not on the controller: an unreachable WLC means the # monitor is working, and restarting it would discard alert-suppression state. HEALTHCHECK --interval=60s --timeout=10s --start-period=90s --retries=3 \ CMD ["python", "/app/wlc_monitor.py", "--healthcheck"] ENTRYPOINT ["python", "/app/wlc_monitor.py"] CMD ["--loop"]