Integrations

Intégration de Vault pour la gestion des clés API CaptchaAI

Une clé API n'a pas besoin d'exister ailleurs que dans la mémoire du processus qui l'utilise : HashiCorp Vault la chiffre au repos, la délivre au runtime contre une identité authentifiée et journalise chaque lecture. Votre clé API CaptchaAI sort du dépôt Git et des variables d'environnement partagées ; elle devient un secret remplaçable sans toucher au code.

Le chemin tient en quatre étapes : écrire le secret, écrire la stratégie de lecture, lire la clé depuis Python et Node.js, puis la faire tourner sans redéploiement.

Avant de commencer

  • Un serveur HashiCorp Vault (auto-hébergé ou HCP Vault)
  • L'accès à la CLI ou à l'API de Vault
  • Une clé API CaptchaAI
  • Python 3.8+ ou Node.js 18+

Ce que Vault change pour vos workers CAPTCHA

Sans Vault Avec Vault
Clé API dans le fichier .env ou dans le code Clé stockée chiffrée dans Vault
Clé partagée via Slack ou par e-mail Accès via API authentifiée
Aucune piste d'audit des accès Chaque lecture journalisée avec l'identité
Rotation manuelle des clés Rotation automatisable
Même clé dans tous les environnements Une clé par environnement, avec stratégies

La dernière ligne est la plus sous-estimée : tant qu'une seule clé circule entre dev, staging et production, la moindre fuite oblige à tout arrêter. Avec des chemins distincts, vous révoquez un périmètre à la fois.

Étape 1 : écrire la clé API dans Vault

Activez le moteur KV v2 s'il ne l'est pas déjà, puis stockez la clé sous un chemin dédié :

# Enable the KV secrets engine (if not already enabled)
vault secrets enable -path=secret kv-v2

# Store the CaptchaAI API key
vault kv put secret/captchaai api_key="YOUR_API_KEY"

# Verify
vault kv get secret/captchaai

KV v2 conserve un historique des versions : après une rotation, l'ancienne valeur reste consultable le temps de vérifier que tous les workers ont basculé.

Étape 2 : restreindre les workers à la lecture seule

Un worker n'a besoin que de lire. Écrivez une stratégie qui n'accorde rien d'autre :

# captcha-worker-policy.hcl
path "secret/data/captchaai" {
  capabilities = ["read"]
}

path "secret/metadata/captchaai" {
  capabilities = ["read"]
}

Appliquez-la :

vault policy write captcha-worker captcha-worker-policy.hcl

Un token porteur de cette seule stratégie ne peut ni écrire, ni supprimer, ni lister d'autres secrets. L'audit devient exploitable : une lecture inattendue désigne un worker précis.

Étape 3 : lire la clé au runtime en Python

Le client hvac lit le secret au démarrage, puis le relit périodiquement pour capter une rotation.

# vault_solver.py
import os
import time
import hvac
import requests

# Connect to Vault
vault_client = hvac.Client(
    url=os.environ.get("VAULT_ADDR", "http://127.0.0.1:8200"),
    token=os.environ.get("VAULT_TOKEN"),
)

def get_api_key():
    """Retrieve CaptchaAI API key from Vault."""
    secret = vault_client.secrets.kv.v2.read_secret_version(
        path="captchaai",
        mount_point="secret",
    )
    return secret["data"]["data"]["api_key"]

class CaptchaSolver:
    """CAPTCHA solver with Vault-managed credentials."""

    def __init__(self):
        self.api_key = get_api_key()
        self.session = requests.Session()
        self._key_fetched_at = time.time()
        self._key_refresh_interval = 3600  # Re-fetch key hourly

    def _refresh_key_if_needed(self):
        """Periodically refresh the key from Vault."""
        if time.time() - self._key_fetched_at > self._key_refresh_interval:
            self.api_key = get_api_key()
            self._key_fetched_at = time.time()

    def solve(self, sitekey, pageurl):
        """Solve reCAPTCHA v2 using Vault-managed key."""
        self._refresh_key_if_needed()

        # Submit
        resp = self.session.get("https://ocr.captchaai.com/in.php", params={
            "key": self.api_key,
            "method": "userrecaptcha",
            "googlekey": sitekey,
            "pageurl": pageurl,
            "json": "1",
        })
        result = resp.json()

        if result.get("status") != 1:
            raise Exception(f"Submit failed: {result.get('request')}")

        task_id = result["request"]
        time.sleep(15)

        for _ in range(25):
            poll = self.session.get("https://ocr.captchaai.com/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": task_id,
                "json": "1",
            })
            poll_result = poll.json()

            if poll_result.get("status") == 1:
                return poll_result["request"]
            if poll_result.get("request") != "CAPCHA_NOT_READY":
                raise Exception(f"Error: {poll_result.get('request')}")

            time.sleep(5)

        raise Exception("Timeout")

# Usage
solver = CaptchaSolver()
token = solver.solve(
    "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
    "https://www.google.com/recaptcha/api2/demo"
)
print(f"Token: {token[:30]}...")

La clé n'est jamais écrite sur disque. L'intervalle d'une heure fixe le délai maximal entre une rotation et sa prise en compte par un worker déjà lancé.

Étape 4 : la même logique en Node.js

Sans bibliothèque dédiée, un appel HTTP sur l'endpoint v1/secret/data/... avec l'en-tête X-Vault-Token suffit. Le token Vault reste injecté par l'environnement d'exécution, jamais par le dépôt.

// vault_solver.js
const axios = require('axios');

const VAULT_ADDR = process.env.VAULT_ADDR || 'http://127.0.0.1:8200';
const VAULT_TOKEN = process.env.VAULT_TOKEN;

async function getApiKey() {
  const resp = await axios.get(
    `${VAULT_ADDR}/v1/secret/data/captchaai`,
    { headers: { 'X-Vault-Token': VAULT_TOKEN } }
  );
  return resp.data.data.data.api_key;
}

class CaptchaSolver {
  constructor() {
    this.apiKey = null;
    this.keyFetchedAt = 0;
    this.refreshInterval = 3600000; // 1 hour
  }

  async init() {
    this.apiKey = await getApiKey();
    this.keyFetchedAt = Date.now();
  }

  async refreshKeyIfNeeded() {
    if (Date.now() - this.keyFetchedAt > this.refreshInterval) {
      this.apiKey = await getApiKey();
      this.keyFetchedAt = Date.now();
    }
  }

  async solve(sitekey, pageurl) {
    await this.refreshKeyIfNeeded();

    const submit = await axios.get('https://ocr.captchaai.com/in.php', {
      params: {
        key: this.apiKey, method: 'userrecaptcha',
        googlekey: sitekey, pageurl, json: '1',
      },
    });

    if (submit.data.status !== 1) throw new Error(submit.data.request);
    const taskId = submit.data.request;

    await new Promise(r => setTimeout(r, 15000));

    for (let i = 0; i < 25; i++) {
      const poll = await axios.get('https://ocr.captchaai.com/res.php', {
        params: { key: this.apiKey, action: 'get', id: taskId, json: '1' },
      });

      if (poll.data.status === 1) return poll.data.request;
      if (poll.data.request !== 'CAPCHA_NOT_READY') throw new Error(poll.data.request);
      await new Promise(r => setTimeout(r, 5000));
    }
    throw new Error('Timeout');
  }
}

(async () => {
  const solver = new CaptchaSolver();
  await solver.init();

  const token = await solver.solve(
    '6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-',
    'https://www.google.com/recaptcha/api2/demo'
  );
  console.log(`Token: ${token.slice(0, 30)}...`);
})();

Choisir la méthode d'authentification Vault

Le VAULT_TOKEN des deux exemples convient au développement. En production, la méthode dépend de l'endroit où tourne le worker, pas du langage.

Méthode Adaptée à Configuration
Token Développement, CI/CD Variable d'environnement VAULT_TOKEN
AppRole Services en production Role ID + Secret ID
Kubernetes Charges de travail K8s JWT du service account
AWS IAM Workers EC2 ou Lambda Rôle d'instance

AppRole : le choix par défaut en production

Remplacez l'initialisation du client par une ouverture de session AppRole :

# AppRole authentication — no static token needed
vault_client = hvac.Client(url=os.environ["VAULT_ADDR"])
vault_client.auth.approle.login(
    role_id=os.environ["VAULT_ROLE_ID"],
    secret_id=os.environ["VAULT_SECRET_ID"],
)

# Now read the secret
secret = vault_client.secrets.kv.v2.read_secret_version(path="captchaai")
api_key = secret["data"]["data"]["api_key"]

AppRole supprime le token statique de longue durée : le worker échange un Role ID et un Secret ID contre un token court, renouvelable. Si un conteneur est compromis, la fenêtre d'exposition se compte en minutes.

Dépannage

Problème Cause Correctif
403 Forbidden renvoyé par Vault La stratégie n'autorise pas la lecture du chemin Vérifiez les chemins dans captcha-worker-policy.hcl
VAULT_TOKEN expiré Durée de vie du token dépassée Passez à AppRole et à des tokens renouvelables
La clé ne change pas après rotation Intervalle de rafraîchissement trop long Réduisez _key_refresh_interval
Vault injoignable au démarrage Incident réseau, ou serveur encore scellé Gardez la clé en cache mémoire et prévoyez un fallback
Erreur de clé invalide côté CaptchaAI Ancienne clé révoquée avant la bascule des workers Révoquez seulement après le dernier cycle de rafraîchissement

Scénario : trois environnements dans une agence

Une agence d'automatisation lyonnaise exécute ses workers sur des instances OVHcloud, avec dev, staging et production séparés. Le montage tient en trois chemins et trois stratégies : secret/captchaai/dev, secret/captchaai/staging, secret/captchaai/prod, chacun lisible par un AppRole distinct.

Deux effets pratiques. Une erreur de configuration en développement ne met plus en jeu la clé de production. Et quand un client demande qui a eu accès à quoi, les logs d'audit Vault répondent, horodatés par identité — utile dans une revue RGPD, où la traçabilité des accès aux secrets compte parmi les mesures techniques à décrire.

Côté capacité, rien ne change : la facturation CaptchaAI repose sur les threads du plan. Passer de BASIC ($15/mois, 5 threads) à ADVANCE ($90/mois, 50 threads) ne modifie rien dans Vault.

Faire tourner la clé sans redéployer

C'est le bénéfice qui justifie tout le montage. Quatre gestes suffisent, workers en marche.

  1. Générez une nouvelle clé API dans le tableau de bord CaptchaAI
  2. Écrivez-la dans Vault : vault kv put secret/captchaai api_key="NEW_KEY"
  3. Les workers récupèrent la nouvelle valeur au prochain cycle de rafraîchissement
  4. Révoquez l'ancienne clé dans le tableau de bord une fois tous les workers passés à la nouvelle

Aucune modification de code, aucun redéploiement : la rotation devient une opération sur un secret, pas une mise en production.

FAQ

Vault ajoute-t-il de la latence à chaque résolution de CAPTCHA ?

Non. La clé est lue au démarrage, puis relue une fois par heure dans les exemples ci-dessus. Le chemin critique d'une résolution ne contient aucun appel à Vault.

Faut-il un secret KV ou un secret dynamique pour une clé API CaptchaAI ?

Un secret KV. Les secrets dynamiques génèrent des identifiants à la demande auprès d'un système compatible (base de données, cloud) ; une clé API CaptchaAI est une valeur externe que vous écrivez vous-même. KV v2 et son versionnage suffisent.

Comment savoir qui a lu la clé lors d'un audit ?

Activez un audit device Vault (file ou syslog) : chaque lecture y apparaît avec l'identité, le chemin et l'horodatage. Croisez ces entrées avec les Role ID de vos AppRoles pour remonter au worker.

Que faire si Vault est scellé au moment où un worker redémarre ?

Il doit échouer explicitement plutôt que démarrer sans clé : journalisez l'erreur, sortez avec un code non nul, laissez l'orchestrateur relancer. Un processus déjà vivant garde sa clé en mémoire, jamais sur disque.

Articles connexes

Prochaines étapes

Récupérez votre clé API, écrivez-la dans Vault et faites-la lire par un AppRole dédié.

Guides associés :

Les commentaires sont désactivés pour cet article.