From 1e641f92254cddb6cf5c093439ff6696a108eb9b Mon Sep 17 00:00:00 2001 From: chriswin Date: Tue, 13 Jan 2026 23:18:01 +0100 Subject: [PATCH] crowdsec and traefik init --- docker-compose.yml | 5 +- services/crowdsec/crowdsec.yml | 23 ++ services/traefik/config/config.yml | 164 ++++++++++++++ services/traefik/config/traefik.yml | 67 ++++++ services/traefik/html/ban.html | 338 ++++++++++++++++++++++++++++ services/traefik/html/captcha.html | 338 ++++++++++++++++++++++++++++ services/traefik/traefik.yml | 96 ++++++++ services/watchtower/watchtower.yml | 26 --- 8 files changed, 1029 insertions(+), 28 deletions(-) create mode 100644 services/crowdsec/crowdsec.yml create mode 100644 services/traefik/config/config.yml create mode 100644 services/traefik/config/traefik.yml create mode 100644 services/traefik/html/ban.html create mode 100644 services/traefik/html/captcha.html create mode 100644 services/traefik/traefik.yml delete mode 100644 services/watchtower/watchtower.yml diff --git a/docker-compose.yml b/docker-compose.yml index 6495eb6..e4c4b9c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,12 +3,13 @@ # Here I will include all "child" docker compose files that I need. # The paths can relative to this file or absolue. I've used INCLUDE_PATH variable to make it more cofigurable. -# Whenever I need to remove some service then I can comment out the lines here. +# Whenever I need to remove some service then I can comment out the lines here. include: - path: - ${SERVICE_PATH}/caddy/caddy.yml + - ${SERVICE_PATH}/crowdsec/crowdsec.yml - ${SERVICE_PATH}/headscale/headscale.yml - - ${SERVICE_PATH}/watchtower/watchtower.yml + # - ${SERVICE_PATH}/traefik/traefik.yml env_file: ${SERVICE_PATH}/.env networks: diff --git a/services/crowdsec/crowdsec.yml b/services/crowdsec/crowdsec.yml new file mode 100644 index 0000000..a76c254 --- /dev/null +++ b/services/crowdsec/crowdsec.yml @@ -0,0 +1,23 @@ +services: + crowdsec: + extends: + file: ${TEMPLATES_PATH} + service: default + container_name: crowdsec + image: crowdsecurity/crowdsec:v1.7.4 + environment: + COLLECTIONS: crowdsecurity/traefik crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-generic-rules crowdsecurity/http-cve + CROWDSEC_BOUNCER_API_KEY: ${CROWDSEC_API_KEY} + CUSTOM_HOSTNAME: crowdsec + ports: + - 6061:8080 + - 6060:6060 + networks: + - ip4net + volumes: + - ${SERVICE_PATH}/crowdsec/config/acquis.yaml:/etc/crowdsec/acquis.yaml:ro + - ${SERVICE_PATH}/crowdsec/config:/etc/crowdsec + - ${SERVICE_PATH}/crowdsec/data:/var/lib/crowdsec/data + - /var/log/traefik:/var/log/crowdsec:ro + - /var/log/syslog:/var/log/syslog:ro + - /var/log/kern.log:/var/log/kern.log:ro diff --git a/services/traefik/config/config.yml b/services/traefik/config/config.yml new file mode 100644 index 0000000..6859554 --- /dev/null +++ b/services/traefik/config/config.yml @@ -0,0 +1,164 @@ +http: + middlewares: + + # Crowdsec + crowdsec-bouncer: + plugin: + crowdsec-bouncer-plugin: + enabled: true + logLevel: INFO + updateIntervalSeconds: 60 + crowdsecMode: stream + crowdsecAppsecEnabled: true + crowdsecAppsecHost: crowdsec:7422 + crowdsecLapiScheme: http + crowdsecLapiHost: crowdsec:8080 + # generated using "docker exec crowdsec cscli bouncers add crowdsecBouncer" + crowdseclapikey: {{ env "TRAEFIK_CROWDSEC_API_KEY" }} + forwardedHeadersTrustedIPs: + - 10.0.6.0/24 + clientTrustedIPs: + - 192.168.178.0/24 + captchaProvider: hcaptcha + captchaSiteKey: b2d20610-8dda-4f40-8688-7ca8e1e628f8 # found in hcaptcha account + captchaSecretKey: ES_9511d34bbec34dada169afad0a36991a + captchaGracePeriodSeconds: 1800 + captchaHTMLFilePath: /captcha.html + banHTMLFilePath: /ban.html + + routers: + authelia: + rule: "Host(`auth.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + audiobookshelf: + rule: "Host(`audiobookshelf.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + gitea: + rule: "Host(`gitea.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + headscale: + rule: "Host(`headscale.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + immich: + rule: "Host(`immich.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + lldap: + rule: "Host(`lldap.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + linkwarden: + rule: "Host(`linkwarden.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + mealie: + rule: "Host(`mealie.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + navidrome: + rule: "Host(`navidrome.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + ntfy: + rule: "Host(`ntfy.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + paperless: + rule: "Host(`paperless.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + pdf: + rule: "Host(`pdf.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + radicale: + rule: "Host(`radicale.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + rss: + rule: "Host(`rss.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + superset: + rule: "Host(`superset.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + vaultwarden: + rule: "Host(`vaultwarden.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + vikunja: + rule: "Host(`vikunja.{{ env "TRAEFIK_PUBLIC_DOMAIN" }}`)" + service: node + entryPoints: https + tls: {} + middlewares: crowdsec-bouncer@file + + services: + node: + loadBalancer: + servers: + - url: {{ env TRAEFIK_MAIN_SERVER_NODE_IP }} + +tls: + stores: + default: + defaultCertificate: + certFile: /etc/certs/server.crt + keyFile: /etc/certs/server.key + certificates: + - certFile: /etc/certs/server.crt + keyFile: /etc/certs/server.key + diff --git a/services/traefik/config/traefik.yml b/services/traefik/config/traefik.yml new file mode 100644 index 0000000..35c4d1d --- /dev/null +++ b/services/traefik/config/traefik.yml @@ -0,0 +1,67 @@ +api: + dashboard: true + +log: + level: "INFO" + +serversTransport: + insecureSkipVerify: true + +accessLog: + filePath: "/var/log/traefik/access.log" # location of traefik logs for crowdsec + format: json + bufferingSize: 100 # Configuring a buffer of 100 lines + filters: + statusCodes: + - "204-299" + - "400-499" + - "500-559" # logged status codes + +entryPoints: + http: + address: "[::]:80" # Create the HTTP entrypoint on port 80 + forwardedHeaders: + insecure: false + trustedIPs: + - "10.0.0.0/8" + - "192.168.178.0/16" + - "2a07:600:200:1::/64" + proxyProtocol: + insecure: false + trustedIPs: + - "10.0.0.0/8" + - "192.168.178.0/16" + - "2a07:600:200:1::/64" + http: + redirections: # HTTPS redirection (80 to 443) + entryPoint: + to: "https" # The target element + scheme: "https" # The redirection target scheme + permanent: true # The target element + + https: + address: "[::]:443" # Create the HTTPS entrypoint on port 443 + forwardedHeaders: + insecure: false + trustedIPs: + - "10.0.0.0/8" + - "192.168.178.0/16" + - "2a07:600:200:1::/64" + proxyProtocol: + insecure: false + trustedIPs: + - "10.0.0.0/8" + - "192.168.178.0/16" + - "2a07:600:200:1::/64" + +providers: + docker: + endpoint: "unix:///var/run/docker.sock" # Listen to the UNIX Docker socket + exposedByDefault: false + file: + directory: "/etc/traefik" # Link to the dynamic configuration + watch: true # Watch for modifications + providersThrottleDuration: "10" # Configuration reload frequency + +metrics: + prometheus: {} diff --git a/services/traefik/html/ban.html b/services/traefik/html/ban.html new file mode 100644 index 0000000..0fd982b --- /dev/null +++ b/services/traefik/html/ban.html @@ -0,0 +1,338 @@ + + + + + CrowdSec Captcha + + + + + + + +
+
+
+ +

CrowdSec Captcha

+
+
+
+
+
+
+

This security check has been powered by

+ + + + + + + + + + + + + + + + + + + + + CrowdSec + +
+
+
+ + + diff --git a/services/traefik/html/captcha.html b/services/traefik/html/captcha.html new file mode 100644 index 0000000..0fd982b --- /dev/null +++ b/services/traefik/html/captcha.html @@ -0,0 +1,338 @@ + + + + + CrowdSec Captcha + + + + + + + +
+
+
+ +

CrowdSec Captcha

+
+
+
+
+
+
+

This security check has been powered by

+ + + + + + + + + + + + + + + + + + + + + CrowdSec + +
+
+
+ + + diff --git a/services/traefik/traefik.yml b/services/traefik/traefik.yml new file mode 100644 index 0000000..9969069 --- /dev/null +++ b/services/traefik/traefik.yml @@ -0,0 +1,96 @@ +services: + traefik: + extends: + file: ${TEMPLATES_PATH} + service: default + image: traefik:v3.6.6 + container_name: traefik + ports: + - "80:80" + - "443:443" + - "8079:8080" + networks: + - ip4net + environment: + TRAEFIK_EMAIL: ${EMAIL} + TRAEFIK_PUBLIC_DOMAIN: ${PUBLIC_DOMAIN} + TRAEFIK_MAIN_SERVER_NODE_IP: ${MAIN_SERVER_NODE_IP} + TRAEFIK_CROWDSEC_API_KEY: ${CROWDSEC_API_KEY} + INFOMANIAK_ACCESS_TOKEN: ${INFOMANIAK_CERTIFICATE_ACCESS_TOKEN} + volumes: + - "/var/log/traefik/:/var/log/traefik/" + - "/var/run/docker.sock:/var/run/docker.sock:ro" + - "${SERVICE_PATH}/traefik/letsencrypt:/letsencrypt" + - "${SERVICE_PATH}/traefik/config:/etc/traefik" + - "${SERVICE_PATH}/traefik/certs:/etc/certs" + - "${SERVICE_PATH}/traefik/html/ban.html:/ban.html" + - "${SERVICE_PATH}/traefik/html/captcha.html:/captcha.html" + labels: + # Traefik + - "traefik.enable=true" + - "traefik.http.routers.traefik.service=api@internal" + - "traefik.http.routers.traefik.rule=Host(`traefik.${LOCAL_DOMAIN}`)" + - "traefik.http.routers.traefik.entrypoints=https" + - "traefik.http.routers.traefik.tls=true" + + # traefik-agent: + # extends: + # file: ${TEMPLATES_PATH} + # service: default + # image: hhftechnology/traefik-log-dashboard-agent:2.4.0 + # container_name: traefik-log-dashboard-agent + # networks: + # - ip4net + # ports: + # - "8078:5000" + # volumes: + # - "/var/log/crowdsec/:/logs:ro" + # - "${SERVICE_PATH}/traefik/log-dashboard/positions:/data" + # environment: + # TRAEFIK_LOG_DASHBOARD_ACCESS_PATH: /logs/traefik.log + # TRAEFIK_LOG_DASHBOARD_AUTH_TOKEN: ${TRAEFIK_DASHBOARD_TOKEN} + # TRAEFIK_LOG_DASHBOARD_SYSTEM_MONITORING: true + # TRAEFIK_LOG_DASHBOARD_LOG_FORMAT: json + # healthcheck: + # test: + # [ + # "CMD", + # "wget", + # "--no-verbose", + # "--tries=1", + # "--spider", + # "http://localhost:5000/api/logs/status", + # ] + # interval: 2m + # timeout: 10s + # retries: 3 + # start_period: 30s + # + # traefik-dashboard: + # extends: + # file: ${TEMPLATES_PATH} + # service: default + # image: hhftechnology/traefik-log-dashboard:2.4.0 + # container_name: traefik-log-dashboard + # networks: + # - ip4net + # ports: + # - "8077:3000" + # volumes: + # - ./data/dashboard:/app/data + # - "${SERVICE_PATH}/traefik/log-dashboard/dashboard:/app/data" + # - "${SERVICE_PATH}/traefik/log-dashboard/positions:/data" + # environment: + # AGENT_API_URL: http://192.168.178.35:8078 + # AGENT_API_TOKEN: ${TRAEFIK_DASHBOARD_TOKEN} + # # Display Configuration + # NEXT_PUBLIC_SHOW_DEMO_PAGE: false + # depends_on: + # traefik-agent: + # condition: service_healthy + # labels: + # # traefik + # - "traefik.enable=true" + # - "traefik.http.routers.traefik-log-dashboard.rule=Host(`traefik-dashboard.${LOCAL_DOMAIN}`)" + # - "traefik.http.routers.traefik-log-dashboard.entrypoints=https" + # - "traefik.http.routers.traefik-log-dashboard.tls=true" diff --git a/services/watchtower/watchtower.yml b/services/watchtower/watchtower.yml deleted file mode 100644 index e623160..0000000 --- a/services/watchtower/watchtower.yml +++ /dev/null @@ -1,26 +0,0 @@ -services: - watchtower: - extends: - file: ${TEMPLATES_PATH} - service: default - image: containrrr/watchtower:latest - container_name: watchtower - environment: - - WATCHTOWER_CLEANUP=true - - WATCHTOWER_POLL_INTERVAL=43200 # 12h - - WATCHTOWER_INCLUDE_RESTARTING=true - - WATCHTOWER_LABEL_ENABLE=true - - WATCHTOWER_HTTP_API_METRICS=true - - WATCHTOWER_HTTP_API_TOKEN=mytoken - - WATCHTOWER_HTTP_API_UPDATE=true - - WATCHTOWER_HTTP_API_PERIODIC_POLLS=true - ports: - - 1003:8080 - networks: - - ip4net - volumes: - # - ${SERVICE_PATH}/watchtower/config:/config.json - - /var/run/docker.sock:/var/run/docker.sock - labels: - # Watchtower - - "com.centurylinklabs.watchtower.enable=true"