AWS Ответы шлюза не передают контекст авторизатора обратно на 401 Unauthorized - PullRequest
0 голосов
/ 03 августа 2020

Я использую AWS SAM для некоторых ламб и авторизатора. Подтверждено, что API-шлюз использует авторизатор, используя Postman (а не функцию тестирования в консоли), и подтвердил, что журналы отображаются в функции авторизатора. Пока не делал ничего особенного с авторизатором, просто пытаюсь передать обратно динамически сгенерированный заголовок WWW-Authenticate, если был недействительный или отсутствующий токен (просто обрабатываем Authorization: bearer [нет текста после носителя] прямо сейчас в качестве примера).

Все эти выходные я бился головой об стену, похоже, не могу заставить авторизатор передать обратно какое-либо значение для контекста. Я пробовал несколько вещей, таких как $context.authorizer.challenge и $event.requestContext.authorizer.challenge, помещая их в тело и заголовок, даже пробовал $context.authorizer.principalId, который тоже ничего не генерирует.

Код авторизатора обрабатывает запрос:

func (a *authHandler) Handler(request events.APIGatewayCustomAuthorizerRequest) (events.APIGatewayCustomAuthorizerResponse, error) {
    fmt.Printf("checking auth\n")

    token := request.AuthorizationToken
    tokenSlice := strings.Split(token, " ")
    var bearerToken string
    if len(tokenSlice) > 1 {
        bearerToken = tokenSlice[len(tokenSlice)-1]
    }
    if bearerToken == "" {
        fmt.Printf("about to return policy for empty token")
        // I've tried multiple combinations of Deny/Allow, context maps, error response, etc. 
        return generatePolicy("user", "Deny", request.MethodArn, map[string]interface{}{"challenge": "testing"}), errors.New("Unauthorized")
    }

    return generatePolicy("user", "Allow", request.MethodArn, map[string]interface{}{"name": bearerToken}), nil
}

func generatePolicy(principalID, effect, resource string, context map[string]interface{}) events.APIGatewayCustomAuthorizerResponse {
    authResponse := events.APIGatewayCustomAuthorizerResponse{PrincipalID: principalID}

    if effect != "" && resource != "" {
        authResponse.PolicyDocument = events.APIGatewayCustomAuthorizerPolicy{
            Version: "2012-10-17",
            Statement: []events.IAMPolicyStatement{
                {
                    Action:   []string{"execute-api:Invoke"},
                    Effect:   effect,
                    Resource: []string{resource},
                },
            },
        }
    }
    authResponse.Context = context
    return authResponse
}

Это просто заполнитель, который должен возвращать Unauthorized, если получен пустой носитель токена, например Authorization: bearer.

template.yaml:

Resources:
  BaseApi:
      Type: AWS::Serverless::Api
      Properties:
        StageName: Prod
        Auth:
          DefaultAuthorizer: TokenAuthorizer
          Authorizers:
            TokenAuthorizer:
              FunctionPayloadType: TOKEN
              FunctionArn: !GetAtt AuthTokenFunction.Arn
              Identity:
                Headers:
        GatewayResponses:
          UNAUTHORIZED:
            StatusCode: 401
            ResponseParameters:
              Headers:
                Access-Control-Expose-Headers: "'WWW-Authenticate'"
                WWW-Authenticate: "context.authorizer.challenge"
                Principal-ID: "context.authorizer.principalId"
            ResponseTemplates:
              "application/json": '{ "message": $context.error.messageString, "testing": "test", "challenge": $context.authorizer.challenge, "challengeEvent": $event.requestContext.authorizer.challenge }'

  AuthTokenFunction:
    Type: AWS::Serverless::Function 
    Properties:
      CodeUri: cmd/lambda/auth
      Handler: services-auth
      Runtime: go1.x
      Role: !GetAtt ParameterAuthTokenFunctionRole.Arn
      Tracing: Active
      Environment:
        Variables:
          APP_CONFIG_PATH: 'parameterAuthToken'
          AWS_XRAY_TRACING_NAME: 'AuthTokenFunction'

  ParameterAuthTokenFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          -
            Effect: Allow
            Principal:
              Service:
                - 'lambda.amazonaws.com'
            Action:
              - 'sts:AssumeRole'
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
      Policies:
        -
          PolicyName: 'ParameterAuthTokenParameterAccess'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              -
                Effect: Allow
                Action:
                  - 'ssm:GetParameter*'
                #Resource: !Sub 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/prod/parameterAuthToken*'
                Resource: '*'
        -
          PolicyName: 'ParameterAuthTokenXRayAccess'
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              -
                Effect: Allow
                Action:
                  - 'xray:PutTraceSegments'
                  - 'xray:PutTelemetryRecords'
                Resource: '*'

  ParameterAuthTokenEncryptionKeyAlias:
    Type: AWS::KMS::Alias
    Properties:
      AliasName: 'alias/ParameterAuthTokenKey'
      TargetKeyId: !Ref ParameterAuthTokenEncryptionKey

  ParameterAuthTokenEncryptionKey:
    Type: AWS::KMS::Key
    Properties:
      Description: 'Encryption key for secret config values for the Parameter Auth Token'
      Enabled: True
      EnableKeyRotation: False
      KeyPolicy:
        Version: '2012-10-17'
        Id: 'key-default-1'
        Statement:
          -
            Sid: 'Allow administration of the key & encryption of new values'
            Effect: Allow
            Principal:
              AWS: 
                - !Sub 'arn:aws:iam::${AWS::AccountId}:user/anthony-local'
            Action:
              - 'kms:Create*'
              - 'kms:Encrypt'
              - 'kms:Describe*'
              - 'kms:Enable*'
              - 'kms:List*'
              - 'kms:Put*'
              - 'kms:Update*'
              - 'kms:Revoke*'
              - 'kms:Disable*'
              - 'kms:Get*'
              - 'kms:Delete*'
              - 'kms:ScheduleKeyDeletion'
              - 'kms:CancelKeyDeletion'
            Resource: '*'
          -
            Sid: 'Allow use of the key'
            Effect: Allow
            Principal:
              AWS: !GetAtt ParameterAuthTokenFunctionRole.Arn
            Action:
              - 'kms:Encrypt'
              - 'kms:Decrypt'
              - 'kms:ReEncrypt*'
              - 'kms:GenerateDataKey*'
              - 'kms:DescribeKey'
            Resource: '*'

  GhostFunction:
    Type: AWS::Serverless::Function 
    Properties:
      CodeUri: cmd/lambda/ghost
      Handler: services-ghost
      Runtime: go1.x
      Tracing: Active 
      Events:
        GetGhost:
          Type: Api 
          Properties:
            Path: /ghost/{id}
            Method: GET
            RestApiId: !Ref BaseApi
            Auth:
              Authorizer: TokenAuthorizer


Ответ:

401 Unauthorized

Headers: 
  WWW-Authenticate: 
  Principal-ID:
  Access-Control-Expose-Headers: WWW-Authenticate
  x-amzn-ErrorType: UnauthorizedException 

Body:
{ "message": "Unauthorized", "testing": "test", "challenge": , "challengeEvent":  }

И некоторые скриншоты консоли:

enter image description here enter image description here введите описание изображения здесь

Есть подсказки? Я собираюсь отказаться от авторизатора и добавить в свой лог c в каждую лямбда-функцию. Кажется, там намного проще сделать что-то вроде применения заголовка.

1 Ответ

0 голосов
/ 04 августа 2020

Кидая полотенце и говоря, что в настоящее время это невозможно, по крайней мере, с golang.

Я смог получить контекст, отображаемый, если я НЕ возвращал ошибку. Итак, здесь появляется контекст:

return generatePolicy("user", "Deny", request.MethodArn, map[string]interface{}{"challenge": "testing"}), nil

Я мог видеть это, когда я вызвал лямбда-выражение auth в консоли aws. Проблема, похоже, в том, что вы не можете передать информацию о политике и ошибку одновременно. Таким образом, контекст никогда не появится в шаблоне ответа.

Более того, как указано в этом потоке , другие люди также не могут получить свои собственные заголовки WWW-Authenticate. С библиотекой golang также невозможно получить пользовательские коды ошибок HTTP. Поэтому мой план использования 402 Payment Required с заголовком WWW-Authenticate невозможен.

Решение:

Сохраните авторизатор, обработайте там auth logi c, но передайте вызов лямбде.

return generatePolicy("user", "Allow", request.MethodArn, map[string]interface{}{"challenge": "testing"}), nil

И все без исключения лямбда I хотите соединиться с этим авторизатором, прочитанным в контексте через $event.requestContext.authorizer.challenge, и таким образом обработать ответ 402 с заголовком. Определенно не идеально, но до тех пор, пока Authorizer не будет достаточно настроен для стандартизации, я думаю, что это будет золотая середина.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...