Auth0 是一家“身份验证即服务”提供商,旨在为开发人员提供简单易用的身份管理服务。为了保持灵活性和可扩展性,Auth0 身份管理平台允许开发人员在身份验证和授权管道中增加自定义代码。而在一个多租户环境中,为了保证不同用户的自定义代码可以互不影响,就需要一种技术提供必要的数据隔离和资源利用保障。在评估了多种现有方案后,Auth0 决定构建自己的沙盒解决方案,并将其命名为 Auth0 Webtasks。Tomasz Janczuk 是 Auth0 的一名工程师。近日,他撰文介绍了这一解决方案。
Auth0 执行的所有用户自定义代码都是使用 Auth0 Webtasks 运行在 HTTP 请求的上下文中。执行时间限制在 HTTP 请求的生命周期内。由于这些限制,他们选择了基于 HTTP 的执行模型。Webtask 集群接收 HTTP POST 请求,后者包含要执行的代码,并给出了 webtask 容器的名称,限定了代码执行边界。用户与 webtask 容器一一对应。Webtask 集群在 webtask 容器中执行自定义代码,并返回一个包含结果的 JSON 响应。
在这个模型中,所有用户都在统一的环境中运行自定义代码,不同用户的自定义代码都只能使用一组相同的功能和模块。Webtask 的运行时是基于 Node.js 的,自定义代码只能使用 webtask 环境中预先配置的 Node.js 模块集合。这样做的好处是,Auth0 很容易修改这个集合,而且他们可以维护一个 webtask 容器池,随时分配给用户使用,减少了容器冷启动延迟。
Webtask 的编程模型非常简单。服务调用者提交一个 JavaScript 函数闭包,webtask 集群调用那个函数,并提供一个回调参数。当自定义代码执行完毕,它就会调用回调函数,并提供一个错误信息或一个结果值。然后,那个值就会序列化为 JSON,并在 HTTP 响应中返回给调用者:
return function (cb) { cb(null, “Hello, world”); }
在决定开发一个新的沙盒解决方案之前,Auth0 评估了几种现有方案。Node-sandbox 只能实现进程级隔离,而不支持操作系统级隔离,并且为每个请求创建新的 Node.js 进程会增加延迟。像 Heroku 或 Windows Azure Web Sites 这样的 PaaS 服务可以满足他们的需求,但需要他们为每个 Auth0 用户配置一个单独的站点,而且从成本上讲不具有可扩展性。而 AWS Lambda 的核心是一个异步编程模型,并不满足他们的需求。最终,他们决定开发 Auth0 Webtasks。
Auth0 Webtasks 基于 AWS 构建,架构图如下:
Auth0 服务需要一个很高的可用性级别,因此:
- 他们将两个 webtask 集群分别部署在不同的 AWS 区域(us-west-1 和 us-east-1),一个作为主集群,另一个作为故障转移集群;
- 每个 webtask 集群包含 3 台虚拟机,前端为 Elastic Load Balancing;
- 一个 AWS 区域内的虚拟机跨多个“可用区域(Availability Zone)”布置,实现集群级高可用性支持。
Webtask VM 的所有出站流量都经过具有固定 Elastic IP 的 NAT VM。这些 NAT VM 还允许通过 SSH 隧道访问单个的 webtask VM。每个 webtask 集群都运行在自己的 VPC 上。该 VPC 在每个可用区域内都有一个私有子网和一个公共子网,前者运行 webtask VM,后者运行 NAT VM 和 ELB。
在软件层面,Auth0 Webtasks 基于 CoreOS 、 Docker 、 etcd 和 fleet 构建。为了实现代码和数据的隔离,每个用户的代码都在他们自己的 Docker 容器(即 webtask 容器)中运行。HTTP 请求到达 webtask VM 后首先会由一个代理进行处理。该代理查看 etcd 配置来确定是否已经为发送该请求的用户分配了 webtask 容器。如果已经分配,代理就会将请求转发给该容器;如果没有,代理就会将 webtask 容器池中的一个容器分配给该用户,并在 etcd 中增加一条记录。该容器池由 fleet 管理,它会重启容器,并将其放回未分配池。
除了在各自的容器中运行各自的自定义代码外,Auth0 在 CoreOS webtask VM 中配置了出口防火墙规则。这些规则能够防止 webtask 容器中的不受信任代码与其它容器或 webtask 基础设施通信。为了限制内存和 CPU 消耗,他们使用了 Docker 的 cgroups 机制。而且,每个 webtask 容器会创建一个 Linux 临时用户,并在启动时配置用户 PAM 限制。
Auth0 Webtasks 使用 bunyan 实现了结构化的、基于 JSON 的日志记录。所有的日志都存储在 Kafka 中。为了隔离不同用户的日志,他们将每个用户的日志发布到单独的 Kafka topic。而为了管理分析,所有的信息都会发布到一个系统级 topic。
虽然 JavaScript 和 Node.js 提供了一个健全的编程模型,但也有许多人喜欢使用.NET 和 C#编写自定义代码。为了满足这种需求,Auth0 Webtasks 允许执行 C#代码。这是通过 Edge.js 实现的,它允许开发人员从 Node.js 中调用 C#异步 lambda 表达式。另外,Edge.js 使用 Mono 作为 Linux 上的 CLR 实现。借助这种机制,开发人员可以使用 Mono 框架的所有功能来编写自定义 C#代码,并在基于 Linux 的 webtask 容器中运行。
目前,Auth0 webtasks 允许用户导入自定义证书数据库以及在身份验证管道中运行自定义逻辑。
感谢郭蕾对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ , @丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流。
评论