LoginSignup
3
1

More than 1 year has passed since last update.

Cloudflare Email Workers を試す

Last updated at Posted at 2023-03-03

Email Workers とは

具体的な機能紹介は、上記のブログに書かれていますが、Email Routing のサービスとともに利用し、受信したメールを EmailEvent として Workers ロジックを経由して転送させることができます。

こちらを見ると、2022 年 3 月 3 日時点では Private Beta となっています。

image-20230303152202344

前提条件

まずは Email Routing を構成してある必要があります。

以下のドキュメントに沿って、ドメインを登録し、Email Routing を有効化しておきます。

image-20230303152651920

Email Workers の構成

Email Workers を作成します。

image-20230303153007178

スターターテンプレートがあるので、Allowlist senders で進めます。

image-20230303153103406

Email Workers が作成できたら、コードを編集します。

image-20230303153209907

以下が今回使用するコードです。

  • テンプレートデフォルトの message.headers.get("from")First Last <email@address> となるため、if 条件分岐には message.from を使う

  • allowListmessage.forward は環境に応じて変更する

    • message.forward 先は Verified な宛先のみ対象にできる
      (迷惑メールの温床とならないためにも当然ですね)

      • Forward this email message to a verified destination address of the account. If you want, you can add extra headers to the email message. Only X-* headers are allowed.
      • When the promise resolves, the message is confirmed to be forwarded to a verified destination address.

      image-20230303154016143

  • EmailEvent に含まれる情報をログに表示する

allowlist-email-worker.ts
export default {
  async email(message, env, ctx) {
    const allowList = ["abc@gmail.com"];
    console.log(`message from: ${message.from}`)
    console.log(`message to: ${message.to}`)
    console.log(`message subject: ${message.headers.get('subject')}`)
    console.log(`message date: ${message.headers.get('date')}`)
    console.log(`message message-id: ${message.headers.get('message-id')}`)
    console.log(`message received: ${message.headers.get('received')}`)
    console.log(`message headers: ${JSON.stringify([...message.headers])}`)
    const { value: messageRaw } = await message.raw.getReader().read()
    const messageRawJSON = new TextDecoder().decode(messageRaw)
    console.log(`message raw: ${messageRawJSON}`)
    console.log(`message rawSize: ${message.rawSize}`)
    if (allowList.indexOf(message.from) == -1) {
      message.setReject("Address not allowed");
    } else {
      await message.forward("test@corp.com");
    }
  }
}

/*
interface EmailMessage<Body = unknown> {
  readonly from: string;
  readonly to: string;
  readonly headers: Headers;
  readonly raw: ReadableStream;
  readonly rawSize: number;

  setReject(reason: String): void;
  forward(rcptTo: string, headers?: Headers): Promise<void>;
}
*/

Route の構成

作成した Email Worker を Email Routing の受信アドレスの Action > Send to a Worker で指定します。

image-20230303154307636

Email Workers を経由させたログ

Email Routing の受信アドレスにメールを送れば、allowlist の場合、 message.forward 先に送られることが確認できます。

{
  "outcome": "ok",
  "scriptName": "allowlist-email-worker",
  "exceptions": [],
  "logs": [
    {
      "message": [
        "message from: abc@gmail.com"
      ],
      "level": "log",
      "timestamp": 1677775133400
    },
    {
      "message": [
        "message to: destination@your-email-routing-domain.com"
      ],
      "level": "log",
      "timestamp": 1677775133400
    },
    {
      "message": [
        "message subject: hello"
      ],
      "level": "log",
      "timestamp": 1677775133400
    },
    {
      "message": [
        "message date: Fri, 3 Mar 2023 01:38:41 +0900"
      ],
      "level": "log",
      "timestamp": 1677775133400
    },
    {
      "message": [
        "message message-id: <xxx@mail.gmail.com>"
      ],
      "level": "log",
      "timestamp": 1677775133400
    },
    {
      "message": [
        "message received: by mail-xxx.google.com with SMTP id xxx3.0 for <destination@your-email-routing-domain.com>; Thu, 02 Mar 2023 08:38:53 -0800 (PST)"
      ],
      "level": "log",
      "timestamp": 1677775133400
    },
    {
      "message": [
        "message headers: [
        [\"content-type\",\"multipart/alternative; boundary=\\\"xxx\\\"\"],
        [\"date\",\"Fri, 3 Mar 2023 01:38:41 +0900\"],[\"dkim-signature\",\"v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=xx; b=xx\"],
        [\"from\",\"First Last <abc@gmail.com>\"],
        [\"message-id\",\"<xxx@mail.gmail.com>\"],
        [\"mime-version\",\"1.0\"],
        [\"received\",\"by mail-xxx.google.com with SMTP id xxx3.0 for <destination@your-email-routing-domain.com>; Thu, 02 Mar 2023 08:38:53 -0800 (PST)\"],
        [\"subject\",\"hello\"],
        [\"to\",\"destination@your-email-routing-domain.com\"],
        [\"x-gm-message-state\",\"xxx\"],
        [\"x-google-dkim-signature\",\"v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=xx; b=xx\"],
        [\"x-google-smtp-source\",\"xxx\"],
        [\"x-received\",\"by xxx with SMTP id xxx.3.1677775132715; Thu, 02 Mar 2023 08:38:52 -0800 (PST)\"]]"
      ],
      "level": "log",
      "timestamp": 1677775133400
    },
    {
      "message": [
        "message raw: Received: from mail-xx.google.com (2607:f8b0:4864:20::1133)\r\n        
        by email.cloudflare.net (unknown) id LJvo9DIQ3xpI\r\n        
        for <destination@your-email-routing-domain.com>; Thu, 02 Mar 2023 16:38:53 +0000\r\n
        DKIM-Signature: v=1; a=rsa-sha256; d=email.cloudflare.net; s=2022; c=relaxed/relaxed; bh=xx; h=from:subject:date:to; t=1677775133; b=xx;\r\n
        Received: by mail-xx.google.com with SMTP id xx3.0\r\n        
        for <destination@your-email-routing-domain.com>; Thu, 02 Mar 2023 08:38:53 -0800 (PST)\r\n
        DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;\r\n        
        d=gmail.com; s=20210112;\r\n        
        h=to:subject:message-id:date:from:mime-version:from:to:cc:subject\r\n         
        :date:message-id:reply-to;\r\n        
        bh=xx=;\r\n        
        b=xx==\r\n
        X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;\r\n        
        d=1e100.net; s=20210112;\r\n        
        h=to:subject:message-id:date:from:mime-version:x-gm-message-state\r\n         
        :from:to:cc:subject:date:message-id:reply-to;\r\n        
        bh=xx\r\n        
        b=xx\r\n
        X-Gm-Message-State: xx\r\n
        X-Google-Smtp-Source: xx\r\n
        X-Received: by xx with SMTP id\r\n 
        xx.3.1677775132715; Thu, 02\r\n 
        Mar 2023 08:38:52 -0800 (PST)\r\n
        MIME-Version: 1.0\r\n
        From: First Last <abc@gmail.com>\r\n
        Date: Fri, 3 Mar 2023 01:38:41 +0900\r\n
        Message-ID: <xxx@mail.gmail.com>\r\n
        Subject: hello\r\n
        To: destination@your-email-routing-domain.com\r\n
        Content-Type: multipart/alternative; boundary=\"xx\"\r\n
        \r\n
        --000000000000cc700b05f5ed7612\r\n
        Content-Type: text/plain; charset=\"UTF-8\"\r\n
        \r\n
        hello world\r\n
        \r\n
        --000000000000cc700b05f5ed7612\r\n
        Content-Type: text/html; charset=\"UTF-8\"\r\n
        \r\n
        <div dir=\"ltr\">hello world<br></div>\r\n
        \r\n
        --000000000000cc700b05f5ed7612--\r\n"
      ],
      "level": "log",
      "timestamp": 1677775133400
    },
    {
      "message": [
        "message rawSize: 3224"
      ],
      "level": "log",
      "timestamp": 1677775133400
    }
  ],
  "eventTimestamp": 1677775133398,
  "event": {
    "rawSize": 3224,
    "rcptTo": "destination@your-email-routing-domain.com",
    "mailFrom": "abc@gmail.com"
  },
  "id": 4
}

message.setReject("Address not allowed") の場合

以下のようなエラーメールが送信者に返ってきます。

  • Reject this email message by returning a permanent SMTP error back to the connecting client, including the given reason.

image-20230303014246742

not verified なメールアドレスを転送先とした場合

message.forward 先は Verified な宛先のみ対象にでき、以下のエラーとなります。

{
  "outcome": "exception",
  "scriptName": "allowlist-email-worker",
  "exceptions": [
    {
      "name": "Error",
      "message": "destination address not verified",
      "timestamp": 1677774635838
    }
  ],
  ...
}

image-20230303013408494

受信したサンプルメール

image.png

感想

  • Allowlist, Blocklist, メール受信を Webhook で通知、くらいであれば簡単にできる
  • X- メールヘッダの追加等も簡単にできる(X-My-Email-Worker-Processed
  • messageRaw の扱いは、以下のコード例にあるパーサーを使うとより効率的
{
  "outcome": "ok",
  "scriptName": "email_worker_parser",
  "exceptions": [],
  "logs": [
    {
      "message": [
        "Mail subject: ",
        "Test"
      ],
      "level": "log",
      "timestamp": 1684431951396
    },
    {
      "message": [
        "HTML version of Email: ",
        "<div dir=\"ltr\">Hello, World!<br></div>\n\n"
      ],
      "level": "log",
      "timestamp": 1684431951396
    },
    {
      "message": [
        "Text version of Email: ",
        "Hello, World!\n\n"
      ],
      "level": "log",
      "timestamp": 1684431951396
    },
    {
      "message": [
        "No attachments"
      ],
      "level": "log",
      "timestamp": 1684431951396
    }
  ],
  "eventTimestamp": 1684431951384,
  "event": {
    "rawSize": 5405,
    "rcptTo": "receiver@example.com",
    "mailFrom": "sender@example.com"
  },
  "id": 0
}
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1