尝试使用 AWS SES API 发送电子邮件时出现“NotAuthorizedException/”

时间:2021-05-16 19:44:12

标签: amazon-web-services amazon-ses

我尝试通过发出 HTTPS 请求在 lua 中使用 AWS SES API 发送电子邮件,但我在响应对象正文中收到“NotAuthorizedException/”。有人可以帮助我了解为什么会出现此错误以及可能的解决方案。

为了创建 AWS 签名版本 4,我使用了 this 代码。

用于调用 API,

local aws_v4            = require ("kong.plugins.kong-rate-limit.ses.v4")

local http              = require "resty.http"
local cjson             = require "cjson.safe"
local meta              = require "kong.meta"
local constants         = require "kong.constants"
local resty_http        = require 'resty.http'

local tostring          = tostring
local tonumber          = tonumber
local type              = type
local fmt               = string.format
local ngx_encode_base64 = ngx.encode_base64
local ngx_time          = ngx.time
local string_match      = string.match
local os_time           = os.time
local concat            = table.concat

local AWS_PORT = 443

local function iso8601_to_epoch(date_iso8601)
    local inYear, inMonth, inDay, inHour, inMinute, inSecond, inZone = string_match(date_iso8601, '^(%d%d%d%d)-(%d%d)-(%d%d)T(%d%d):(%d%d):(%d%d)(.-)$')
    local zHours, zMinutes = string_match(inZone, '^(.-):(%d%d)$')
    local returnTime = os_time({year=inYear, month=inMonth, day=inDay, hour=inHour, min=inMinute, sec=inSecond, isdst=false})
    if zHours then
      returnTime = returnTime - ((tonumber(zHours)*3600) + (tonumber(zMinutes)*60))
    end
    return returnTime
  end
  
  local function get_keys_from_metadata(iam_role,metadata_url)
    local httpc = resty_http:new()
    httpc:set_timeout(300) -- set timeout to 300ms
  
    local res, err = httpc:request_uri(metadata_url .. iam_role, {
          ssl_verify = false,
          keepalive  = false
    })
  
    if err then
      kong.log.err("Could not get keys from meta-data endpoint")
    end
  
    if not res then
      kong.log.err("Empty response from meta-data endpoint")
    end
  
    if res.status ~= 200 then
      kong.log.err("Not OK (HTTP 200) response from meta-data endpoint")
    end
  
    local body = cjson.decode(res.body)
    local expiration = iso8601_to_epoch(body.Expiration) - ngx_time() -- aws keys auto regenerates 5 min before expiration. Maybe have to change this.
    return { ["AccessKeyId"] = body.AccessKeyId, ["SecretAccessKey"] = body.SecretAccessKey, ["Token"] = body.Token }, nil, expiration
  end
  
  
  local function get_keys_from_cache(iam_role,metadata_url,override_ttl,ttl)
      if override_ttl then
        local cred, err = kong.cache:get(iam_role .. "_cred", {ttl=ttl} , get_keys_from_metadata, iam_role, metadata_url)
      else
        local cred, err = kong.cache:get(iam_role .. "_cred", nil , get_keys_from_metadata, iam_role, metadata_url)
      end
      if err then
          kong.log.err("Could not get/put ",err)  
      end
      if cred then
        return cred.AccessKeyId, cred.SecretAccessKey, cred.Token
      end
      return nil
  end

  local function publish_to_ses(client, request)
    local res, err = client:request {
      method = "GET",
      path = request.url,
      body = request.body,
      headers = request.headers
    }
    
    return res
  end  
  
  
  return {
  
      publish = function(conf, body, email_body, notified_key)
  
                    
        local message_body=ngx.escape_uri("This is body")
        local email_subject = ngx.escape_uri("This is subject")
        local source = ngx.escape_uri("example@gmail.com")
        local host = fmt("email.%s.amazonaws.com", conf.aws_region)
        local path = "/"
        
        local query = concat({'Version=2010-12-01&Action=SendEmail&Source=' , source, '&Destination.ToAddresses.member.1=', source ,'&Message.Subject.Data=', email_subject,'&Message.Body.Text.Data=' , message_body})
        local port = conf.port or AWS_PORT
  
        local aws_key, aws_secret, aws_token
        if conf.aws_key == nil and conf.aws_secret == nil and conf.aws_iam_role ~= nil then
          if conf.store_creds_in_cache then
            aws_key, aws_secret, aws_token = get_keys_from_cache(conf.aws_iam_role, conf.aws_metadata_url, conf.override_cache_creds_ttl, conf.cache_creds_ttl)
          else
            local cred, e, ttl = get_keys_from_metadata(conf.aws_iam_role, conf.aws_metadata_url)
            if cred and e == nil then
              aws_key, aws_secret, aws_token = cred.AccessKeyId, cred.SecretAccessKey, cred.Token
            end
          end
        else
          aws_key = conf.aws_key
          aws_secret = conf.aws_secret
        end
  
        local opts = {
          region = conf.aws_region,
          service = "ses",
          method = "GET",
          headers = {
            ["Content-Type"] = "text/plain",
          },
          path = path,
          host = host,
          port = port,
          access_key = aws_key,
          secret_key = aws_secret,
          query = query
        }
  
        if aws_token then
          opts["headers"]['X-Amz-Security-Token'] = aws_token
        end
  
        local request, err = aws_v4(opts)
        if err then
          kong.log.err("An unexpected error occurred while signing request according to AWS signature(V4)",err)
        end
  
        -- Trigger request
        local client = http.new()
        client:set_timeout(conf.timeout)
        client:connect(host, port)
        local ok, err = client:ssl_handshake()
        if not ok then
          kong.log.err("[SES]An unexpected error occurred while connecting:  ",err)
        end
  
        local res = publish_to_ses(client, request)
        if not res then
          if conf.policy == "local" then
            service_in_memory:delete(notified_key)
          else
            res = publish_to_ses(client, request)  
          end  
                  
        end
  
        local content = res:read_body()
        
        local ok, err = client:close()
        if not ok then
          kong.log.err("[SES]Could not close connection :",err)
        end
  
        if res.status == 200 then
          local MessageId=content:match("MessageId>(.*)</MessageId")
          content=cjson.encode({
            MessageId=MessageId,
          })
        end
  
        kong.log.info("[SES]Message published: ", content)
      
      end,
  
  }
  
  
  

我正在尝试从 AWS EC2 元数据获取凭据

任何小帮助都会对我有很大帮助, 谢谢。

0 个答案:

没有答案