Construindo um lançador de jogos econômico com URLs pré-assinados da AWS

Introdução Somos um pequeno estúdio de jogos com sede na Nova Zelândia. Como muitos estúdios modernos, construímos nosso próprio lançador de jogos (pense em estilo de lançador de jogos Steam ou Epic) que permite que os jogadores efetuem login em nossa própria solução de autenticação, usando seu provedor de identidade favorito – Google, Epic, Steam, Outlook e muito mais. A partir daí, eles podem baixar e atualizar nossos jogos. Parece direto, certo? Bem … não exatamente. O problema que nossos jogos e binários de lançadores são armazenados no Amazon S3, liderados pela CloudFront com um WAF para segurança. The Catch: Mesmo com o Cloudfront e o WAF no lugar, os baldes S3 ainda eram públicos. Isso significava que qualquer um que descobriu o URL poderia: baixar o jogo repetidamente. Compartilhe links diretos com outras pessoas. Aumente nossa conta de transferência de dados da CDN. Quando você está distribuindo grandes binárias (pense em dezenas de gigabytes por jogo), o custo dos downloads irrestritos rapidamente se torna doloroso. A solução que precisávamos de controlar quem pode baixar e garantir que os downloads estivessem vinculados a usuários autenticados reais. Digite URLs pré-assinados no CloudFront. Nossa infraestrutura se parece com o seguinte: API Gateway – Ponto de entrada pública para solicitações de download. Autorizador Lambda – valida a autenticação/sessão do usuário. Lambda Backend-emite URLs pré-assinados em nuvem de vida curta. SQS + DLQ-fornece uma fila de letra morta para solicitações com falha ou erros de processamento, garantindo que nenhum dados seja perdido e permitindo depuração e recuperação mais fáceis. O fluxo é simples: o lançador autentica o jogador. Ele chama nosso endpoint /Download-Url com o ID de ativo solicitado. O back -end Lambda valida o direito e retorna um URL assinado com um TTL curto (60 a 300). O lançador inicia imediatamente o download via CloudFront. Se alguma etapa falhar, o evento será enviado ao SQS DLQ para observabilidade. Resultado: Somente os usuários autorizados e intitulados recebem links de download válidos. Cache e TTL (o que realmente muda) Comportamento de cache: nossa resposta da API que retorna o URL assinado é enviado com controle de cache: privado, máximo = 0. Isso impede o cache acidental da resposta da API. O próprio ativo permanece em cache na borda; Os portões de assinatura acessam, não em cache. Estratégia TTL: Mantemos os URLs assinados por curto (60 a 120s para a maioria dos ativos). Isso é longo o suficiente para o lançador iniciar a transferência, mas curto o suficiente para reduzir o vazamento de links. Terraforma mínima (cópia) # 1) Chave pública em nuvem (a metade pública do seu teclado) Recurso “aws_cloudfront_public_key” “downloads_pk” {name = “downloads-public-key” coded_key = file (“$ {path.module}/cloudfront-public- 2) Grupo de chave que inclui o recurso de chave público “AWS_CLOUDFRONT_KEY_GROUP” “Downloads_kg” {name = “Downloads-key-group” itens = [aws_cloudfront_public_key.downloads_pk.id]
} # 3) Origin (S3 ou uma origem com controle de controle S3) Resource “AWS_CLOUDFRONT_ORIGIN_ACCESS_CONTROL” “OAC” {name = “Downloads-oac” Descrição = “OAC para S3 downloads” Origin_Access_Control_origin_Type = “S3″ Signing_Haver ” “AWS_S3_BUCKET” “ASSETS” {bucket = “Game-atire-o-Exemplo”} # 4) Distribuição com comportamento que requer URLS Resource assinado “AWS_CLOUDFRONT_DISTRIBUTION” “Downloads” {Enabled = “Comment =” Games downloads (Urls assinado) “Default_root_object” “” aws_s3_bucket.assets.bucket_regional_domain_name orige_id = “s3-assets” orige_access_control_id = aws_cloudfront_origin_access_control.oac.id} default_cache_behavior {TARGEN_ORGIN_ID = ” “Redirecionar-para-https” permitido_methods = [“GET”, “HEAD”]
cached_methods = [“GET”, “HEAD”]

# Requer URLs assinados por meio deste grupo -chave confied_key_groups = [aws_cloudfront_key_group.downloads_kg.id]

compress = true # Cache policy & origin request policy can be customized as needed cache_policy_id = data.aws_cloudfront_cache_policy.caching_optimized.id origin_request_policy_id = data.aws_cloudfront_origin_request_policy.cors_s3_origin.id } restrictions { geo_restriction { Restription_type = “None”}} Viewer_Certificate {Cloudfront_default_Certificate = true}} dados “AWS_CLOUDFRONT_CACHE_POLICY” “Caching_Optimized” {Name = “Managent-CachingOptimized”} “Aws_Cloud_ortfront” {name = “gerenciado-cors-s3origin”} Digite o modo de tela fullcreen Sair Modo de tela cheia Significador Lambda Mínimo (Python) Aqui está um pequeno extrato com base em nosso back-end que mostra o caminho de assinatura do núcleo: leia o par-de-ídhe-Id da Env, carregue a chave privada do Secrets Manager, assine um url, retorno {url, expire}. Importar OS, JSON, BASE64 do DateTime Importar DateTime, Timedelta Importar boto3 da criptografia.hazmat.Primitivos Importar serialização, hashes da criptografia.hazmat.priMitives.Assymmetric Secrets Padding Secrets = BOTO3.CLIENT (“STRETSMANAGER”) _BS _B64 base64.b64Encode (dados) .Decode (“utf-8”) retorna s.Replace (“+”, “-“). substituir (“/”, “~”). substituir (“=”, “_”) def sign_url (url: str, expires_secs: int, key_pair_id: strl_key_keM: int ((dateTime.utcnow () + timedelta (segundos = expires_secs)). timestamp ()) policy = {“declaração”: [{“Resource”: url, “Condition”: {“DateLessThan”: {“AWS:EpochTime”: expire_ts}}}]} policy_json = json.dumps(policy, separators=(“,”, “:”)) private_key = serialization.load_pem_private_key(private_key_pem.encode(), password=None) signature = private_key.sign(policy_json.encode(), padding.PKCS1v15(), hashes.SHA1()) return F “{url}? Policy = {_ B64url (Policy_Json.Encode ())} & Signature = {_ B64url (Signature)} & ke-pair-ID = {key_pair_id}” Def Handler (Event, _Context): CF_DOMAIN = OS.ENVIRN[“CLOUDFRONT_DOMAIN_NAME”] # por exemplo, dxxxxx.cloudfront.net key_pair_id = os.environ[“CLOUDFRONT_PUBLIC_KEY_ID”] # The Public Key ID secret_arn = os.environ[“CLOUDFRONT_PRIVATE_KEY_SECRET”] # Pem Chave Private no Secrets Manager Path = JSON.Loads (Event.get (“Body”, “{}”)).[“SecretString”]
url = f “ttl = int (os.environ.get (” Presigned_url_expiry “,” 120 “)) assinado = signo_url (url, ttl, key_pair_id, Pem) # importante: prevenir cache de cache de * API * Responder Return {” Status “: 200,” Headers “: {{” “Private, max-Arane = 0”, “Access-Control-Alow-Origin”: “*”}, “Body”: json.dumps ({“url”: assinado, “expiresat”: int (mode de dados fullcreen (). Cachehitrate: deve ser atendido para os ativos de jogo. → Lambda logs → Absolutamente. IS: Somente os usuários autorizados podem acessar o Gateway Scalable-API, Lambda e SQS, de forma alta. EUA: Continue refinando a observabilidade (vinculando dados do SQS em painéis) e expandindo a lógica de direito para futuros títulos.

Fonte

Você pode ter perdido