diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 522320ed10524c3ffe6d5eadcd598b93a32b8564..dc5789d039d5c8137de35507128de0dcecffd3de 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,11 +24,14 @@ test: stage: test script: - apk add bash - - docker stop $SERVICE || true && docker rm $SERVICE || true - - docker run --env-file .env --name $SERVICE -d $REGISTRY/medina/$SERVICE:$VERSION - - sleep 5 + - docker network create test-ec + - docker run --rm --network=test-ec --env-file .env --name $SERVICE -d $REGISTRY/medina/$SERVICE:$VERSION + - docker run --rm --network=test-ec toschneck/wait-for-it $SERVICE:7890 -t 240 - bash test/test.sh - - docker stop $SERVICE && docker container rm $SERVICE + after_script: + - SERVICE=$(grep SERVICE MANIFEST | cut -d '=' -f2) + - docker kill $SERVICE || docker network rm test-ec + - docker network rm test-ec push: stage: push diff --git a/Dockerfile b/Dockerfile index 139f77b2e15bbb5dfeecb5463e643ad10679879c..e6114392a210b760d0192b68fd109711d9f46b86 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,6 +9,6 @@ RUN pip3 install -r requirements.txt COPY . . -RUN apt-get update && apt-get install -y redis-server +RUN apt-get update && apt-get install -y redis-server netcat -ENTRYPOINT ["./entrypoint.sh"] \ No newline at end of file +ENTRYPOINT ["./entrypoint.sh"] diff --git a/MANIFEST b/MANIFEST index 784075e05b795840d8b1c9856fd67683a065eed9..2bde23df0a2173d6034bad9a93a9c6d4b948c8aa 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,2 +1,2 @@ -VERSION=v0.0.11 +VERSION=v0.0.12 SERVICE=evidence-collector diff --git a/Makefile b/Makefile index 0118f54f211f92007b8d3c025e577f6d5e874af9..ea933d047636462aa129883b6e8c7da12be83aaf 100644 --- a/Makefile +++ b/Makefile @@ -2,4 +2,4 @@ build: docker build -t evidence-collector . run: - docker run --env-file .env evidence-collector \ No newline at end of file + docker run --env-file .env evidence-collector diff --git a/entrypoint.sh b/entrypoint.sh index 57d2e43bf563fe14aee345fae1a1fdfe5c829fa3..087cfffc23e74c7d0dfc38959c170a412f2bae39 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -8,4 +8,8 @@ rqscheduler --host $redis_host --port $redis_port & python3 -m scheduler.scheduler -tail -f /var/log/evidence_collector.log \ No newline at end of file +# open a listener on port 7890 for 1 second +# only for testing - CI script contains a wait-for-it that binds to this port +nc -l -p 7890 -w 1 + +tail -f /var/log/evidence_collector.log diff --git a/forward_evidence/clouditor_authentication.py b/forward_evidence/clouditor_authentication.py index 592f51061fe721ce209aec659958239c28ee072f..31abd14ee6d3b43655eb400aeda59ab6fc871ef4 100644 --- a/forward_evidence/clouditor_authentication.py +++ b/forward_evidence/clouditor_authentication.py @@ -30,6 +30,8 @@ class ClouditorAuthentication(object): urllib3.exceptions.MaxRetryError, requests.exceptions.ConnectionError) as err: self.logger.error(err) self.logger.error("Clouditor OAuth2 token endpoint not available") + self.__access_token = None + self.__token_expiration_time = None else: token = json.loads(access_token_response.text) @@ -41,7 +43,7 @@ class ClouditorAuthentication(object): def get_token(self): # In practice this condition isn't even needed as every scheduled job creates new ClouditorAuthentication object and acquires new token. - if (datetime.utcnow() > self.__token_expiration_time): + if (self.__token_expiration_time != None and datetime.utcnow() > self.__token_expiration_time): self.logger.debug("OAuth2 token expired.") self.request_token() diff --git a/forward_evidence/forward_evidence.py b/forward_evidence/forward_evidence.py index 99ca4622aa766733202c16e242f8dfaf07d983f6..0c283d9f16439a03dbdf427be8e53105ae262fa8 100644 --- a/forward_evidence/forward_evidence.py +++ b/forward_evidence/forward_evidence.py @@ -14,7 +14,10 @@ class ForwardEvidence(object): def send_evidence(self, assessevidencerequest, token): try: - metadata = [('authorization', 'Bearer ' + token)] + if token is not None: + metadata = [('authorization', 'Bearer ' + token)] + else: + metadata = None response = self.stub.AssessEvidence(assessevidencerequest, metadata=metadata) self.logger.info('gRPC evidence forwarded: ' + str(response)) diff --git a/test/test.sh b/test/test.sh index 7a6489119c53b97bfeb25b44f5710a8fa2719c6f..4fbf02adba062571d3914d5ef5f202d3e065567b 100755 --- a/test/test.sh +++ b/test/test.sh @@ -6,6 +6,7 @@ redis2="Ready to accept connections" scheduler="Registering birth" worker1="Worker rq:worker:" worker2="Listening on " +oauth2token="Clouditor OAuth2 token endpoint not available" if ! [[ $logs =~ $redis1 ]] then @@ -36,3 +37,9 @@ if ! [[ $logs =~ $worker2 ]] echo "Redis worker not started" 1>&2 exit 1 fi + +if ! [[ $logs =~ $oauth2token ]] + then + echo "OAuth2 token authentication not working" 1>&2 + exit 1 +fi diff --git a/wazuh_evidence_collector/checker.py b/wazuh_evidence_collector/checker.py index a090a9179367fdf705a417da4451f01cafbcb603..6480d231bcd9a44a955c21a9e14af9aeba8cf070 100644 --- a/wazuh_evidence_collector/checker.py +++ b/wazuh_evidence_collector/checker.py @@ -1,11 +1,13 @@ from wazuh_evidence_collector.wazuh_client import WazuhClient -from elasticsearch import Elasticsearch +import elasticsearch +import urllib3 from elasticsearch_dsl import Search class Checker: - def __init__(self, wc, es): + def __init__(self, wc, es, logger): self.wc = wc self.es = es + self.logger = logger # Check if syscheck enabled def check_syscheck(self, agent): @@ -92,8 +94,15 @@ class Checker: .query("match", rule__description="Clamd restarted") \ .query("match", agent__id=agent[0]) - body = s.execute().to_dict() - + try: + body = s.execute().to_dict() + except (elasticsearch.exceptions.ConnectionError, TimeoutError, urllib3.exceptions.NewConnectionError, + urllib3.exceptions.MaxRetryError) as err: + self.logger.error(err) + self.logger.error("Elasticsearch not available") + + return None, False + measurement_result = len(body['hits']['hits']) > 0 return body, measurement_result diff --git a/wazuh_evidence_collector/wazuh_client.py b/wazuh_evidence_collector/wazuh_client.py index d0027517b21049bd2ad76f2848ed027893da2af8..43cf1e4bf6858c2b58dccb2507d9c5a1f51a7e21 100644 --- a/wazuh_evidence_collector/wazuh_client.py +++ b/wazuh_evidence_collector/wazuh_client.py @@ -3,12 +3,13 @@ import urllib3 class WazuhClient: - def __init__(self, ip, port, username, password): + def __init__(self, ip, port, username, password, logger): self._ip = ip self._port = port self._username = username self._password = password self._auth_token = None + self.logger = logger def req(self, method, resource, data=None, headers={}, auth_retry=True): # TODO: add cert verification @@ -19,7 +20,13 @@ class WazuhClient: if self._auth_token: headers['Authorization'] = 'Bearer %s' % self._auth_token - resp = c.request(method, url, headers=headers, body=data) + try: + resp = c.request(method, url, headers=headers, body=data) + except (TimeoutError, urllib3.exceptions.NewConnectionError, + urllib3.exceptions.MaxRetryError) as err: + self.logger.error(err) + self.logger.error("Wazuh manager not available") + if resp.status == 401: if not auth_retry: raise Exception("Authentication Error") diff --git a/wazuh_evidence_collector/wazuh_evidence_collector.py b/wazuh_evidence_collector/wazuh_evidence_collector.py index 694eedb1b1b2e0cf30a6091362267af6d376d9ea..c13a063929527aae82c002c2b3bf3f44af90caf8 100644 --- a/wazuh_evidence_collector/wazuh_evidence_collector.py +++ b/wazuh_evidence_collector/wazuh_evidence_collector.py @@ -27,7 +27,7 @@ ELASTIC_USERNAME = os.environ.get("elastic_username") ELASTIC_PASSWORD = os.environ.get("elastic_password") if not DEMO: - wc = WazuhClient(WAZUH_HOST, WAZUH_PORT, WAZUH_USERNAME, WAZUH_PASSWORD) + wc = WazuhClient(WAZUH_HOST, WAZUH_PORT, WAZUH_USERNAME, WAZUH_PASSWORD, LOGGER) es = Elasticsearch( ELASTIC_HOST, @@ -69,7 +69,7 @@ def main(): # Wrapper function that runs all the checks (for every manager/agent) def run_collector(): - checker = Checker(wc, es) if not DEMO else DemoChecker() + checker = Checker(wc, es, LOGGER) if not DEMO else DemoChecker() # Get list of all agent ids (including manager's) def get_agents(wc):