Aufbau eines serverlosen verbundenen Grills als SaaS - Teil 4 - AuthZ

Diese Datei wurde automatisch von KI ubersetzt, es konnen Fehler auftreten
Wir haben bereits auf Datenisolierung und Zeilenebensicherheit in einer Multi-Tenant-SaaS-Umgebung eingegangen. Nun ist es an der Zeit, uns damit zu befassen, wie Autorisierung gehandhabt wird – der Prozess, der den Zugriff auf Daten und Funktionalitäten steuert, basierend darauf, wer der Benutzer ist und was er tun darf.
In diesem Beitrag werden wir die Architektur eines zentralen Policy Decision Point (PDP) mit verteilten Policy Enforcement Points (PEPs) untersuchen, um eine konsistente und sichere Autorisierung über Ihre SaaS-Plattform hinweg zu sichern. Wir werden auch die wichtigsten Unterschiede zwischen Authentifizierung (AuthN) und Autorisierung (AuthZ) erklären.
Falls Sie es noch nicht getan haben, hier sind Teil eins, Teil zwei und Teil drei
Authentifizierung (AuthN) vs. Autorisierung (AuthZ)
Bevor wir mit dem Aufbau der Architektur beginnen, müssen wir ein gemeinsames Verständnis etablieren. Es ist entscheidend, zwischen Authentifizierung und Autorisierung zu unterscheiden – zwei Begriffe, die oft vermischt werden und die ich schon oft erklären musste, aber sehr unterschiedliche Zwecke erfüllen.
Authentifizierung (AuthN)
Bei der Authentifizierung geht es darum, die Identität zu überprüfen. Sie beantwortet die Frage: Wer bist du? Wenn sich ein Benutzer bei unserer SaaS-Anwendung anmeldet, stellt die Authentifizierung sicher, dass er der ist, für den er sich ausgibt. Dies könnte etwas so Einfaches wie ein Benutzername und Passwort oder eine komplexere Multi-Faktor-Authentifizierung (MFA) sein.
Wir verwenden in dieser Serie Amazon Cognito User Pools für unsere Authentifizierung.
Autorisierung (AuthZ)
Sobald die Identität eines Benutzers authentifiziert ist, kommt die Autorisierung ins Spiel. Dieser Prozess beantwortet die Frage: Was darfst du tun? Die Autorisierung bestimmt, auf welche Ressourcen, Daten und Aktionen ein Benutzer basierend auf seinen Rollen und Berechtigungen zugreifen darf.
In einer Multi-Tenant-Umgebung ist es wichtig, die Autorisierung richtig zu implementieren. Wir müssen sicherstellen, dass Benutzer nur auf die Daten und Funktionalitäten zugreifen können, die für ihren Tenant und seine Rolle relevant sind. Hier kommt ein robustes, skalierbares Autorisierungssystem ins Spiel.
Zentralisierter PDP und verteilte PEPs: Das Rückgrat der Autorisierung
Nachdem wir den Unterschied zwischen AuthN und AuthZ geklärt haben, konzentrieren wir uns auf den Aufbau eines Autorisierungssystems mit einem zentralen PDP und verteilten PEPs.
Zentralisierter PDP
Ein Policy Decision Point (PDP) ist der Ort, an dem alle Ihre Autorisierungsentscheidungen getroffen werden. Es ist das Gehirn des Autorisierungssystems, das jede Anfrage gegen vordefinierte Richtlinien oder Rollen auswertet und entscheidet, ob die Anfrage zugelassen oder abgelehnt werden soll. Es gibt einige klare Vorteile eines zentralen PDP:
Konsistenz: Jede Anfrage wird gegen denselben Satz von Richtlinien ausgewertet, was konsistente Entscheidungen über unsere Systeme hinweg sichert.
Verwaltung und Compliance: Da alle Richtlinien an einem Ort verwaltet werden, werden Updates und Audits vereinfacht. Das Protokollieren von Entscheidungen zu Audit-Zwecken ist für einige regulatorische Compliance wichtig.
Ein Nachteil wäre jedoch die erhöhte Latenz. Jedes Autorisierungsanfragen über einen einzigen PDP zu leiten, kann die Dinge verlangsamen, besonders in einem verteilten System.
Mit Amazon Verified Permissions (AVP) ist ein zentraler PDP erforderlich.
Verteilte PEPs
Policy Enforcement Points (PEPs) sind die Stellen, an denen die vom PDP getroffenen Autorisierungsentscheidungen durchgesetzt werden. Diese Punkte sind über unser System verteilt. Unser Lambda-basierter Authorizer ist ein Beispiel für einen PEP. Die Vorderfront unserer Mikrodienste kann ebenfalls als PEP dienen und unseren PDP aufrufen.
Wir haben einige Vorteile, wenn wir unsere PEPs verteilt betreiben.
Reduzierte Latenz: Indem wir PEPs nahe an den Stellen platzieren, an denen Entscheidungen durchgesetzt werden müssen, können wir die Latenz reduzieren, mit einer Caching-Strategie kann sie noch weiter reduziert werden.
Skalierbarkeit: Wenn Ihr System wächst, stellen verteilte PEPs sicher, dass kein einzelner Punkt zum Engpass wird, insbesondere mit aktiviertem Caching.
Architekturüberblick
In unserer neuen Autorisierungsarchitektur werden wir einen neuen Autorisierungsdienst einführen, der unser zentraler PDP sein wird. Die Logik für die Autorisierung wird von unserem vorherigen Lambda-Authorizer zum PDP verschoben, stattdessen wird unser Lambda-Authorizer nun den PDP aufrufen, der die Zugriffsentscheidung zurückgibt.

Autorisierungsdienst (PDP) erstellen
Das Erste, was wir tun müssen, ist, unseren Autorisierungsdienst (PDP) zu erstellen, wie gezeigt, wird dies ein API Gateway und eine Lambda-Funktion sein. Ein interessanter Ansatz, den wir verwenden könnten, ist, das API Gateway zu entfernen und nur Lambda-Funktions-URLs zu verwenden. Für eine einfachere Erweiterung später habe ich mich jedoch entschieden, ein API Gateway zu verwenden.
AWSTemplateFormatVersion: "2010-09-09"
Transform: "AWS::Serverless-2016-10-31"
Description: Connected BBQ Application Tenant Service
Parameters:
ApplicationName:
Type: String
Description: Name of owning application
Default: bbq-iot
UserPoolStackName:
Type: String
Description: The name of the Stack with the Cognito User Pool
Globals:
Function:
Timeout: 30
MemorySize: 2048
Runtime: python3.12
Resources:
LambdaPDPFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: Lambda/AuthZ
Handler: authz.handler
Environment:
Variables:
JWKS_URL:
Fn::ImportValue: !Sub ${UserPoolStackName}:jwks-url
AUDIENCE:
Fn::ImportValue: !Sub ${UserPoolStackName}:app-audience
Events:
AuthZ:
Type: Api
Properties:
Path: /authz
Method: post
RestApiId: !Ref PDPApi
Auth:
AuthorizationType: AWS_IAM
PDPApi:
Type: AWS::Serverless::Api
Properties:
Name: !Sub ${ApplicationName}-pdp-api
StageName: prod
EndpointConfiguration: REGIONAL
Cors:
AllowMethods: "'GET,PUT,POST,DELETE,OPTIONS'"
AllowHeaders: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
AllowOrigin: "'*'"
Auth:
DefaultAuthorizer: AWS_IAM
Outputs:
PDPApiID:
Value: !Ref PDPApi
Description: The ID of the PDP API
Export:
Name: !Sub ${AWS::StackName}:pdp-api-id
Unsere API wird AWS IAM für Machine-2-Machine-Autorisierung verwenden, genau wie unsere Admin-API, die wir im vorherigen Teil erstellt haben.
Als Nächstes verschieben wir unsere AuthZ-Logik von unserem Lambda-Authorizer zu unserer neuen PDP-Implementierung. Wir werden sowohl die Autorisierung für Benutzer- als auch für Tenant-Zugriffe behandeln und zwei neue Ressourcen tenant und tenantUser einführen.
import os
import json
import jwt
from jwt import PyJWKClient
def handler(event, context):
data = json.loads(event["body"])
jwt_token = data["jwt_token"]
try:
jwks_url = os.environ["JWKS_URL"]
jwks_client = PyJWKClient(jwks_url)
signing_key = jwks_client.get_signing_key_from_jwt(jwt_token)
decoded_token = jwt.decode(
jwt_token,
signing_key.key,
algorithms=["RS256"],
audience=os.environ["AUDIENCE"],
)
# Ressource prüfen und eine AuthZ-Entscheidung treffen
if data["resource"] == "tenant":
tenant_id = data["tenant_id"]
token_tenant_id = decoded_token.get("tenant")
if token_tenant_id == tenant_id:
response_body = generate_access(
"Allow", data["action"], data["resource"]
)
return {
"statusCode": 200,
"body": json.dumps(response_body),
"headers": {"Content-Type": "application/json"},
}
elif data["resource"] == "tenantUser":
user_id = data["user_id"]
token_user_id = decoded_token.get("cognito:username")
if token_user_id == user_id:
response_body = generate_access(
"Allow", data["action"], data["resource"]
)
return {
"statusCode": 200,
"body": json.dumps(response_body),
"headers": {"Content-Type": "application/json"},
}
except Exception as e:
print(f"Authorization error: {str(e)}")
# Standardantwort generieren, die den Zugriff verweigert
response_body = generate_access("Deny", data["action"], data["resource"])
return {
"statusCode": 403,
"body": json.dumps(response_body),
"headers": {"Content-Type": "application/json"},
}
def generate_access(effect, action, resource):
auth_response = {
"effect": effect,
"action": action,
"resource": resource,
}
return auth_response
Als Nächstes müssen wir natürlich den Lambda-Authorizer aktualisieren, um unseren PDP für die Autorisierung aufzurufen.
api_endpoint = os.environ.get("PDP_AUTHZ_API_ENDPOINT")
def handler(event, context):
token = event["headers"].get("authorization", "")
if not token:
raise Exception("Unauthorized")
token = token.replace("Bearer ", "")
try:
......
path_tenant_id = event["pathParameters"]["tenantId"]
session = boto3.Session()
credentials = session.get_credentials().get_frozen_credentials()
region = os.environ["AWS_REGION"]
auth = AWS4Auth(
credentials.access_key,
credentials.secret_key,
region,
"execute-api",
session_token=credentials.token,
)
data = {
"jwt_token": token,
"resource": "tenant",
"action": "read",
"resource_path": event["path"],
"tenant_id": event["pathParameters"]["tenantId"],
}
headers = {"Content-type": "application/json"}
response = requests.post(
api_endpoint + "/authz", data=json.dumps(data), headers=headers, auth=auth
)
......
except Exception as e:
print(f"Authorization error: {str(e)}")Zusammenfassung
Mit diesen Änderungen haben wir unsere Autorisierung zu einem zentralen PDP mit einem verteilten PEP-Setup verlegt. Dies schafft eine gute Grundlage für uns, um unsere AuthZ weiterzuentwickeln. In späteren Beiträgen werden wir RBAC (Role Based Access Control) und ABAC (Attribute Based Access Control) einführen und auch zu Amazon Verified Permissions wechseln.
Holen Sie sich den Code
Das vollständige Setup mit allem Code ist auf der Serverless Handbook verfügbar.
Abschließende Worte
In diesem Beitrag haben wir uns die Architektur eines zentralen Autorisierungssystems mit einem PDP und verteilten PEPs angesehen. Wir haben auch die Unterschiede zwischen Authentifizierung (AuthN) und Autorisierung (AuthZ) hervorgehoben.
In den kommenden Beiträgen werden wir uns mit der Geräte-Onboarding und Daten befassen, bleiben Sie gespannt!
Schauen Sie sich mein Serverless Handbook an, um den Code für die in dieser Beitragsreihe erstellte Lösung zu erhalten.
Vergessen Sie nicht, mir auf LinkedIn und X zu folgen, um weitere Inhalte zu erhalten, und lesen Sie meine übrigen Blogs
Wie Werner sagt! Jetzt loslegen!