configure crowdsec captcha with cloudflare turnstile

This commit is contained in:
2025-12-03 15:07:37 +01:00
parent 17a0f23bbb
commit f4cb04c9c9
7 changed files with 261 additions and 74 deletions

View File

@@ -327,6 +327,7 @@
# format: json
access:
enabled: true
timezone: Europe/Paris
# format: json
podSecurityContext:
runAsGroup: 65532

View File

@@ -5,77 +5,6 @@
# debugger: on_failed
tasks:
- name: Récupérer le nom du pod CrowdSec LAPI
kubernetes.core.k8s_info:
kind: Pod
namespace: tools
label_selectors:
- k8s-app = crowdsec
- type = lapi
register: crowdsec_lapi_pods
- name: Vérifier qu'un pod a été trouvé
assert:
that: crowdsec_lapi_pods.resources | length > 0
fail_msg: "Aucun pod CrowdSec LAPI trouvé dans le namespace 'tools' avec les labels 'k8s-app=crowdsec, type=lapi'."
- name: Définir le nom du pod CrowdSec LAPI
set_fact:
crowdsec_lapi_pod_name: "{{ crowdsec_lapi_pods.resources[0].metadata.name }}"
- name: Récupérer la clé API du bouncer CrowdSec
kubernetes.core.k8s_exec:
namespace: tools
pod: "{{ crowdsec_lapi_pod_name }}"
container: crowdsec-lapi
command: >
cscli bouncers add traefik-plugin
register: bouncer_key_result
ignore_errors: yes
- name: Supprimer le bouncer existant en cas d'échec
kubernetes.core.k8s_exec:
namespace: tools
pod: "{{ crowdsec_lapi_pod_name }}"
container: crowdsec-lapi
command: >
cscli bouncers delete traefik-plugin
when: bouncer_key_result.failed
- name: Réessayer de récupérer la clé API
kubernetes.core.k8s_exec:
namespace: tools
pod: "{{ crowdsec_lapi_pod_name }}"
container: crowdsec-lapi
command: >
cscli bouncers add traefik-plugin
register: bouncer_key_result
when: bouncer_key_result.failed
- name: Créer le Middleware Traefik pour CrowdSec
kubernetes.core.k8s:
state: present
definition:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: crowdsec
namespace: kube-system
spec:
plugin:
crowdsec-bouncer:
enabled: true
crowdsecMode: stream
crowdsecLapiScheme: http
crowdsecLapiHost: crowdsec-service.tools.svc.cluster.local:8080
crowdsecLapiKey: "{{ bouncer_key_result.stdout_lines[2].strip() }}"
htttTimeoutSeconds: 60
crowdsecAppsecEnabled: false
crowdsecAppsecHost: crowdsec:7422
crowdsecAppsecFailureBlock: true
crowdsecAppsecUnreachableBlock: true
forwardedHeadersTrustedIPs:
- 10.0.10.23/32
- 10.0.20.0/24
clientTrustedIPs:
- 192.168.1.0/24
- name: Setup crowdsec middleware for traefik
include_role:
name: crowdsec

View File

@@ -0,0 +1 @@
traefik_pvc_name: traefik

View File

@@ -0,0 +1,94 @@
---
- name: Inject captcha.html into Traefik PVC
block:
# ---------------------
# Scale to 0
# ---------------------
- name: Scale Traefik to 0
kubernetes.core.k8s_scale:
api_version: apps/v1
kind: Deployment
namespace: kube-system
name: traefik
replicas: 0
# ---------------------
# Create Job
# ---------------------
- name: Deploy captcha injection Job
kubernetes.core.k8s:
state: present
namespace: kube-system
definition:
apiVersion: batch/v1
kind: Job
metadata:
name: inject-captcha
spec:
backoffLimit: 0
template:
spec:
restartPolicy: Never
volumes:
- name: traefik-data
persistentVolumeClaim:
claimName: "{{ traefik_pvc_name }}"
containers:
- name: write-captcha
image: alpine:3.20
command:
- /bin/sh
- -c
- |
echo "Writing captcha.html into PVC..."
cat << 'EOF' > /data/captcha.html
{{ lookup('template', 'captcha.html.j2') | indent(20) }}
EOF
volumeMounts:
- name: traefik-data
mountPath: /data
# ---------------------
# Wait for job success
# ---------------------
- name: Wait for Job completion
kubernetes.core.k8s_info:
api_version: batch/v1
kind: Job
name: inject-captcha
namespace: kube-system
register: job_status
until: job_status.resources[0].status.succeeded | default(0) | int > 0
retries: 20
delay: 5
# ---------------------
# Clean Job
# ---------------------
- name: Remove captcha injection Job
kubernetes.core.k8s:
state: absent
api_version: batch/v1
kind: Job
name: inject-captcha
namespace: kube-system
rescue:
- name: Log failure
ansible.builtin.debug:
msg: "An error occurred during captcha injection. Traefik will still be scaled back up."
always:
# ---------------------
# Ensure Traefik is scaled back to 1 NO MATTER WHAT
# ---------------------
- name: Ensure Traefik is scaled back to 1
kubernetes.core.k8s_scale:
api_version: apps/v1
kind: Deployment
namespace: kube-system
name: traefik
replicas: 1
wait: yes
wait_timeout: 300

View File

@@ -0,0 +1,143 @@
- name: Créer le ServiceAccount pour l'authentification Vault
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: ServiceAccount
metadata:
name: factory-ansible-tool-crowdsec-traefik-plugin
namespace: kube-system
wait: yes
wait_timeout: 30
- name: Créer la ressource VaultAuth
kubernetes.core.k8s:
state: present
definition:
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultAuth
metadata:
name: factory-ansible-tool-crowdsec
namespace: kube-system
spec:
method: kubernetes
mount: kubernetes
kubernetes:
role: factory_crowdsec_conf
serviceAccount: factory-ansible-tool-crowdsec-traefik-plugin
audiences:
- vault
wait: yes
wait_timeout: 30
- name: Créer la ressource VaultStaticSecret
kubernetes.core.k8s:
state: present
definition:
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: factory-ansible-tool-crowdsec-turnstile-secret
namespace: kube-system
spec:
type: kv-v2
mount: kvv2
path: cms/factory/turnstile
destination:
name: factory-ansible-tool-crowdsec-traefik-plugin-captcha-params
create: true
refreshAfter: 30s
vaultAuthRef: factory-ansible-tool-crowdsec
wait: yes
wait_timeout: 30
- name: Récupérer le secret Kubernetes
kubernetes.core.k8s_info:
kind: Secret
name: factory-ansible-tool-crowdsec-traefik-plugin-captcha-params
namespace: kube-system
register: crowdsec_captcha_secret
- name: Récupérer le nom du pod CrowdSec LAPI
kubernetes.core.k8s_info:
kind: Pod
namespace: tools
label_selectors:
- k8s-app = crowdsec
- type = lapi
register: crowdsec_lapi_pods
- name: Vérifier qu'un pod a été trouvé
assert:
that: crowdsec_lapi_pods.resources | length > 0
fail_msg: "Aucun pod CrowdSec LAPI trouvé dans le namespace 'tools' avec les labels 'k8s-app=crowdsec, type=lapi'."
- name: Définir le nom du pod CrowdSec LAPI
set_fact:
crowdsec_lapi_pod_name: "{{ crowdsec_lapi_pods.resources[0].metadata.name }}"
- name: Récupérer la clé API du bouncer CrowdSec
kubernetes.core.k8s_exec:
namespace: tools
pod: "{{ crowdsec_lapi_pod_name }}"
container: crowdsec-lapi
command: >
cscli bouncers add traefik-plugin
register: bouncer_key_result
ignore_errors: yes
- name: Supprimer le bouncer existant en cas d'échec
kubernetes.core.k8s_exec:
namespace: tools
pod: "{{ crowdsec_lapi_pod_name }}"
container: crowdsec-lapi
command: >
cscli bouncers delete traefik-plugin
when: bouncer_key_result.failed
- name: Réessayer de récupérer la clé API
kubernetes.core.k8s_exec:
namespace: tools
pod: "{{ crowdsec_lapi_pod_name }}"
container: crowdsec-lapi
command: >
cscli bouncers add traefik-plugin
register: bouncer_key_result
when: bouncer_key_result.failed
- name: Inject captcha.html into Traefik PVC
include_tasks: inject_captcha_html.yml
- name: Créer le Middleware Traefik pour CrowdSec
kubernetes.core.k8s:
state: present
definition:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: crowdsec
namespace: kube-system
spec:
plugin:
crowdsec-bouncer:
enabled: true
logLevel: DEBUG
crowdsecMode: stream
crowdsecLapiScheme: http
crowdsecLapiHost: crowdsec-service.tools.svc.cluster.local:8080
crowdsecLapiKey: "{{ bouncer_key_result.stdout_lines[2].strip() }}"
htttTimeoutSeconds: 60
crowdsecAppsecEnabled: false
crowdsecAppsecHost: crowdsec:7422
crowdsecAppsecFailureBlock: true
crowdsecAppsecUnreachableBlock: true
forwardedHeadersTrustedIPs:
- 10.0.10.23/32
- 10.0.20.0/24
clientTrustedIPs:
- 192.168.1.0/24
- 10.42.0.0/16
captchaProvider: turnstile
captchaSiteKey: "{{ crowdsec_captcha_secret.resources[0].data.sitekey | b64decode }}"
captchaSecretKey: "{{ crowdsec_captcha_secret.resources[0].data.secret | b64decode }}"
captchaHTMLFilePath: "/data/captcha.html"

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Captcha verification</title>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
</head>
<body>
<form method="POST">
<div class="cf-turnstile"
data-sitekey="{{ crowdsec_captcha_secret.resources[0].data.sitekey | b64decode }}"
data-theme="auto"
data-size="normal">
</div>
<button type="submit">Valider</button>
</form>
</body>
</html>

View File

@@ -71,6 +71,7 @@ module "cf_arcodange_cms_token" {
"zone:Zone Settings Write",
"zone:DNS Write",
"account:Cloudflare Tunnel Write",
"account:Turnstile Sites Write",
]
}
}