apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: name: crowdsec namespace: infra-net spec: interval: 30m timeout: 15m dependsOn: - name: ingress-nginx - name: loki namespace: infra-monitor chart: spec: chart: crowdsec version: 0.22.0 sourceRef: kind: HelmRepository name: crowdsec namespace: infra-gitops interval: 12h values: container_runtime: containerd image: tag: v1.7.6 agent: isDeployment: true additionalAcquisition: - source: loki log_level: info url: http://loki.infra-monitor:3100/ limit: 1000 query: | {job="infra-net/ingress-nginx"} labels: type: nginx env: - name: COLLECTIONS value: "crowdsecurity/base-http-scenarios crowdsecurity/http-dos" - name: SCENARIOS value: "crowdsecurity/nginx-req-limit-exceeded" persistentVolume: config: enabled: false appsec: enabled: false acquisitions: - source: appsec listen_addr: "0.0.0.0:7422" path: / appsec_config: crowdsecurity/crs-vpatch labels: type: appsec configs: mycustom-appsec-config.yaml: | name: crowdsecurity/crs-vpatch default_remediation: ban #log_level: debug outofband_rules: - crowdsecurity/crs inband_rules: - crowdsecurity/base-config - crowdsecurity/vpatch-* env: - name: COLLECTIONS value: "crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-crs" lapi: resources: requests: cpu: 150m memory: 100Mi persistentVolume: config: enabled: false data: enabled: false env: - name: DB_PASSWORD valueFrom: secretKeyRef: name: cnpg17-cluster-app key: password config: # api config.yaml配置 config.yaml.local: | db_config: type: postgresql host: cnpg17-cluster-rw.infra-data port: 5432 db_name: crowdsec user: app password: ${DB_PASSWORD} sslmode: require api: server: auto_registration: enabled: true token: "${REGISTRATION_TOKEN}" allowed_ranges: - "127.0.0.1/32" - "192.168.0.0/16" - "172.16.0.0/12" - "10.0.0.0/8" # api profiles.yaml配置 profiles.yaml: | name: captcha_remediation filters: # 规则过滤条件 1.范围为Ip 2.触发场景为http或nginx 3.24小时内决策次数小于等于3 - Alert.Remediation == true && Alert.GetScope() == "Ip" && (Alert.GetScenario() contains "http" || Alert.GetScenario() contains "nginx") && GetDecisionsSinceCount(Alert.GetValue(), "24h") <= 3 decisions: - type: captcha duration: 4h on_success: break --- name: default_ip_remediation filters: - Alert.Remediation == true && Alert.GetScope() == "Ip" decisions: - type: ban duration: 4h on_success: break --- name: default_range_remediation filters: - Alert.Remediation == true && Alert.GetScope() == "Range" decisions: - type: ban duration: 4h on_success: break # agent parsers 配置 parsers: s01-parse: # 新增nginx json日志解析 nginx-logs.yaml: | filter: "evt.Parsed.program startsWith 'nginx'" onsuccess: next_stage name: crowdsecurity/nginx-logs description: "Parse nginx access and error logs" pattern_syntax: NGCUSTOMURIPATH: "(?:/[A-Za-z0-9$.+!*'\\(\\)\\{\\},~:;=@\\#%&_\\-]*)+" NGCUSTOMURIPATHPARAM: '%{NGCUSTOMURIPATH}(?:%{URIPARAM})?' nodes: # nginx access logs - filter: TrimSpace(evt.Parsed.message) startsWith "{" && UnmarshalJSON(evt.Parsed.message, evt.Unmarshaled, "nginx") in ["", nil] statics: - meta: service value: http - meta: log_type value: http_access-log - target: evt.StrTime expression: evt.Unmarshaled.nginx.time_local - meta: source_ip expression: evt.Unmarshaled.nginx.remote_addr - meta: http_status expression: evt.Unmarshaled.nginx.status - meta: http_path expression: evt.Unmarshaled.nginx.request_uri - meta: http_verb expression: evt.Unmarshaled.nginx.request_method - meta: http_user_agent expression: evt.Unmarshaled.nginx.http_user_agent - meta: target_fqdn expression: evt.Unmarshaled.nginx.server_name # nginx error logs - grok: pattern: '(%{IPORHOST:target_fqdn} )?%{NGINXERRTIME:time} \[%{LOGLEVEL:loglevel}\] %{NONNEGINT:pid}#%{NONNEGINT:tid}: (\*%{NONNEGINT:cid} )?%{GREEDYDATA:message}, client: %{IPORHOST:remote_addr}, server: %{DATA:target_fqdn}, request: "%{WORD:verb} ([^/]+)?%{NGCUSTOMURIPATHPARAM:request}( HTTP/%{NUMBER:http_version})?", host: "%{IPORHOST}(:%{NONNEGINT})?"' apply_on: message statics: - meta: service value: http - meta: log_type value: http_error-log - target: evt.StrTime expression: evt.Parsed.time - meta: source_ip expression: evt.Parsed.remote_addr - meta: http_status expression: evt.Parsed.status - meta: http_path expression: evt.Parsed.request - meta: http_verb expression: evt.Parsed.verb - meta: http_user_agent expression: evt.Parsed.http_user_agent - meta: target_fqdn expression: evt.Parsed.target_fqdn pattern_syntax: NO_DOUBLE_QUOTE: '[^"]+' onsuccess: next_stage nodes: - filter: "evt.Parsed.message contains 'was not found in'" pattern_syntax: USER_NOT_FOUND: 'user "%{NO_DOUBLE_QUOTE:username}" was not found in "%{NO_DOUBLE_QUOTE}"' grok: pattern: '%{USER_NOT_FOUND}' apply_on: message statics: - meta: sub_type value: "auth_fail" - meta: username expression: evt.Parsed.username - filter: "evt.Parsed.message contains 'password mismatch'" pattern_syntax: PASSWORD_MISMATCH: 'user "%{NO_DOUBLE_QUOTE:username}": password mismatch' grok: pattern: '%{PASSWORD_MISMATCH}' apply_on: message statics: - meta: sub_type value: "auth_fail" - meta: username expression: evt.Parsed.username - filter: "evt.Parsed.message contains 'limiting requests, excess'" statics: - meta: sub_type value: "req_limit_exceeded"