diff --git a/defaults/settings.yml.default b/defaults/settings.yml.default index 5af5a6f..b796101 100644 --- a/defaults/settings.yml.default +++ b/defaults/settings.yml.default @@ -9,6 +9,7 @@ dynamic_ip: false data_folder: /mnt/storage tz: "Europe/Berlin" notifications_email: example@mail.com +use_crowdsec: true cloudflare: tag_ddns: latest tag_companion: latest @@ -176,7 +177,11 @@ speedtesttracker: folder: /opt/speedtesttracker subdomain: speedtesttracker crowdsec: + tag: latest-debian folder: /opt/crowdsec subdomain: crowdsec + console_enrollment_key: console_enrollment_key + firewall_bouncer_key: firewall_bouncer_key + traefik_bouncer_key: traefik_bouncer_key tinymotd: folder: /opt/tinymotd diff --git a/roles/crowdsec/tasks/config/main.yml b/roles/crowdsec/tasks/config/main.yml new file mode 100644 index 0000000..3faed0b --- /dev/null +++ b/roles/crowdsec/tasks/config/main.yml @@ -0,0 +1,19 @@ +--- +- name: Wait for CrowdSec container to be healthy + become: true + community.docker.docker_container_info: + name: crowdsec + register: crowdsec_info + until: crowdsec_info.container.State.Running + retries: 10 + delay: 3 + +- name: Enroll CrowdSec console + community.docker.docker_container_exec: + container: crowdsec + command: "cscli console enroll {{ crowdsec.console_enrollment_key }}" + +- name: Enroll CrowdSec Firewall Bouncer + community.docker.docker_container_exec: + container: crowdsec + command: "crowdsec cscli bouncers add firewall-bouncer --key {{ crowdsec.firewall_bouncer_key }}" diff --git a/roles/crowdsec/tasks/firewall/main.yml b/roles/crowdsec/tasks/firewall/main.yml new file mode 100644 index 0000000..56331e9 --- /dev/null +++ b/roles/crowdsec/tasks/firewall/main.yml @@ -0,0 +1,35 @@ +- name: Install syslog-ng + become: yes + ansible.builtin.apt: + name: syslog-ng + state: present + update_cache: yes + +- name: Install CrowdSec Repository + become: yes + ansible.builtin.shell: | + curl -s https://install.crowdsec.net | sh + args: + executable: /bin/bash + +- name: Install CrowdSec firewall bouncer + become: yes + ansible.builtin.apt: + name: crowdsec-firewall-bouncer-iptables + state: present + update_cache: yes + +- name: Create firewall bouncer config file + template: + src: firewall-bouncer.yaml.j2 + dest: /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml + force: yes + owner: root + group: root + mode: 0600 + +- name: Enable and start CrowdSec Firewall Bouncer + ansible.builtin.systemd: + name: crowdsec-firewall-bouncer + enabled: true + state: started \ No newline at end of file diff --git a/roles/crowdsec/tasks/main.yml b/roles/crowdsec/tasks/main.yml new file mode 100644 index 0000000..f7d9dd5 --- /dev/null +++ b/roles/crowdsec/tasks/main.yml @@ -0,0 +1,57 @@ +--- +- name: Create directories + file: + path: "{{ item }}" + state: directory + owner: "{{ user }}" + group: "{{ group }}" + mode: 0775 + recurse: yes + loop: + - "{{ crowdsec.folder }}" + - "{{ crowdsec.folder }}/config" + - "{{ crowdsec.folder }}/database" + +- name: "Create acquis config file" + template: + src: acquis.yaml.j2 + dest: "{{ crowdsec.folder }}/acquis.yaml" + force: yes + owner: "{{ user }}" + group: "{{ group }}" + mode: 0775 + +- name: Create and start container + docker_container: + name: crowdsec + image: "crowdsecurity/crowdsec:{{ crowdsec.tag }}" + pull: yes + recreate: true + volumes: + - "{{ crowdsec.folder }}/config:/etc/crowdsec" + - "{{ crowdsec.folder }}/acquis.yaml:/etc/crowdsec/acquis.yaml" + - "{{ crowdsec.folder }}/database:/var/lib/crowdsec/data" + # System logs + - /var/log/auth.log:/var/log/auth.log:ro + - /var/log/syslog:/var/log/syslog.log:ro + - /var/log/kern.log:/var/log/kern.log:ro + # Traefik logs + - "{{ traefik.folder }}/logs:/var/log/traefik:ro" + networks: + - name: web + aliases: + - crowdsec + ports: + - 127.0.0.1:9876:8080 # port mapping for local firewall bouncer + restart_policy: unless-stopped + state: started + labels: + env: + GID: "{{ gid | int }}" + COLLECTIONS: "crowdsecurity/linux crowdsecurity/traefik crowdsecurity/http-cve crowdsecurity/base-http-scenarios crowdsecurity/sshd crowdsecurity/appsec-generic-rules crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-crs" + +- name: Configure Crowdsec + include_tasks: "config/main.yml" + +- name: Configure Firewall Bouncer + include_tasks: "firewall/main.yml" \ No newline at end of file diff --git a/roles/crowdsec/templates/acquis.yaml.j2 b/roles/crowdsec/templates/acquis.yaml.j2 new file mode 100644 index 0000000..12470b2 --- /dev/null +++ b/roles/crowdsec/templates/acquis.yaml.j2 @@ -0,0 +1,20 @@ +--- +filenames: + - /var/log/auth.log + - /var/log/syslog + - /var/log/kern.log +labels: + type: syslog +--- +poll_without_inotify: false +filenames: + - /var/log/traefik/*.log +labels: + type: traefik +--- +listen_addr: 0.0.0.0:7422 +appsec_config: crowdsecurity/appsec-default +name: myAppSecComponent +source: appsec +labels: + type: appsec \ No newline at end of file diff --git a/roles/crowdsec/templates/firewall-bouncer.yaml.j2 b/roles/crowdsec/templates/firewall-bouncer.yaml.j2 new file mode 100644 index 0000000..60df3af --- /dev/null +++ b/roles/crowdsec/templates/firewall-bouncer.yaml.j2 @@ -0,0 +1,63 @@ +mode: iptables +update_frequency: 10s +log_mode: file +log_dir: /var/log/ +log_level: info +log_compression: true +log_max_size: 100 +log_max_backups: 3 +log_max_age: 30 +api_url: http://127.0.0.1:9876/ +api_key: {{ crowdsec.firewall_bouncer_key }} +## TLS Authentication +# cert_path: /etc/crowdsec/tls/cert.pem +# key_path: /etc/crowdsec/tls/key.pem +# ca_cert_path: /etc/crowdsec/tls/ca.crt +insecure_skip_verify: false +disable_ipv6: false +deny_action: DROP +deny_log: false +supported_decisions_types: + - ban +#to change log prefix +#deny_log_prefix: "crowdsec: " +#to change the blacklists name +blacklists_ipv4: crowdsec-blacklists +blacklists_ipv6: crowdsec6-blacklists +#type of ipset to use +ipset_type: nethash +#if present, insert rule in those chains +iptables_chains: + - INPUT +# - FORWARD +# - DOCKER-USER +iptables_add_rule_comments: true + +## nftables +nftables: + ipv4: + enabled: true + set-only: false + table: crowdsec + chain: crowdsec-chain + priority: -10 + ipv6: + enabled: true + set-only: false + table: crowdsec6 + chain: crowdsec6-chain + priority: -10 + +nftables_hooks: + - input + - forward + +# packet filter +pf: + # an empty string disables the anchor + anchor_name: "" + +prometheus: + enabled: false + listen_addr: 127.0.0.1 + listen_port: 60601 \ No newline at end of file diff --git a/roles/hardening/tasks/main.yml b/roles/hardening/tasks/main.yml index 988e57b..6ee16a0 100644 --- a/roles/hardening/tasks/main.yml +++ b/roles/hardening/tasks/main.yml @@ -8,6 +8,7 @@ - name: Install Fail2Ban include_tasks: "fail2ban/main.yml" + when: not use_crowdsec | bool - name: Install Unattended Upgrades include_tasks: "unattendedupgrades/main.yml" \ No newline at end of file diff --git a/roles/traefik/tasks/main.yml b/roles/traefik/tasks/main.yml index 54497ea..15b4af7 100644 --- a/roles/traefik/tasks/main.yml +++ b/roles/traefik/tasks/main.yml @@ -10,6 +10,7 @@ loop: - "{{ traefik.folder }}" - "{{ traefik.folder }}/certificates" + - "{{ traefik.folder }}/logs" - name: Download Cloudflare certificate for mTLS ansible.builtin.get_url: @@ -41,6 +42,11 @@ dest: "{{ traefik.folder }}/dynamic.yml" mode: 0600 +- name: Ensure access.log file exists + file: + path: "{{ traefik.folder }}/logs/access.log" + state: touch + - name: Create the web network docker_network: name: web @@ -59,9 +65,10 @@ volumes: - "{{ traefik.folder }}/traefik.yml:/etc/traefik/traefik.yml" - "{{ traefik.folder }}/dynamic.yml:/etc/traefik/dynamic/dynamic.yml" - - "{{ traefik.folder }}/certificates/cf_authenticated_origin_pull_ca.pem:/etc/traefik/dynamic/authenticated_origin_pull_ca.pem" - - "{{ traefik.folder }}/certificates/origin_certificate.pem:/etc/traefik/dynamic/{{ domain }}.pem" - - "{{ traefik.folder }}/certificates/origin_certificate.key:/etc/traefik/dynamic/{{ domain }}.key" + - "{{ traefik.folder }}/certificates/cf_authenticated_origin_pull_ca.pem:/etc/traefik/dynamic/authenticated_origin_pull_ca.pem:ro" + - "{{ traefik.folder }}/certificates/origin_certificate.pem:/etc/traefik/dynamic/{{ domain }}.pem:ro" + - "{{ traefik.folder }}/certificates/origin_certificate.key:/etc/traefik/dynamic/{{ domain }}.key:ro" + - "{{ traefik.folder }}/logs/access.log:/var/log/traefik/access.log" - /etc/localtime:/etc/localtime:ro - /var/run/docker.sock:/var/run/docker.sock labels: @@ -83,3 +90,9 @@ env: CF_API_KEY: "{{ cloudflare.api_key }}" CF_API_EMAIL: "{{ cloudflare.api_email }}" + +- name: Enroll Traefik Firewall Bouncer + community.docker.docker_container_exec: + container: crowdsec + command: "crowdsec cscli bouncers add traefik-bouncer --key {{ crowdsec.traefik_bouncer_key }}" + when: use_crowdsec | bool \ No newline at end of file diff --git a/roles/traefik/templates/dynamic.yml.j2 b/roles/traefik/templates/dynamic.yml.j2 index 7a9097a..fa561f2 100644 --- a/roles/traefik/templates/dynamic.yml.j2 +++ b/roles/traefik/templates/dynamic.yml.j2 @@ -12,3 +12,32 @@ tls: defaultCertificate: certFile: /etc/traefik/dynamic/{{ domain }}.pem keyFile: /etc/traefik/dynamic/{{ domain }}.key + +{% if use_crowdsec %} +http: + middlewares: + crowdsec: + plugin: + bouncer: + enabled: true + defaultDecisionSeconds: 60 + crowdsecMode: live + crowdsecAppsecEnabled: false + crowdsecAppsecHost: crowdsec:7422 + crowdsecAppsecFailureBlock: true + crowdsecAppsecUnreachableBlock: true + crowdsecLapiKey: {{ crowdsec.traefik_bouncer_key }} + crowdsecLapiHost: crowdsec:8080 + crowdsecLapiScheme: http + crowdsecLapiTLSInsecureVerify: false + forwardedHeadersTrustedIPs: + # private class ranges + - 10.0.0.0/8 + - 172.16.0.0/12 + - 192.168.0.0/16 + clientTrustedIPs: + # private class ranges + - 10.0.0.0/8 + - 172.16.0.0/12 + - 192.168.0.0/16 +{% endif %} \ No newline at end of file diff --git a/roles/traefik/templates/traefik.yml.j2 b/roles/traefik/templates/traefik.yml.j2 index 4af397d..4c038e7 100644 --- a/roles/traefik/templates/traefik.yml.j2 +++ b/roles/traefik/templates/traefik.yml.j2 @@ -19,11 +19,70 @@ entryPoints: entryPoint: to: websecure scheme: https + {% if use_crowdsec %} + forwardedHeaders: + trustedIPs: &trustedIps + # Start of Cloudlare's public IP list + - 103.21.244.0/22 + - 103.22.200.0/22 + - 103.31.4.0/22 + - 104.16.0.0/13 + - 104.24.0.0/14 + - 108.162.192.0/18 + - 131.0.72.0/22 + - 141.101.64.0/18 + - 162.158.0.0/15 + - 172.64.0.0/13 + - 173.245.48.0/20 + - 188.114.96.0/20 + - 190.93.240.0/20 + - 197.234.240.0/22 + - 198.41.128.0/17 + - 2400:cb00::/32 + - 2606:4700::/32 + - 2803:f800::/32 + - 2405:b500::/32 + - 2405:8100::/32 + - 2a06:98c0::/29 + - 2c0f:f248::/32 + # End of Cloudlare's public IP list + {% endif %} websecure: address: :443 http: tls: true - + {% if use_crowdsec %} + middlewares: + - crowdsec@file + {% endif %} + {% if use_crowdsec %} + forwardedHeaders: + trustedIPs: &trustedIps + # Start of Cloudlare's public IP list + - 103.21.244.0/22 + - 103.22.200.0/22 + - 103.31.4.0/22 + - 104.16.0.0/13 + - 104.24.0.0/14 + - 108.162.192.0/18 + - 131.0.72.0/22 + - 141.101.64.0/18 + - 162.158.0.0/15 + - 172.64.0.0/13 + - 173.245.48.0/20 + - 188.114.96.0/20 + - 190.93.240.0/20 + - 197.234.240.0/22 + - 198.41.128.0/17 + - 2400:cb00::/32 + - 2606:4700::/32 + - 2803:f800::/32 + - 2405:b500::/32 + - 2405:8100::/32 + - 2a06:98c0::/29 + - 2c0f:f248::/32 + # End of Cloudlare's public IP list + {% endif %} providers: docker: network: web @@ -31,3 +90,25 @@ providers: file: directory: /etc/traefik/dynamic/ watch: true + +accessLog: + filePath: /var/log/traefik/access.log + format: json + filters: + statusCodes: + - "200-299" # log successful http requests + - "400-599" # log failed http requests + bufferingSize: 0 # collect logs as in-memory buffer before writing into log file + fields: + headers: + defaultMode: drop # drop all headers per default + names: + User-Agent: keep # log user agent strings + +{% if use_crowdsec %} +experimental: + plugins: + bouncer: + moduleName: github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin + version: v1.4.5 +{% endif %} \ No newline at end of file diff --git a/seedbox.yml b/seedbox.yml index 0e07bd2..93c1a16 100644 --- a/seedbox.yml +++ b/seedbox.yml @@ -13,6 +13,7 @@ - { role: homer, tags: ["core", "homer"] } - { role: watchtower, tags: ["core", "watchtower"] } - { role: postfix, tags: ["core", "watchtower", "postfix"] } + - { role: crowdsec, tags: ["security", "crowdsec"] } # Download - { role: flood, tags: ["download", "torrent", "flood"] }