app.py 7,49 ko
Newer Older
import hashlib
import itertools
import logging
import subprocess
import re  # <-- Import pour extraire le numéro du port
from flask import Flask, request, jsonify
from flask_cors import CORS  # Import pour activer CORS

# Configuration du logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = Flask(__name__)
CORS(app)  # Activation de CORS pour toutes les routes
# Configuration de Redis
redis_host = os.getenv("REDIS_HOST", "redis")  # "redis" correspond au service Redis dans Docker
redis_url = os.getenv("REDIS_PORT", "6379")  # Valeur par défaut

# Extraction du numéro de port de Redis (compatible avec GitLab CI/CD et Docker)
redis_port_match = re.search(r":(\d+)", redis_url)
redis_port = int(redis_port_match.group(1)) if redis_port_match else 6379

# Connexion à Redis
redis_client = redis.StrictRedis(host=redis_host, port=redis_port, db=0, decode_responses=True)
# Clé pour le compteur de requêtes reçues
REQUEST_RECEIVED_KEY = "request_received_count"

# Initialisation du compteur si inexistant
if not redis_client.exists(REQUEST_RECEIVED_KEY):
    redis_client.set(REQUEST_RECEIVED_KEY, 0)


@app.before_request
def count_requests():
    """
    Middleware exécuté avant chaque requête pour incrémenter le compteur global de requêtes reçues.
    """
    redis_client.incr(REQUEST_RECEIVED_KEY)
    logger.info(f"📩 Nouvelle requête reçue ! Nombre total : {redis_client.get(REQUEST_RECEIVED_KEY)}")


@app.route("/health", methods=["GET"])
def health():
    """ Vérifie si l'application fonctionne correctement. """
    logger.info("✅ Vérification du statut de l'application (health check)")
    return jsonify({"status": "ok"}), 200

@app.route("/bruteforce", methods=["POST"])
def bruteforce():
    """
    Endpoint pour bruteforcer un hash MD5.
    """
    data = request.json
    if not data or "hash" not in data:
        logger.warning("⚠️ Requête invalide reçue sur /bruteforce")
        return jsonify({
            "status": "fail",
            "data": None,
            "errors": {"message": "Invalid payload. Please provide a valid hash."}
        }), 400

    target_hash = data["hash"]
    logger.info(f"🔍 Bruteforce demandé pour le hash : {target_hash}")
    # Vérifier si le hash existe déjà dans Redis
    if redis_client.exists(target_hash):
        original = redis_client.get(target_hash)
        logger.info(f"✅ Hash trouvé en cache : {original}")
        return jsonify({
            "status": "success",
            "data": {"hash": target_hash, "original": original},
    # Logique de brute-force
    charset = "abcdefghijklmnopqrstuvwxyz0123456789"
    for length in range(1, 8):
        for guess in itertools.product(charset, repeat=length):
            guess_str = ''.join(guess)
            if hashlib.md5(guess_str.encode()).hexdigest() == target_hash:
                redis_client.set(target_hash, guess_str)
                logger.info(f"✅ Hash {target_hash} trouvé : {guess_str}")
                return jsonify({
                    "status": "success",
                    "data": {"hash": target_hash, "original": guess_str},
    logger.warning(f"❌ Aucun résultat trouvé pour le hash : {target_hash}")
    return jsonify({
        "status": "fail",
        "data": None,
        "errors": {"message": "No match found."}

@app.route("/resolved", methods=["GET"])
def get_resolved_hashes():
    """
    Endpoint pour récupérer tous les hash résolus.
    """
    keys = redis_client.keys()
    resolved_hashes = {key: redis_client.get(key) for key in keys if key != REQUEST_RECEIVED_KEY}
    logger.info(f"📜 Liste des hash résolus récupérée ({len(resolved_hashes)} entrées)")
    return jsonify({
        "status": "success",
        "data": resolved_hashes,
        "errors": None
    }), 200


@app.route("/resolved/<string:hash_value>", methods=["GET"])
def get_resolved_hash(hash_value):
    """
    Endpoint pour récupérer un hash spécifique.
    """
    if redis_client.exists(hash_value):
        original = redis_client.get(hash_value)
        logger.info(f"🔎 Recherche du hash {hash_value} : {original}")
        return jsonify({
            "status": "success",
            "data": {"hash": hash_value, "original": original},
            "errors": None
        }), 200
    else:
        logger.warning(f"⚠️ Aucune entrée trouvée pour le hash : {hash_value}")
        return jsonify({
            "status": "fail",
            "data": None,
            "errors": {"message": f"No resolved entry found for hash: {hash_value}"}
@app.route("/request_count", methods=["GET"])
def get_request_count():
    """
    Endpoint pour récupérer le nombre total de requêtes reçues.
    """
    count = redis_client.get(REQUEST_RECEIVED_KEY)
    logger.info(f"📊 Nombre total de requêtes reçues : {count}")
    return jsonify({
        "status": "success",
        "data": {"request_received_count": int(count) if count else 0},
        "errors": None
    }), 200


@app.route("/clear_cache", methods=["DELETE"])
def clear_cache():
    """
    Endpoint pour effacer toutes les entrées stockées dans Redis (sauf le compteur de requêtes).
    """
    keys = redis_client.keys()
    for key in keys:
        if key != REQUEST_RECEIVED_KEY:
            redis_client.delete(key)

    logger.info("🗑️ Cache nettoyé, toutes les entrées supprimées sauf le compteur de requêtes.")
    return jsonify({"status": "success", "message": "Cache cleared."}), 200


@app.route("/replicas", methods=["GET"])
def get_replicas():
    """
    Endpoint pour récupérer le nombre actuel de réplicas du backend dans Docker Swarm.
    """
    SERVICE_NAME = "md5_stack_backend"
    
    try:
        result = subprocess.run(["docker", "service", "ls"], capture_output=True, text=True)
        for line in result.stdout.split("\n"):
            if SERVICE_NAME in line:
                replicas = line.split()[3].split("/")[0]  # Extraction du nombre de réplicas actuels
                logger.info(f"🔢 Nombre de réplicas actuels : {replicas}")
                return jsonify({"status": "success", "data": {"replicas": int(replicas)}, "errors": None}), 200
    except Exception as e:
        logger.error(f"❌ Erreur récupération réplicas : {e}")
        return jsonify({"status": "fail", "data": None, "errors": {"message": str(e)}}), 500

    return jsonify({"status": "fail", "data": None, "errors": {"message": "Service not found"}}), 404


@app.route("/cpu_usage", methods=["GET"])
def get_cpu_usage():
    """
    Endpoint pour récupérer l'utilisation CPU actuelle du service backend dans Docker Swarm.
    """
    SERVICE_NAME = "md5_stack_backend"

    try:
        result = subprocess.run(["docker", "stats", "--no-stream", "--format", "{{.Name}} {{.CPUPerc}}"],
                                capture_output=True, text=True)
        for line in result.stdout.split("\n"):
            if SERVICE_NAME in line:
                cpu_usage = line.split()[1].replace("%", "")  # Extraction de l'utilisation CPU
                logger.info(f"⚡ Utilisation CPU actuelle : {cpu_usage}%")
                return jsonify({"status": "success", "data": {"cpu_usage": float(cpu_usage)}, "errors": None}), 200
    except Exception as e:
        logger.error(f"❌ Erreur récupération CPU : {e}")
        return jsonify({"status": "fail", "data": None, "errors": {"message": str(e)}}), 500


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)