diff --git a/Dockerfile b/Dockerfile index 068cca3..956631e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,16 +20,19 @@ COPY ap.py /app/ap.py ARG HTML_FILE=index.html COPY ${HTML_FILE} /app/index.html COPY ${HTML_FILE} /var/www/html/index.html +# L'API n'est plus joignable que via Apache : le HTML appelle /api sur le même port (1001) +RUN sed -i 's#const API_URL=[^;]*;#const API_URL="";#' /app/index.html /var/www/html/index.html COPY apache-ap.conf /etc/apache2/sites-available/000-default.conf COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh && rm -f /var/www/html/index.html.orig \ - && sed -i 's/^Listen 80$/Listen 1001/; s/^\s*Listen 443$/ Listen 1443/' /etc/apache2/ports.conf + && sed -i 's/^Listen 80$/Listen ${AP_WEB_PORT}/; s/^\s*Listen 443$/ Listen 1443/' /etc/apache2/ports.conf ENV AP_DB_FILE=/data/ap-dashboard.sqlite3 \ AP_CONNECTION_FILE=/data/connection \ AP_UPTIME_FILE=/data/uptime VOLUME ["/data", "/etc/NetworkManager/system-connections", "/var/lib/NetworkManager"] -EXPOSE 1001 8080 +ENV AP_WEB_PORT=1001 +EXPOSE 1001 ENTRYPOINT ["/entrypoint.sh"] diff --git a/ap.py b/ap.py index 045910a..e88a375 100644 --- a/ap.py +++ b/ap.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 -import base64, hashlib, hmac, json, os, re, secrets, socket, sqlite3, subprocess, threading, time, urllib.parse +import base64, hashlib, hmac, json, os, re, secrets, socket, sqlite3, subprocess, threading, time, urllib.parse, socketserver, shutil from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer INTERFACE = os.environ.get('AP_INTERFACE', 'wls16b1') -HOST, PORT = '0.0.0.0', 8080 +SOCKET_PATH = os.environ.get('AP_SOCKET', '/run/ap-api.sock') BASE_DIR = os.path.dirname(os.path.abspath(__file__)) DB_FILE = os.environ.get('AP_DB_FILE', os.path.join(BASE_DIR, 'ap-dashboard.sqlite3')) CONNECTION_FILE = os.environ.get('AP_CONNECTION_FILE', os.path.join(BASE_DIR, 'ap-dashboard-connection')) @@ -501,5 +501,19 @@ def monitor(): except Exception: pass time.sleep(2) +class UnixServer(ThreadingHTTPServer): + address_family = socket.AF_UNIX + daemon_threads = True + def server_bind(self): + socketserver.TCPServer.server_bind(self) + self.server_name, self.server_port = 'localhost', 0 + if __name__ == '__main__': - db(); threading.Thread(target=monitor,daemon=True).start(); print(f'API AP sur http://0.0.0.0:{PORT}'); ThreadingHTTPServer((HOST,PORT),Handler).serve_forever() + db(); threading.Thread(target=monitor,daemon=True).start() + try: os.remove(SOCKET_PATH) + except OSError: pass + server = UnixServer(SOCKET_PATH, Handler) + os.chmod(SOCKET_PATH, 0o660) + try: shutil.chown(SOCKET_PATH, user='root', group='www-data') + except (LookupError, OSError): os.chmod(SOCKET_PATH, 0o666) + print(f'API AP sur le socket {SOCKET_PATH} (aucun port TCP)'); server.serve_forever() diff --git a/apache-ap.conf b/apache-ap.conf index 2a40f24..9d14813 100644 --- a/apache-ap.conf +++ b/apache-ap.conf @@ -1,6 +1,6 @@ ServerName localhost - + DocumentRoot /var/www/html DirectoryIndex index.html @@ -12,8 +12,8 @@ ServerName localhost # L'API Python lit l'IP réelle du client dans X-Real-IP RequestHeader set X-Real-IP "expr=%{REMOTE_ADDR}" ProxyPreserveHost On - ProxyPass /api http://127.0.0.1:8080/api - ProxyPassReverse /api http://127.0.0.1:8080/api + ProxyPass /api unix:/run/ap-api.sock|http://localhost/api + ProxyPassReverse /api http://localhost/api ErrorLog /dev/stderr CustomLog /dev/stdout combined diff --git a/docker-compose.yml b/docker-compose.yml index 33ebe0c..4027446 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,31 +1,18 @@ -# 1. Trouver l'interface de la carte Wi-Fi : iw dev -# 2. Lancer : AP_INTERFACE=wlan0 docker compose up -d --build -# 3. Injecter la carte Wi-Fi dans le conteneur (à refaire à chaque redémarrage du conteneur) : -# sudo ./attach-wifi.sh wlan0 -# 4. Ouvrir : http://IP-DE-LA-MACHINE:1001/ -# -# Si votre fichier HTML ne s'appelle pas index.html, ajoutez sous "build:" -# args: -# HTML_FILE: ap_dashboard.html - services: - ap-wifi: - build: . - container_name: ap-wifi + AP-Manageur: + image: gitea.yoann-nas.fr/yoannmultigaming/apmanageur.git:latest + container_name: ap-manageur restart: unless-stopped - ports: - - "1001:1001" # interface web (Apache) - - "8080:8080" # API (le HTML l'appelle sur ce port) + network_mode: host # accès direct à la carte Wi-Fi de l'hôte (Apache sur le port 1001) privileged: true # NetworkManager interne : rfkill, NAT, sysctl environment: - AP_INTERFACE: ${AP_INTERFACE:-} + # Laisser vide = détection automatique de la carte Wi-Fi. + # Écrire un nom (ex. "wlan0") = cette interface sera utilisée. + AP_INTERFACE: "" + # Port d'écoute de l'interface web. + AP_WEB_PORT: "1001" volumes: - - ap-data:/data - - nm-connections:/etc/NetworkManager/system-connections - - nm-state:/var/lib/NetworkManager - - /run/udev:/run/udev:ro # NetworkManager reconnaît ainsi la carte USB - -volumes: - ap-data: - nm-connections: - nm-state: + - /path/to/APmanageur/data:/data + - /path/to/APmanageur/nm-connections:/etc/NetworkManager/system-connections + - /path/to/APmanageur/nm-state:/var/lib/NetworkManager + - /run/udev:/run/udev:ro # NetworkManager reconnaît ainsi la carte USB \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh index 1a23711..37ec50e 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -3,18 +3,33 @@ set -euo pipefail log() { echo "[ap-wifi] $*"; } -# 1. Interface Wi-Fi (Netgear) -------------------------------------------------- -IFACE="${AP_INTERFACE:-}" -mapfile -t FOUND < <(iw dev 2>/dev/null | awk '$1=="Interface"{print $2}') -if [[ -z "$IFACE" ]]; then - if [[ ${#FOUND[@]} -eq 1 ]]; then - IFACE="${FOUND[0]}" - else - log "ERREUR : précisez AP_INTERFACE. Interfaces Wi-Fi détectées : ${FOUND[*]:-aucune}" - exit 1 - fi +# Port d'écoute d'Apache (défini dans docker-compose.yml) +AP_WEB_PORT="${AP_WEB_PORT:-1001}" +if ! [[ "$AP_WEB_PORT" =~ ^[0-9]+$ ]] || (( AP_WEB_PORT < 1 || AP_WEB_PORT > 65535 )); then + log "ERREUR : AP_WEB_PORT invalide ('$AP_WEB_PORT'), attendu un nombre entre 1 et 65535" + exit 1 fi -iw dev "$IFACE" info >/dev/null 2>&1 || { log "ERREUR : interface '$IFACE' introuvable (détectées : ${FOUND[*]:-aucune})"; exit 1; } +export AP_WEB_PORT + +# 1. Interface Wi-Fi -------------------------------------------------- +IFACE="${AP_INTERFACE:-}" +log "Recherche de la carte Wi-Fi…" +FOUND=() +for _ in $(seq 1 180); do + mapfile -t FOUND < <(iw dev 2>/dev/null | awk '$1=="Interface"{print $2}') + if [[ -n "$IFACE" ]]; then + printf '%s\n' "${FOUND[@]:-}" | grep -qx "$IFACE" && break + elif [[ ${#FOUND[@]} -ge 1 ]]; then + IFACE="${FOUND[0]}" + for d in "${FOUND[@]}"; do + p="$(iw dev "$d" info 2>/dev/null | awk '/wiphy/{print "phy"$2}')" + if [[ -n "$p" ]] && iw "$p" info 2>/dev/null | grep -qE '^\s+\* AP$'; then IFACE="$d"; break; fi + done + break + fi + sleep 1 +done +[[ -n "$IFACE" ]] && iw dev "$IFACE" info >/dev/null 2>&1 || { log "ERREUR : interface '${IFACE:-?}' absente après 3 min (détectées : ${FOUND[*]:-aucune})"; exit 1; } export AP_INTERFACE="$IFACE" log "Interface utilisée : $IFACE" @@ -48,7 +63,7 @@ nmcli radio wifi on 2>/dev/null || true nmcli device set "$IFACE" managed yes 2>/dev/null || true log "NetworkManager prêt" -# 3. API Python (port 8080) + Apache (port 80) ------------------------------------ +# 3. API Python (socket Unix, aucun port) + Apache (port 1001) ------------------------------------ python3 /app/ap.py & API_PID=$! apache2ctl -D FOREGROUND & @@ -56,7 +71,7 @@ WEB_PID=$! trap 'log "Arrêt"; kill "$WEB_PID" "$API_PID" "$NM_PID" 2>/dev/null || true; exit 0' TERM INT -log "Interface web : http://:1001/ (API directe : port 8080)" +log "Interface web : http://:${AP_WEB_PORT}/ (API interne par socket, aucun port)" wait -n log "Un service s'est arrêté, fermeture du conteneur" kill "$WEB_PID" "$API_PID" "$NM_PID" 2>/dev/null || true