Rdeirection BDE
This commit is contained in:
16
.env
16
.env
@@ -1,18 +1,4 @@
|
||||
DB_HOST=162.19.78.131
|
||||
DB_HOST=192.168.1.100
|
||||
DB_USER=SG
|
||||
DB_PASSWORD=A*Rb2Q@i72m8tM6@!$*
|
||||
|
||||
|
||||
MQTT_HOST=162.19.78.131
|
||||
MQTT_USER=sondes
|
||||
MQTT_PASSWORD=3J@bjYP0
|
||||
|
||||
# === EMAIL SMTP ===# === EMAIL SMTP ===
|
||||
SMTP_HOST=smtp.mail.ovh.net
|
||||
SMTP_PORT=465
|
||||
SMTP_USER=alertes_saclay@domo91.fr
|
||||
SMTP_PASSWORD=Kdpke674y23Feq^H
|
||||
EMAIL_FROM=alertes_saclay@domo91.fr
|
||||
EMAIL_SACLAY=Cuisine<cuisine@bw-paris-saclay.com>
|
||||
EMAIL_MEUDON=meudon@domo91.fr
|
||||
EMAIL_DEFAULT=services@domo91.fr
|
||||
4
.idea/Fichiers_perso.iml
generated
4
.idea/Fichiers_perso.iml
generated
@@ -1,7 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.11 virtualenv at C:\Users\miche\PycharmProjects\Gestion_sondes\.venv" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
|
||||
2
.idea/dataSources.local.xml
generated
2
.idea/dataSources.local.xml
generated
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="dataSourceStorageLocal" created-in="PY-252.25557.178">
|
||||
<component name="dataSourceStorageLocal" created-in="PY-261.23567.174">
|
||||
<data-source name="SG" uuid="068e6b2a-97f7-4f17-ae58-a11df8c5f332">
|
||||
<database-info product="MariaDB" version="10.11.11-MariaDB-0+deb12u1" jdbc-version="4.2" driver-name="MariaDB Connector/J" driver-version="3.3.3" dbms="MARIADB" exact-version="10.11.11" exact-driver-version="3.3">
|
||||
<extra-name-characters>#@</extra-name-characters>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<dataSource name="SG">
|
||||
<database-model serializer="dbm" dbms="MARIADB" family-id="MARIADB" format-version="4.53">
|
||||
<database-model serializer="dbm" dbms="MARIADB" family-id="MARIADB" format-version="4.55">
|
||||
<root id="1">
|
||||
<DefaultCasing>exact</DefaultCasing>
|
||||
<DefaultEngine>InnoDB</DefaultEngine>
|
||||
|
||||
23
.idea/dataSources/data_sources_history.xml
generated
Normal file
23
.idea/dataSources/data_sources_history.xml
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
<DataSourcesHistory>
|
||||
<DataSourceFromHistory isRemovedFromProject="false">
|
||||
<data-source source="LOCAL" name="SG" uuid="068e6b2a-97f7-4f17-ae58-a11df8c5f332">
|
||||
<database-info product="MariaDB" version="10.11.11-MariaDB-0+deb12u1" jdbc-version="4.2" driver-name="MariaDB Connector/J" driver-version="3.3.3" dbms="MARIADB" exact-version="10.11.11" exact-driver-version="3.3">
|
||||
<extra-name-characters>#@</extra-name-characters>
|
||||
<identifier-quote-string>`</identifier-quote-string>
|
||||
</database-info>
|
||||
<case-sensitivity plain-identifiers="exact" quoted-identifiers="exact" />
|
||||
<driver-ref>mariadb</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.mariadb.jdbc.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:mariadb://162.19.78.131:3306/SG</jdbc-url>
|
||||
<secret-storage>master_key</secret-storage>
|
||||
<user-name>SG</user-name>
|
||||
<schema-mapping>
|
||||
<introspection-scope>
|
||||
<node kind="schema" qname="@" />
|
||||
</introspection-scope>
|
||||
</schema-mapping>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</DataSourceFromHistory>
|
||||
</DataSourcesHistory>
|
||||
69
FICHIERS SUPERVISOR.txt
Normal file
69
FICHIERS SUPERVISOR.txt
Normal file
@@ -0,0 +1,69 @@
|
||||
FICHIERS SUPERVISOR
|
||||
|
||||
|
||||
|
||||
[program:Outils]
|
||||
directory=/home/debian/Outils
|
||||
command=/home/debian/Outils/.venv/bin/streamlit run app/users.py --server.port=8503 --server.address=162.19.78.131 --server.headless=true
|
||||
user=debian
|
||||
autostart=true
|
||||
autorestart=true
|
||||
startsecs=5
|
||||
stopsignal=TERM
|
||||
stopasgroup=true
|
||||
killasgroup=true
|
||||
stdout_logfile=/var/log/users.out.log
|
||||
stderr_logfile=/var/log/users.err.log
|
||||
environment=PYTHONUNBUFFERED="1"
|
||||
|
||||
|
||||
|
||||
[program:tracker]
|
||||
command=/home/debian/Gestion_sondes/venv/bin/streamlit run /home/debian/Gestion_sondes/Outils/tracker.py --server.headless true --server.address 162.19.78.131 --server.po>
|
||||
directory=/home/debian/Gestion_sondes
|
||||
user=debian
|
||||
autostart=true
|
||||
autorestart=true
|
||||
startsecs=8
|
||||
environment=PYTHONUNBUFFERED="1"
|
||||
stdout_logfile=/home/debian/Gestion_sondes/Logs/tracker.out.log
|
||||
stderr_logfile=/home/debian/Gestion_sondes/Logs/tracker.err.log
|
||||
|
||||
|
||||
|
||||
|
||||
[program:log_viewer]
|
||||
directory=/home/debian/Gestion_sondes/Outils
|
||||
command=/home/debian/Outils/myenv/bin/streamlit run Logs.py --server.port=8515
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stderr_logfile=/home/debian/Gestion_sondes/Logs/log_viewer_err.log
|
||||
stdout_logfile=/home/debian/Gestion_sondes/Logs/log_viewer_out.log
|
||||
user=debian
|
||||
environment=PATH="/home/debian/Outils/myenv/bin",HOME="/home/debian"
|
||||
|
||||
|
||||
program:cuisine_meudon]
|
||||
command=/home/debian/Gestion_sondes/myenv/bin/python -u /home/debian/Gestion_sondes/app/Mqtt_meudon.py
|
||||
directory=/home/debian/Gestion_sondes
|
||||
user=debian
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stdout_logfile=/home/debian/Gestion_sondes/Logs/cuisine_meudon.out.log
|
||||
stderr_logfile=/home/debian/Gestion_sondes/Logs/cuisine_meudon.err.log
|
||||
environment=PYTHONUNBUFFERED="1"
|
||||
|
||||
|
||||
[program:cuisine_saclay]
|
||||
command=/home/debian/Gestion_sondes/myenv/bin/python -u /home/debian/Gestion_sondes/app/Mqtt_saclay.py
|
||||
directory=/home/debian/Gestion_sondes
|
||||
user=debian
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stdout_logfile=/home/debian/Gestion_sondes/Logs/cuisine_saclay.out.log
|
||||
stderr_logfile=/home/debian/Gestion_sondes/Logs/cuisine_saclay.err.log
|
||||
environment=PYTHONUNBUFFERED="1"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ conn = mysql.connector.connect(
|
||||
host=os.getenv("DB_HOST"),
|
||||
user=os.getenv("DB_USER"),
|
||||
password=os.getenv("DB_PASSWORD"),
|
||||
database="SG"
|
||||
database="Societe_Generale"
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
23
TestMysqlConnector.py
Normal file
23
TestMysqlConnector.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import mysql.connector
|
||||
|
||||
try:
|
||||
conn = mysql.connector.connect(
|
||||
host="192.168.1.100",
|
||||
port=3306,
|
||||
user="Michel",
|
||||
password="wuP^wu&6xjx61bh*kjS^",
|
||||
database="TestDB"
|
||||
)
|
||||
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT VERSION();")
|
||||
version = cursor.fetchone()
|
||||
|
||||
print("Connexion OK")
|
||||
print("Version MariaDB :", version[0])
|
||||
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
except mysql.connector.Error as err:
|
||||
print("Erreur MySQL :", err)
|
||||
55
Test_SMS.py
Normal file
55
Test_SMS.py
Normal file
@@ -0,0 +1,55 @@
|
||||
import os, time
|
||||
from dotenv import load_dotenv
|
||||
import ovh
|
||||
|
||||
load_dotenv()
|
||||
|
||||
client = ovh.Client(
|
||||
endpoint=os.getenv("OVH_ENDPOINT", "ovh-eu"),
|
||||
application_key=os.getenv("OVH_APPLICATION_KEY"),
|
||||
application_secret=os.getenv("OVH_APPLICATION_SECRET"),
|
||||
consumer_key=os.getenv("OVH_CONSUMER_KEY"),
|
||||
)
|
||||
|
||||
service = os.getenv("OVH_SMS_SERVICE")
|
||||
sender = os.getenv("OVH_SMS_SENDER")
|
||||
to = os.getenv("TEST_RECEIVER")
|
||||
|
||||
message = "ALERTE TEST DOMO91 (transactionnel)."
|
||||
|
||||
# Envoi immédiat (pas de differedDelivery)
|
||||
payload = {
|
||||
"sender": sender,
|
||||
"receivers": [to],
|
||||
"message": message,
|
||||
"priority": "high",
|
||||
"coding": "7bit",
|
||||
"class": "phoneDisplay",
|
||||
"noStopClause": True, # => H24 si autorisé par OVH
|
||||
"senderForResponse": False, # True si vous utilisez un numéro et attendez des réponses
|
||||
"validityPeriod": 2880, # minutes où le SMS reste valable (2 jours)
|
||||
"tag": "test-ovh",
|
||||
}
|
||||
|
||||
print("Envoi en cours…")
|
||||
resp = client.post(f"/sms/{service}/jobs", **payload)
|
||||
# La réponse contient des taskIds pour suivre l'état
|
||||
print("Réponse OVH:", resp)
|
||||
|
||||
task_ids = resp.get("ids", [])
|
||||
if task_ids:
|
||||
task_id = task_ids[0]
|
||||
# Boucle de suivi simple (optionnelle)
|
||||
while True:
|
||||
task = client.get(f"/sms/{service}/jobs/{task_id}")
|
||||
print("Etat tâche:", task.get("status"))
|
||||
if task.get("status") in ("done", "error", "cancelled"):
|
||||
break
|
||||
time.sleep(2)
|
||||
|
||||
# On peut aussi lister les SMS sortants pour voir le statut final
|
||||
outgoing_ids = client.get(f"/sms/{service}/outgoing")
|
||||
if outgoing_ids:
|
||||
last_id = outgoing_ids[-1]
|
||||
last = client.get(f"/sms/{service}/outgoing/{last_id}")
|
||||
print("Dernier SMS:", {k: last[k] for k in ("creationDatetime","receiver","status","messageId")})
|
||||
@@ -82,10 +82,10 @@ def on_message(_client, _userdata, message):
|
||||
|
||||
|
||||
# Configuration du client MQTT
|
||||
mqtt_broker = "46.105.92.116"
|
||||
mqtt_broker = "162.19.78.131"
|
||||
mqtt_port = 1883 # Vérifiez si votre broker utilise un autre port
|
||||
mqtt_user = "Bwps"
|
||||
mqtt_password = "scJ5ACj2keRfI^"
|
||||
mqtt_user = "sondes"
|
||||
mqtt_password = "3J@bjYP0"
|
||||
mqtt_topic = "Tracker/sondes"
|
||||
|
||||
client = mqtt.Client()
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
import paramiko
|
||||
# Configuration
|
||||
hostname = "162.19.78.131"
|
||||
port = 22
|
||||
username = "debian"
|
||||
password = "lpZwixbBUFtGY"
|
||||
chemin_script = "/home/debian/Gestion_sondes/app/Vider_Logs.py"
|
||||
|
||||
# Connexion SSH avec clé + passphrase
|
||||
try:
|
||||
print("Connexion SSH en cours...")
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
ssh.connect(hostname, port=port, username=username, password=password)
|
||||
|
||||
# Exécution du script distant
|
||||
print(f"Exécution du script : {chemin_script}")
|
||||
stdin, stdout, stderr = ssh.exec_command(f"python3 {chemin_script}")
|
||||
|
||||
print("\n--- Résultat ---")
|
||||
for ligne in stdout:
|
||||
print(ligne.strip())
|
||||
|
||||
erreurs = stderr.read().decode()
|
||||
if erreurs:
|
||||
print("\n--- Erreurs ---")
|
||||
print(erreurs)
|
||||
|
||||
ssh.close()
|
||||
print("\n✅ Terminé avec succès.")
|
||||
except Exception as e:
|
||||
print(f"❌ Erreur : {e}")
|
||||
# --- FIN DU CODE ACTIF ---
|
||||
|
||||
# ============================================================================
|
||||
# 👇 CI-DESSOUS : VERSION DU SCRIPT À DÉPLOYER SUR LE VPS (à copier/coller)
|
||||
# ============================================================================
|
||||
|
||||
"""
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
|
||||
racine_logs = "/var/log"
|
||||
|
||||
nb_vides = 0
|
||||
for dossier, _, fichiers in os.walk(racine_logs):
|
||||
for fichier in fichiers:
|
||||
if fichier.endswith(".log"):
|
||||
try:
|
||||
with open(os.path.join(dossier, fichier), "w") as f:
|
||||
pass
|
||||
nb_vides += 1
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
print(f"{nb_vides} fichiers log vidés.")
|
||||
"""
|
||||
|
||||
# ============================================================================
|
||||
# FIN DU SCRIPT
|
||||
# ============================================================================
|
||||
222
telegram_watch.py
Normal file
222
telegram_watch.py
Normal file
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import ipaddress
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
|
||||
import geoip2.database
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
from rich import box
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
def is_public_ip(ip: str) -> bool:
|
||||
try:
|
||||
addr = ipaddress.ip_address(ip)
|
||||
return not (
|
||||
addr.is_private
|
||||
or addr.is_loopback
|
||||
or addr.is_multicast
|
||||
or addr.is_link_local
|
||||
or addr.is_reserved
|
||||
)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
def geo_lookup(reader, ip: str):
|
||||
try:
|
||||
r = reader.country(ip)
|
||||
iso = r.country.iso_code or "??"
|
||||
name = r.country.name or "Inconnu"
|
||||
return iso, name
|
||||
except Exception:
|
||||
return "??", "Inconnu"
|
||||
|
||||
|
||||
def run_tshark_capture(interface: str, duration: int, output_pcap: str):
|
||||
if shutil.which("tshark") is None:
|
||||
console.print("[red]tshark n'est pas installé ou pas dans le PATH.[/red]")
|
||||
sys.exit(1)
|
||||
|
||||
cmd = [
|
||||
"tshark",
|
||||
"-i", interface,
|
||||
"-a", f"duration:{duration}",
|
||||
"-w", output_pcap,
|
||||
]
|
||||
|
||||
console.print(
|
||||
f"[cyan]Capture en cours sur {interface} pendant {duration} s...[/cyan]\n"
|
||||
"[yellow]Envoie maintenant tes messages Telegram.[/yellow]"
|
||||
)
|
||||
result = subprocess.run(cmd)
|
||||
if result.returncode != 0:
|
||||
console.print("[red]La capture TShark a échoué.[/red]")
|
||||
sys.exit(result.returncode)
|
||||
|
||||
|
||||
def extract_remote_ips(pcap_file: str, local_ip: str = None):
|
||||
fields = ["-e", "ip.src", "-e", "ip.dst", "-e", "tcp.dstport", "-e", "udp.dstport"]
|
||||
|
||||
cmd = [
|
||||
"tshark",
|
||||
"-r", pcap_file,
|
||||
"-T", "fields",
|
||||
"-E", "separator=|",
|
||||
"-E", "occurrence=f",
|
||||
*fields,
|
||||
]
|
||||
|
||||
proc = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if proc.returncode != 0:
|
||||
console.print("[red]Impossible de relire le fichier pcapng.[/red]")
|
||||
console.print(proc.stderr)
|
||||
sys.exit(proc.returncode)
|
||||
|
||||
stats = defaultdict(lambda: {"count": 0, "ports": set(), "direction": set()})
|
||||
|
||||
for line in proc.stdout.splitlines():
|
||||
parts = line.split("|")
|
||||
if len(parts) < 4:
|
||||
continue
|
||||
|
||||
src = parts[0].strip()
|
||||
dst = parts[1].strip()
|
||||
tcp_port = parts[2].strip()
|
||||
udp_port = parts[3].strip()
|
||||
|
||||
if not src or not dst:
|
||||
continue
|
||||
|
||||
if local_ip:
|
||||
if src == local_ip and is_public_ip(dst):
|
||||
remote = dst
|
||||
direction = "sortant"
|
||||
elif dst == local_ip and is_public_ip(src):
|
||||
remote = src
|
||||
direction = "entrant"
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
candidates = []
|
||||
if is_public_ip(src):
|
||||
candidates.append((src, "entrant"))
|
||||
if is_public_ip(dst):
|
||||
candidates.append((dst, "sortant"))
|
||||
for remote, direction in candidates:
|
||||
stats[remote]["count"] += 1
|
||||
stats[remote]["direction"].add(direction)
|
||||
if tcp_port:
|
||||
stats[remote]["ports"].add(f"tcp/{tcp_port}")
|
||||
if udp_port:
|
||||
stats[remote]["ports"].add(f"udp/{udp_port}")
|
||||
continue
|
||||
|
||||
stats[remote]["count"] += 1
|
||||
stats[remote]["direction"].add(direction)
|
||||
if tcp_port:
|
||||
stats[remote]["ports"].add(f"tcp/{tcp_port}")
|
||||
if udp_port:
|
||||
stats[remote]["ports"].add(f"udp/{udp_port}")
|
||||
|
||||
return stats
|
||||
|
||||
|
||||
def render_table(stats, mmdb_path: str):
|
||||
reader = geoip2.database.Reader(mmdb_path)
|
||||
|
||||
table = Table(
|
||||
title="IP observées pendant l'activité Telegram",
|
||||
box=box.SIMPLE_HEAVY,
|
||||
show_lines=False
|
||||
)
|
||||
table.add_column("IP", style="bold")
|
||||
table.add_column("Pays")
|
||||
table.add_column("Code")
|
||||
table.add_column("Direction")
|
||||
table.add_column("Ports")
|
||||
table.add_column("Paquets", justify="right")
|
||||
table.add_column("Alerte")
|
||||
|
||||
rows = []
|
||||
for ip, data in stats.items():
|
||||
iso, country = geo_lookup(reader, ip)
|
||||
non_fr = iso != "FR"
|
||||
rows.append((
|
||||
non_fr,
|
||||
ip,
|
||||
country,
|
||||
iso,
|
||||
",".join(sorted(data["direction"])),
|
||||
", ".join(sorted(data["ports"]))[:80],
|
||||
str(data["count"]),
|
||||
"NON FR" if non_fr else ""
|
||||
))
|
||||
|
||||
rows.sort(key=lambda r: (not r[0], r[1]))
|
||||
|
||||
non_fr_count = 0
|
||||
for non_fr, ip, country, iso, direction, ports, count, alert in rows:
|
||||
if non_fr:
|
||||
non_fr_count += 1
|
||||
table.add_row(
|
||||
f"[red]{ip}[/red]",
|
||||
f"[red]{country}[/red]",
|
||||
f"[red]{iso}[/red]",
|
||||
f"[red]{direction}[/red]",
|
||||
f"[red]{ports}[/red]",
|
||||
f"[red]{count}[/red]",
|
||||
f"[bold red]{alert}[/bold red]",
|
||||
)
|
||||
else:
|
||||
table.add_row(
|
||||
ip, country, iso, direction, ports, count, ""
|
||||
)
|
||||
|
||||
console.print(table)
|
||||
console.print(
|
||||
f"\n[bold]Total IP publiques observées :[/bold] {len(rows)}\n"
|
||||
f"[bold red]IP non françaises :[/bold red] {non_fr_count}"
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Surveille les IP vues pendant une activité Telegram et met en évidence les IP non françaises."
|
||||
)
|
||||
parser.add_argument("-i", "--interface", default="ens3", help="Interface réseau, ex: ens3")
|
||||
parser.add_argument("-d", "--duration", type=int, default=20, help="Durée de capture en secondes")
|
||||
parser.add_argument("-o", "--output", default="telegram-test.pcapng", help="Fichier de capture")
|
||||
parser.add_argument("--local-ip", help="IP locale à filtrer, ex: 192.168.1.112")
|
||||
parser.add_argument(
|
||||
"--mmdb",
|
||||
default="/usr/share/GeoIP/GeoLite2-Country.mmdb",
|
||||
help="Chemin vers GeoLite2-Country.mmdb"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--read-only",
|
||||
action="store_true",
|
||||
help="N'effectue pas de capture, relit seulement le fichier -o"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.read_only:
|
||||
run_tshark_capture(args.interface, args.duration, args.output)
|
||||
|
||||
stats = extract_remote_ips(args.output, args.local_ip)
|
||||
|
||||
if not stats:
|
||||
console.print("[yellow]Aucune IP publique trouvée dans cette capture.[/yellow]")
|
||||
sys.exit(0)
|
||||
|
||||
render_table(stats, args.mmdb)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
20
test_synology_chat.py
Normal file
20
test_synology_chat.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import json
|
||||
import requests
|
||||
|
||||
WEBHOOK_URL = "https://mj91.fr/webapi/entry.cgi?api=SYNO.Chat.External&method=incoming&version=2&token=UN7nhD70vrhrHFh1VeDdOpsklIHiIFRop2qB7b6YusMEY3clY3R8CXe4hFzz4KKc"
|
||||
|
||||
payload = {
|
||||
"text": "✅ Test Python vers Synology Chat"
|
||||
}
|
||||
|
||||
try:
|
||||
r = requests.post(
|
||||
WEBHOOK_URL,
|
||||
data={"payload": json.dumps(payload, ensure_ascii=False)},
|
||||
timeout=10,
|
||||
verify=True
|
||||
)
|
||||
print("HTTP:", r.status_code)
|
||||
print("Réponse:", r.text)
|
||||
except Exception as e:
|
||||
print("Erreur:", repr(e))
|
||||
183
watcher.py
Normal file
183
watcher.py
Normal file
@@ -0,0 +1,183 @@
|
||||
import json
|
||||
import os
|
||||
import sqlite3
|
||||
import time
|
||||
from datetime import datetime
|
||||
from ipaddress import ip_address
|
||||
|
||||
import geoip2.database
|
||||
|
||||
DB_PATH = "/host_logs/synolog/.SYNOCONNDB"
|
||||
GEOIP_DB = "/config/GeoLite2-Country.mmdb"
|
||||
STATE_FILE = "/data/state.json"
|
||||
OUTPUT_LOG = "/logs/watcher.log"
|
||||
|
||||
POLL_SECONDS = 60
|
||||
|
||||
KNOWN_USERS = {
|
||||
"Michel",
|
||||
"Samsung_A13",
|
||||
"Aurélie",
|
||||
"Gilberte",
|
||||
"Chloé",
|
||||
}
|
||||
|
||||
WHITELIST_IPS = {
|
||||
"192.168.1.254",
|
||||
"192.168.1.90",
|
||||
"192.168.1.80",
|
||||
}
|
||||
|
||||
WATCH_CHAT_ONLY = True
|
||||
|
||||
def load_state():
|
||||
if os.path.exists(STATE_FILE):
|
||||
try:
|
||||
with open(STATE_FILE, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
except Exception:
|
||||
return {}
|
||||
return {}
|
||||
|
||||
def save_state(state):
|
||||
with open(STATE_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(state, f, indent=2)
|
||||
|
||||
def log_event(message):
|
||||
ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
line = f"[{ts}] {message}\n"
|
||||
with open(OUTPUT_LOG, "a", encoding="utf-8") as f:
|
||||
f.write(line)
|
||||
print(line, end="")
|
||||
|
||||
def is_private_ip(value: str) -> bool:
|
||||
try:
|
||||
ip = ip_address(value)
|
||||
return ip.is_private or ip.is_loopback
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def get_country_code(reader, ip: str):
|
||||
if not ip or is_private_ip(ip):
|
||||
return "PRIVATE"
|
||||
try:
|
||||
response = reader.country(ip)
|
||||
return response.country.iso_code or "UNKNOWN"
|
||||
except Exception:
|
||||
return "UNKNOWN"
|
||||
|
||||
def fetch_new_rows(last_id: int):
|
||||
if not os.path.exists(DB_PATH):
|
||||
log_event(f"ERREUR base introuvable: {DB_PATH}")
|
||||
return []
|
||||
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
conn.row_factory = sqlite3.Row
|
||||
|
||||
query = """
|
||||
SELECT id, time, level, username, msg, user, uid, ip, protocol, token, useragent
|
||||
FROM logs
|
||||
WHERE id > ?
|
||||
ORDER BY id ASC
|
||||
"""
|
||||
|
||||
rows = conn.execute(query, (last_id,)).fetchall()
|
||||
conn.close()
|
||||
return rows
|
||||
|
||||
def classify_event(row, reader):
|
||||
username = (row["username"] or "").strip()
|
||||
ip = (row["ip"] or "").strip()
|
||||
protocol = (row["protocol"] or "").strip()
|
||||
msg = (row["msg"] or "").strip()
|
||||
useragent = (row["useragent"] or "").strip()
|
||||
|
||||
if WATCH_CHAT_ONLY and "Synology_Chat" not in useragent:
|
||||
return None
|
||||
|
||||
reasons = []
|
||||
level = "INFO"
|
||||
country = get_country_code(reader, ip)
|
||||
|
||||
if username not in KNOWN_USERS:
|
||||
level = "ALERTE"
|
||||
reasons.append("user_inconnu")
|
||||
|
||||
if ip in WHITELIST_IPS:
|
||||
reasons.append("ip_whitelist")
|
||||
country = "PRIVATE"
|
||||
|
||||
if is_private_ip(ip):
|
||||
reasons.append("ip_privee")
|
||||
else:
|
||||
reasons.append("ip_publique")
|
||||
|
||||
if country == "FR":
|
||||
reasons.append("pays_fr")
|
||||
if level == "INFO":
|
||||
level = "INFO"
|
||||
elif country == "UNKNOWN":
|
||||
reasons.append("pays_inconnu")
|
||||
if level != "ALERTE":
|
||||
level = "SURVEILLANCE"
|
||||
else:
|
||||
reasons.append(f"pays_{country}")
|
||||
if level != "ALERTE":
|
||||
level = "SUSPECT"
|
||||
|
||||
if "failed" in msg.lower():
|
||||
reasons.append("echec_connexion")
|
||||
level = "ALERTE"
|
||||
|
||||
return {
|
||||
"id": row["id"],
|
||||
"dt": datetime.fromtimestamp(row["time"]).strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"username": username,
|
||||
"ip": ip,
|
||||
"protocol": protocol,
|
||||
"country": country,
|
||||
"level": level,
|
||||
"reasons": ",".join(reasons),
|
||||
"msg": msg,
|
||||
}
|
||||
|
||||
def main():
|
||||
if not os.path.exists(GEOIP_DB):
|
||||
raise FileNotFoundError(f"Base GeoIP introuvable: {GEOIP_DB}")
|
||||
|
||||
log_event("Démarrage ipwatcher sqlite + GeoIP")
|
||||
state = load_state()
|
||||
last_id = state.get("last_id", 0)
|
||||
|
||||
with geoip2.database.Reader(GEOIP_DB) as reader:
|
||||
while True:
|
||||
try:
|
||||
rows = fetch_new_rows(last_id)
|
||||
for row in rows:
|
||||
last_id = row["id"]
|
||||
event = classify_event(row, reader)
|
||||
if event is None:
|
||||
continue
|
||||
|
||||
log_event(
|
||||
f"{event['level']} "
|
||||
f"id={event['id']} "
|
||||
f"dt={event['dt']} "
|
||||
f"user={event['username']} "
|
||||
f"ip={event['ip']} "
|
||||
f"country={event['country']} "
|
||||
f"protocol={event['protocol']} "
|
||||
f"reasons={event['reasons']} "
|
||||
f"msg={event['msg']}"
|
||||
)
|
||||
|
||||
state["last_id"] = last_id
|
||||
save_state(state)
|
||||
|
||||
except Exception as e:
|
||||
log_event(f"ERREUR traitement sqlite: {e}")
|
||||
|
||||
time.sleep(POLL_SECONDS)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user