
微博热搜API的免费调用与应用场景解析
保护您的基础设施对于您的业务成功至关重要。忽视安全性可能会带来严重后果,包括遭受罚款、失去客户信任以及无法履行关键业务功能。基础设施即代码工具和 CI/CD 系统的增长使开发人员能够将基础设施管理集成到我们的典型开发工作流程中,从而提高质量和交付速度。
同时,为了管理您的基础设施,使用的 CI/CD 系统需要访问敏感凭证。在Spacelift,我们的宗旨是为用户在灵活性与安全性之间寻求最佳的平衡点。因此,我们提供了多种选项来将您的 Azure 订阅连接到 Azure,包括设置静态凭据、使用我们的完全托管的集成,以及利用私人工作人员来完全避免共享凭据。
在这篇文章中,我们想从技术角度向你概述 Spacelift Azure 集成的工作原理,并讨论我们在设计和开发它时遇到和解决的一些问题。
作为一个 CI/CD 系统,我们做了大量的工作来与用户使用的其他系统集成。有时,就像在 Azure 中一样,事情会变得非同小适中。如果你热衷于了解更多信息,请继续阅读,无需 Azure 知识!
让我们先简单介绍一下我们的 Cloud 集成的工作原理。整个工作流程非常简单,如下所示:
分解这些要求,我们需要能够获取用户云提供商账户的管理凭证,并通过环境变量的方式将其传递给Terraform,此时 Terraform 可以运行具有客户基础设施访问权限的应用程序。
注意:为简单起见,本文在所有示例中都使用了 Terraform,但这种整体方法也适用于我们支持的其他工具,例如 Pulumi。
Azure 集成背后的概念是提供与我们的 AWS 和 GCP 集成类似的体验,但面向我们的 Azure 客户。下图显示了 AWS 集成工作原理的简化概述:
AWS 提供了临时代入其他 AWS 账户 中的角色的功能。这允许我们的用户在 IAM 中创建一个角色,其中包含他们可能想要授予 Spacelift 的任何权限。随后,他们可以与我们的AWS账户建立对该角色的信任关系,从而授权我们的AWS账户以该角色的身份进行操作。
角色代入为我们提供了原始 AWS 凭证,并与任何 AWS 工具无缝协作,包括 Terraform AWS 提供商。它还允许我们指定有效期,因此每次运行都可以获得自己的凭证,这些凭证被限制在短时间内。
尽管 Azure 没有相同的功能,但它提供了另一种称为 Azure Active Directory 应用程序的方法,该方法允许创建服务帐户。Azure AD应用程序是一种资源,它使Spacelift能够无缝地管理对客户Azure资源的访问。
值得解释一下本文其余部分中使用的一些术语:
我们为集成设定了许多目标:
最初,我们的想法是创建一个多租户 AD 应用程序:
我们的想法是,我们将生成一个只能用于特定客户目录的访问令牌,并在运行期间将该令牌传递给 Terraform Azure RM 提供程序。最后,由于以下问题,我们不得不修改我们的方法:
经过几天的头脑风暴,我们想出了一种新的架构。我们可以以编程方式为 Spacelift 用户创建的每个 Azure 集成生成一个新的 Azure AD 应用程序。
这样,拥有Azure AD应用程序凭据的访问权限仅能够访问用户端对应的单一Azure AD租户。这种方法允许将 Client Secrets 传递给 Spacelift 运行,而不必担心用户间权限泄漏。最终设计结果如下图所示:
应用程序通过名为“管理员同意”的流程被安装到客户的Active Directory租户中。完成管理员同意后,将在用户的 Azure Active Directory 中创建一个服务主体,用户可以向其授予权限。这允许用户决定 Spacelift 对其资源的确切访问级别。
我们面临的下一个问题与生成运行凭证有关。如提供程序文档中所述,可以通过设置某些环境变量来配置 Azure RM 提供程序。最初,我们采用一种基本方法,即尝试在 Spacelift 运行期间生成凭证。这就是我们为 AWS 和 GCP 集成所做的工作,因此,我们预测不会出现重大问题。所采取的步骤如下所示:
这似乎有时奏效…但并非总是如此。
在测试集成时,发生了奇怪的事情。例如,虽然运行的规划阶段能够成功完成,但在应用过程中却会出现错误,具体表现为来自Azure的权限问题导致失败。经过调查,我们得出结论,这是由 Azure AD 中的最终一致性引起的。
您可以使用下图来可视化问题(注意:这只是一个插图,并不意味着完全准确):
在上面的示例中,步骤 2 可能会成功或失败,具体取决于机密是否已设法复制到其请求路由到的 Azure AD 服务器。最初,我们尝试通过发出 API 请求来测试密钥是否可用,然后使用指数退避重试直到请求成功。我们很快意识到,即便如此,后续的请求也有可能会被路由到另一个Azure AD实例上,而那个实例尚未接收到新的客户端密码,因此可能会导致请求失败。
即使可以验证密钥何时完全复制,等待复制完成也会增加至少 30 秒,甚至可能再增加几分钟。因此,我们决定将凭证生成和轮换从运行流中移出,并移到计划任务中:
计划任务每小时运行一次,生成有效期为 24 小时的密钥,并尝试在旧密钥过期前大约 2 小时为集成生成新密钥。这允许凭证轮换,同时始终保持有效的密钥。
生成新密钥时,我们使用 AWS 的 Key Management Service 对其进行加密,以便它永远不会以明文形式存储。
触发运行时,我们会尝试在距离到期之前最长时间查找集成的密钥。为了避免因Azure AD体系结构的最终一致性问题而导致的潜在麻烦,我们会在生成新机密后大约等待10分钟再进行使用。
您可以使用下图可视化密钥生命周期:
此外,在创建新集成时,我们会立即生成密钥。这有助于确保在触发运行时,机密已在 Azure AD 中成功传播。
我们面临的最后一个主要问题是弄清楚如何为我们自己的管理账户实施凭证轮换。集成本身使用 Azure AD 服务主体来管理使用 Microsoft Graph API 的客户 AD 应用程序。
由于我们在 AWS 中运行自己的大部分基础设施,因此我们无法选择使用托管服务身份,这意味着我们需要自己处理凭证轮换。此外,我们的目标是实现流程的自动化,以减轻开发人员定期执行手动任务的负担,并减少在凭证到期前忘记续订的风险。
最后,我们决定采用相对简单的方法,将证书存储在 Secrets Manager 中,并编写一个计划任务来定期检查证书是否已准备好过期,类似于我们对集成客户端密钥采用的方法。如果是这样,计划任务将生成新证书并将其上传到 Secrets Manager 和 Azure AD:
系统中需要使用客户端证书进行身份验证的部分会定期检查更新的证书。与客户端密钥轮换一样,我们会避免在新证书生成后的约10分钟内使用它,以确保证书有足够的时间在整个Azure AD系统中传播生效。
与集成客户端密钥类似,Secrets Manager 使用 AWS Key Management Service 对静态证书进行加密。
希望这篇文章能让您了解 Spacelift 的 Azure 集成的内部结构,以及我们在实施它时必须解决的一些问题。您可能已经察觉到,我们在确保为用户提供既安全又愉快的体验方面,是非常愿意的。
原文链接:https://spacelift.io/blog/building-secure-cicd-integration-with-azure