diff --git a/.github/workflows/automatic_image_genration.yml b/.github/workflows/automatic_image_genration.yml new file mode 100644 index 0000000..620d775 --- /dev/null +++ b/.github/workflows/automatic_image_genration.yml @@ -0,0 +1,84 @@ +name: Automatic Image Generation + +on: + push: + paths: + - 'src/plantuml/**' + - 'src/drawio/**' + workflow_dispatch: + +jobs: + generate_images: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 # Updated to Node.js 20 compatible version + with: + fetch-depth: 2 # Ensures at least two commits are fetched + + - name: Set up JDK 11 + uses: actions/setup-java@v3.11.0 # Updated to Node.js 20 compatible version + with: + distribution: 'temurin' + java-version: '11' + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y graphviz xdotool xvfb + sudo snap install drawio --classic + + - name: Download PlantUML jar + run: | + wget https://github.com/plantuml/plantuml/releases/latest/download/plantuml.jar -O /usr/local/bin/plantuml.jar + + - name: Check for changed files + id: changed_files + run: | + BASE_SHA=$(git rev-parse HEAD^1) + CHANGED_FILES=$(git diff --name-only $BASE_SHA HEAD | grep -E 'src/plantuml/.*\.puml|src/drawio/.*\.drawio' || true) + echo "changed_files=$CHANGED_FILES" >> $GITHUB_ENV + echo "::set-output name=changed_files::$CHANGED_FILES" + + - name: Debug changed files + run: | + echo "Debug: Changed files are ${{ steps.changed_files.outputs.changed_files }}" + + - name: Generate images from changed PlantUML files + if: ${{ steps.changed_files.outputs.changed_files }} != '' + run: | + for file in $(echo "${{ steps.changed_files.outputs.changed_files }}" | grep 'src/plantuml/.*\.puml' || true); do + echo "Processing PlantUML file: $file" + output_dir=$(dirname "$file" | sed 's|^src/plantuml|images|') + mkdir -p "$output_dir" + java -jar /usr/local/bin/plantuml.jar -tpng "$file" -o "${PWD}/$output_dir" + java -jar /usr/local/bin/plantuml.jar -tsvg "$file" -o "${PWD}/$output_dir" + done + + - name: Generate images from changed drawio files + if: ${{ steps.changed_files.outputs.changed_files }} != '' + run: | + for file in $(echo "${{ steps.changed_files.outputs.changed_files }}" | grep 'src/drawio/.*\.drawio' || true); do + echo "Processing drawio file: $file" + output_dir=$(dirname "$file" | sed 's|^src/drawio|images|') + mkdir -p "$output_dir" + xvfb-run -a drawio -x -f png -o "${PWD}/$output_dir/$(basename "$file" .drawio).png" "$file" + xvfb-run -a drawio -x -f svg -o "${PWD}/$output_dir/$(basename "$file" .drawio).svg" "$file" + done + + - name: Debug images folder + run: | + echo "Contents of the images folder:" + ls -R images + + - name: Commit and push generated images + if: ${{ steps.changed_files.outputs.changed_files }} != '' + run: | + git config --global user.name 'github-actions' + git config --global user.email 'github-actions@github.com' + git add -A images + git commit -m "Generate images from changed PlantUML and drawio files" + git push + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 9dfaa78..2105abb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ *.tar.gz *.pyc __pycache__/ -var/ \ No newline at end of file +var/ +console \ No newline at end of file diff --git a/components/fastapi-pip-pap/README.adoc b/components/fastapi-pip-pap/README.adoc index 1580bfe..a713447 100644 --- a/components/fastapi-pip-pap/README.adoc +++ b/components/fastapi-pip-pap/README.adoc @@ -18,11 +18,11 @@ poetry install poetry shell ---- -The files to start the service in the `/components/fastapi-pip-pap` directory of the project. +The files to start the service are in the `/components/fastapi-pip-pap` directory of the project. You need to `create bundle.tar.gz` files and place them in the `///` directory. Example: `/var/tmp/fastapi-pip-pap/KIM/2.0/pap/bundle.tar.gz` -The following command starts pip-pap-service on localhost:8200. +The following command starts pip-pap-service on localhost:8080. [source,sh] ---- python fastapi-pip-pap.py diff --git a/components/fastapi-pip-pap/config.yaml b/components/fastapi-pip-pap/config.yaml index 00113cf..b9e3561 100644 --- a/components/fastapi-pip-pap/config.yaml +++ b/components/fastapi-pip-pap/config.yaml @@ -1,12 +1,12 @@ # pip-pap-service configuration file -# Base path for bundle storage -bundle_storage_path: /var/tmp/fastapi-pip-pap +# GitHub repository for bundle storage +github_repo: https://raw.githubusercontent.com/gem-cp/zt-opa-bundles/main # logging configuration logging: - loglevel: DEBUG + loglevel: INFO log_to_console: True log_to_file: True - # If log _to_file is True then the logfile is: .log + # If log_to_file is True then the logfile is: .log service_name: fastapi-pip-pap \ No newline at end of file diff --git a/components/fastapi-pip-pap/docker-compose.yml b/components/fastapi-pip-pap/docker-compose.yml index 6092bbf..cb08f65 100644 --- a/components/fastapi-pip-pap/docker-compose.yml +++ b/components/fastapi-pip-pap/docker-compose.yml @@ -8,8 +8,5 @@ services: - "8080:8080" volumes: - ./config.yaml:/app/config.yaml # Mount the config file - # Mount the bundle directory; - # From config.yaml: bundle_storage_path: /var/tmp/fastapi-pip-pap - - /var/tmp/fastapi-pip-pap:/var/tmp/fastapi-pip-pap environment: - PYTHONPATH=/app diff --git a/components/fastapi-pip-pap/fastapi-pip-pap.py b/components/fastapi-pip-pap/fastapi-pip-pap.py index b72e6e6..490ddb3 100644 --- a/components/fastapi-pip-pap/fastapi-pip-pap.py +++ b/components/fastapi-pip-pap/fastapi-pip-pap.py @@ -5,46 +5,121 @@ import argparse import yaml import hashlib +import requests +import jwcrypto.jwk as jwk from fastapi import FastAPI, HTTPException, Header, Response -from fastapi.responses import FileResponse +from fastapi.responses import FileResponse, JSONResponse from typing import Optional +from tempfile import NamedTemporaryFile +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives import serialization +from jwcrypto import jwk, jwt +import tarfile +import json +import io +import time app = FastAPI() class Bundles: - """Class to handle the bundle storage and retrieval.""" - def __init__(self, bundle_storage_path, policies, application, label, filename): + """Class to handle the bundle storage and retrieval from GitHub.""" + def __init__(self, github_repo, application, label, filename): """Initialize the Bundles class.""" - self.bundle_storage_path = bundle_storage_path - self.policies = policies + self.github_repo = github_repo self.application = application self.label = label self.filename = filename - self.bundle_path = self.get_bundle_path() - - def get_bundle_path(self): - """Return the path to the bundle.""" - return os.path.join( - self.bundle_storage_path, - self.policies, - self.application, - self.label, - self.filename - ) - - def get_etag(self): - """Calculates the ETag header value based on the file content hash. - - Returns: - str: The ETag header value. - """ - # Simulate reading file content - with open(self.bundle_path, 'rb') as f: - content = f.read() - # Calculate hash of the content - hasher = hashlib.sha256(content) - etag = hasher.hexdigest() - return etag + self.bundle_url = self.get_bundle_url() + + def get_bundle_url(self): + """Return the URL to the bundle.""" + return f"{self.github_repo}/opa-bundles/{self.application}/{self.label}/{self.filename}" + + def download_bundle(self): + """Download the bundle from GitHub.""" + response = requests.get(self.bundle_url) + if response.status_code != 200: + raise HTTPException(status_code=response.status_code, detail=response.text) + + temp_file = NamedTemporaryFile(delete=False) + with open(temp_file.name, 'wb') as f: + f.write(response.content) + + return temp_file.name + + def get_etag(self, file_path): + """Calculates the ETag header value based on the file content hash.""" + hasher = hashlib.sha256() + with open(file_path, 'rb') as f: + while chunk := f.read(8192): + hasher.update(chunk) + etag = hasher.hexdigest() + return etag + + def calculate_hash(self, file_content, algorithm='SHA-256'): + """Calculate the hash of the file content.""" + hasher = hashlib.new(algorithm.lower().replace("-", "")) + hasher.update(file_content) + return hasher.hexdigest() + + def sign_bundle(self, file_hashes, private_key): + """Signs the bundle using the given private key.""" + claims = { + "files": file_hashes, + "iat": int(time.time()), + "iss": "JWTSercice" + } + + header = { + "alg": "ES256", + "typ": "JWT", + "kid": "myPublicKey" + } + + token = jwt.JWT(header=header, claims=claims) + token.make_signed_token(private_key) + return token.serialize() + + def create_signed_tarball(self, original_bundle_file, signature): + """Creates a new tarball including the original files and the signature.""" + signed_bundle_file = NamedTemporaryFile(delete=False) + + with tarfile.open(original_bundle_file, "r:gz") as tar: + with tarfile.open(signed_bundle_file.name, "w:gz") as signed_tar: + for member in tar.getmembers(): + file_data = tar.extractfile(member).read() + tarinfo = tarfile.TarInfo(name=member.name) + tarinfo.size = len(file_data) + signed_tar.addfile(tarinfo, fileobj=io.BytesIO(file_data)) + + # Add signature file + signature_info = tarfile.TarInfo(name=".signatures.json") + signature_info.size = len(signature) + signed_tar.addfile(signature_info, io.BytesIO(signature.encode())) + + return signed_bundle_file.name + +def generate_keys(): + """Generate a new ECC key pair.""" + private_key = ec.generate_private_key(ec.SECP256R1()) + public_key = private_key.public_key() + return private_key, public_key + +#def get_jwks(public_key): +# """Generate a JWKS representation of the given public key.""" + # Create a JWK object from ECC public key + #jwk_key = jwk.JWK() + #jwk_key.import_key(public_key) + + # Create a JWKSet containing the JWK object + #jwks = jwk.JWKSet(keys=[jwk_key]) + + # Export the JWKS as JSON string + #return jwks.export(private_keys=False) + +#private_key, public_key = generate_keys() +#jwks = get_jwks(public_key) @app.options("/policies/{application}/{label}") async def options_bundle( @@ -52,7 +127,7 @@ async def options_bundle( label: str ): """Handle OPTIONS request for bundle endpoint.""" - return Response(status_code=200, headers={"Allow": "GET, HEAD, OPTIONS"}) + return Response(status_code=200, headers={"Allow": "GET, OPTIONS"}) @app.get("/policies/{application}/{label}") async def get_bundle( @@ -60,28 +135,64 @@ async def get_bundle( label: str, if_none_match: Optional[str] = Header(None) ): - - filename = "bundle.tar.gz" """Get the requested bundle.""" - bundle_storage_path = app.state.config["bundle_storage_path"] - bundle = Bundles(bundle_storage_path, "policies", application, label, filename) - bundle_path = bundle.get_bundle_path() + filename = "bundle.tar.gz" + github_repo = app.state.config["github_repo"] + bundle = Bundles(github_repo, application, label, filename) + + try: + bundle_file = bundle.download_bundle() + except HTTPException as e: + # Handle HTTP errors from download + raise e - if not os.path.exists(bundle_path): - raise HTTPException(status_code=404, detail="The requested bundle does not exist.") + if bundle_file is None: + # Handle scenario where download_bundle() did not return a valid file path + raise HTTPException(status_code=500, detail="Failed to download bundle") - etag = bundle.get_etag() + # Calculate ETag header value + etag = bundle.get_etag(bundle_file) if if_none_match == etag: return Response(status_code=304) + # Extract files and calculate their hashes + file_hashes = [] + with tarfile.open(bundle_file, "r:gz") as tar: + for member in tar.getmembers(): + file_data = tar.extractfile(member).read() + file_hash = bundle.calculate_hash(file_data) + file_hashes.append({ + "name": member.name, + "hash": file_hash, + "algorithm": "SHA-256" + }) + + # Sign the bundle + signature = bundle.sign_bundle(file_hashes, private_key) + + # Create a new tarball including the original files and the signature + signed_bundle_file = bundle.create_signed_tarball(bundle_file, signature) + # Add Content-Disposition header - return FileResponse(bundle_path, media_type="application/gzip", headers={ - "ETag": etag, - #"Content-Disposition": f'attachment; filename="{os.path.basename(bundle_path)}"' - "Content-Disposition": f"attachment; filename={filename}" + response = FileResponse(signed_bundle_file, media_type="application/gzip", headers={ + "ETag": etag, + "Content-Disposition": f"attachment; filename={filename}" }) + # Clean up the temporary files after sending the response + if bundle_file: + response.background = lambda: os.remove(bundle_file) + if signed_bundle_file: + response.background = lambda: os.remove(signed_bundle_file) + + return response + +#@app.get("/jwks") +#async def get_jwks_endpoint(): +# """Serve the JWKS.""" +# return JSONResponse(content=jwks) + def load_config(filename): """Load the configuration from the given file.""" try: @@ -132,4 +243,4 @@ def setup_logging(log_config): import uvicorn # Start the server - uvicorn.run(app, host=args.servername, port=int(args.port), log_level=logger.level) \ No newline at end of file + uvicorn.run(app, host=args.servername, port=int(args.port), log_level=logger.level) diff --git a/components/fastapi-pip-pap/requirements.txt b/components/fastapi-pip-pap/requirements.txt index 9197313..720826d 100644 --- a/components/fastapi-pip-pap/requirements.txt +++ b/components/fastapi-pip-pap/requirements.txt @@ -1,22 +1,69 @@ +aiofiles==24.1.0 annotated-types==0.6.0 anyio==4.3.0 +argcomplete==3.2.3 +beartype==0.18.5 +black==24.2.0 blinker==1.7.0 +boolean.py==4.0 +certifi==2024.2.2 +cffi==1.16.0 +charset-normalizer==3.3.2 click==8.1.7 coloredlogs==15.0.1 colorlog==6.8.2 +crypto==1.4.1 +cryptography==42.0.5 +datamodel-code-generator==0.25.4 +dnspython==2.6.1 +email_validator==2.1.1 fastapi==0.110.0 Flask==3.0.2 +genson==1.2.2 h11==0.14.0 +httpcore==1.0.5 +httpx==0.27.0 humanfriendly==10.0 idna==3.6 +inflect==5.6.2 +isodate==0.6.1 +isort==5.13.2 itsdangerous==2.1.2 Jinja2==3.1.3 +jwcrypto==1.5.6 +license-expression==30.3.0 +loguru==0.7.2 +Mako==1.3.2 +Markdown==3.5.2 MarkupSafe==2.1.5 +mypy-extensions==1.0.0 +Naked==0.1.32 +oauthlib==3.2.2 +packaging==24.0 +pathspec==0.12.1 +pdoc3==0.10.0 +platformdirs==4.2.0 +ply==3.11 +pycparser==2.21 pydantic==2.6.4 pydantic_core==2.16.3 +Pygments==2.17.2 +pyOpenSSL==24.0.0 +pyparsing==3.1.2 PyYAML==6.0.1 +rdflib==7.0.0 +requests==2.31.0 +requests-oauthlib==1.3.1 +semantic-version==2.10.0 +shellescape==3.8.1 +six==1.16.0 sniffio==1.3.1 +spdx-tools==0.8.2 starlette==0.36.3 typing_extensions==4.10.0 +uritools==4.0.3 +urllib3==2.2.1 uvicorn==0.28.0 +uWSGI==2.0.24 Werkzeug==3.0.1 +xmltodict==0.13.0 diff --git a/components/gemini-pip-pap/console b/components/gemini-pip-pap/console deleted file mode 100644 index b7f74df..0000000 --- a/components/gemini-pip-pap/console +++ /dev/null @@ -1,18 +0,0 @@ -INFO Starting fastapi-pip-pap server ... -INFO Starting fastapi-pip-pap server ... -INFO Starting fastapi-pip-pap server ... -INFO Starting fastapi-pip-pap server ... -INFO Starting fastapi-pip-pap server ... -INFO Starting fastapi-pip-pap server ... -INFO Starting fastapi-pip-pap server ... -INFO Starting fastapi-pip-pap server ... -INFO Returning bundle: /var/tmp/gemini-pip-pap/KIM/2.0/pap/bundle.tar.gz (etag: 1710672019.7567704) -INFO Returning bundle: /var/tmp/gemini-pip-pap/KIM/2.0/pap/bundle.tar.gz (etag: 1710672019.7567704) -INFO Returning bundle: /var/tmp/gemini-pip-pap/KIM/2.0/pap/bundle.tar.gz (etag: 1710672019.7567704) -INFO Starting fastapi-pip-pap server ... -INFO Returning bundle: /var/tmp/gemini-pip-pap/KIM/2.0/pap/bundle.tar.gz (etag: 1710672019.7567704) -INFO Starting fastapi-pip-pap server ... -INFO Starting fastapi-pip-pap server ... -INFO Starting fastapi-pip-pap server ... -INFO Starting fastapi-pip-pap server ... -INFO Starting fastapi-pip-pap server ... diff --git a/docs/CI_CD_Concept.adoc b/docs/CI_CD_Concept.adoc new file mode 100644 index 0000000..d920561 --- /dev/null +++ b/docs/CI_CD_Concept.adoc @@ -0,0 +1,121 @@ +ifdef::env-github[] +:tip-caption: :bulb: +:note-caption: :information_source: +:important-caption: :heavy_exclamation_mark: +:caution-caption: :fire: +:warning-caption: :warning: +:source-style: listing +endif::[] + +ifndef::env-github[:source-style: source] + +:imagesdir: /images/ + +image::gematik_logo.svg[gematik,width="30%"] + += CI/CD Konzept für den Zero Trust Cluster + +== Einleitung + +Die Zero Trust Komponenten PEP und PDP werden pro TI 2.0 Dienst in einem Kubernetes (K8s) Cluster betrieben. Der K8s Cluster wird im folgenden als Zero Trust (ZT) Cluster bezeichnet. Die Dienste der TI 2.0 nutzen diesen ZT Cluster als Sicherheitsleistung, sodass nur berechtigte Nutzer mit zugelassenen Apps und erlaubten Geräten auf die Ressourcen der Fachdienste zugreifen können. Um die Entwicklung des ZT Clusters und dieSicherheit und Zuverlässigkeit im Betrieb zu gewährleisten, ist eine CI/CD-Pipeline mit Quality Gates vorgesehen. In diesem Konzept wird eine mögliche Umsetzung der CI/CD Pipelines beschrieben. + +Die Entwicklung und Bereitstellung der ZT Cluster-Komponenten PEP und PDP erfolgt durch die gematik oder einen von der gematik beauftragten Dienstleister. Mit der hier vorgestellten CI Pipeline werden die Bereitstellung, Weiterentwicklung und weitgehend automatisierte Tests des ZT Clusters kontinuierlich ermöglicht. Über Quality Gates werden auch Sicherheitstests, wie Schwachstellenscans und Compliance-Checks, im Entwicklungsprozess integriert. + +Die Aktualisierung des ZT Cluster beim Betreiber eines TI 2.0 Dienstes erfolgt über einen CD Prozess. Dazu gehören das Update von PEP und PDP (nach erfolgreichem Durchlauf der CI Pipeline), die Aktualisierung der ZT Cluster-Konfiguration sowie die Aktualisierung der Policies und Daten für den PDP sowie die Anpassung der ZT Cluster Konfiguration an den spzifischen TI 2.0 Dienst. Der CD Prozess liegt in der Verantwortung der gematik oder eines von der gematik beauftragten Dienstleisters. + +=== Abgrenzung + +Die Policies und Daten, die der PDP vom PIP und PAP Service bezieht werden in einer weiteren CI-Pipeline entwickelt, qualitätsgesichert und in einem separaten GitHub Repository öffentlich bereitgestellt. Diese CI-Pipeline ist nicht Gegenstand des vorliegenden Konzeptes. + +== CI/CD Konzept + +Als Ergebnis der Entwicklung in der CI Pipeline werden die ZT Cluster-Komponenten PEP und PDP als signierte Docker Container und die ZT Cluster-Konfiguration als K8s-Infrastruktur-Code in einem GitHub Repository öffentlich bereitgestellt. Die dienst-spezifischen ZT Cluster-Konfigurationen werden als Ergebnis der CD Pipeline in einem separaten GitHub Repository entwickelt und ebenfalls öffentlich bereitgestellt. Die Betreiber von TI 2.0 Diensten aktualisieren ihren ZT Cluster direkt aus dem GitHub Repository. Dadurch ist eine Trennung der Verantwortlichkeiten von Entwicklung des ZT Clusters (CI-Pipeline) und Betrieb (CD-Pipeline) möglich. +Die CI Pipeline wird durch GitHub Actions ausgelöst und überwacht. Sie beinhaltet automatisierte Tests, Builds und Bereitstellungsschritte. +Der ZT Cluster wird in der Infrastruktur des Anbieters des Fachdienstes betrieben. Der Anbieter des Fachdienstes ist für den Betrieb des ZT Clusters verantwortlich. Die gematik hat keinen direkten Zugriff auf den ZT Cluster. Die gematik erhält Zugriff auf die Logs und Metriken des ZT Clusters, um die Leistung und Sicherheit zu überwachen. + +Es gibt zwei Möglichkeiten, wie der ZT Cluster betrieben werden kann. + +. Betrieb in Verantwortung des Fachdienst-Anbieters: +Der Fachdienst-Anbieter stellt sicher, dass der ZT Cluster den Sicherheitsstandards der gematik entspricht und die Anforderungen des Fachdienstes erfüllt. Diese Variante setzt voraus, dass der Anbieter die notwendigen Ressourcen und Expertise für den Betrieb des ZT Clusters hat und dass die gematik dem Anbieter vertraut. Die gematik kann von sich aus nicht sicherstellen, dass der ZT Cluster den Anforderungen entspricht. + +. Betrieb in Verantwortung der gematik: +Die gematik stellt sicher, dass der ZT Cluster den Sicherheitsstandards entspricht und die Anforderungen des Fachdienstes erfüllt. Diese Variante setzt voraus, dass die gematik die notwendigen Ressourcen und Expertise für den Betrieb des ZT Clusters hat. + +=== CI Pipeline + +Basis der CI-Pipeline ist ein GitHub Repository. Dort werden Source-Code, Testcases und Konfigurationsdaten gespeichert. Die Stationen der CI Pipeline werden durch GitHub Actions ausgelöst und überwacht. Zur Ausführung der Testfälle werden die Komponenten in der gcloud deployed. Beim Passieren eines Quality Gates werden Labels vergeben. Folgende Schritte sind vorgesehen: + +. ZT Cluster Development: Implementierung von Änderungen an PEP und PDP, einschließlich neuer Funktionen, Bugfixes und Sicherheitsupdates. Erstellung von Container-Images für die Komponenten des ZT Clusters mit Docker. Erstellung von Kubernetes-Manifesten für die ZT Cluster Komponenten (Infrastructure as Code). Überprüfung der Änderungen durch Code-Reviews. Entwicklung der Testkomponenten (Dienst und Clients) sowie der Testfälle. +. Quality Gate Dev: Automatisierte Tests für den Anwendungscode, insbesondere Unittests. +. Quality Gate ZT Cluster Integration: Tests der Kubernetes-Infrastruktur, inklusive Sicherheitstests und Lasttests. Die gesamte Testumgebung wird in der gematik gcloud deployed und die Testfälle dort ausgeführt. +. Quality Gate Referenz-Integration: End-to-End-Tests mit den Referenz-Integrations-Komponenten, inklusive Sicherheitstests und ggf. Lasttests. Die gesamte Testumgebung wird in der gematik gcloud deployed und die Testfälle dort ausgeführt. +. Build: Erstellung von Container-Images für die Komponenten des ZT Clusters mit Docker. Multistage-Builds, um sicherzustellen, dass nur die notwendigen Abhängigkeiten im finalen Image vorhanden sind. +. ZT Cluster PU und RU: Signierung und Überprüfung von Container-Images, um die Integrität zu gewährleisten. Label Version, reference und latest. + +=== CD Pipeline + +Die CD Pipeline knüpft an das Ergebnis der CI Pipeline an. Zweck dieser Pipeline ist es spezifische ZT Cluster Konfigurationen für die TI 2.0 Dienste zu erzeugen und durchzusetzen, dass in den dienstspezifischen ZT Cluster-Instanzen die im GitHub CD Repository freigegebene Cluster-Konfiguration der jeweiligen TI 2.0 Dienste ausgeführt wird. + +Die hier beschriebene Pipeline ist optional. Wenn diese Pipeline nicht genutzt wird, erfolgt die dienstspezifische ZT-Cluster-Konfiguration direkt beim Hersteller des TI 2.0 Dienstes. Die Pipeline dort würde zusätzlich die Komponenten des Dienstes für Tests verwenden. Über git submodule werden die dienstspezifischen ZT Cluster Konfigurationen aus den GitHub Repositories der Dienst-Betreiber in das gematik CD GitHub Repository integriert, sodass die Überwachung der ZT-Cluster Konfigurationen wieder möglich ist. + +Die ZT Cluster Manifeste und Terraform Scripte werden über git Submodule direkt in das GitHub CD Repository integriert. +Neben PEP und PDP enthält der dienst-spezifische ZT Cluster eine Management-Komponente (z. B. Argo CD), die überwacht, dass immer die korrekte ZT Cluster-Konfiguration aus dem GitHub Repository verwendet wird. Die Management-Komponente überwacht auch die Integrität der Container-Images und die Konfiguration des ZT Clusters. + +. TI 2.0 Dienst ZT Cluster Entwicklung: Automatisierung der Bereitstellung des TI 2.0 dienst-spezifischem K8s-Cluster basierend auf den Änderungen im CI Repository. +. Quality Gate Dev: Automatisierte Tests des dienst-spezifischem K8s-Clusters +. Quality Gate TI 2.0 Dienst ZT Cluster Integration: Automatische Integrations-Tests mit den dienst-spezifischen Referenz-Komponenten der RU +. TI 2.0 Dienst ZT Cluster RU: Der dienstspezifische Cluster ist damit vollständig getestet und wird dem Betreiber für seine Tests bereitgestellt. +. Quality Gate TI 2.0 Dienst ZT Cluster Integration: Der Betreiber des TI 2.0 Dienstes führt seine Tests aus. +. TI 2.0 Dienst ZT Cluster PU: Der dienstspezifische ZT Cluster steht für den Einsatz in der PU bereit. + +Die Abbildung zeigt die Pipelines und wie sie verknüpft sind. + +image::CI_CD_Concept/ZT_CI-CD-Pipeline.png[Zero Trust CI/CP Pipeline,width="100%"] + +== Rollen und Verantwortlichkeiten + +Im CI/CD Prozess sind verschiedene Rollen und Verantwortlichkeiten definiert. Diese umfassen: + +|=== +|Rolle|Beschreibung + +|ZT Cluster Hersteller +|Entwickelt den PEP + +Erzeugt die K8s yaml Dateien für den ZT Cluster + +Steuert und Überwacht den Durchlauf des CI Prozesses inkl. Quality Gates + +Erstellt eine Installationsanleitung für den ZT Cluster inkl. Systemvoraussetzungen + +Ist verantwortlich für den CI Prozess + +Leistet Support für die entwickelte SW und die yaml Dateien für den ZT Cluster. Leistet Support für den ZT Cluster + +|ZT Cluster Anbieter +|Ist verantwortlich für den CD Prozess inkl. Quality Gates + +Leistet Support für die dienst-spezifischen ZT Cluster Konfigurationen + +|ZT Cluster Betreiber +|Ist verantwortlich für den Betrieb des ZT Clusters im Zusammenspiel mit dem TI 2.0 Dienst + +|=== + +== Anhang + +=== Risiken beim Betrieb des ZT Clusters + +Die Bereitstellung und der Betrieb des ZT Clusters sind mit verschiedenen Risiken verbunden. Dazu gehören: + +. Sicherheitsrisiken: Schwachstellen im Code, in der Konfiguration des ZT Clusters oder in den Policies und Daten können zu Sicherheitslücken führen, die von Angreifern ausgenutzt werden können. +. Datenschutzrisiken: Verstöße gegen den Datenschutz oder die Privatsphäre der Nutzer können zu rechtlichen Konsequenzen führen und das Vertrauen der Nutzer gefährden. +. Betriebsrisiken: Ausfälle oder Störungen im Betrieb des ZT Clusters können zu Beeinträchtigungen der Fachdienste führen und die Verfügbarkeit der Ressourcen beeinträchtigen. +. Partnerschaftsrisiken: Abhängigkeit von externen Dienstleistern oder Partnern für den Betrieb des ZT Clusters kann zu Risiken in Bezug auf Vertraulichkeit, Verfügbarkeit und Integrität der Daten führen. +. Managementrisiken: Fehlende Dokumentation, Schulung und Überwachung können zu Managementproblemen führen und die Effizienz des Betriebs beeinträchtigen. +. Budgetrisiken: Unvorhergesehene Kosten für den Betrieb des ZT Clusters können das Budget des Fachdienstes belasten und die Rentabilität des Projekts gefährden. +. Reputationsrisiken: Sicherheitsvorfälle oder Betriebsstörungen des ZT Clusters können das Ansehen des Fachdienstes und der gematik beeinträchtigen und das Vertrauen der Nutzer gefährden. +. Technologierisiken: Veraltete Technologien oder fehlende Updates können die Leistung und Sicherheit des ZT Clusters beeinträchtigen und die Skalierbarkeit des Systems einschränken. +. Innovationsrisiken: Fehlende Innovation und Weiterentwicklung des ZT Clusters können die Wettbewerbsfähigkeit des Fachdienstes beeinträchtigen und die Attraktivität des Angebots für die Nutzer verringern. +. Personalrisiken: Fehlende Expertise oder Ressourcen für den Betrieb des ZT Clusters können zu Personalengpässen führen und die Effizienz des Betriebs beeinträchtigen. +. Wettbewerbsrisiken: Konkurrenzdruck und Marktentwicklungen können die Rentabilität des ZT Clusters beeinträchtigen und die Position des Fachdienstes am Markt gefährden. \ No newline at end of file diff --git a/docs/zero_trust_api.md b/docs/zero_trust_api.md new file mode 100644 index 0000000..8aa3a2b --- /dev/null +++ b/docs/zero_trust_api.md @@ -0,0 +1,76 @@ +![gematik logo](images/gematik_logo.svg) +# Zero Trust API + +Eine gute API-Dokumentation sollte umfassend und klar strukturiert sein, um Entwicklern die Nutzung der API so einfach wie möglich zu machen. Hier sind die wichtigsten Elemente, die eine gute API-Dokumentation enthalten sollte: + +## Einführung +Zweck der API: Eine kurze Erklärung, was die API tut und welche Probleme sie löst. +Zielgruppe: Wer sollte diese API nutzen? Welche technischen Vorkenntnisse werden erwartet? +Voraussetzungen: Informationen über benötigte Tools, Bibliotheken oder SDKs. +## Authentifizierung und Autorisierung +API-Schlüssel oder Token: Wie erhält der Nutzer einen API-Schlüssel oder ein Authentifizierungstoken? +Autorisierungsverfahren: Beschreibung der verwendeten Authentifizierungsmethoden (z.B. OAuth, Basic Auth). +Beispiele: Beispielanfragen für die Authentifizierung. +## Basis-URL und Endpunkte +Basis-URL: Die grundlegende URL, von der alle API-Aufrufe ausgehen. +Endpunkte: Detaillierte Beschreibung aller verfügbaren Endpunkte, einschließlich: +HTTP-Methode: GET, POST, PUT, DELETE, etc. +Pfadparameter: Parameter, die in der URL enthalten sind. +Query-Parameter: Parameter, die in der URL als Abfrage angehängt werden. +Body-Parameter: Parameter, die im Body einer Anfrage gesendet werden. +## Antworten und Statuscodes +Beispielantworten: JSON-, XML- oder andere Formatbeispiele der API-Antworten. +Statuscodes: Liste der möglichen HTTP-Statuscodes mit Erklärungen (z.B. 200 OK, 404 Not Found, 500 Internal Server Error). +Fehlermeldungen: Beschreibung der möglichen Fehler und wie sie zu beheben sind. +## Beispiele für Anfragen und Antworten +Codebeispiele: Beispielcode in verschiedenen Programmiersprachen (z.B. Python, JavaScript, Curl), um die API-Aufrufe zu demonstrieren. +Erwartete Antworten: Darstellung der typischen API-Antworten für die gegebenen Anfragen. +## Datenstrukturen und Modelle +Datenformate: Erklärung der verwendeten Datenformate (z.B. JSON, XML). +Datenmodelle: Beschreibung der verwendeten Datenmodelle, inklusive aller Felder und Datentypen. +Beziehungen: Erklärung von Beziehungen zwischen verschiedenen Datenmodellen, falls zutreffend. +## Rate Limits und Einschränkungen +Rate Limits: Informationen über die Anzahl der erlaubten Anfragen pro Zeiteinheit. +Nutzungseinschränkungen: Informationen über eventuelle Einschränkungen der API-Nutzung, wie z.B. die maximale Größe von Anfragen. +## Versionierung +API-Versionierung: Hinweise darauf, wie Versionen der API verwaltet werden und wie Benutzer zwischen verschiedenen Versionen wechseln können. +Änderungsprotokoll: Ein Changelog, das alle wichtigen Änderungen und Updates dokumentiert. +## Support und Kontaktinformationen +Hilfe: Informationen darüber, wo und wie Benutzer Unterstützung erhalten können (z.B. Forum, E-Mail-Support). +Fehlerberichterstattung: Wie können Nutzer Bugs melden oder Feature-Anfragen stellen? +## FAQs und Troubleshooting +Häufige Fragen: Antworten auf häufige Fragen zur Nutzung der API. +Fehlerbehebung: Leitfaden zur Behebung häufiger Probleme. +## Interaktive Dokumentation (optional) +Swagger/OpenAPI: Ein interaktives Interface, mit dem Entwickler API-Endpunkte direkt aus der Dokumentation heraus testen können. +API-Sandbox: Eine Testumgebung, in der Entwickler sicher mit der API experimentieren können. +Eine gut strukturierte API-Dokumentation erleichtert es Entwicklern, die API effizient zu nutzen, und trägt dazu bei, häufige Fragen und Probleme zu minimieren. + +## Branch Modell + +In diesem Repository werden Branches verwendet um den Status der Weiterentwicklung und das Review von Änderungen abzubilden. + +Folgende Branches werden verwendet + +- *main* (enthält den letzten freigegebenen Stand der Entwicklung; besteht permanent) +- *develop* (enthält den Stand der fertig entwickelten Features und wird zum Review durch Industriepartner und Gesellschafter verwendet; basiert auf main; nach Freigabe erfolgt ein merge in main und ein Release wird erzeugt; besteht permanent) +- *feature/* (in feature branches werden neue Features entwickelt; basiert auf develop; nach Fertigstellung erfolgt ein merge in develop; wird nach dem merge gelöscht) +- *hotfix/* (in hotfix branches werden Hotfixes entwickelt; basiert auf main; nach Fertigstellung erfolgt ein merge in develop und in main; wird nach dem merge gelöscht) +- *concept/* (in feature branches werden neue Konzepte entwickelt; basiert auf develop; dient der Abstimmung mit Dritten; es erfolgt kein merge; wird nach Bedarf gelöscht) +- *misc/* (nur für internen Gebrauch der gematik; es erfolgt kein merge; wird nach Bedarf gelöscht) + +## Lizenzbedingungen + +Copyright (c) 2024 gematik GmbH + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/images/CI_CD_Concept/ZT_CI-CD-Pipeline.png b/images/CI_CD_Concept/ZT_CI-CD-Pipeline.png new file mode 100644 index 0000000..030bc57 Binary files /dev/null and b/images/CI_CD_Concept/ZT_CI-CD-Pipeline.png differ diff --git a/images/SM-B_Auth_with_DPoP.png b/images/SM-B_Auth_with_DPoP.png new file mode 100644 index 0000000..c75b46a Binary files /dev/null and b/images/SM-B_Auth_with_DPoP.png differ diff --git a/images/SM-B_Auth_with_DPoP.svg b/images/SM-B_Auth_with_DPoP.svg new file mode 100644 index 0000000..9a6dc50 --- /dev/null +++ b/images/SM-B_Auth_with_DPoP.svg @@ -0,0 +1 @@ +LEIBetreiberZT ClusterTI 2.0 DienstClientKonnektor orTI-GatewaySM-BPEPhttp ProxyPDPAuthorization ServerPDPPolicy EngineResource ServerClientKonnektor orTI-GatewaySM-BPEPhttp ProxyPDPAuthorization ServerPDPPolicy EngineResource Serveralt[Client has no Authorization Server FQDN][Client has Authorization Server FQDN](01)GET /resource(02)401 Unauthorized; json body with Well-Known json Document (RFC8414)(03)GET /.well-known/oauth-authorization-server (04)200 OK; json body with Well-Known json Document (RFC8414)(05)generate DPoP key pairalt[Client has no valid refresh token][Client has valid Refresh token](06)GET /nonce(07)return 200 OK new-nonce: nonce(08)create Client Assertion JWT with nonceClient and OS InformationDPoP Thumbprint(09)externalAuthenticate JWT-hash(10)sign JWT-hash(11)JWT-Signature(12)JWT-signature(13)add SM-B Signature to Client Assertion JWTcC4h... (Client Assertion JWT)(14)create DPoP JWT with nonce(15)POST /tokenContent-Type: application/x-www-form-urlencodedDPoP: ... (DPoP JWT)grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearerassertion=eyJh... (Client Assertion JWT)RFC7523 (JWT Client Assertion), RFC9449 (DPoP)(16)verify Client Assertion JWT and DPoP JWT(17)Create state for Identity/App/Device(18)create DPoP JWT without nonce(19)POST /tokenDPoP: ... (DPoP JWT)grant_type=refresh_tokenrefresh_token= ... (Refresh token)(20)verify Refresh token and DPoP JWT(21)invalidate Refresh token(22)POST /v1/data/authz, json body { "input": {...}}(23)200 OK, json body {"result": {"allow": true,"access_token_attributes": {...}}(24)issue Access and Refresh token with DPoP Bindingand Access token attributes(25)200 OK, Access token, Refresh token, bound to DPoP(26)Create DPoP Proof for RS(27)GET /resourceAuthorization: DPoP ... (Accesss token)DPoP: ... (DPoP token)(28)verify Access token and DPoP Proof(29)additional checks according toAccess token attributes(30)forward GET /resourcewith header and header content as json(31)provideresourceaccess(32)200 OK, resource(33)200 OK, resource \ No newline at end of file diff --git a/images/Zero-Trust_Architektur.png b/images/Zero-Trust_Architektur.png new file mode 100644 index 0000000..a31503b Binary files /dev/null and b/images/Zero-Trust_Architektur.png differ diff --git a/images/Zero-Trust_Architektur.svg b/images/Zero-Trust_Architektur.svg new file mode 100644 index 0000000..933a510 --- /dev/null +++ b/images/Zero-Trust_Architektur.svg @@ -0,0 +1,3 @@ + + +
TI 2.0 Dienst Betreiber
TI 2.0 Dienst Betreiber
Zero Trust Cluster
Zero Trust Cluster
Ingress
und
Egress
Ingress...
Legende
Legende
gematik
gematik
PDP
PDP
(3)
(3)
(4b)
(4b)
(2.4)
(2.4)
Authorization Server
(User Session)
Authorization Server...
(11)
(11)
Policy Engine
Policy Engine
Client Registry
(User/Device/App Data)
Client Registry...
(8.1)
(8.1)
Resource Server
(Application Backend)
Resource Server...
(8.2)
(8.2)
Application Authorization Backend
Application Authoriz...
(7)
(7)
Telemetrie-Daten Service
Telemetrie-Daten Ser...
(12)
(12)
Cluster Management
Service
Cluster Management...
(9.1)
(9.1)
Monitoring
Monitoring
(1)
(1)
(2.2)
(2.2)
Clientsystem
Clientsystem
Nutzer
Nutzer
E-Mail
Confirmation-Code/Link
E-Mail...
eGK, SM-B,
HBA
eGK, SM-B,...
Federated IDP,
IDP-Dienst
Federated IDP,...
(2.1)
(2.1)
(9.2)
(9.2)
BDE Lieferung
BDE Lieferung
(10.2)
(10.2)
SIEM
SIEM
(10.1)
(10.1)
BDE
BDE
PIP und PAP Service
PIP und PAP Service
TI SIEM
TI SIEM
Zero Trust Cluster Git Repository
Zero Trust Cluster G...
Zero Trust
Zero Trust
Anwendung
Anwendung
Bestehende Systeme
Bestehende Systeme
Kommunikation
Kommunikation
Kommunikation optional
Kommunikation optional
(2)
(2)
(4a)
(4a)
(5)
(5)
Trust Client
Trust Client
PEP
PEP
http Proxy
http Proxy
(2.3)
(2.3)
(6)
(6)
Text is not SVG - cannot display
\ No newline at end of file diff --git a/images/Zero-Trust_CI-CD_Overview.png b/images/Zero-Trust_CI-CD_Overview.png new file mode 100644 index 0000000..c33c9f6 Binary files /dev/null and b/images/Zero-Trust_CI-CD_Overview.png differ diff --git a/images/Zero-Trust_CI-CD_Overview.svg b/images/Zero-Trust_CI-CD_Overview.svg new file mode 100644 index 0000000..3923112 --- /dev/null +++ b/images/Zero-Trust_CI-CD_Overview.svg @@ -0,0 +1,3 @@ + + +
gematik gcloud
gematik gcloud
Betrieb
Betrieb
Test
Test
Legende
Legende
gematik GitHub
gematik GitHub
TI 2.0 Dienst Betreiber
TI 2.0 Dienst Betreiber
Zero Trust Cluster
Zero Trust Cluster
PEP
PEP
OPA bundle
download
OPA bundle...
PDP
PDP
Telemetrie-Daten Service
Telemetrie-Daten Ser...
Cluster Management
Service
Cluster Management...
Quality Gate
Lasttest
Quality Gate...
ZT Cluster CI
GitHub Repository
ZT Cluster CI...
Kubernetes
Manifeste
Kubernetes...
ZT Cluster CD GitHub Repository
ZT Cluster CD GitHub...
ZT PIP und PAP CI Prozess
ZT PIP und PAP CI Prozess
ZT Cluster CI Prozess
ZT Cluster CI Prozess
ZT Cluster CD Prozess
ZT Cluster CD Prozess
OPA Bundle
Signatur-Service
OPA Bundle...
signierte
OPA bundles
signierte...
PIP und PAP Service
PIP und PAP Service
Zero Trust
Integrationstest
Zero Trust...
signierte
Container
signierte...
Container Registry
Container Registry
Zero Trust
Lasttest
Zero Trust...
Zero Trust Komponenten
Zero Trust Komponenten
Quality Gate
Integrationstest
Quality Gate...
ZT PIP und PAP CI
GitHub Repository
ZT PIP und PAP CI...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/images/gematik_logo.svg b/images/gematik_logo.svg new file mode 100644 index 0000000..59ee2b3 --- /dev/null +++ b/images/gematik_logo.svg @@ -0,0 +1 @@ + diff --git a/src/drawio/Zero-Trust_Architektur.drawio b/src/drawio/Zero-Trust_Architektur.drawio new file mode 100644 index 0000000..318227a --- /dev/null +++ b/src/drawio/Zero-Trust_Architektur.drawio @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/drawio/Zero-Trust_CI-CD_Overview.drawio b/src/drawio/Zero-Trust_CI-CD_Overview.drawio new file mode 100644 index 0000000..914c421 --- /dev/null +++ b/src/drawio/Zero-Trust_CI-CD_Overview.drawio @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plantuml/device-registration-dpop.puml b/src/plantuml/device-registration-dpop.puml new file mode 100644 index 0000000..a0ab9dd --- /dev/null +++ b/src/plantuml/device-registration-dpop.puml @@ -0,0 +1,44 @@ +@startuml "" + +actor u as "User" +participant c as "Client" +participant aas as "App Attestation\nService" +participant as as "PEP\nAuthorization Server" +participant fd as "Fachdienst" +participant idp as "IDP" + +activate c + +c -> as: https GET /fd_ressource +activate as +as --> c: 401 Unauthorized {"supported_idp_list":[...], "attestation_required": "true",\n"DPoP_required": "true"} + +c -> as: GET /register/choosen_idp +as -> idp: POST /par +activate idp +idp --> as: 200 OK +as --> c: Redirect\nlocation: idp_url + +c -> idp: GET /authorize +group Authentication + note over u, idp: idp specific authentication +end group +idp --> c: 302 Redirect url_with_authorization_code +deactivate idp +c -> c: generate key pair (for DPoP) + +c -> aas: Request App/Device attestation\nParameter: nonce +activate aas +aas --> c: signed_attestation_token +deactivate aas + +c -> as: POST /register\n[signed_attestation_token, jkt] +as --> as: creaate and sign\njwt device_registration_token +as --> c: [jwt device_registration_token; includes jkt] +deactivate as + + + +deactivate c + +@enduml \ No newline at end of file diff --git a/src/plantuml/mobile_auth.puml b/src/plantuml/mobile_auth.puml new file mode 100644 index 0000000..b8296dd --- /dev/null +++ b/src/plantuml/mobile_auth.puml @@ -0,0 +1,127 @@ +@startuml "Mobile_Auth_with_DPoP" +autonumber "(00)" +skinparam defaultFontSize 10 +skinparam defaultFontName Helvetica +skinparam DefaultMonospacedFontName Courier +skinparam lengthAdjust none + +!pragma teoz true + +actor Nutzer as "Nutzer" +box Mobiles Gerät #GhostWhite + participant App as "App" #DarkSeaGreen + participant AuthModul as "Authenticator\nModul" +endbox +participant IDP as "IDP" +box "Betreiber" #TECHNOLOGY + box "ZT Cluster" #SandyBrown + participant AS as "PEP\nAuthorization Server" + participant Proxy as "PEP\nhttp Proxy" + participant PDP as "PDP" + endbox + box TI 2.0 Dienst #DarkSeaGreen + participant RS as "Resource Server" + endbox +end box + +Nutzer -> App +activate App +alt App has no Authorization Server FQDN + App -> Proxy: GET /resource + activate Proxy + Proxy --> App: 401 Unauthorized; json body with Well-Known json Document (RFC8414) + deactivate Proxy +else App has Authorization Server FQDN + App -> Proxy: GET /.well-known/oauth-authorization-server  + activate Proxy + Proxy --> App: 200 OK; json body with Well-Known json Document (RFC8414) + deactivate Proxy +end + +alt App has no DPoP key pair +App -> App: generate DPoP key pair +end + +alt App has no valid refresh token +App -> App: Retrieve IDP list (from /idp.app.ti-dienste.de/directory/fed_idp_list) +App -> App: Select IDP +App -> AS: GET /nonce +activate AS +AS --> App: return 200 OK new-nonce: nonce +App -> App: Create DPoP JWT with nonce +App -> AS: GET /auth (selected_idp)\n\ + DPoP: ... (DPoP JWT) +AS -> IDP: POST /par (App_id, state, redirect_uri\n\ +code_challenge, code_challenge_method, response_type=code,\n\ +nonce, scope, acr_values) +activate IDP +alt #White AS Entity statement is unknown + IDP -> AS: GET /.well-known/openid-federation + AS --> IDP: 200 OK, (Entity statement) +end +IDP --> AS: 200 OK,\n\ +(request_uri, expires_in) +group #White User Authentication and consent +AS --> AuthModul: 302 Redirect (location: IDP authorization_endpoint, request_uri) +activate AuthModul +AuthModul -> IDP: GET /authorize (request_uri) +IDP -> AuthModul: Challenge (Consent page) +AuthModul -> Nutzer: Authentication and confirmation +Nutzer --> AuthModul: +AuthModul --> IDP: Challenge response +deactivate AuthModul +end +IDP --> App: 302 Redirect, (location: /app/op-intermediary-callback, code, state) + +App -> App: create DPoP JWT with nonce +App -> AS: POST /token\n\ + DPoP: eyJ... (DPoP JWT)\n\ + (code, state) +AS -> AS: verify state and DPoP JWT +AS -> IDP: POST /token\n\ + Authorization: Basic ... (App_id + App_secret)\n\ + (authorization_code=code, grant_type=authoriz-ation_code,\n\ + redirect_uri, code_verifier=...) +IDP --> AS: 200 OK, (id_token, expires_in) +deactivate IDP +AS -> AS: verify id_token +AS -> AS: Create state for Identity/App/Device + +else App has valid Refresh token +App -> App: create DPoP JWT without nonce +App -> AS: POST /token\n\ + (Refresh token and DPoP JWT) +AS -> AS: verify Refresh token and DPoP JWT +AS -> AS: invalidate Refresh token +end + +AS -> PDP: POST /v1/data/authz, json body { "input": {...}} +activate PDP +PDP --> AS: 200 OK, json body {"result": {"allow": true, ...}} +deactivate PDP +AS -> AS: issue Access and Refresh token with DPoP Binding +AS --> App: 200 OK, Access token, Refresh token, bound to DPoP +deactivate AS +App -> App: Create DPoP Proof for RS + +App -> Proxy: GET /resource\n\ + Authorization: DPoP ... (Accesss token)\n\ + DPoP: ... (DPoP token) +activate Proxy +Proxy -> Proxy: verify access token and\nDPoP Binding +Proxy -> RS: forward GET //resource\n\ + Authorization: DPoP ... (Accesss token)\n\ + DPoP: ... (DPoP token) +activate RS +RS -> RS: provide\n\ +resource\n\ +access +RS --> Proxy: 200 OK, resource +deactivate RS +Proxy --> App: 200 OK, resource +deactivate Proxy +deactivate RS +deactivate App + + +@enduml \ No newline at end of file diff --git a/src/plantuml/sm-b-auth.puml b/src/plantuml/sm-b-auth.puml new file mode 100644 index 0000000..1b50709 --- /dev/null +++ b/src/plantuml/sm-b-auth.puml @@ -0,0 +1,109 @@ +@startuml "SM-B_Auth_with_DPoP" +autonumber "(00)" +skinparam defaultFontSize 10 +skinparam defaultFontName Helvetica +skinparam DefaultMonospacedFontName Courier +skinparam lengthAdjust none + +!pragma teoz true + +box "LEI" #GhostWhite + participant Client + participant Konnektor as "Konnektor or\nTI-Gateway" + participant SMB as "SM-B" +end box + +box "Betreiber" #TECHNOLOGY + box "ZT Cluster" #SandyBrown + participant HP as "PEP\nhttp Proxy" + participant AS as "PDP\nAuthorization Server" + participant PE as "PDP\nPolicy Engine" + endbox + box TI 2.0 Dienst #DarkSeaGreen + participant RS as "Resource Server" + endbox +end box + +activate Client +alt Client has no Authorization Server FQDN + Client -> HP: GET /resource + activate HP + HP --> Client: 401 Unauthorized; json body with Well-Known json Document (RFC8414) + deactivate HP +else Client has Authorization Server FQDN + Client -> HP: GET /.well-known/oauth-authorization-server  + activate HP + HP --> Client: 200 OK; json body with Well-Known json Document (RFC8414) + deactivate HP +end + +Client -> Client: generate DPoP key pair + +alt Client has no valid refresh token +Client -> AS: GET /nonce +activate AS +AS --> Client: return 200 OK new-nonce: nonce +Client -> Client: create Client Assertion JWT with nonce\n Client and OS Information\n DPoP Thumbprint +Client -> Konnektor: externalAuthenticate JWT-hash +activate Konnektor +Konnektor -> SMB: sign JWT-hash +activate SMB +SMB -> Konnektor: JWT-Signature +deactivate SMB +Konnektor -> Client: JWT-signature +deactivate Konnektor + +Client -> Client: add SM-B Signature to Client Assertion JWT\n\ +cC4h... (Client Assertion JWT) +Client -> Client: create DPoP JWT with nonce +Client -> AS: POST /token\n\ + Content-Type: application/x-www-form-urlencoded\n\ + DPoP: ... (DPoP JWT)\n\ + grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer\n\ + assertion=eyJh... (Client Assertion JWT) +note left of AS + RFC7523 (JWT Client Assertion), RFC9449 (DPoP) +end note +AS -> AS: verify Client Assertion JWT and DPoP JWT +AS -> AS: Create state for Identity/App/Device + +else Client has valid Refresh token +Client -> Client: create DPoP JWT without nonce +Client -> AS: POST /token\n\ + DPoP: ... (DPoP JWT)\n\ + grant_type=refresh_token\n\ + refresh_token= ... (Refresh token) +AS -> AS: verify Refresh token and DPoP JWT +AS -> AS: invalidate Refresh token +end + +AS -> PE: POST /v1/data/authz, json body { "input": {...}} +activate PE +PE --> AS: 200 OK, json body {"result": {"allow": true,\n\ +"access_token_attributes": {...}} +deactivate PE +AS -> AS: issue Access and Refresh token with DPoP Binding\n\ + and Access token attributes +AS --> Client: 200 OK, Access token, Refresh token, bound to DPoP +deactivate AS + +Client -> Client: Create DPoP Proof for RS +Client -> HP: GET /resource\n\ + Authorization: DPoP ... (Accesss token)\n\ + DPoP: ... (DPoP token) +activate HP +HP -> HP: verify Access token and DPoP Proof +HP -> HP: additional checks according to\n\ +Access token attributes +HP -> RS: forward GET /resource\n\ +with header and header content as json +activate RS +RS -> RS: provide\nresource\naccess +RS --> HP: 200 OK, resource +deactivate RS +HP --> Client: 200 OK, resource +deactivate HP +deactivate RS +deactivate Client + +@enduml \ No newline at end of file diff --git a/src/schemas/as-well-known.yaml b/src/schemas/as-well-known.yaml new file mode 100644 index 0000000..e3deed6 --- /dev/null +++ b/src/schemas/as-well-known.yaml @@ -0,0 +1,87 @@ +# Schema for the PEP authorization server .well-known json object +$schema: "http://json-schema.org/draft-07/schema#" +type: object +properties: + issuer: + type: string + format: uri + description: "The URL of the issuer." + authorization_endpoint: + type: string + format: uri + description: "The URL of the authorization endpoint." + token_endpoint: + type: string + format: uri + description: "The URL of the token endpoint." + nonce_endpoint: + type: string + format: uri + description: "The URL of the nonce endpoint." + openid_providers_endpoint: + type: string + format: uri + description: "The URL of the openid providers endpoint." + jwks_uri: + type: string + format: uri + description: "The URL of the JSON Web Key Set." + scopes_supported: + type: array + description: "The scopes supported by the authorization server." + items: + type: string + response_types_supported: + type: array + description: "The response types supported by the authorization server." + items: + type: string + enum: + - code + - token + response_modes_supported: + type: array + description: "The response modes supported by the authorization server." + items: + type: string + grant_types_supported: + type: array + description: "The grant types supported by the authorization server." + items: + type: string + token_endpoint_auth_methods_supported: + type: array + description: "The token endpoint authentication methods supported." + items: + type: string + token_endpoint_auth_signing_alg_values_supported: + type: array + description: "The signing algorithms supported at the token endpoint." + items: + type: string + service_documentation: + type: string + format: uri + description: "A URL to the service documentation." + ui_locales_supported: + type: array + description: "The UI locales supported by the authorization server." + items: + type: string + code_challenge_methods_supported: + type: array + description: "The code challenge methods supported for PKCE." + items: + type: string +required: + - issuer + - authorization_endpoint + - token_endpoint + - jwks_uri + - scopes_supported + - response_types_supported + - grant_types_supported + - token_endpoint_auth_methods_supported + - token_endpoint_auth_signing_alg_values_supported + - ui_locales_supported + - code_challenge_methods_supported diff --git a/src/schemas/sbom.yaml b/src/schemas/sbom.yaml new file mode 100644 index 0000000..8da8249 --- /dev/null +++ b/src/schemas/sbom.yaml @@ -0,0 +1,80 @@ +$schema: "http://json-schema.org/draft-07/schema#" +title: "Software Bill of Materials (SBOM)" +type: "object" +properties: + sbom: + type: "object" + properties: + version: + type: "string" + description: "The version of the SBOM format." + metadata: + type: "object" + properties: + timestamp: + type: "string" + format: "date-time" + description: "The timestamp of when the SBOM was generated." + tool: + type: "object" + properties: + name: + type: "string" + description: "The name of the tool used to generate the SBOM." + version: + type: "string" + description: "The version of the tool used to generate the SBOM." + required: + - name + - version + required: + - timestamp + - tool + components: + type: "array" + items: + type: "object" + properties: + name: + type: "string" + description: "The name of the component." + version: + type: "string" + description: "The version of the component." + type: + type: "string" + description: "The type of the component (e.g., library, application)." + supplier: + type: "string" + description: "The supplier or origin of the component." + licenses: + type: "array" + items: + type: "string" + description: "The licenses under which the component is distributed." + hashes: + type: "object" + properties: + SHA-256: + type: "string" + description: "The SHA-256 hash of the component." + required: + - SHA-256 + dependencies: + type: "array" + items: + type: "string" + description: "The dependencies of the component." + required: + - name + - version + - type + - supplier + - licenses + - hashes + required: + - version + - metadata + - components +required: + - sbom