mirror of
https://github.com/DarrylNixon/CrowdTLS-server.git
synced 2024-09-22 18:19:43 -07:00
Basic structure in place
This commit is contained in:
parent
ffd4fe4012
commit
8edd610e7c
9 changed files with 201 additions and 1 deletions
3
.flake8
Normal file
3
.flake8
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[flake8]
|
||||||
|
max-line-length = 160
|
||||||
|
exclude = docs/*, .git, __pycache__, build
|
29
.pre-commit-config.yaml
Normal file
29
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v3.2.0
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: check-yaml
|
||||||
|
- id: check-added-large-files
|
||||||
|
- repo: https://github.com/asottile/reorder_python_imports
|
||||||
|
rev: v3.9.0
|
||||||
|
hooks:
|
||||||
|
- id: reorder-python-imports
|
||||||
|
args: [--application-directories, '.:crowdtls', --py39-plus]
|
||||||
|
- repo: https://github.com/psf/black
|
||||||
|
rev: 23.3.0
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
language_version: python3.11
|
||||||
|
- repo: https://github.com/PyCQA/bandit
|
||||||
|
rev: 1.7.5
|
||||||
|
hooks:
|
||||||
|
- id: bandit
|
||||||
|
args: ["-c", "pyproject.toml"]
|
||||||
|
additional_dependencies: ["bandit[toml]"]
|
||||||
|
- repo: https://github.com/pycqa/flake8
|
||||||
|
rev: 6.0.0
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
exclude: ^migrations/versions/
|
31
README.md
31
README.md
|
@ -1 +1,30 @@
|
||||||
# CrowdTLS-server
|
<div align="center">
|
||||||
|
<img src="icons/crowdtls.png" alt="CrowdTLS Logo">
|
||||||
|
|
||||||
|
# CrowdTLS-server
|
||||||
|
|
||||||
|
CrowdTLS validates SSL/TLS certificates against the crowd.
|
||||||
|
|
||||||
|
This is the backend server repository for it.<br/>
|
||||||
|
|
||||||
|
[Installation](#installation) •
|
||||||
|
[License](#license)
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
**What is this? I'm looking for the browser extension!**
|
||||||
|
|
||||||
|
You're in the wrong place. The browser extension can be found [here](https://sillyhats.mips.uk/pdf/CrowdTLS).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the MPL 2.0 License. See the `LICENSE` file for details.
|
||||||
|
|
||||||
|
I carefully evaluated various open-source licenses and chose the Mozilla Public License 2.0 (MPL 2.0) for CrowdTLS due to its compatibility with other licenses, strong copyleft provisions, and its alignment with my values and goals. MPL 2.0 ensures that the source code remains open and available, while allowing for flexibility in terms of collaboration and incorporation into other projects.
|
||||||
|
|
||||||
|
While I understand that different licenses may have their merits, I believe that MPL 2.0 provides the best balance of openness, collaborative potential, and legal clarity for the development and distribution of CrowdTLS.
|
||||||
|
|
BIN
crowdtls.png
Normal file
BIN
crowdtls.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
0
crowdtls/__init__.py
Normal file
0
crowdtls/__init__.py
Normal file
50
crowdtls/cli.py
Normal file
50
crowdtls/cli.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
from typing import Dict
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
from sqlalchemy.ext.asyncio import create_async_engine
|
||||||
|
from sqlalchemy.future import select
|
||||||
|
from sqlmodel import SQLModel
|
||||||
|
|
||||||
|
from crowdtls.helpers import decode_der
|
||||||
|
from db import CertificateChain
|
||||||
|
|
||||||
|
DATABASE_URL = "postgresql+asyncpg://user:password@localhost:5432/database"
|
||||||
|
engine = create_async_engine(DATABASE_URL, echo=True)
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_event("startup")
|
||||||
|
async def startup_event():
|
||||||
|
async with engine.begin() as connection:
|
||||||
|
await connection.run_sync(SQLModel.metadata.create_all)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/check")
|
||||||
|
async def check_fingerprints(fingerprints: Dict[str, List[int]]):
|
||||||
|
fps = fingerprints.get("fps")
|
||||||
|
async with AsyncSession(engine) as session:
|
||||||
|
for fp in fps:
|
||||||
|
stmt = select(CertificateChain).where(CertificateChain.fingerprint == fp)
|
||||||
|
result = await session.execute(stmt)
|
||||||
|
certificate = result.scalars().first()
|
||||||
|
if not certificate:
|
||||||
|
return {"send": True}
|
||||||
|
return {"send": False}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/new")
|
||||||
|
async def new_fingerprints(fingerprints: Dict[str, List[int]]):
|
||||||
|
async with AsyncSession(engine) as session:
|
||||||
|
for fp, _ in fingerprints.items():
|
||||||
|
stmt = select(CertificateChain).where(CertificateChain.fingerprint == fp)
|
||||||
|
result = await session.execute(stmt)
|
||||||
|
certificate = result.scalars().first()
|
||||||
|
if not certificate:
|
||||||
|
new_certificate = decode_der(fp)
|
||||||
|
session.add(new_certificate)
|
||||||
|
pass
|
||||||
|
await session.commit()
|
||||||
|
return {"status": "OK"}
|
22
crowdtls/db.py
Normal file
22
crowdtls/db.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
from sqlalchemy import Integer
|
||||||
|
from sqlalchemy import LargeBinary
|
||||||
|
from sqlalchemy.dialects.postgresql import JSONB
|
||||||
|
from sqlmodel import Field
|
||||||
|
from sqlmodel import SQLModel
|
||||||
|
|
||||||
|
|
||||||
|
class CertificateChain(SQLModel, table=True):
|
||||||
|
id: int = Field(default=None, primary_key=True)
|
||||||
|
fingerprint: str = Field(index=True)
|
||||||
|
domain_name: str
|
||||||
|
raw_der_certificate: LargeBinary
|
||||||
|
version: int
|
||||||
|
serial_number: str
|
||||||
|
signature: LargeBinary
|
||||||
|
issuer: JSONB
|
||||||
|
validity: JSONB
|
||||||
|
subject: JSONB
|
||||||
|
subject_public_key_info: JSONB
|
||||||
|
issuer_unique_id: Integer
|
||||||
|
subject_unique_id: Integer
|
||||||
|
extensions: JSONB
|
30
crowdtls/helpers.py
Normal file
30
crowdtls/helpers.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from cryptography import x509
|
||||||
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
|
||||||
|
from crowdtls.db import CertificateChain
|
||||||
|
|
||||||
|
|
||||||
|
def decode_der(raw_der_certificate: List[int]) -> CertificateChain:
|
||||||
|
# Convert list of integers to bytes
|
||||||
|
der_cert_bytes = bytes(raw_der_certificate)
|
||||||
|
|
||||||
|
# Parse the DER certificate
|
||||||
|
cert = x509.load_der_x509_certificate(der_cert_bytes, default_backend())
|
||||||
|
|
||||||
|
certificate_chain = CertificateChain(
|
||||||
|
raw_der_certificate=der_cert_bytes,
|
||||||
|
version=cert.version.value,
|
||||||
|
serial_number=cert.serial_number,
|
||||||
|
signature=cert.signature,
|
||||||
|
issuer=cert.issuer.rfc4514_string(),
|
||||||
|
validity={"not_valid_before": cert.not_valid_before, "not_valid_after": cert.not_valid_after},
|
||||||
|
subject=cert.subject.rfc4514_string(),
|
||||||
|
subject_public_key_info=cert.public_key().public_bytes(),
|
||||||
|
issuer_unique_id=cert.issuer_unique_id,
|
||||||
|
subject_unique_id=cert.subject_unique_id,
|
||||||
|
extensions=[str(ext) for ext in cert.extensions],
|
||||||
|
)
|
||||||
|
|
||||||
|
return certificate_chain
|
37
pyproject.toml
Normal file
37
pyproject.toml
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=67.8"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "crowdtls"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = [{ name = "crowdtls", email = "git@nixon.mozmail.com" }]
|
||||||
|
description = "Backend server for CrowdTLS browser extension"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.9"
|
||||||
|
license = { text = "MIT" }
|
||||||
|
dependencies = [
|
||||||
|
"fastapi==0.95.2",
|
||||||
|
"uvicorn==0.22.0",
|
||||||
|
"loguru==0.7.0",
|
||||||
|
"pydantic==1.10.8",
|
||||||
|
"asyncpg==0.27.0",
|
||||||
|
"greenlet==2.0.2",
|
||||||
|
"sqlmodel==0.0.8",
|
||||||
|
"sqlalchemy==1.4.41",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
homepage = "https://github.com/DarrylNixon/CrowdTLS"
|
||||||
|
repository = "https://github.com/DarrylNixon/CrowdTLS-server"
|
||||||
|
|
||||||
|
[tool.setuptools]
|
||||||
|
py-modules = ["crowdtls"]
|
||||||
|
|
||||||
|
[tool.bandit]
|
||||||
|
exclude_dirs = ["/doc", "/build"]
|
||||||
|
# TODO: Stop skipping B104 (binding on 0.0.0.0), is there a nice way to get a good docker bind address?
|
||||||
|
skips = ["B104"]
|
||||||
|
|
||||||
|
[tool.black]
|
||||||
|
line-length = 120
|
Loading…
Reference in a new issue