diff --git a/roles/crowdsec/defaults/main.yml b/roles/crowdsec/defaults/main.yml new file mode 100644 index 0000000..8a7891d --- /dev/null +++ b/roles/crowdsec/defaults/main.yml @@ -0,0 +1,47 @@ +--- + +# Version to install +crowdsec_version: 1.0.7 +# URL of the archive +crowdsec_archive_url: https://github.com/crowdsecurity/crowdsec/releases/download/v{{ crowdsec_version }}/crowdsec-release.tgz +# Expected sha1 of the archive +crowdsec_archive_sha1: 7c9dc58c6648c8fd43b297427d6a53fe940cbf13 + +crowdsec_db_server: "{{ mysql_server | default('localhost') }}" +crowdsec_db_port: 3306 +crowdsec_db_name: crowdsec +crowdsec_db_user: crowdsec +# If not defined, a random one will be generated and store in /etc/crowdsec/meta/ansible_dbpass +# crowdsec_db_pass: S3cr3t. + +# You can disable the Local API, if using a remote one for example +crowdsec_lapi_enabled: True +# Set to true if Local API is enabled, and you intend to use it through a trusted reverse proxy +crowdsec_use_forwarded_headers: False +# Port on which the Local API will listen +crowdsec_lapi_port: 8080 +# List of IP/CIDR allowed to access crowdsec_lapi_port +crowdsec_lapi_src_ip: [] + +# Address of the Local API server +# The default config will make it standalone +crowdsec_lapi_url: http://localhost:{{ crowdsec_lapi_port }}/ +crowdsec_lapi_user: "{{ inventory_hostname }}" +# On installation, ansible will register this host on the Local API +# And will then validate the registration on the following server. +# So set it to your own Local API server so ansible will delegate the task +crowdsec_lapi_server: "{{ inventory_hostname }}" + +# Use the central API, to share your banned IP, and received list of IP to ban +# Requires crowdsec_lapi_enabled to be true too +crowdsec_capi_enabled: False + +# Prometheus metrics +crowdsec_prometheus_enabled: False +# Port on which the prometheus metric endpoint will bind to +crowdsec_prometheus_port: 6060 +# List of IP/CIDR allowed to access the prometheus port +crowdsec_prometheus_src_ip: [] + +# Default duration of a ban +crowdsec_ban_duration: 15m diff --git a/roles/crowdsec/handlers/main.yml b/roles/crowdsec/handlers/main.yml new file mode 100644 index 0000000..e2e79b8 --- /dev/null +++ b/roles/crowdsec/handlers/main.yml @@ -0,0 +1,7 @@ +--- + +- name: restart crowdsec + service: name=crowdsec state=restarted + +- name: reload crowdsec + service: name=crowdsec state=reloaded diff --git a/roles/crowdsec/meta/main.yml b/roles/crowdsec/meta/main.yml new file mode 100644 index 0000000..f5eb73e --- /dev/null +++ b/roles/crowdsec/meta/main.yml @@ -0,0 +1,5 @@ +--- + +dependencies: + - role: mysql_server + when: crowdsec_db_server in ['localhost','127.0.0.1'] diff --git a/roles/crowdsec/tasks/cleanup.yml b/roles/crowdsec/tasks/cleanup.yml new file mode 100644 index 0000000..167e790 --- /dev/null +++ b/roles/crowdsec/tasks/cleanup.yml @@ -0,0 +1,8 @@ +--- + +- name: Remove temp and obsolete files + file: path={{ item }} state=absent + loop: + - /tmp/crowdsec-release.tgz + - /tmp/crowdsec-v{{ crowdsec_version }} + tags: crowdsec diff --git a/roles/crowdsec/tasks/conf.yml b/roles/crowdsec/tasks/conf.yml new file mode 100644 index 0000000..47dd98d --- /dev/null +++ b/roles/crowdsec/tasks/conf.yml @@ -0,0 +1,44 @@ +--- + +- name: Deploy configuration + template: src={{ item }}.j2 dest=/etc/crowdsec/{{ item }} + loop: + - config.yaml + - acquis.yaml + - simulation.yaml + - profile.yaml + notify: reload crowdsec + tags: crowdsec + +# Create the database +- import_tasks: ../includes/webapps_create_mysql_db.yml + vars: + - db_name: "{{ crowdsec_db_name }}" + - db_user: "{{ crowdsec_db_user }}" + - db_server: "{{ crowdsec_db_server }}" + - db_pass: "{{ crowdsec_db_pass }}" + tags: crowdsec + +- name: Declare on the local API + command: cscli machines add {{ crowdsec_lapi_user }} --auto + register: crowdsec_lapi_add + when: inventory_hostname == crowdsec_lapi_server + changed_when: crowdsec_lapi_add.rc == 0 + failed_when: crowdsec_lapi_add.rc not in [0,1] + tags: crowdsec + +- when: inventory_hostname != crowdsec_lapi_server + block: + - name: Register against the Local API + command: cscli lapi register --machine {{ crowdsec_lapi_user }} --url {{ crowdsec_lapi_url }} + register: crowdsec_lapi_registration + changed_when: crowdsec_lapi_registration.rc == 0 + failed_when: crowdsec_lapi_registration.rc not in [0,1] # RC 1 when machine already exists + notify: reload crowdsec + + - name: Validate crowdsec registration on the Local API server + command: cscli machines validate {{ crowdsec_lapi_user }} + delegate_to: "{{ crowdsec_lapi_server }}" + when: crowdsec_lapi_registration.rc == 0 + + tags: crowdsec diff --git a/roles/crowdsec/tasks/directories.yml b/roles/crowdsec/tasks/directories.yml new file mode 100644 index 0000000..4464393 --- /dev/null +++ b/roles/crowdsec/tasks/directories.yml @@ -0,0 +1,10 @@ +--- + +- name: Create required directories + file: path={{ item.dir }} state=directory owner={{ item.owner | default(omit) }} group={{ item.group | default(omit) }} mode={{ item.mode | default(omit) }} + loop: + - dir: /etc/crowdsec + mode: 755 + - dir: /etc/crowdsec/meta + mode: 700 + tags: crowdsec diff --git a/roles/crowdsec/tasks/facts.yml b/roles/crowdsec/tasks/facts.yml new file mode 100644 index 0000000..bafe3ff --- /dev/null +++ b/roles/crowdsec/tasks/facts.yml @@ -0,0 +1,45 @@ +--- + +- name: Set initial facts + block: + - set_fact: crowdsec_install_mode='none' + - set_fact: crowdsec_current_version='' + tags: crowdsec + +- name: Check if crowdsec is installed + stat: path=/usr/local/bin/crowdsec + register: crowdsec_bin + tags: crowdsec + +- name: Check installed version + shell: | + crowdsec -version 2>&1 | perl -ne 'm/version: v(\d+(\.\d+)*)/ && print $1' + register: crowdsec_current_version + changed_when: False + when: crowdsec_bin.stat.exists + tags: crowdsec + +- name: Set install mode + set_fact: crowdsec_install_mode='install' + when: not crowdsec_bin.stat.exists + tags: crowdsec + +- name: Set upgrade mode + set_fact: crowdsec_install_mode='upgrade' + when: + - crowdsec_bin.stat.exists + - crowdsec_current_version.stdout != crowdsec_version + tags: crowdsec + +# Create a random db password if needed +- block: + - import_tasks: ../includes/get_rand_pass.yml + vars: + - pass_file: "/etc/crowdsec/meta/ansible_db_pass" + - complex: False + - set_fact: crowdsec_db_pass={{ rand_pass }} + when: + - crowdsec_db_pass is not defined + - crowdsec_lapi_enabled + tags: crowdsec + diff --git a/roles/crowdsec/tasks/install.yml b/roles/crowdsec/tasks/install.yml new file mode 100644 index 0000000..4d66ea4 --- /dev/null +++ b/roles/crowdsec/tasks/install.yml @@ -0,0 +1,27 @@ +--- + +- when: crowdsec_install_mode != 'none' + block: + - name: Download crowdsec + get_url: + url: "{{ crowdsec_archive_url }}" + dest: /tmp/ + checksum: sha1:{{ crowdsec_archive_sha1 }} + + - name: Extract crowdsec + unarchive: + src: /tmp/crowdsec-release.tgz + dest: /tmp/ + remote_src: True + + - name: Install or upgrade crowdsec + command: ./wizard.sh --bin{{ crowdsec_install_mode }} + args: + chdir: /tmp/crowdsec-v{{ crowdsec_version }}/ + notify: restart crowdsec + + - name: Update crowdsec hub + command: cscli hub update + + tags: crowdsec + diff --git a/roles/crowdsec/tasks/iptables.yml b/roles/crowdsec/tasks/iptables.yml new file mode 100644 index 0000000..2adcad9 --- /dev/null +++ b/roles/crowdsec/tasks/iptables.yml @@ -0,0 +1,15 @@ +--- + +- name: Handle crowdsec port in the firewall + iptables_raw: + name: "{{ item.name }}" + state: "{{ (item.src_ip | length > 0) | ternary('present','absent') }}" + rules: "-A INPUT -m state --state NEW -p tcp --dport {{ item.port }} -s {{ item.src_ip | join(',') }} -j ACCEPT" + loop: + - name: crowdsec_lapi_port + port: "{{ crowdsec_lapi_port }}" + src_ip: "{{ crowdsec_lapi_src_ip }}" + - name: crowdsec_prometheus_port + port: "{{ crowdsec_prometheus_port }}" + src_ip: "{{ crowdsec_prometheus_src_ip }}" + tags: firewall,crowdsec diff --git a/roles/crowdsec/tasks/main.yml b/roles/crowdsec/tasks/main.yml new file mode 100644 index 0000000..9575cea --- /dev/null +++ b/roles/crowdsec/tasks/main.yml @@ -0,0 +1,10 @@ +--- + +- include: directories.yml +- include: facts.yml +- include: install.yml +- include: conf.yml +- include: iptables.yml + when: iptables_manage | default(True) +- include: services.yml +- include: cleanup.yml diff --git a/roles/crowdsec/tasks/services.yml b/roles/crowdsec/tasks/services.yml new file mode 100644 index 0000000..994109e --- /dev/null +++ b/roles/crowdsec/tasks/services.yml @@ -0,0 +1,5 @@ +--- + +- name: Start and enable the service + service: name=crowdsec state=started enabled=True + tags: crowdsec diff --git a/roles/crowdsec/templates/acquis.yaml.j2 b/roles/crowdsec/templates/acquis.yaml.j2 new file mode 100644 index 0000000..5b623f6 --- /dev/null +++ b/roles/crowdsec/templates/acquis.yaml.j2 @@ -0,0 +1,5 @@ +--- +journalctl_filter: + - "_SYSTEMD_UNIT=sshd.service" +labels: + type: syslog diff --git a/roles/crowdsec/templates/config.yaml.j2 b/roles/crowdsec/templates/config.yaml.j2 new file mode 100644 index 0000000..0268213 --- /dev/null +++ b/roles/crowdsec/templates/config.yaml.j2 @@ -0,0 +1,58 @@ +common: + daemonize: true + pid_dir: /var/run/ + log_media: stdout + log_level: info + working_dir: . + +config_paths: + config_dir: /etc/crowdsec/ + data_dir: /var/lib/crowdsec/data/ + simulation_path: /etc/crowdsec/simulation.yaml + hub_dir: /etc/crowdsec/hub/ + index_path: /etc/crowdsec/hub/.index.json + +crowdsec_service: + acquisition_path: /etc/crowdsec/acquis.yaml + parser_routines: 1 + +cscli: + output: human + hub_branch: master + +db_config: + log_level: info + type: mysql + user: {{ crowdsec_db_user }} + password: {{ crowdsec_db_pass | quote }} + db_name: {{ crowdsec_db_name }} + host: {{ crowdsec_db_server }} + port: {{ crowdsec_db_port }} + flush: + max_items: 100000 + max_age: 730d + +api: + client: + insecure_skip_verify: false + credentials_path: /etc/crowdsec/local_api_credentials.yaml + +{% if crowdsec_lapi_enabled %} + server: + log_level: info + listen_uri: 0.0.0.0:{{ crowdsec_lapi_port }} + profiles_path: /etc/crowdsec/profiles.yaml +{% if crowdsec_capi_enabled %} + online_client: + credentials_path: /etc/crowdsec/online_api_credentials.yaml +{% endif %} +{% endif %} + +{% if crowdsec_prometheus_enabled %} +prometheus: + enabled: true + level: full + listen_addr: 0.0.0.0 + listen_port: {{ crowdsec_prometheus_port }} +{% endif %} + diff --git a/roles/crowdsec/templates/profile.yaml.j2 b/roles/crowdsec/templates/profile.yaml.j2 new file mode 100644 index 0000000..2d58e39 --- /dev/null +++ b/roles/crowdsec/templates/profile.yaml.j2 @@ -0,0 +1,7 @@ +name: default_ip_remediation +filters: + - Alert.Remediation == true && Alert.GetScope() == "Ip" +decisions: + - type: ban + duration: {{ crowdsec_ban_duration }} +on_success: break diff --git a/roles/crowdsec/templates/simulation.yaml.j2 b/roles/crowdsec/templates/simulation.yaml.j2 new file mode 100644 index 0000000..94adcc5 --- /dev/null +++ b/roles/crowdsec/templates/simulation.yaml.j2 @@ -0,0 +1 @@ +simulation: off diff --git a/roles/crowdsec_bouncer_firewall/defaults/main.yml b/roles/crowdsec_bouncer_firewall/defaults/main.yml new file mode 100644 index 0000000..508859b --- /dev/null +++ b/roles/crowdsec_bouncer_firewall/defaults/main.yml @@ -0,0 +1,14 @@ +--- + +# Version of the firewall bouncer to install +crowdsec_fw_version: 0.0.10 +# URL of the firewall bouncer archive +crowdsec_fw_archive_url: https://github.com/crowdsecurity/cs-firewall-bouncer/releases/download/v{{ crowdsec_fw_version }}/cs-firewall-bouncer.tgz +# Expected sha1 of the archive +crowdsec_fw_archive_sha1: 46863e95bdc8f48434583f55e89b7720fce5736d + +# API on which the bouncer should listen for alerts +crowdsec_fw_lapi_url: "{{ crowdsec_lapi_url | default('http://localhost:8080/') }}" +# If not defined, ansible will try to register the bouncer on the Local API server +# crowdsec_fw_lapi_key: aaabbbccc + diff --git a/roles/crowdsec_bouncer_firewall/handlers/main.yml b/roles/crowdsec_bouncer_firewall/handlers/main.yml new file mode 100644 index 0000000..c8c6d1e --- /dev/null +++ b/roles/crowdsec_bouncer_firewall/handlers/main.yml @@ -0,0 +1,4 @@ +--- + +- name: restart cs-firewall-bouncer + service: name=cs-firewall-bouncer state=restarted diff --git a/roles/crowdsec_bouncer_firewall/tasks/cleanup.yml b/roles/crowdsec_bouncer_firewall/tasks/cleanup.yml new file mode 100644 index 0000000..607f42f --- /dev/null +++ b/roles/crowdsec_bouncer_firewall/tasks/cleanup.yml @@ -0,0 +1,8 @@ +--- + +- name: Remove temp and obsolete files + file: path={{ item }} state=absent + loop: + - /tmp/cs-firewall-bouncer.tgz + - /tmp/cs-firewall-bouncer-v{{ crowdsec_fw_version }} + tags: crowdsec diff --git a/roles/crowdsec_bouncer_firewall/tasks/conf.yml b/roles/crowdsec_bouncer_firewall/tasks/conf.yml new file mode 100644 index 0000000..8a7c334 --- /dev/null +++ b/roles/crowdsec_bouncer_firewall/tasks/conf.yml @@ -0,0 +1,6 @@ +--- + +- name: Deploy configuration + template: src=cs-firewall-bouncer.yaml.j2 dest=/etc/crowdsec/cs-firewall-bouncer/cs-firewall-bouncer.yaml mode=600 + notify: restart cs-firewall-bouncer + tags: crowdsec diff --git a/roles/crowdsec_bouncer_firewall/tasks/directories.yml b/roles/crowdsec_bouncer_firewall/tasks/directories.yml new file mode 100644 index 0000000..43aab36 --- /dev/null +++ b/roles/crowdsec_bouncer_firewall/tasks/directories.yml @@ -0,0 +1,4 @@ +--- + +# Just pull the tasks from the crowdsec role as it needs the same dir +- include: ../crowdsec/tasks/directories.yml diff --git a/roles/crowdsec_bouncer_firewall/tasks/facts.yml b/roles/crowdsec_bouncer_firewall/tasks/facts.yml new file mode 100644 index 0000000..25827cc --- /dev/null +++ b/roles/crowdsec_bouncer_firewall/tasks/facts.yml @@ -0,0 +1,62 @@ +--- + +- name: Check if API key is available + stat: path=/etc/crowdsec/meta/bouncer_fw_api_key + register: crowdsec_fw_lapi_key_file + tags: crowdsec + +- when: crowdsec_fw_lapi_key is not defined and (not crowdsec_fw_lapi_key_file.stat.exists or crowdsec_fw_lapi_key_file.stat.size == 0) + block: + - name: Register the bouncer + command: cscli bouncers add {{ inventory_hostname }}-firewall -o raw + register: crowdsec_bouncer_add + failed_when: crowdsec_bouncer_add.rc not in [0,1] + changed_when: crowdsec_bouncer_add.rc == 0 + delegate_to: "{{ crowdsec_lapi_server | default(inventory_hostname) }}" + + - name: Record the API key for later use + copy: content={{ crowdsec_bouncer_add.stdout }} dest=/etc/crowdsec/meta/bouncer_fw_api_key mode=600 + + tags: crowdsec + +- when: crowdsec_fw_lapi_key is not defined + block: + - name: Read the API key + slurp: src=/etc/crowdsec/meta/bouncer_fw_api_key + register: crowdsec_fw_lapi_generated_key + - set_fact: crowdsec_fw_lapi_key={{ crowdsec_fw_lapi_generated_key.content | b64decode | trim }} + tags: crowdsec + +- name: Set initial facts + block: + - set_fact: crowdsec_fw_current_version='' + - set_fact: crowdsec_fw_install_mode='none' + tags: crowdsec + +- name: Check if the bouncer is installed + stat: path=/usr/local/bin/cs-firewall-bouncer + register: crowdsec_fw_bin + tags: crowdsec + +- when: crowdsec_fw_bin.stat.exists + block: + - name: Detect installed version + shell: | + cs-firewall-bouncer -c /dev/null 2>&1 | perl -ne 'm/cs-firewall-bouncer v(\d+(\.\d+)*)/ && print $1' + register: crowdsec_fw_current_version + changed_when: False + + - set_fact: crowdsec_fw_current_version={{ crowdsec_fw_current_version.stdout }} + tags: crowdsec + +- name: Set install mode + set_fact: crowdsec_fw_install_mode='install' + when: not crowdsec_fw_bin.stat.exists + tags: crowdsec + +- name: Set upgrade mode + set_fact: crowdsec_fw_install_mode='upgrade' + when: + - crowdsec_fw_bin.stat.exists + - crowdsec_fw_current_version != crowdsec_fw_version + tags: crowdsec diff --git a/roles/crowdsec_bouncer_firewall/tasks/install.yml b/roles/crowdsec_bouncer_firewall/tasks/install.yml new file mode 100644 index 0000000..5de4fa9 --- /dev/null +++ b/roles/crowdsec_bouncer_firewall/tasks/install.yml @@ -0,0 +1,53 @@ +--- + +- when: crowdsec_fw_install_mode != 'none' + block: + + - name: Download the bouncer + get_url: + url: "{{ crowdsec_fw_archive_url }}" + dest: /tmp + checksum: sha1:{{ crowdsec_fw_archive_sha1 }} + + - name: Extract the archive + unarchive: + src: /tmp/cs-firewall-bouncer.tgz + dest: /tmp + remote_src: True + + - name: Install or upgrade + command: ./{{ crowdsec_fw_install_mode }}.sh + args: + chdir: /tmp/cs-firewall-bouncer-v{{ crowdsec_fw_version }} + notify: restart cs-firewall-bouncer + + tags: crowdsec + +- name: Create systemd unit snippet dir + file: path=/etc/systemd/system/cs-firewall-bouncer.service.d state=directory + tags: crowdsec + +- name: Link cs-firewall-bouncer with the iptables service + copy: + content: | + [Unit] + # Ensure cs-firewall-bouncer starts before iptables + # so ipset are available + Before=iptables.service + # But it should start after crowdsec to be able to register on the API + After=crowdsec.service + + [Service] + # Restart on failure + Restart=on-failure + StartLimitInterval=0 + RestartSec=30 + dest: /etc/systemd/system/cs-firewall-bouncer.service.d/ansible.conf + register: crodwsec_fw_unit + notify: restart cs-firewall-bouncer + tags: crowdsec + +- name: Reload systemd + systemd: daemon_reload=True + when: crodwsec_fw_unit.changed + tags: crowdsec diff --git a/roles/crowdsec_bouncer_firewall/tasks/iptables.yml b/roles/crowdsec_bouncer_firewall/tasks/iptables.yml new file mode 100644 index 0000000..10e366b --- /dev/null +++ b/roles/crowdsec_bouncer_firewall/tasks/iptables.yml @@ -0,0 +1,17 @@ +--- + +- name: Ensure ipsets exist + shell: | + ipset list crowdsec-blacklists || ipset create crowdsec-blacklists nethash timeout 300 + ipset list crowdsec6-blacklists || ipset create crowdsec6-blacklists nethash timeout 300 family inet6 + changed_when: False + tags: crowdsec + +- name: Add DROP rules + iptables_raw: + name: crowdsec_blacklist + weight: 9 + rules: | + -A INPUT -m set --match-set crowdsec-blacklists src -j DROP + -A FORWARD -m set --match-set crowdsec-blacklists src -j DROP + tags: crowdsec diff --git a/roles/crowdsec_bouncer_firewall/tasks/main.yml b/roles/crowdsec_bouncer_firewall/tasks/main.yml new file mode 100644 index 0000000..9575cea --- /dev/null +++ b/roles/crowdsec_bouncer_firewall/tasks/main.yml @@ -0,0 +1,10 @@ +--- + +- include: directories.yml +- include: facts.yml +- include: install.yml +- include: conf.yml +- include: iptables.yml + when: iptables_manage | default(True) +- include: services.yml +- include: cleanup.yml diff --git a/roles/crowdsec_bouncer_firewall/tasks/services.yml b/roles/crowdsec_bouncer_firewall/tasks/services.yml new file mode 100644 index 0000000..23d41d2 --- /dev/null +++ b/roles/crowdsec_bouncer_firewall/tasks/services.yml @@ -0,0 +1,5 @@ +--- + +- name: Start and enable the service + service: name=cs-firewall-bouncer state=started enabled=True + tags: crowdsec diff --git a/roles/crowdsec_bouncer_firewall/templates/cs-firewall-bouncer.yaml.j2 b/roles/crowdsec_bouncer_firewall/templates/cs-firewall-bouncer.yaml.j2 new file mode 100644 index 0000000..b22302d --- /dev/null +++ b/roles/crowdsec_bouncer_firewall/templates/cs-firewall-bouncer.yaml.j2 @@ -0,0 +1,12 @@ +--- + +mode: iptables +piddir: /var/run/ +update_frequency: 10s +daemonize: true +log_mode: stdout +log_level: info +api_url: {{ crowdsec_fw_lapi_url }} +api_key: {{ crowdsec_fw_lapi_key }} +disable_ipv6: false +