codefilterph-logcollector 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. codefilterph_logcollector-0.1.1/LICENSE +21 -0
  2. codefilterph_logcollector-0.1.1/MANIFEST.in +12 -0
  3. codefilterph_logcollector-0.1.1/PKG-INFO +56 -0
  4. codefilterph_logcollector-0.1.1/README.md +29 -0
  5. codefilterph_logcollector-0.1.1/codefilterph_logcollector.egg-info/PKG-INFO +56 -0
  6. codefilterph_logcollector-0.1.1/codefilterph_logcollector.egg-info/SOURCES.txt +59 -0
  7. codefilterph_logcollector-0.1.1/codefilterph_logcollector.egg-info/dependency_links.txt +1 -0
  8. codefilterph_logcollector-0.1.1/codefilterph_logcollector.egg-info/requires.txt +12 -0
  9. codefilterph_logcollector-0.1.1/codefilterph_logcollector.egg-info/top_level.txt +2 -0
  10. codefilterph_logcollector-0.1.1/crowdsec_integration/__init__.py +1 -0
  11. codefilterph_logcollector-0.1.1/crowdsec_integration/apps.py +6 -0
  12. codefilterph_logcollector-0.1.1/crowdsec_integration/migrations/0001_initial.py +22 -0
  13. codefilterph_logcollector-0.1.1/crowdsec_integration/migrations/__init__.py +1 -0
  14. codefilterph_logcollector-0.1.1/crowdsec_integration/models.py +14 -0
  15. codefilterph_logcollector-0.1.1/crowdsec_integration/services.py +70 -0
  16. codefilterph_logcollector-0.1.1/log_collector/__init__.py +1 -0
  17. codefilterph_logcollector-0.1.1/log_collector/apps.py +13 -0
  18. codefilterph_logcollector-0.1.1/log_collector/consumers.py +56 -0
  19. codefilterph_logcollector-0.1.1/log_collector/docs/api.md +27 -0
  20. codefilterph_logcollector-0.1.1/log_collector/docs/configuration.md +82 -0
  21. codefilterph_logcollector-0.1.1/log_collector/docs/installation.md +40 -0
  22. codefilterph_logcollector-0.1.1/log_collector/docs/operations-guide.md +52 -0
  23. codefilterph_logcollector-0.1.1/log_collector/forms.py +160 -0
  24. codefilterph_logcollector-0.1.1/log_collector/integrations.py +54 -0
  25. codefilterph_logcollector-0.1.1/log_collector/management/__init__.py +0 -0
  26. codefilterph_logcollector-0.1.1/log_collector/management/commands/__init__.py +0 -0
  27. codefilterph_logcollector-0.1.1/log_collector/management/commands/backfill_security_incidents.py +30 -0
  28. codefilterph_logcollector-0.1.1/log_collector/management/commands/manage_security_block.py +58 -0
  29. codefilterph_logcollector-0.1.1/log_collector/management/commands/retry_security_sms.py +33 -0
  30. codefilterph_logcollector-0.1.1/log_collector/middleware.py +57 -0
  31. codefilterph_logcollector-0.1.1/log_collector/migrations/0001_initial.py +127 -0
  32. codefilterph_logcollector-0.1.1/log_collector/migrations/0002_remove_logcollectorentity_allowed_api_key_names_and_more.py +67 -0
  33. codefilterph_logcollector-0.1.1/log_collector/migrations/0003_blockedipaddress_securityincident_ipblockauditevent_and_more.py +169 -0
  34. codefilterph_logcollector-0.1.1/log_collector/migrations/0004_app_appearance.py +31 -0
  35. codefilterph_logcollector-0.1.1/log_collector/migrations/__init__.py +1 -0
  36. codefilterph_logcollector-0.1.1/log_collector/models.py +601 -0
  37. codefilterph_logcollector-0.1.1/log_collector/permissions.py +283 -0
  38. codefilterph_logcollector-0.1.1/log_collector/routing.py +76 -0
  39. codefilterph_logcollector-0.1.1/log_collector/security_events.py +35 -0
  40. codefilterph_logcollector-0.1.1/log_collector/services/__init__.py +1 -0
  41. codefilterph_logcollector-0.1.1/log_collector/services/blocking.py +309 -0
  42. codefilterph_logcollector-0.1.1/log_collector/services/client_ip.py +75 -0
  43. codefilterph_logcollector-0.1.1/log_collector/services/incidents.py +326 -0
  44. codefilterph_logcollector-0.1.1/log_collector/services/nginx.py +54 -0
  45. codefilterph_logcollector-0.1.1/log_collector/services/remote.py +32 -0
  46. codefilterph_logcollector-0.1.1/log_collector/services/response.py +67 -0
  47. codefilterph_logcollector-0.1.1/log_collector/services/security.py +379 -0
  48. codefilterph_logcollector-0.1.1/log_collector/services/security_sms.py +98 -0
  49. codefilterph_logcollector-0.1.1/log_collector/services/sync.py +103 -0
  50. codefilterph_logcollector-0.1.1/log_collector/services/trusted_ips.py +55 -0
  51. codefilterph_logcollector-0.1.1/log_collector/static/log_collector/appearance.css +46 -0
  52. codefilterph_logcollector-0.1.1/log_collector/tasks.py +261 -0
  53. codefilterph_logcollector-0.1.1/log_collector/templates/log_collector/dashboard.html +1544 -0
  54. codefilterph_logcollector-0.1.1/log_collector/templates/log_collector/entity_form.html +45 -0
  55. codefilterph_logcollector-0.1.1/log_collector/templates/log_collector/entity_manage.html +425 -0
  56. codefilterph_logcollector-0.1.1/log_collector/templates/log_collector/entity_selection.html +306 -0
  57. codefilterph_logcollector-0.1.1/log_collector/templates/log_collector/security_report.html +99 -0
  58. codefilterph_logcollector-0.1.1/log_collector/urls.py +67 -0
  59. codefilterph_logcollector-0.1.1/log_collector/views.py +1889 -0
  60. codefilterph_logcollector-0.1.1/pyproject.toml +53 -0
  61. codefilterph_logcollector-0.1.1/setup.cfg +4 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CodefilterPH | Euge
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,12 @@
1
+ include README.md
2
+ include LICENSE
3
+ prune log_collector/docs
4
+ include log_collector/docs/installation.md
5
+ include log_collector/docs/configuration.md
6
+ include log_collector/docs/api.md
7
+ include log_collector/docs/operations-guide.md
8
+ recursive-include log_collector/templates *.html
9
+ recursive-include log_collector/static *.css
10
+ prune log_collector/tests
11
+ prune packaging
12
+ global-exclude __pycache__ *.py[cod] *.sqlite3 *.db .env*
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.4
2
+ Name: codefilterph-logcollector
3
+ Version: 0.1.1
4
+ Summary: Django Nginx log collection, security monitoring, and remote collector dashboards
5
+ Author: CodefilterPH | Euge
6
+ License-Expression: MIT
7
+ Project-URL: Documentation, https://github.com/codefilterPH/logcollector/tree/main/log_collector/docs
8
+ Project-URL: Source, https://github.com/codefilterPH/logcollector
9
+ Classifier: Framework :: Django
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Topic :: System :: Logging
13
+ Requires-Python: <3.14,>=3.12
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: Django<6.1,>=6.0
17
+ Requires-Dist: channels<5,>=4.2
18
+ Requires-Dist: celery<6,>=5.5
19
+ Requires-Dist: djangorestframework<4,>=3.16
20
+ Requires-Dist: requests<3,>=2.32
21
+ Requires-Dist: cryptography<47,>=44
22
+ Provides-Extra: redis
23
+ Requires-Dist: channels-redis<5,>=4.2; extra == "redis"
24
+ Provides-Extra: asgi
25
+ Requires-Dist: daphne<5,>=4.2; extra == "asgi"
26
+ Dynamic: license-file
27
+
28
+ # LOG Collector
29
+
30
+ Developed by: CodefilterPH | Euge
31
+
32
+ LOG Collector is a Django application for local Nginx access-log collection,
33
+ security incidents, API access, WebSocket updates, Celery processing, and
34
+ read-only connections to other LOG Collector installations. CrowdSec connection
35
+ management is included in the distribution.
36
+
37
+ Install with `pip install codefilterph-logcollector`. Add `log_collector` and
38
+ `crowdsec_integration` to `INSTALLED_APPS`, mount `log_collector.urls`, and run
39
+ migrations. Nginx file access, ASGI, a channel layer, and Celery services are
40
+ configured by the host project; installing the wheel does not provision them.
41
+
42
+ The developer guides are installed in `log_collector/docs/` inside the Python
43
+ package and are also available in the source repository:
44
+
45
+ - [Installation](https://github.com/codefilterPH/logcollector/blob/main/log_collector/docs/installation.md)
46
+ - [Configuration](https://github.com/codefilterPH/logcollector/blob/main/log_collector/docs/configuration.md)
47
+ - [API integration](https://github.com/codefilterPH/logcollector/blob/main/log_collector/docs/api.md)
48
+ - [Operations](https://github.com/codefilterPH/logcollector/blob/main/log_collector/docs/operations-guide.md)
49
+
50
+ The app title defaults to **LOG Collector**. A Django superuser can set the
51
+ displayed title and shared theme on the hosting entity Manage page. The developer
52
+ credit is fixed in the application and cannot be edited through its settings.
53
+
54
+ License: [MIT](LICENSE).
55
+
56
+ Release owner: [first-release account setup](https://github.com/codefilterPH/logcollector/blob/main/RELEASE_SETUP.md).
@@ -0,0 +1,29 @@
1
+ # LOG Collector
2
+
3
+ Developed by: CodefilterPH | Euge
4
+
5
+ LOG Collector is a Django application for local Nginx access-log collection,
6
+ security incidents, API access, WebSocket updates, Celery processing, and
7
+ read-only connections to other LOG Collector installations. CrowdSec connection
8
+ management is included in the distribution.
9
+
10
+ Install with `pip install codefilterph-logcollector`. Add `log_collector` and
11
+ `crowdsec_integration` to `INSTALLED_APPS`, mount `log_collector.urls`, and run
12
+ migrations. Nginx file access, ASGI, a channel layer, and Celery services are
13
+ configured by the host project; installing the wheel does not provision them.
14
+
15
+ The developer guides are installed in `log_collector/docs/` inside the Python
16
+ package and are also available in the source repository:
17
+
18
+ - [Installation](https://github.com/codefilterPH/logcollector/blob/main/log_collector/docs/installation.md)
19
+ - [Configuration](https://github.com/codefilterPH/logcollector/blob/main/log_collector/docs/configuration.md)
20
+ - [API integration](https://github.com/codefilterPH/logcollector/blob/main/log_collector/docs/api.md)
21
+ - [Operations](https://github.com/codefilterPH/logcollector/blob/main/log_collector/docs/operations-guide.md)
22
+
23
+ The app title defaults to **LOG Collector**. A Django superuser can set the
24
+ displayed title and shared theme on the hosting entity Manage page. The developer
25
+ credit is fixed in the application and cannot be edited through its settings.
26
+
27
+ License: [MIT](LICENSE).
28
+
29
+ Release owner: [first-release account setup](https://github.com/codefilterPH/logcollector/blob/main/RELEASE_SETUP.md).
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.4
2
+ Name: codefilterph-logcollector
3
+ Version: 0.1.1
4
+ Summary: Django Nginx log collection, security monitoring, and remote collector dashboards
5
+ Author: CodefilterPH | Euge
6
+ License-Expression: MIT
7
+ Project-URL: Documentation, https://github.com/codefilterPH/logcollector/tree/main/log_collector/docs
8
+ Project-URL: Source, https://github.com/codefilterPH/logcollector
9
+ Classifier: Framework :: Django
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Topic :: System :: Logging
13
+ Requires-Python: <3.14,>=3.12
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: Django<6.1,>=6.0
17
+ Requires-Dist: channels<5,>=4.2
18
+ Requires-Dist: celery<6,>=5.5
19
+ Requires-Dist: djangorestframework<4,>=3.16
20
+ Requires-Dist: requests<3,>=2.32
21
+ Requires-Dist: cryptography<47,>=44
22
+ Provides-Extra: redis
23
+ Requires-Dist: channels-redis<5,>=4.2; extra == "redis"
24
+ Provides-Extra: asgi
25
+ Requires-Dist: daphne<5,>=4.2; extra == "asgi"
26
+ Dynamic: license-file
27
+
28
+ # LOG Collector
29
+
30
+ Developed by: CodefilterPH | Euge
31
+
32
+ LOG Collector is a Django application for local Nginx access-log collection,
33
+ security incidents, API access, WebSocket updates, Celery processing, and
34
+ read-only connections to other LOG Collector installations. CrowdSec connection
35
+ management is included in the distribution.
36
+
37
+ Install with `pip install codefilterph-logcollector`. Add `log_collector` and
38
+ `crowdsec_integration` to `INSTALLED_APPS`, mount `log_collector.urls`, and run
39
+ migrations. Nginx file access, ASGI, a channel layer, and Celery services are
40
+ configured by the host project; installing the wheel does not provision them.
41
+
42
+ The developer guides are installed in `log_collector/docs/` inside the Python
43
+ package and are also available in the source repository:
44
+
45
+ - [Installation](https://github.com/codefilterPH/logcollector/blob/main/log_collector/docs/installation.md)
46
+ - [Configuration](https://github.com/codefilterPH/logcollector/blob/main/log_collector/docs/configuration.md)
47
+ - [API integration](https://github.com/codefilterPH/logcollector/blob/main/log_collector/docs/api.md)
48
+ - [Operations](https://github.com/codefilterPH/logcollector/blob/main/log_collector/docs/operations-guide.md)
49
+
50
+ The app title defaults to **LOG Collector**. A Django superuser can set the
51
+ displayed title and shared theme on the hosting entity Manage page. The developer
52
+ credit is fixed in the application and cannot be edited through its settings.
53
+
54
+ License: [MIT](LICENSE).
55
+
56
+ Release owner: [first-release account setup](https://github.com/codefilterPH/logcollector/blob/main/RELEASE_SETUP.md).
@@ -0,0 +1,59 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ codefilterph_logcollector.egg-info/PKG-INFO
6
+ codefilterph_logcollector.egg-info/SOURCES.txt
7
+ codefilterph_logcollector.egg-info/dependency_links.txt
8
+ codefilterph_logcollector.egg-info/requires.txt
9
+ codefilterph_logcollector.egg-info/top_level.txt
10
+ crowdsec_integration/__init__.py
11
+ crowdsec_integration/apps.py
12
+ crowdsec_integration/models.py
13
+ crowdsec_integration/services.py
14
+ crowdsec_integration/migrations/0001_initial.py
15
+ crowdsec_integration/migrations/__init__.py
16
+ log_collector/__init__.py
17
+ log_collector/apps.py
18
+ log_collector/consumers.py
19
+ log_collector/forms.py
20
+ log_collector/integrations.py
21
+ log_collector/middleware.py
22
+ log_collector/models.py
23
+ log_collector/permissions.py
24
+ log_collector/routing.py
25
+ log_collector/security_events.py
26
+ log_collector/tasks.py
27
+ log_collector/urls.py
28
+ log_collector/views.py
29
+ log_collector/docs/api.md
30
+ log_collector/docs/configuration.md
31
+ log_collector/docs/installation.md
32
+ log_collector/docs/operations-guide.md
33
+ log_collector/management/__init__.py
34
+ log_collector/management/commands/__init__.py
35
+ log_collector/management/commands/backfill_security_incidents.py
36
+ log_collector/management/commands/manage_security_block.py
37
+ log_collector/management/commands/retry_security_sms.py
38
+ log_collector/migrations/0001_initial.py
39
+ log_collector/migrations/0002_remove_logcollectorentity_allowed_api_key_names_and_more.py
40
+ log_collector/migrations/0003_blockedipaddress_securityincident_ipblockauditevent_and_more.py
41
+ log_collector/migrations/0004_app_appearance.py
42
+ log_collector/migrations/__init__.py
43
+ log_collector/services/__init__.py
44
+ log_collector/services/blocking.py
45
+ log_collector/services/client_ip.py
46
+ log_collector/services/incidents.py
47
+ log_collector/services/nginx.py
48
+ log_collector/services/remote.py
49
+ log_collector/services/response.py
50
+ log_collector/services/security.py
51
+ log_collector/services/security_sms.py
52
+ log_collector/services/sync.py
53
+ log_collector/services/trusted_ips.py
54
+ log_collector/static/log_collector/appearance.css
55
+ log_collector/templates/log_collector/dashboard.html
56
+ log_collector/templates/log_collector/entity_form.html
57
+ log_collector/templates/log_collector/entity_manage.html
58
+ log_collector/templates/log_collector/entity_selection.html
59
+ log_collector/templates/log_collector/security_report.html
@@ -0,0 +1,12 @@
1
+ Django<6.1,>=6.0
2
+ channels<5,>=4.2
3
+ celery<6,>=5.5
4
+ djangorestframework<4,>=3.16
5
+ requests<3,>=2.32
6
+ cryptography<47,>=44
7
+
8
+ [asgi]
9
+ daphne<5,>=4.2
10
+
11
+ [redis]
12
+ channels-redis<5,>=4.2
@@ -0,0 +1,2 @@
1
+ crowdsec_integration
2
+ log_collector
@@ -0,0 +1,6 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class CrowdSecIntegrationConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "crowdsec_integration"
@@ -0,0 +1,22 @@
1
+ """Initial CrowdSec connection schema for standalone installations."""
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ initial = True
8
+ dependencies = []
9
+ operations = [
10
+ migrations.CreateModel(
11
+ name="CrowdSecConnection",
12
+ fields=[
13
+ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
14
+ ("entity_id", models.PositiveBigIntegerField(db_index=True, unique=True)),
15
+ ("api_url", models.URLField(max_length=500)),
16
+ ("machine_id", models.CharField(max_length=255)),
17
+ ("encrypted_password", models.TextField()),
18
+ ("updated_at", models.DateTimeField(auto_now=True)),
19
+ ],
20
+ options={"ordering": ("-updated_at",)},
21
+ ),
22
+ ]
@@ -0,0 +1,14 @@
1
+ from django.db import models
2
+
3
+
4
+ class CrowdSecConnection(models.Model):
5
+ """CrowdSec LAPI credentials for the local Log Collector entity."""
6
+
7
+ entity_id = models.PositiveBigIntegerField(unique=True, db_index=True)
8
+ api_url = models.URLField(max_length=500)
9
+ machine_id = models.CharField(max_length=255)
10
+ encrypted_password = models.TextField()
11
+ updated_at = models.DateTimeField(auto_now=True)
12
+
13
+ class Meta:
14
+ ordering = ("-updated_at",)
@@ -0,0 +1,70 @@
1
+ import base64
2
+ import hashlib
3
+ from urllib.parse import urlsplit
4
+
5
+ import requests
6
+ from cryptography.fernet import Fernet, InvalidToken
7
+ from django.conf import settings
8
+
9
+ from .models import CrowdSecConnection
10
+
11
+
12
+ class CrowdSecError(Exception):
13
+ pass
14
+
15
+
16
+ def _fernet():
17
+ key = base64.urlsafe_b64encode(hashlib.sha256(settings.SECRET_KEY.encode()).digest())
18
+ return Fernet(key)
19
+
20
+
21
+ def encrypt_password(password):
22
+ return _fernet().encrypt(password.encode()).decode()
23
+
24
+
25
+ def _password(connection):
26
+ try:
27
+ return _fernet().decrypt(connection.encrypted_password.encode()).decode()
28
+ except (InvalidToken, ValueError) as exc:
29
+ raise CrowdSecError("Saved credential cannot be decrypted. Re-enter the CrowdSec credential.") from exc
30
+
31
+
32
+ def _url(api_url, path):
33
+ parsed = urlsplit(api_url)
34
+ if parsed.scheme not in {"https", "http"} or not parsed.hostname or parsed.username or parsed.password:
35
+ raise CrowdSecError("Enter a valid CrowdSec Local API URL.")
36
+ if parsed.scheme != "https" and parsed.hostname not in {"localhost", "127.0.0.1", "::1"}:
37
+ raise CrowdSecError("CrowdSec connections must use HTTPS except for loopback development.")
38
+ return f"{api_url.rstrip('/')}/{path.lstrip('/')}"
39
+
40
+
41
+ def request(connection, method, path, **kwargs):
42
+ try:
43
+ login = requests.post(
44
+ _url(connection.api_url, "v1/watchers/login"),
45
+ json={"machine_id": connection.machine_id, "password": _password(connection)},
46
+ timeout=8,
47
+ )
48
+ login.raise_for_status()
49
+ token = login.json().get("token")
50
+ if not token:
51
+ raise CrowdSecError("CrowdSec returned no session token.")
52
+ response = requests.request(
53
+ method, _url(connection.api_url, path),
54
+ headers={"Authorization": f"Bearer {token}"}, timeout=8, **kwargs,
55
+ )
56
+ response.raise_for_status()
57
+ return response.json() if response.content else {}
58
+ except CrowdSecError:
59
+ raise
60
+ except (requests.RequestException, ValueError) as exc:
61
+ raise CrowdSecError("CrowdSec connection failed. Check the URL, credentials, and LAPI permissions.") from exc
62
+
63
+
64
+ def validate_connection(api_url, machine_id, password):
65
+ class Candidate:
66
+ pass
67
+ candidate = Candidate()
68
+ candidate.api_url, candidate.machine_id = api_url, machine_id
69
+ candidate.encrypted_password = encrypt_password(password)
70
+ request(candidate, "GET", "v1/alerts?limit=1")
@@ -0,0 +1,13 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class LogCollectorConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "log_collector"
7
+
8
+ def ready(self):
9
+ from django.core.checks import register
10
+
11
+ from log_collector.services.client_ip import check_security_cidrs
12
+
13
+ register(check_security_cidrs)
@@ -0,0 +1,56 @@
1
+ """Authenticated, read-only security incident and block notifications."""
2
+
3
+ import json
4
+ from urllib.parse import parse_qsl
5
+
6
+ from channels.db import database_sync_to_async
7
+ from channels.generic.websocket import AsyncWebsocketConsumer
8
+
9
+ from log_collector.models import LogCollectorEntity
10
+ from log_collector.security_events import sanitize_security_event, security_group_name
11
+
12
+
13
+ class SecurityEventsConsumer(AsyncWebsocketConsumer):
14
+ async def connect(self):
15
+ self.security_group = None
16
+ user = self.scope.get("user")
17
+ credentials = {"token", "api_key", "api_token", "access_token", "authorization", "key"}
18
+ query = self.scope.get("query_string", b"").decode("utf-8", errors="replace")
19
+ if (not user or not user.is_authenticated or not user.is_active
20
+ or any(key.lower() in credentials for key, _ in parse_qsl(query, keep_blank_values=True))):
21
+ await self.close(code=4401)
22
+ return
23
+ if not await self.is_data_center_member(user):
24
+ await self.close(code=4403)
25
+ return
26
+ self.entity_id = await self.active_entity_id(self.scope["url_route"]["kwargs"]["slug"])
27
+ if self.entity_id is None:
28
+ await self.close(code=4404)
29
+ return
30
+ self.security_group = security_group_name(self.entity_id)
31
+ await self.channel_layer.group_add(self.security_group, self.channel_name)
32
+ await self.accept()
33
+
34
+ @database_sync_to_async
35
+ def is_data_center_member(self, user):
36
+ return user.groups.filter(name="DATA_CENTER").exists()
37
+
38
+ @database_sync_to_async
39
+ def active_entity_id(self, slug):
40
+ return LogCollectorEntity.objects.filter(slug=slug, is_active=True).values_list("pk", flat=True).first()
41
+
42
+ async def disconnect(self, close_code):
43
+ if self.security_group:
44
+ await self.channel_layer.group_discard(self.security_group, self.channel_name)
45
+
46
+ async def receive(self, text_data=None, bytes_data=None):
47
+ # No client protocol: text, JSON actions and binary input have no effects.
48
+ pass
49
+
50
+ async def security_event(self, message):
51
+ payload = message.get("event", {})
52
+ if payload.get("entity_id") != self.entity_id:
53
+ return
54
+ event = sanitize_security_event(self.entity_id, payload.get("type"), payload)
55
+ if event is not None:
56
+ await self.send(text_data=json.dumps(event))
@@ -0,0 +1,27 @@
1
+ # API integration
2
+
3
+ Mounting `log_collector.urls` at `/log-collector/` exposes read-only entity
4
+ resources under `/log-collector/api/entities/<slug>/`. Resources include
5
+ `metrics/`, `access-logs/`, `geolocation/`, `user-sessions/`, `endpoints/`, and
6
+ `audit-events/`, subject to the entity's feature flags and caller permissions.
7
+ Table resources support pagination; supported resources accept `export=csv`.
8
+
9
+ Generate an entity-scoped collector token as a superuser on the entity Manage
10
+ page. The raw token is shown once. Server-to-server callers send it in the
11
+ `X-Api-Key` header. The server stores a hash, not the raw token. Tokens may
12
+ have expiry, caller IP/CIDR, and origin restrictions. Do not embed a collector
13
+ token in browser or mobile application code.
14
+
15
+ To view another installation, create an integration entity with its HTTPS base
16
+ URL, remote entity slug, and remote collector token. Remote calls are read-only
17
+ and fetched on demand; they do not copy remote raw logs into the local database.
18
+ The remote service must expose the LOG Collector API. Arbitrary logging APIs
19
+ need a separate adapter.
20
+
21
+ The available entity routes also include `security-summary/`,
22
+ `security-incidents/`, `security-blocks/`, and CrowdSec status and decisions.
23
+ State-changing security endpoints require an authorized same-origin session,
24
+ CSRF protection, and the required operator role; a read-only API token cannot
25
+ perform those actions. An expired, revoked, mismatched, or caller-restricted
26
+ token is denied. Inspect recent token usage on the entity Manage page to
27
+ diagnose denials without logging the raw key.
@@ -0,0 +1,82 @@
1
+ # Configuration
2
+
3
+ ## Nginx collection
4
+
5
+ Set `LOG_COLLECTOR_NGINX_ACCESS_LOG_PATHS` to the Nginx access-log files that
6
+ the Django and worker processes can read. The default path is
7
+ `/var/log/nginx/access.log`; set `[]` when no local file is available. The
8
+ reader accepts Nginx common and combined access-log entries. Configure the
9
+ hosting entity's paths, cooldown, maximum lines per sync, and retention on its
10
+ Manage page. Access-log rotation must leave new files readable by workers.
11
+
12
+ ## ASGI and WebSockets
13
+
14
+ Set `ASGI_APPLICATION` and a production `CHANNEL_LAYERS` backend, such as
15
+ `channels_redis`, in the host project. Include the collector route under the
16
+ host's authenticated ASGI WebSocket application:
17
+
18
+ ```python
19
+ from channels.auth import AuthMiddlewareStack
20
+ from channels.routing import ProtocolTypeRouter, URLRouter
21
+ from django.core.asgi import get_asgi_application
22
+
23
+ django_asgi_app = get_asgi_application()
24
+ from log_collector.routing import websocket_urlpatterns
25
+
26
+ application = ProtocolTypeRouter({
27
+ "http": django_asgi_app,
28
+ "websocket": AuthMiddlewareStack(URLRouter(websocket_urlpatterns)),
29
+ })
30
+ ```
31
+
32
+ The route is `/ws/log-collector/entities/<slug>/security/`. Its origin
33
+ validator is part of the route. Set the host's `ALLOWED_HOSTS` correctly.
34
+ The browser Origin must match the WebSocket Host and port; when
35
+ `LOG_COLLECTOR_SECURITY_WEBSOCKET_ALLOWED_ORIGINS` is set, that exact origin
36
+ must also appear in the list.
37
+
38
+ ## Celery
39
+
40
+ Configure a Celery broker, worker, and beat scheduler in the host project.
41
+ The default queue handles `log_collector.tasks.collect_analyze_and_respond`,
42
+ `expire_security_blocks`, and `retry_security_edge_actions`. Schedule the
43
+ first task at the desired polling interval (five seconds in the original
44
+ deployment) and the latter two at one-minute intervals. An `sms` queue worker
45
+ is required when security SMS is enabled; route `queue_security_incident_sms_alert`,
46
+ `send_security_incident_sms_chunk`, and `retry_security_incident_sms_alert` there.
47
+ Collection can also run on dashboard/API requests, but continuous collection
48
+ requires beat and a worker.
49
+
50
+ ## Optional host integrations
51
+
52
+ The local geolocation and user-session APIs need models supplied by the host:
53
+
54
+ ```python
55
+ LOG_COLLECTOR_GEOLOCATION_MODEL = "your_geo_app.UserLocation"
56
+ LOG_COLLECTOR_USER_SESSION_MODEL = "your_sessions_app.UserSession"
57
+ ```
58
+
59
+ The geolocation model must expose `user`, `captured_at`, `ip_address`,
60
+ `latitude`, `longitude`, `country`, and `city`; the session model must expose
61
+ `user`, `session_key`, `ip`, `created_at`, `last_seen_at`, and `user_agent`.
62
+ Disable the entity's geolocation or user-session feature when no compatible
63
+ model exists. With an enabled feature but no model configured, its local API
64
+ returns HTTP 503 naming the missing setting; security summaries still work
65
+ without session data. Remote entities use the remote collector API instead.
66
+
67
+
68
+ The package does not assume a particular user-profile or SMS provider. Set
69
+ the following to importable dotted Python callables when using those actions:
70
+
71
+ - `LOG_COLLECTOR_ACCOUNT_CONTAINMENT_FUNCTION`: accepts a user and returns an
72
+ object with `as_dict()` describing revoked authentication state.
73
+ - `LOG_COLLECTOR_SMS_RECIPIENTS_FUNCTION`: returns iterable
74
+ `SecuritySMSRecipient(user, phone_number)` values from
75
+ `log_collector.integrations`.
76
+ - `LOG_COLLECTOR_SMS_SENDER_FUNCTION`: accepts `mobile_number`, `message`, and
77
+ `context` keyword arguments.
78
+
79
+ `LOG_COLLECTOR_SECURITY_SMS_ENABLED` defaults to false. Enable it only after
80
+ the recipient and sender functions, provider, and `sms` worker are configured.
81
+ The optional Nginx edge-block helper is supplied and reviewed by the host;
82
+ the wheel does not install privileged executables. See [Operations](operations-guide.md).
@@ -0,0 +1,40 @@
1
+ # Installation
2
+
3
+ LOG Collector supports Python 3.12–3.13 and Django 6.0.x. Use a virtual
4
+ environment in the host Django project:
5
+
6
+ ```shell
7
+ python -m pip install 'codefilterph-logcollector[redis,asgi]'
8
+ ```
9
+
10
+ Add the apps and mount the HTTP URLs:
11
+
12
+ ```python
13
+ INSTALLED_APPS += [
14
+ "channels",
15
+ "rest_framework",
16
+ "crowdsec_integration.apps.CrowdSecIntegrationConfig",
17
+ "log_collector.apps.LogCollectorConfig",
18
+ ]
19
+
20
+ # In the project's urls.py:
21
+ urlpatterns += [path("log-collector/", include("log_collector.urls"))]
22
+ ```
23
+
24
+ Keep Django's `auth`, `contenttypes`, and `sessions` apps installed. Apply the
25
+ database schema with `python manage.py migrate`. The collector uses
26
+ `AUTH_USER_MODEL`, so configure a custom user model before its first migration
27
+ if your project needs one.
28
+
29
+ Add `log_collector.middleware.SecurityIPBlockMiddleware` after Django's
30
+ `AuthenticationMiddleware` if application-level IP blocking is wanted. Give
31
+ authorized dashboard users membership in the `DATA_CENTER` group. A user also
32
+ needs `is_superuser=True` to open the Manage page or change configuration.
33
+
34
+ The default hosting entity is created on first access and has slug
35
+ `hosting-app`. Open `/log-collector/entities/hosting-app/manage/` as a
36
+ superuser to set the displayed app name, shared theme, and local collection
37
+ paths. An unset name displays **LOG Collector**.
38
+
39
+ See [Configuration](configuration.md) before enabling collection, WebSockets,
40
+ Celery, SMS, or Nginx edge blocking.