Forráskód Böngészése

Add functional tests for plugins (#941)

* Add functional tests for plugins

Signed-off-by: Shivam Sandbhor <shivam.sandbhor@gmail.com>
(cherry picked from commit 1a11c87296454aac1ebbc95c06b813ae67c91819)

* Update plugin dir in functional tests

Signed-off-by: Shivam Sandbhor <shivam.sandbhor@gmail.com>

* Update plugin process config in func tests

Signed-off-by: Shivam Sandbhor <shivam.sandbhor@gmail.com>

* Robust config replacement

Signed-off-by: Shivam Sandbhor <shivam.sandbhor@gmail.com>
Shivam Sandbhor 3 éve
szülő
commit
e89543f725

+ 4 - 0
.github/workflows/ci_functests-install.yml

@@ -72,6 +72,10 @@ jobs:
       run: |
           cd scripts/func_tests/
           ./tests_post-install_5simulation.sh
+    - name: "Test post-install plugins"
+      run: |
+          cd scripts/func_tests/
+          sudo ./tests_post-install_7_plugin.sh
     - name: "Uninstall"
       run: sudo ./wizard.sh --uninstall
     - name: "Test post remove"

+ 5 - 0
scripts/func_tests/config/config.yaml

@@ -11,11 +11,16 @@ config_paths:
   simulation_path: /etc/crowdsec/simulation.yaml
   hub_dir: /etc/crowdsec/hub/
   index_path: /etc/crowdsec/hub/.index.json
+  notification_dir: /etc/crowdsec/notifications/
+  plugin_dir: /usr/local/lib/crowdsec/plugins
 crowdsec_service:
   acquisition_path: /etc/crowdsec/acquis.yaml
   parser_routines: 1
 cscli:
   output: human
+plugin_config:
+  user: nobody # plugin process would be ran on behalf of this user
+  group: nogroup # plugin process would be ran on behalf of this group
 db_config:
   log_level: info
   type: sqlite

+ 5 - 0
scripts/func_tests/config/config_no_agent.yaml

@@ -12,6 +12,8 @@ config_paths:
   simulation_path: /etc/crowdsec/simulation.yaml
   hub_dir: /etc/crowdsec/hub/
   index_path: /etc/crowdsec/hub/.index.json
+  notification_dir: /etc/crowdsec/notifications/
+  plugin_dir: /usr/local/lib/crowdsec/plugins
 cscli:
   output: human
 db_config:
@@ -21,6 +23,9 @@ db_config:
   flush:
     max_items: 5000
     max_age: 7d
+plugin_config:
+  user: nobody # plugin process would be ran on behalf of this user
+  group: nogroup # plugin process would be ran on behalf of this group
 api:
   client:
     insecure_skip_verify: false

+ 5 - 0
scripts/func_tests/config/config_no_capi.yaml

@@ -11,6 +11,8 @@ config_paths:
   simulation_path: /etc/crowdsec/simulation.yaml
   hub_dir: /etc/crowdsec/hub/
   index_path: /etc/crowdsec/hub/.index.json
+  notification_dir: /etc/crowdsec/notifications/
+  plugin_dir: /usr/local/lib/crowdsec/plugins
 crowdsec_service:
   acquisition_path: /etc/crowdsec/acquis.yaml
   parser_routines: 1
@@ -23,6 +25,9 @@ db_config:
   flush:
     max_items: 5000
     max_age: 7d
+plugin_config:
+  user: nobody # plugin process would be ran on behalf of this user
+  group: nogroup # plugin process would be ran on behalf of this group
 api:
   client:
     insecure_skip_verify: false

+ 5 - 0
scripts/func_tests/config/config_no_lapi.yaml

@@ -11,11 +11,16 @@ config_paths:
   simulation_path: /etc/crowdsec/simulation.yaml
   hub_dir: /etc/crowdsec/hub/
   index_path: /etc/crowdsec/hub/.index.json
+  notification_dir: /etc/crowdsec/notifications/
+  plugin_dir: /usr/local/lib/crowdsec/plugins
 crowdsec_service:
   acquisition_path: /etc/crowdsec/acquis.yaml
   parser_routines: 1
 cscli:
   output: human
+plugin_config:
+  user: nobody # plugin process would be ran on behalf of this user
+  group: nogroup # plugin process would be ran on behalf of this group
 db_config:
   log_level: info
   type: sqlite

+ 25 - 0
scripts/func_tests/config/http.yaml

@@ -0,0 +1,25 @@
+# Don't change this
+type: http
+
+name: http_default # this must match with the registered plugin in the profile
+log_level: info # Options include: trace, debug, info, warn, error, off
+
+format: |  # This template receives list of models.Alert objects. The request body would contain this. 
+  {{.|toJson}}
+
+url: http://localhost:9999 # plugin will make requests to this url. Eg value https://www.example.com/
+
+method: POST # eg either of "POST", "GET", "PUT" and other http verbs is valid value. 
+
+# headers:
+#   Authorization: token 0x64312313
+
+# skip_tls_verification:  # either true or false. Default is false
+
+# group_wait: # duration to wait collecting alerts before sending to this plugin, eg "30s"
+
+group_threshold: 2 # if alerts exceed this, then the plugin will be sent the message. eg "10"
+
+# max_retry: # number of tries to attempt to send message to plugins in case of error.
+
+# timeout: # duration to wait for response from plugin before considering this attempt a failure. eg "10s"

+ 12 - 0
scripts/func_tests/config/profiles.yaml

@@ -0,0 +1,12 @@
+name: default_ip_remediation
+#debug: true
+filters:
+ - 1==1
+decisions:
+ - type: ban
+   duration: 4h
+notifications:
+#   - slack_default  # Set the webhook in /etc/crowdsec/notifications/slack.yaml before enabling this.
+#   - splunk_default # Set the splunk url and token in /etc/crowdsec/notifications/splunk.yaml  before enabling this.
+  - http_default # Set the required http parameters in  /etc/crowdsec/notifications/http.yaml before enabling this.
+on_success: break

+ 26 - 0
scripts/func_tests/mock_http_server.py

@@ -0,0 +1,26 @@
+import json
+from http.server import HTTPServer, BaseHTTPRequestHandler
+
+class RequestHandler(BaseHTTPRequestHandler):
+    def do_POST(self):
+        request_path = self.path
+        request_body = self.rfile.read(int(self.headers['Content-Length']))
+        request_body = json.loads(request_body)
+        log = {
+            "path": request_path,
+            "status": 200, 
+            "request_body": request_body,
+        }
+        print(json.dumps(log))
+        self.send_response(200)
+        self.send_header('Content-type','application/json')
+        self.end_headers()
+        self.wfile.write(json.dumps({}).encode())
+        return
+    
+    def log_message(self, format, *args):
+        return
+
+if __name__ == "__main__" :
+    server = HTTPServer(('', 9999), RequestHandler)
+    server.serve_forever()

+ 81 - 0
scripts/func_tests/tests_post-install_7_plugin.sh

@@ -0,0 +1,81 @@
+#! /usr/bin/env bash
+# -*- coding: utf-8 -*-
+
+source tests_base.sh
+
+MOCK_SERVER_PID=""
+
+function backup () {
+    cat /etc/crowdsec/profiles.yaml > ./backup_profiles.yaml
+    cat /etc/crowdsec/notifications/http.yaml > ./backup_http.yaml
+}
+
+function restore_backup () {
+    cat ./backup_profiles.yaml > /etc/crowdsec/profiles.yaml  
+    cat ./backup_http.yaml > /etc/crowdsec/notifications/http.yaml
+}
+
+function clear_backup() {
+    rm ./backup_profiles.yaml
+    rm ./backup_http.yaml
+}
+
+function modify_config() {
+    cp ./config/http.yaml /etc/crowdsec/notifications/http.yaml
+    cp ./config/profiles.yaml /etc/crowdsec/profiles.yaml
+    systemctl restart crowdsec
+}
+
+function setup_tests() {
+    backup
+    cscli decisions delete --all
+    modify_config
+    python3 -u mock_http_server.py > mock_http_server_logs.log &
+    MOCK_SERVER_PID=$!
+}
+
+function cleanup_tests() {
+    restore_backup
+    clear_backup
+    kill -9 $MOCK_SERVER_PID
+    rm mock_http_server_logs.log
+    systemctl restart crowdsec
+}
+
+function run_tests() {
+    log_line_count=$(cat mock_http_server_logs.log | wc -l)
+    if [[ $log_line_count -ne "0" ]] ; then
+        cleanup_tests
+        fail "expected 0 log lines fom mock http server before adding decisions"
+    fi
+    cscli decisions add --ip 1.2.3.4 --duration 30s
+    cscli decisions add --ip 1.2.3.5 --duration 30s
+    sleep 5
+    log_line_count=$(cat mock_http_server_logs.log | wc -l)
+    if [[ $log_line_count -ne "1" ]] ; then
+        cleanup_tests
+        fail "expected 1 log line from http server"
+    fi
+
+    total_alerts=$(cat mock_http_server_logs.log   | jq  .request_body | jq length)
+    if [[ $total_alerts -ne "2" ]] ; then
+        cleanup_tests
+        fail "expected to receive 2 alerts in the request body from plugin"
+    fi
+
+    first_received_ip=$(cat mock_http_server_logs.log  | jq -r .request_body[0].decisions[0].value)
+    if [[ $first_received_ip != "1.2.3.4" ]] ; then
+        cleanup_tests
+        fail "expected to receive IP 1.2.3.4 as value of first decision"
+    fi 
+
+    second_received_ip=$(cat mock_http_server_logs.log  | jq -r .request_body[1].decisions[0].value)
+    if [[ $second_received_ip != "1.2.3.5" ]] ; then
+        cleanup_tests
+        fail "expected to receive IP 1.2.3.5 as value of second decision"
+    fi 
+}
+
+setup_tests
+run_tests
+cleanup_tests