-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Shivam Sandbhor <[email protected]> Signed-off-by: Shivam Sandbhor <[email protected]> Co-authored-by: Shivam Sandbhor <[email protected]> Co-authored-by: Shivam Sandbhor <[email protected]>
- Loading branch information
1 parent
910291b
commit 4f39d84
Showing
20 changed files
with
773 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
name: Functional Tests | ||
|
||
on: | ||
push: | ||
branches: [ main ] | ||
pull_request: | ||
branches: [ main ] | ||
|
||
jobs: | ||
install_crowdsec: | ||
name: Build | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Set up Go 1.19 | ||
uses: actions/setup-go@v3 | ||
with: | ||
go-version: 1.19 | ||
id: go | ||
- name: Check out code into the Go module directory | ||
uses: actions/checkout@v3 | ||
- id: cache-pipenv | ||
uses: actions/cache@v3 | ||
with: | ||
path: ~/.local/share/virtualenvs | ||
key: ${{ runner.os }}-pipenv-${{ hashFiles('**/Pipfile.lock') }} | ||
- name: Install deps for tests | ||
run: | | ||
sudo apt install -y nftables iptables ipset | ||
python3 -m pip install --upgrade pipenv wheel | ||
- name: Run tests | ||
run: | | ||
make build | ||
pipenv install --deploy | ||
pipenv run pytest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[packages] | ||
pytest = "*" | ||
flask = "*" | ||
pytimeparse = "*" | ||
psutil = "*" | ||
|
||
[dev-packages] | ||
gnureadline = "*" | ||
ipdb = "*" | ||
|
||
[requires] | ||
python_version = "3.10" |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[pytest] | ||
# drop to pdb on first failure | ||
addopts = --pdb --pdbcls=IPython.terminal.debugger:Pdb |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[pytest] | ||
addopts = |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
import datetime | ||
import logging | ||
from datetime import timedelta | ||
from ipaddress import ip_address | ||
from threading import Thread | ||
from time import sleep | ||
|
||
from flask import Flask, abort, request | ||
from pytimeparse.timeparse import timeparse | ||
from werkzeug.serving import make_server | ||
|
||
|
||
# This is the "database" of our dummy LAPI | ||
class DataStore: | ||
def __init__(self) -> None: | ||
self.id = 0 | ||
self.decisions = [] | ||
self.bouncer_lastpull_by_api_key = {} | ||
|
||
def insert_decisions(self, decisions): | ||
for i, _ in enumerate(decisions): | ||
decisions[i]["created_at"] = datetime.datetime.now() | ||
decisions[i]["deleted_at"] = self.get_decision_expiry_time(decisions[i]) | ||
decisions[i]["id"] = self.id | ||
self.id += 1 | ||
self.decisions.extend(decisions) | ||
|
||
# This methods can be made more generic by taking lambda expr as input for filtering | ||
# decisions to delete | ||
def delete_decisions_by_ip(self, ip): | ||
for i, decision in enumerate(self.decisions): | ||
if ip_address(decision["value"]) == ip_address(ip): | ||
self.decisions[i]["deleted_at"] = datetime.datetime.now() | ||
|
||
def delete_decision_by_id(self, id): | ||
for i, decision in enumerate(self.decisions): | ||
if decision["id"] == id: | ||
self.decisions[i]["deleted_at"] = datetime.datetime.now() | ||
break | ||
|
||
def update_bouncer_pull(self, api_key): | ||
self.bouncer_lastpull_by_api_key[api_key] = datetime.datetime.now() | ||
|
||
def get_active_and_expired_decisions_since(self, since): | ||
expired_decisions = [] | ||
active_decisions = [] | ||
|
||
for decision in self.decisions: | ||
# decision["deleted_at"] > datetime.datetime.now() means that decision hasn't yet expired | ||
if decision["deleted_at"] > since and decision["deleted_at"] < datetime.datetime.now(): | ||
expired_decisions.append(decision) | ||
|
||
elif decision["created_at"] > since: | ||
active_decisions.append(decision) | ||
return active_decisions, expired_decisions | ||
|
||
def get_decisions_for_bouncer(self, api_key, startup=False): | ||
if startup or api_key not in self.bouncer_lastpull_by_api_key: | ||
since = datetime.datetime.min | ||
self.bouncer_lastpull_by_api_key[api_key] = since | ||
else: | ||
since = self.bouncer_lastpull_by_api_key[api_key] | ||
|
||
self.update_bouncer_pull(api_key) | ||
return self.get_active_and_expired_decisions_since(since) | ||
|
||
@staticmethod | ||
def get_decision_expiry_time(decision): | ||
return decision["created_at"] + timedelta(seconds=timeparse(decision["duration"])) | ||
|
||
|
||
class MockLAPI: | ||
def __init__(self) -> None: | ||
self.app = Flask(__name__) | ||
self.app.add_url_rule("/v1/decisions/stream", view_func=self.decisions) | ||
log = logging.getLogger("werkzeug") | ||
log.setLevel(logging.ERROR) | ||
self.app.logger.disabled = True | ||
log.disabled = True | ||
self.ds = DataStore() | ||
|
||
def decisions(self): | ||
api_key = request.headers.get("x-api-key") | ||
if not api_key: | ||
abort(404) | ||
startup = True if request.args.get("startup") == "true" else False | ||
active_decisions, expired_decisions = self.ds.get_decisions_for_bouncer(api_key, startup) | ||
return { | ||
"new": formatted_decisions(active_decisions), | ||
"deleted": formatted_decisions(expired_decisions), | ||
} | ||
|
||
def start(self, port=8081): | ||
self.server_thread = ServerThread(self.app, port=port) | ||
self.server_thread.start() | ||
|
||
def stop(self): | ||
self.server_thread.shutdown() | ||
|
||
|
||
def formatted_decisions(decisions): | ||
formatted_decisions = [] | ||
for decision in decisions: | ||
expiry_time = decision["created_at"] + timedelta(seconds=timeparse(decision["duration"])) | ||
duration = expiry_time - datetime.datetime.now() | ||
formatted_decisions.append( | ||
{ | ||
"duration": f"{duration.total_seconds()}s", | ||
"id": decision["id"], | ||
"origin": decision["origin"], | ||
"scenario": "cscli", | ||
"scope": decision["scope"], | ||
"type": decision["type"], | ||
"value": decision["value"], | ||
} | ||
) | ||
return formatted_decisions | ||
|
||
|
||
# Copied from https://stackoverflow.com/a/45017691 . | ||
# We run server inside thread instead of process to avoid | ||
# huge complexity of sharing objects | ||
class ServerThread(Thread): | ||
def __init__(self, app, port=8081): | ||
Thread.__init__(self) | ||
self.server = make_server("127.0.0.1", port, app) | ||
self.ctx = app.app_context() | ||
self.ctx.push() | ||
|
||
def run(self): | ||
self.server.serve_forever() | ||
|
||
def shutdown(self): | ||
self.server.shutdown() | ||
|
||
|
||
if __name__ == "__main__": | ||
MockLAPI().start() | ||
sleep(100) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
bin_path: tests/stdinmode/custombinary | ||
feed_via_stdin: true # Invokes binary once and feeds incoming decisions to it's stdin. | ||
total_retries: 2 # number of times to restart binary. relevant if feed_via_stdin=true . Set to -1 for infinite retries. | ||
scenarios_containing: [] # ignore IPs banned for triggering scenarios not containing either of provided word, eg ["ssh", "http"] | ||
scenarios_not_containing: [] # ignore IPs banned for triggering scenarios containing either of provided word | ||
origins: [] | ||
piddir: /var/run/ | ||
update_frequency: 0.1s | ||
cache_retention_duration: 10s | ||
daemonize: false | ||
log_mode: stdout | ||
log_dir: /var/log/ | ||
log_level: info | ||
api_url: http://localhost:8081/ | ||
api_key: 1237adaf7a1724ac68a3288828820a67 | ||
|
||
prometheus: | ||
enabled: false | ||
listen_addr: 127.0.0.1 | ||
listen_port: 60602 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#!/bin/bash | ||
rm data.txt > /dev/null | ||
while read line | ||
do | ||
echo "$line" >> data.txt | ||
done | ||
# rm data.txt > /dev/null |
Oops, something went wrong.