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.
- codefilterph_logcollector-0.1.1/LICENSE +21 -0
- codefilterph_logcollector-0.1.1/MANIFEST.in +12 -0
- codefilterph_logcollector-0.1.1/PKG-INFO +56 -0
- codefilterph_logcollector-0.1.1/README.md +29 -0
- codefilterph_logcollector-0.1.1/codefilterph_logcollector.egg-info/PKG-INFO +56 -0
- codefilterph_logcollector-0.1.1/codefilterph_logcollector.egg-info/SOURCES.txt +59 -0
- codefilterph_logcollector-0.1.1/codefilterph_logcollector.egg-info/dependency_links.txt +1 -0
- codefilterph_logcollector-0.1.1/codefilterph_logcollector.egg-info/requires.txt +12 -0
- codefilterph_logcollector-0.1.1/codefilterph_logcollector.egg-info/top_level.txt +2 -0
- codefilterph_logcollector-0.1.1/crowdsec_integration/__init__.py +1 -0
- codefilterph_logcollector-0.1.1/crowdsec_integration/apps.py +6 -0
- codefilterph_logcollector-0.1.1/crowdsec_integration/migrations/0001_initial.py +22 -0
- codefilterph_logcollector-0.1.1/crowdsec_integration/migrations/__init__.py +1 -0
- codefilterph_logcollector-0.1.1/crowdsec_integration/models.py +14 -0
- codefilterph_logcollector-0.1.1/crowdsec_integration/services.py +70 -0
- codefilterph_logcollector-0.1.1/log_collector/__init__.py +1 -0
- codefilterph_logcollector-0.1.1/log_collector/apps.py +13 -0
- codefilterph_logcollector-0.1.1/log_collector/consumers.py +56 -0
- codefilterph_logcollector-0.1.1/log_collector/docs/api.md +27 -0
- codefilterph_logcollector-0.1.1/log_collector/docs/configuration.md +82 -0
- codefilterph_logcollector-0.1.1/log_collector/docs/installation.md +40 -0
- codefilterph_logcollector-0.1.1/log_collector/docs/operations-guide.md +52 -0
- codefilterph_logcollector-0.1.1/log_collector/forms.py +160 -0
- codefilterph_logcollector-0.1.1/log_collector/integrations.py +54 -0
- codefilterph_logcollector-0.1.1/log_collector/management/__init__.py +0 -0
- codefilterph_logcollector-0.1.1/log_collector/management/commands/__init__.py +0 -0
- codefilterph_logcollector-0.1.1/log_collector/management/commands/backfill_security_incidents.py +30 -0
- codefilterph_logcollector-0.1.1/log_collector/management/commands/manage_security_block.py +58 -0
- codefilterph_logcollector-0.1.1/log_collector/management/commands/retry_security_sms.py +33 -0
- codefilterph_logcollector-0.1.1/log_collector/middleware.py +57 -0
- codefilterph_logcollector-0.1.1/log_collector/migrations/0001_initial.py +127 -0
- codefilterph_logcollector-0.1.1/log_collector/migrations/0002_remove_logcollectorentity_allowed_api_key_names_and_more.py +67 -0
- codefilterph_logcollector-0.1.1/log_collector/migrations/0003_blockedipaddress_securityincident_ipblockauditevent_and_more.py +169 -0
- codefilterph_logcollector-0.1.1/log_collector/migrations/0004_app_appearance.py +31 -0
- codefilterph_logcollector-0.1.1/log_collector/migrations/__init__.py +1 -0
- codefilterph_logcollector-0.1.1/log_collector/models.py +601 -0
- codefilterph_logcollector-0.1.1/log_collector/permissions.py +283 -0
- codefilterph_logcollector-0.1.1/log_collector/routing.py +76 -0
- codefilterph_logcollector-0.1.1/log_collector/security_events.py +35 -0
- codefilterph_logcollector-0.1.1/log_collector/services/__init__.py +1 -0
- codefilterph_logcollector-0.1.1/log_collector/services/blocking.py +309 -0
- codefilterph_logcollector-0.1.1/log_collector/services/client_ip.py +75 -0
- codefilterph_logcollector-0.1.1/log_collector/services/incidents.py +326 -0
- codefilterph_logcollector-0.1.1/log_collector/services/nginx.py +54 -0
- codefilterph_logcollector-0.1.1/log_collector/services/remote.py +32 -0
- codefilterph_logcollector-0.1.1/log_collector/services/response.py +67 -0
- codefilterph_logcollector-0.1.1/log_collector/services/security.py +379 -0
- codefilterph_logcollector-0.1.1/log_collector/services/security_sms.py +98 -0
- codefilterph_logcollector-0.1.1/log_collector/services/sync.py +103 -0
- codefilterph_logcollector-0.1.1/log_collector/services/trusted_ips.py +55 -0
- codefilterph_logcollector-0.1.1/log_collector/static/log_collector/appearance.css +46 -0
- codefilterph_logcollector-0.1.1/log_collector/tasks.py +261 -0
- codefilterph_logcollector-0.1.1/log_collector/templates/log_collector/dashboard.html +1544 -0
- codefilterph_logcollector-0.1.1/log_collector/templates/log_collector/entity_form.html +45 -0
- codefilterph_logcollector-0.1.1/log_collector/templates/log_collector/entity_manage.html +425 -0
- codefilterph_logcollector-0.1.1/log_collector/templates/log_collector/entity_selection.html +306 -0
- codefilterph_logcollector-0.1.1/log_collector/templates/log_collector/security_report.html +99 -0
- codefilterph_logcollector-0.1.1/log_collector/urls.py +67 -0
- codefilterph_logcollector-0.1.1/log_collector/views.py +1889 -0
- codefilterph_logcollector-0.1.1/pyproject.toml +53 -0
- 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 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -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 @@
|
|
|
1
|
+
|
|
@@ -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 @@
|
|
|
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.
|