使用 亚马逊云科技 Lambda 扩展和 Tailscale 构建安全的网络挂钩转发器

作者: 詹姆斯·贝斯威克 | 202

这篇文章由企业架构师邓肯·帕森斯和高级顾问西蒙·科克撰写。

在构建基于事件的架构时,Webhook 可以帮助开发人员与第三方系统或设备集成。

但是,有时对目标网络环境的控制会受到限制,或者目标会更改 IP 地址。此外,一些端点缺乏足够的安全防护,需要反向代理和对来自互联网的入站流量进行额外的安全检查。

设置和维护高度可用的安全反向代理以检查多个端点的这些后端系统并将其发送到这些后端系统可能很复杂。本博客介绍如何使用 亚马逊云科技 L am bda 扩展来构建云原生无服务器 webhook 转发器,以最低的维护和运行成本满足这一需求。

自定义 Lambda 扩展与有状态防火墙和 NAT 网关后面的私有子网中的目标形成安全的 WireGuard VPN 连接。 此示例设置了一个公共 HTTPS 端点以通过 WireGuard 连接接收事件、有选择地过滤和代理请求。此示例使用无服务器架构来最大限度地减少维护开销和运行成本。

示例概述

GitHub 上提供了部署以下架构的示例代码 。此示例使用 亚马逊云科技 CodePipelin e 和 亚马逊云科技 C odeBuild 来构建代码项目,并通过 亚马逊云科技 云开发套件 (CDK) 使用 亚马逊云科技 C loudFormation 进行署。 它使用 亚马逊 API Gateway 来管理 HTTPS 端点,并使用 Lambda 服务来执行应用程序功能。 亚马逊云科技 Secrets Manager 存储 Tailscale 的证书。

要编排 WireGuard 连接,你可以在 Ta il scale 服务上使用免费账户。 或者,使用开源 Headscal e 示例设置自己的协调层。

Reference architecture

  1. 事件创建者向 API 网关网址发送 HTTP 请求。
  2. API 网关将请求代理给 Lambda 授权器函数。它根据请求的源 IP 返回授权决定。
  3. API Gateway 将请求代理到运行 Tailscale 扩展程序的 Secure Webhook Forwarder Lambda 函数。
  4. 首次调用时,Lambda 扩展会从 Secrets M anager 中检索 Tailscale 身份验证密钥, 并使用该 密 钥与相应的 Tailscale 网络建立连接。然后,该扩展将连接作为本地 SOCKS5 端口暴露给 Lambda 函数。
  5. Lambda 扩展程序通过 Tailscale 协调服务器保持与 Tailscale 网络的连接。通过此协调服务器,网络上的所有其他设备都可以知道正在运行的 Lambda 函数,反之亦然。 Lambda 函数被配置为拒绝传入的 WireGuard 连接,请在此处阅读有关 -- shields-up 命令的更多信息。
  6. 建立与 Tailscale 网络的连接后,Secure Webhook Forwarder Lambda 函数使用 WireGuard 连接通过互联网将请求代理到目标。连接通过 Tailscale 协调服务器建立,通过 NAT 网关到达私有子网内的 A mazon EC2 实例。EC2 实例使用来自本地 Python 网络服务器的 HTML 响应进行响应。
  7. 部署时,Secrets Manager 每 60 天自动轮换 Tailscale 身份验证密钥。它使用凭证轮换 Lambda 函数,该函数从密钥管理器中检索 OAuth 证书,并使用这些凭证使用 Tailscale API 创建新的 Tailscale 身份验证密钥,并将新密钥存储在密钥管理器中。

为了在逻辑上将网络连接层与应用程序代码层分开,Lambda 扩展封装了形成 Tailscale VPN 连接所需的代码,并通过本地 SOCK5 端口将其提供给 Lambda 函数应用程序代码。通过附加扩展,您可以在多个 Lambda 函数中重复使用这种连接,用于多个用例。

要部署示例,请按照存储库 自述文件 中的说明进行操作。部署可能需要 20—30 分钟。

Lambda 扩展程序的工作原理

Lambda 扩展程序 创建了网络隧道,并将其作为在端口 1055 上运行的 SOCKS5 服务器向 Lambda 函数开放。 Lambda 生命周期 分为三个阶段 :初始化、调用和关闭。

Lambda extension deep dive

使用 Tailscale Lambda 扩展,大部分工作都是在初始阶段完成的。webhook 转发器 Lambda 函数的生命周期如下:

  1. 初始阶段:
    1. 扩展初始化 — 扩展连接到 Tailscale 网络并通过本地 SOCKS5 端口公开 WireGuard 隧道。
    2. 运行时初始化 — 启动 Node.js 运行时。
    3. 函数初始化 — 导入所需的 Node.js 模块。
  2. 调用阶段:
    1. 扩展程序故意不注册以接收任何调用事件。Tailscale 网络将保持在线状态,直到指示该功能关闭。
    2. Node.js 处理函数 以 2.0 格式 接收来自 API Gateway 的请求,然后将其代理到 SOCKS5 端口,通过 WireGuard 连接将请求发送到目标。一旦函数收到来自目标 EC2 实例的响应并可选择将其返回到 API Gateway 以继续转发到原始事件源,调用阶段即告结束。
  3. 关闭阶段:
    1. 扩展程序退出 Tailscale 网络并记录关闭事件的接收。
    2. 函数执行环境与 Lambda 函数的执行环境一起关闭。

扩展文件结构

扩展代码以 zip 文件形式存在,同时还有一些元数据在作为 亚马逊云科技 Lambda 层发布时设置。zip 文件包含三个文件夹:

  1. /ex tensions — 包含扩展代码,是 Lambda 服务在初始化 Lambda 扩展时查找要运行的代码的目录。
  2. /bin —包括可执行文件的依赖关系。例如,在 tsextension.sh 脚本中,它运行 tailscale、tailscaled、curl、jq 和 OpenSSL 二进制文件。
  3. /ssl —存储证书颁发机构 (CA) 信任存储(包含可信连接的根 CA 证书)。OpenSSL 使用这些来验证 SSL 和 TLS 证书。

tsextension.sh 文件是扩展的核心。大部分代码都在 Lambda 函数的初始化阶段运行。扩展代码分为三个阶段。前两个阶段与 Lambda 函数初始化生命周期阶段有关,第三阶段涵盖调用和关闭生命周期阶段。

扩展阶段 1:初始化

在此阶段,扩展会初始化 Tailscale 连接并等待连接变为可用。

第一步从 Secrets Manager 中检索 Ta ilscale 身份验证密 钥 。为了缩小扩展程序的大小,该扩展程序使用了一系列 Bash 命令,而不是打包 亚马逊云科技 CLI 来向密钥管理器发出 Sigv4 请求。

Lambda 函数的临时证书由 Lambda 执行环境作为环境变量提供,扩展程序使用该环境对 Sigv4 请求进行身份验证。检索密钥的 IAM 权限由 CDK 代码添加到 Lambda 执行角色中。为了优化安全性,密钥的策略将读取权限限制为 (1) 此 Lambda 函数和 (2) 每 60 天轮换一次 Lambda 函数。

Tailscale 代理开始使用 Tailscale 身份验证密钥。tailscale 和 tailscale 二进制文件都以用户空间网络模式启动,因为每个 Lambda 函数都在自己的虚拟机上自己的容器中运行。有关用户空间网络模式的更多信息可以在T ailscal e文档中找到。

在 Tailscale 进程运行时,该进程必须等待与 Tailnet(Tailscale 网络的名称)建立连接,并等待 SOCKS5 端口可用以接受连接。为此,扩展程序只需等待 “tailscale status” 命令不返回带有 “已停止” 的消息,然后进入第 2 阶段。

扩展阶段 2:注册

现在,该扩展将自己注册为使用 Lambda 服务初始化。这是通过向 Lambda 服务扩展 API 发送 POST 请求来执行的,其中包含应转发到扩展程序的事件。

接下来是运行时初始化(这将初始化 Lambda 函数本身的 Node.js 运行时),然后是函数初始化(事件处理程序外部的代码)。对于 Tailscale Lambda 扩展,它仅注册扩展以接收 “SHUTDOWN” 事件。一旦 SOCKS5 服务启动并可用,扩展程序就不会对函数的每次后续调用采取任何操作。

扩展阶段 3:事件处理

为了表示扩展程序已准备好接收事件,需要向 Lambda 运行时 API 的 “下一步” 端点发出 GET 请求。这会阻止扩展脚本的执行,直到发送 SHUTDOWN 事件(因为这是为此 Lambda 扩展程序注册的唯一事件)。

发送此消息后,扩展程序将退出 Tailscale 服务,Lambda 函数将关闭。如果还注册了 INVOKE 事件,则扩展程序会处理该事件。然后,它向 Lambda 运行时 API 发出信号,表示扩展已准备好通过向 “下一步” 端点发送 GET 请求来接收另一个事件。

访问控制

此示例中包含一个 Lambda 授权器示例。请注意,建议使用 亚马逊云科技 Web 应用程序防火墙 服务为您的公有 API 终端节点添加额外保护,并强化示例代码以供生产使用。

就本演示而言,该实现演示了基本的源 IP CIDR 范围限制,但您可以使用请求的任何属性作为授权决策的依据。 在此处阅读有关 HTTP API 的 Lambda 授权方的更多信息。 要使用源 IP 限制,请在 Lambda 授权器函数 AUTHD_SOURCE_CIDR 环境变量上更新您要接受的 IP 的 CIDR 范围。

成本

您需要为该项目使用的所有资源付费。手动发布最后的管道步骤后,NAT Gateway 和 EC2 实例将被管道销毁,以最大限度地降低成本。 亚马逊云科技 Lambda Power T uning 工具在通过 Tailscale 网络轮询演示 EC2 实例时,可以帮助找到性能和成本之间的平衡。

以下结果显示,256 MB 内存是实现最低执行成本的最佳选择。演示堆栈被销毁后,估计每月100万次请求的费用不到3美元。

Power Tuning results

结论

使用 Lambda 扩展可以为扩展无服务器架构的功能开辟多种选择。这篇博客展示了一个 Lambda 扩展,它使用 WireGuard 协议和 Tailscale 服务创建了一个安全的 VPN 隧道,将事件代理到无法从互联网访问的 EC2 实例。

这是为了通过自动部署管道最大限度地减少运营开销。Lambda 授权方可保护端点,提供根据请求内容和上下文实现自定义逻辑的能力。

如需更多无服务器学习资源,请访问 无服务器世界