chore: add helm chart and docker
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
{{- define "gestion-traefik.labels" -}}
|
||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||
app.kubernetes.io/name: {{ .Chart.Name }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "gestion-traefik.certResolver" -}}
|
||||
{{- if eq .Values.tls.mode "letsencrypt" -}}
|
||||
letsencrypt
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
# HTTP → HTTPS redirect
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: gestion-http
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "gestion-traefik.labels" . | nindent 4 }}
|
||||
spec:
|
||||
entryPoints:
|
||||
- web
|
||||
routes:
|
||||
- match: Host(`{{ .Values.domain }}`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: redirect-https
|
||||
services:
|
||||
- name: noop@internal
|
||||
kind: TraefikService
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: redirect-https
|
||||
namespace: {{ .Release.Namespace }}
|
||||
spec:
|
||||
redirectScheme:
|
||||
scheme: https
|
||||
permanent: true
|
||||
---
|
||||
# HTTPS — routes principales
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: gestion-https
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "gestion-traefik.labels" . | nindent 4 }}
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
# API backend — WAF + headers + rate limit
|
||||
- match: Host(`{{ .Values.domain }}`) && (PathPrefix(`/api/`) || PathPrefix(`/webhook/`))
|
||||
kind: Rule
|
||||
priority: 20
|
||||
middlewares:
|
||||
- name: waf-chain
|
||||
services:
|
||||
- name: {{ .Values.backend.serviceName }}
|
||||
namespace: {{ .Values.servicesNamespace }}
|
||||
port: {{ .Values.backend.servicePort }}
|
||||
|
||||
# Fichiers uploadés — WAF désactivé dans les règles Coraza pour ce path
|
||||
- match: Host(`{{ .Values.domain }}`) && PathPrefix(`/uploads/`)
|
||||
kind: Rule
|
||||
priority: 15
|
||||
middlewares:
|
||||
- name: security-headers
|
||||
services:
|
||||
- name: {{ .Values.backend.serviceName }}
|
||||
namespace: {{ .Values.servicesNamespace }}
|
||||
port: {{ .Values.backend.servicePort }}
|
||||
|
||||
# Frontend SPA — WAF + headers + rate limit
|
||||
- match: Host(`{{ .Values.domain }}`)
|
||||
kind: Rule
|
||||
priority: 1
|
||||
middlewares:
|
||||
- name: waf-chain
|
||||
services:
|
||||
- name: {{ .Values.frontend.serviceName }}
|
||||
namespace: {{ .Values.servicesNamespace }}
|
||||
port: {{ .Values.frontend.servicePort }}
|
||||
|
||||
tls:
|
||||
{{- if eq .Values.tls.mode "letsencrypt" }}
|
||||
certResolver: letsencrypt
|
||||
{{- else }}
|
||||
secretName: {{ .Values.tls.secretName }}
|
||||
{{- end }}
|
||||
domains:
|
||||
- main: {{ .Values.domain }}
|
||||
@@ -0,0 +1,13 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: waf-chain
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "gestion-traefik.labels" . | nindent 4 }}
|
||||
spec:
|
||||
chain:
|
||||
middlewares:
|
||||
- name: rate-limit
|
||||
- name: coraza-waf
|
||||
- name: security-headers
|
||||
@@ -0,0 +1,27 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: security-headers
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "gestion-traefik.labels" . | nindent 4 }}
|
||||
spec:
|
||||
headers:
|
||||
stsSeconds: 63072000
|
||||
stsIncludeSubdomains: true
|
||||
stsPreload: true
|
||||
frameDeny: true
|
||||
contentTypeNosniff: true
|
||||
referrerPolicy: "strict-origin-when-cross-origin"
|
||||
permissionsPolicy: "geolocation=(), microphone=(), camera=(), payment=()"
|
||||
customResponseHeaders:
|
||||
X-XSS-Protection: "0"
|
||||
Content-Security-Policy: >-
|
||||
default-src 'self';
|
||||
script-src 'self' 'unsafe-inline';
|
||||
style-src 'self' 'unsafe-inline';
|
||||
img-src 'self' data: blob: https:;
|
||||
font-src 'self' data:;
|
||||
connect-src 'self' https:;
|
||||
frame-ancestors 'self';
|
||||
Server: ""
|
||||
@@ -0,0 +1,17 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: rate-limit
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "gestion-traefik.labels" . | nindent 4 }}
|
||||
spec:
|
||||
rateLimit:
|
||||
average: {{ .Values.rateLimit.average }}
|
||||
burst: {{ .Values.rateLimit.burst }}
|
||||
period: 1m
|
||||
sourceCriterion:
|
||||
ipStrategy:
|
||||
# depth=1 : utilise la première IP de X-Forwarded-For
|
||||
# (Traefik pose lui-même ce header depuis la vraie IP TCP, non spoofable)
|
||||
depth: 1
|
||||
@@ -0,0 +1,61 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: coraza-waf
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "gestion-traefik.labels" . | nindent 4 }}
|
||||
spec:
|
||||
plugin:
|
||||
coraza-traefik:
|
||||
directives:
|
||||
# -------------------------------------------------------
|
||||
# Moteur et body inspection
|
||||
# -------------------------------------------------------
|
||||
- "SecRuleEngine On"
|
||||
- "SecRequestBodyAccess On"
|
||||
- "SecRequestBodyLimit 104857600"
|
||||
- "SecRequestBodyInMemoryLimit 131072"
|
||||
- "SecResponseBodyAccess Off"
|
||||
- "SecDebugLogLevel 0"
|
||||
|
||||
# -------------------------------------------------------
|
||||
# OWASP CRS — inclus dans le plugin Coraza
|
||||
# -------------------------------------------------------
|
||||
- "Include @coraza.conf-recommended"
|
||||
- "Include @crs-setup.conf.example"
|
||||
- "Include @owasp_crs/*.conf"
|
||||
|
||||
# -------------------------------------------------------
|
||||
# Niveau de paranoia {{ .Values.waf.paranoia }}
|
||||
# -------------------------------------------------------
|
||||
- "SecAction \"id:900000,phase:1,nolog,pass,t:none,setvar:tx.paranoia_level={{ .Values.waf.paranoia }}\""
|
||||
- "SecAction \"id:900001,phase:1,nolog,pass,t:none,setvar:tx.inbound_anomaly_score_threshold={{ .Values.waf.anomalyInbound }}\""
|
||||
- "SecAction \"id:900002,phase:1,nolog,pass,t:none,setvar:tx.outbound_anomaly_score_threshold={{ .Values.waf.anomalyOutbound }}\""
|
||||
|
||||
# -------------------------------------------------------
|
||||
# Suppressions CRS pour les routes légitimes
|
||||
# -------------------------------------------------------
|
||||
- "SecRuleRemoveById 932235"
|
||||
- "SecRuleRemoveById 911100"
|
||||
|
||||
- "SecRule REQUEST_URI \"@streq /api/v2/admin/protected/products\" \"id:399001,phase:2,nolog,pass,ctl:ruleRemoveById=932235\""
|
||||
- "SecRule REQUEST_URI \"@streq /api/v2/admin/protected/products\" \"id:399002,phase:2,nolog,pass,ctl:ruleRemoveById=920120,ctl:ruleRemoveById=920121\""
|
||||
- "SecRule REQUEST_URI \"@streq /api/v1/panier/remove\" \"id:399010,phase:1,nolog,pass,ctl:ruleRemoveById=911100,ctl:ruleRemoveById=920350\""
|
||||
- "SecRule REQUEST_URI \"@streq /api/v1/panier/clear\" \"id:399011,phase:1,nolog,pass,ctl:ruleRemoveById=911100,ctl:ruleRemoveById=920350\""
|
||||
|
||||
# Désactiver le WAF sur les uploads (fichiers statiques en lecture)
|
||||
- "SecRule REQUEST_URI \"@beginsWith /uploads/\" \"id:1000,phase:1,pass,nolog,ctl:ruleEngine=Off\""
|
||||
|
||||
# -------------------------------------------------------
|
||||
# IP banning automatique sur attaques détectées
|
||||
# Note : en-mémoire par pod — pour un ban distribué utiliser Redis + Coraza persistence
|
||||
# -------------------------------------------------------
|
||||
- "SecRule TX:SQL_INJECTION_SCORE \"@ge 5\" \"id:100001,phase:2,deny,status:403,log,msg:'SQL Injection detected',setvar:'ip.banned=1',expirevar:'ip.banned=172800'\""
|
||||
- "SecRule TX:XSS_SCORE \"@ge 5\" \"id:100010,phase:2,deny,status:403,log,msg:'XSS detected',setvar:'ip.banned=1',expirevar:'ip.banned=172800'\""
|
||||
- "SecRule TX:RCE_SCORE \"@ge 5\" \"id:100020,phase:2,deny,status:403,log,msg:'RCE detected',setvar:'ip.banned=1',expirevar:'ip.banned=259200'\""
|
||||
- "SecRule TX:LFI_SCORE \"@ge 5\" \"id:100030,phase:2,deny,status:403,log,msg:'LFI detected',setvar:'ip.banned=1',expirevar:'ip.banned=172800'\""
|
||||
- "SecRule TX:INBOUND_ANOMALY_SCORE \"@ge 20\" \"id:100060,phase:2,deny,status:403,log,msg:'Critical anomaly score',setvar:'ip.banned=1',expirevar:'ip.banned=172800'\""
|
||||
|
||||
- "SecRule IP:BANNED \"@eq 1\" \"id:100000,phase:1,deny,status:403,log,msg:'IP is banned'\""
|
||||
- "SecRule IP:REPUTATION_SCORE \"@ge 100\" \"id:100099,phase:1,deny,status:403,log,msg:'Critical reputation score',setvar:'ip.blocked=1',expirevar:'ip.blocked=86400'\""
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: TLSStore
|
||||
metadata:
|
||||
name: default
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "gestion-traefik.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if eq .Values.tls.mode "secret" }}
|
||||
defaultCertificate:
|
||||
secretName: {{ .Values.tls.secretName }}
|
||||
{{- end }}
|
||||
defaultGeneratedCert:
|
||||
resolver: {{ include "gestion-traefik.certResolver" . | default "letsencrypt" }}
|
||||
domain:
|
||||
main: {{ .Values.domain }}
|
||||
Reference in New Issue
Block a user