Lambda@Edge를 사용한 기본 인증

🖼 배경



최근에 나는 사이트에 액세스하기 위해 간단한 암호를 요구하는 빠르고 더러운 솔루션으로 Basic Authentication을 추가하여 S3에서 호스팅되는 정적 웹 사이트를 "보안"(예: 공개되지 않게)하라는 요청을 받았습니다. 이 문서에서는 Cloudfront 및 Lambda@Edge의 도움으로 이를 달성할 수 있는 방법을 설명합니다. 실제로 민감한 모든 것에 이것을 사용하는 것은 끔찍한 생각이라는 점에 유의하십시오. 이것은 정적 웹 사이트에 대한 암호 요구 사항을 추가하는 매우 빠르고 간단한 방법일 뿐입니다. Lambda@Edge로 손을 더럽히는 것도 재미있는 프로젝트입니다! Cloudfront 배포가 전면에 있는 S3에서 호스팅되는 웹 사이트가 이미 있다고 가정하겠습니다. 그렇지 않은 경우 interwebz에서 설정하는 방법에 대한 많은 가이드가 있습니다.

🤫 그냥 해봐 친구



좋아, 좋아, 시작하자. 여기서 아이디어는 Lambda@Edge를 사용하여 Cloudfront 요청 수명 주기에 연결하여 요청을 가로채서 실제 인증을 수행할 수 있다는 것입니다.
npm init -y 로 빈 폴더에 새 프로젝트를 초기화하여 서버리스 앱을 만드는 것으로 시작하겠습니다. 이제 서비스를 배포하는 데 필요한 것을 설치해 보겠습니다.

npm install serverless serverless-lambda-edge-pre-existing-cloudfront --save-dev

serverless-lambda-edge-pre-existing-cloudfront 플러그인을 사용하면 기억하기 쉬운 이름 외에도 Lambda@Edge 함수를 기존 Cloudfront 배포에 연결할 수 있습니다.

다음으로 Lambda 함수를 생성해 보겠습니다.

// basic-auth.js
const handler = async (event) => {
  const { request } = event.Records[0].cf;
  const headers = request.headers;

  const username = 'username';
  const password = 'password';

  const base64Credentials = Buffer.from(`${username}:${password}`).toString('base64');
  const authString = `Basic ${base64Credentials}`;

  // If authorization header isn't present or doesn't match expected authString, deny the request
  if (
    typeof headers.authorization == 'undefined' ||
    headers.authorization[0].value !== authString
  ) {
    return {
      body: 'Unauthorized',
      headers: {
        'www-authenticate': [{ key: 'WWW-Authenticate', value: 'Basic' }]
      },
      status: '401',
      statusDescription: 'Unauthorized',
    };
  }

  // Continue request processing
  return request;
};

module.exports.handler = handler;


코드에 사용자 이름과 비밀번호를 하드코딩하는 것은 절대 좋은 생각이 아니며 예를 들어 DynamoDB 테이블을 사용하여 대신 런타임에 이를 가져올 수 있습니다. 그러나 Lambda@Edgedoes not support environment variables . 실제로 Lambda@Edge에는 상당히 많은 단점과 예상치 못한 제한 사항이 있으므로 무엇이든 변경하고 문제가 발생하면 제한 사항 문서를 자세히 살펴보는 것이 좋습니다.

이제 serverless.yml에서 우리의 아름다운 serverless 서비스를 다음과 같이 설명하겠습니다.

service:
  name: basic-auth-demo

plugins:
  - serverless-lambda-edge-pre-existing-cloudfront

provider:
  name: aws
  # Cloudfront only supports Lambda@Edge functions defined 
  # in us-east-1
  region: 'us-east-1'
  runtime: nodejs12.x
  versionFunctions: true
  memorySize: 128
  role: role
  timeout: 5

functions:
  basic-auth:
    handler: basic-auth.handler
    events:
      - preExistingCloudFront:
          distributionId: ${env:CLOUDFRONT_DISTRIBUTION_ID}
          pathPattern: '*'
          eventType: viewer-request
          includeBody: false

resources:
  Resources:
    role:
      Type: AWS::IAM::Role
      Properties:
        RoleName: role
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                  - lambda.amazonaws.com
                  - edgelambda.amazonaws.com
              Action: sts:AssumeRole
        ManagedPolicyArns:
          - arn:aws:iam::aws:policy/service-role/AWSLambdaRole


이 서비스를 배포하면 방금 만든 Lambda 함수가 정적 웹 사이트 앞의 Cloudfront 배포에 연결됩니다. 환경 변수CLOUDFRONT_DISTRIBUTION_ID를 배포 ID로 설정해야 합니다.
[default]~/.aws/credentials 프로필에 유효한 AWS 자격 증명이 있다고 가정하면 이제 이 서비스를 배포할 수 있습니다.

export CLOUDFRONT_DISTRIBUTION_ID=abc123 
npx serverless deploy 


이제 귀하의 웹사이트에 액세스하면 귀하가 누구인지 즉시 설명하라는 매우 불쾌한 대화 상자가 표시됩니다 🎉



지금까지 다음과 같이 질문할 수 있습니다.

But Mr. Elk, can't someone just access my website by going straight to the S3 resource, bypassing Cloudfront?



우수한 질문 익명의 인터넷 사용자 #12339 - 아니요. Origin Access Identity을 사용하여 S3 파일에 대한 액세스를 제한하는 경우에는 그렇지 않습니다.

이 가이드가 마음에 들었고 더 많은 내용을 보려면 내가 서버리스 기술, AWS 및 개발자 생산성에 대해 자주 쓰는 Twitter에서 나를 팔로우하십시오!

행복한 해킹! 🚀

좋은 웹페이지 즐겨찾기