亚马逊AWS官方博客

在 Amazon ECS 的 Linux 容器上搭配使用 Windows 身份验证和 gMSA

更新:2023 年 7 月 17 日,AWS 开始支持在未加入域(无域)的 Amazon ECS Linux 容器实例上使用 gMSA 进行 Windows 身份验证。这篇博客文章已更新,涵盖这两种模式,默认介绍无域模式。

简介

我们于近日宣布可将 Credentials FetcherAmazon Elastic Container Service(Amazon ECS)集成。这种集成可让开发人员更轻松地使用 Microsoft Active Directory(AD)组托管服务账户(gMSA),在 Amazon ECS 上运行的 Linux 容器中实现 Windows 身份验证。Credentials Fetcher 进程守护程序允许在 Linux 主机上运行的容器使用 gMSA 凭证进行身份验证。

gMSA 是提供自动密码管理的托管域账户。典型的 AD 账户需要 IT 管理员手动设置、轮换和同步密码,而 gMSA 密码则由 Active Directory 自动管理,包括跨多个客户端的无缝同步。这种类型的账户非常适合 Amazon ECS 中的容器化应用程序,因为任务定义的所有实例都应具有相同的权限,并且正在运行的实例数量可以动态扩展。

在以前的版本中,使用 gMSA 凭证在 Amazon ECS 上运行的工作负载必须使用 Windows 容器运行。Linux 容器可以节省成本、延长正常运行时间并提高可扩展性,但是在 Linux 容器中运行的工作负载必须使用解决方案,例如“Sidecar 容器”,该解决方案使用存储在 AWS Secrets Manager 中的凭证向 AD 进行身份验证。这种方法的一个缺点是 sidecar 容器不会自动轮换或同步 AD 账户密码。当 AD 中的密码更改时,容器的身份验证会失败,因为存储的凭证不再有效。

在今天发布的版本中,利用 Linux 容器对其应用程序进行现代化改造的客户现在可以通过具有自动密码管理功能的 Kerberos 协议使用 Windows 身份验证。这就让客户能够部署安全、易于管理、经济高效且可扩展的工作负载。

解决方案概述

要探索 gMSA 对 Amazon ECS 上的 Linux 容器支持的工作原理,可使用以下示例解决方案:

图 1:示例解决方案架构图

您将部署:

部署这些组件后,您将为创建的 gMSA 账户生成凭证规范(CredSpec)文件,并将其上传到 AWS Systems Manager 的功能之一:Parameter Store,Credentials Fetcher 将在其中检索该文件。

最后,您将在 Amazon ECS 上的 Linux 容器中构建和部署简单的.NET Web 应用程序。此 Web 应用程序配置为使用 Windows 集成安全性来安全地连接到数据库。Credentials Fetcher 负责从 gMSA 获取 Kerberos 票证并将其提供给 Linux 容器。

示例解决方案使用 AWS Cloud Development Kit(AWS CDK) 通过 TypeScript 预置云资源。AWS CDK 可让开发人员使用各种熟悉的编程语言构建 AWS 基础设施,包括 JavaScript、C#、Python、Java 和 Go。

在进行任何生产部署之前,您应始终咨询当地安全团队,根据您的环境和安全状况审查安全控制和要求。

先决条件

为完成本教程,您应该具备以下先决条件:

无域和加入域模式

有两种模式可以支持使用 gMSA 对应用程序进行 Windows 身份验证:未加入域(无域)模式和加入域模式。

无域模式下,Amazon ECS 容器实例无需加入目标 AD 域。这是适用于大多数工作负载的推荐模式,尤其是在需要扩缩时。

加入域模式下,您需要先让 Amazon ECS 容器实例加入目标 AD 域,然后再在这些实例上部署任务。如果您不想管理单个 AD 用户账户,请使用此模式。

这篇博客文章将指导您使用无域模式,但会指出使用加入域模式时的区别。

部署基础设施

首先,在本地计算机上创建示例解决方案目录。将此 GitHub 存储库克隆到该目录中。

在克隆存储库的 cdk-typescript 目录中打开终端,将 {KEY_PAIR_NAME} 替换为您的 Amazon EC2 密钥对名称,并且如果您使用的是 Bash,请运行以下命令:

导出 AWS_DEFAULT_REGION={YOUR REGION}
导出 EC2_INSTANCE_KEYPAIR_NAME="{KEY_PAIR_NAME}"
导出 MY_SG_INGRESS_IP=$(curl checkip.amazonaws.com)
导出 DOMAIN_JOIN_ECS=0

npm install
cdk deploy "*" --require-approval "never"

如果您使用的是 PowerShell,请运行以下命令:

$Env:AWS_DEFAULT_REGION = "{YOUR REGION}"
$Env:EC2_INSTANCE_KEYPAIR_NAME = "{KEY_PAIR_NAME}"
$Env:MY_SG_INGRESS_IP = $(Invoke-WebRequest -URI https://checkip.amazonaws.com).ToString().Trim()
$Env:DOMAIN_JOIN_ECS = 0   

npm install
cdk deploy "*" --require-approval "never"

注意要使用加入域模式请在部署 AWS CDK 解决方案之前将 DOMAIN_JOIN_ECS 变量设置为 1

这将开始部署包含示例解决方案的三个 AWS CloudFormation 堆栈。大约需要一小时才能完成部署。

部署完成后,导航到 AWS CloudFormation 控制台。此时将显示类似于下图的内容:

图 2:AWS 管理控制台中的 CloudFormation 资源

部署期间,在 AD 中创建安全组、用户和 gMSA。用户被设置为安全组的成员,该安全组有权从 gMSA 检索密码。AD 用户的密码是随机生成的并存储在密钥中,其名称为:

aws/directory-services/[directory-id]/seamless-domain-join.

演练

在以下部分中,您将探讨如何配置 Amazon ECS 和 Credentials Fetcher 以在 Web 应用程序中使用这些 AD 安全主体。

在 Amazon ECS 中安装 Credentials Fetcher

首先,必须在 Amazon ECS 中安装 Credentials Fetcher。要对所有 Amazon ECS 容器实例自动执行此操作,您需要在 Auto Scaling 组的启动模板/配置中更新用户数据,并添加以下命令:

echo "ECS_GMSA_SUPPORTED=true" >> /etc/ecs/ecs.config
sudo yum install dotnet credentials-fetcher realmd oddjob oddjob-mkhomedir sssd adcli krb5-workstation samba-common-tools -y
sudo systemctl start credentials-fetcher

示例解决方案已经应用了此配置。您可以继续阅读下一节。

生成 CredSpec 文件

CredSpec 文件是一个 JSON 文档,其中包含有关每个容器中使用的 gMSA 账户的元数据。Credentials Fetcher 使用 CredSpec 文件请求 Kerberos 票证,然后将其提供给容器。只有以 AD 的管理员用户身份登录时,才能在 Windows 计算机中生成 CredSpec 文件。

要开始生成此文件的过程,请导航到 AWS Secrets Manager 控制台并复制 amazon-ecs-gmsa-linux/active-directory-administrator-password 秘密的值。

图 3:AWS 管理控制台中的 Active Directory 密码秘密

也可以通过在 Bash 终端中运行如下命令来获取秘密值:

aws secretsmanager get-secret-value –secret-id amazon-ecs-gmsa-linux/active-directory-administrator-password

如果使用的是 PowerShell,则可以通过运行以下命令来获取秘密值:

Get-SECSecretValue -SecretId amazon-ecs-gmsa-linux/active-directory-administrator-password

接下来,导航到 Amazon EC2 控制台,然后选择名为 amazon-ecs-gmsa-linux-bastion/active-directory-management-instance 的实例。

图 4:AWS 管理控制台中的 Active Directory 管理实例

按照如下说明使用远程桌面连接到实例。使用用户名 directory.amazon-ecs-gmsa-linux.com\admin 和从 Secrets Manager 检索到的密码。(如果您无法登录,则该实例仍在设置数据库和 AD。请等待 10-15 分钟,然后重试。)

在远程桌面会话中,打开 PowerShell 窗口并运行以下命令:

C:\SampleConfig\Generate-CredSpec.ps1

注意在加入域模式下运行脚本时请附加以下参数-DomainlessArn ""

此命令将创建 CredSpec 文件并将其最小化的内容存储为系统管理器参数,凭证获取器将检索该文件。在无域模式下,CredSpec 文件将具有以下附加对象:

{
  ...
  "ActiveDirectoryConfig": { 
    ...
    "HostAccountConfig": {
      "PortableCcgVersion": "1",
      "PluginGUID": "{859E1386-BDB4-49E8-85C7-3070B13920E1}",
      "PluginInput": {
 	   "CredentialArn": "[SECRET ARN]"
      }
    } 
  }
}

这提供了对 Secrets Manager 密钥的引用,其中包含 AD 用户的用户名和密码,用于检索 gMSA 密码和目标 AD 域的名称。示例解决方案以 credentials-fetcher-identity 的名称自动创建此密钥。在此 Amazon EC2 实例中无需执行任何其他操作,因此您可以退出远程桌面会话。

配置用于检索 gMSA 密码的 Active Directory 身份

要从 gMSA 检索密码,凭证获取器需要一个安全主体进行 AD 身份验证并检索 gMSA 密码。对于无域模式,使用 AD 用户,而对于加入域模式,则使用主机安全主体。在这两种情况下,都应授权安全主体直接或通过 AD 安全组检索 gMSA 密码。该示例解决方案将创建并使用名为 SampleWebAppGmsaPrincipals  的 AD 安全组来访问 gMSA 密码。

在无域模式下,凭证获取程序使用存储在 Secrets Manager 密钥中的凭据检索 gMSA 密码,该密码是您在 CredSpec 文件的 HostAccountConfig 对象中提供的(如前所述),因此您无需执行任何其他操作。

在加入域模式下,您需要将 Amazon ECS 实例加入到域中,并将计算机安全主体添加到授权的 AD 安全组。 您可以使用 Systems Manager 关联和 Linux 的无缝域加入自动将 Amazon ECS 容器实例加入到 AD 域。目前,Linux 上没有可用于将计算机安全主体添加到 AD 安全组的命令,因此您需要通过在 Windows 上运行的 Add-ADGroupMember PowerShell cdmlet 来完成此操作。每次创建新的 Amazon ECS 容器实例时都必须执行此操作。

作为示例解决方案的一部分,您将在 AD 管理 Amazon EC2 实例中找到 PowerShell 脚本,该脚本会将集群中的所有 Amazon ECS 容器实例添加到相应的 AD 安全组。可以在路径 C:\SampleConfig\Add-ECSContainerInstancesToADGroup.ps1 中找到此脚本。需要将 Amazon ECS 的 ASG 的名称作为参数传递,可在 amazon-ecs-gmsa-linux-infrastructure AWS CloudFormation 堆栈的 ECSAutoScalingGroupName 输出中找到此名称。

现在,您可以继续阅读下一部分,其中将构建.NET 示例应用程序。

构建 .NET 应用程序容器

在示例存储库中有一个 ASP.NET Core 应用程序,其连接到示例 SQL Server 数据库中的表。此应用程序可帮助您验证面向 SQL Server 的连接是否确实使用了集成安全。解决方案文件位于 /web-app/web-app.sln。确保该解决方案使用 Visual Studio、Visual Studio Code 或 Web 应用程序文件夹中的以下命令成功构建:

dotnet build web-app.sln

图 5:dotnet 构建输出

成功构建应用程序后,您需要构建 Docker 容器并将其推送到 Amazon ECR。为此,导航到 Amazon ECR 控制台。选择 amazon-ecs-gmsa-linux/web-site 存储库,然后选择查看推送命令

图 6:Amazon ECR 控制台

按照说明标记您的映像并将其推送到 Amazon ECR 存储库。

如果您在装有 Apple M1/M2 处理器的 Mac 计算机上构建应用程序,则默认情况下,容器将使用 amr64 处理器架构构建。Credentials Fetcher 仅支持 x86-64 容器,因此为构建 x86-64 容器,您需要运行以下命令,而不是对话框窗口中显示的命令:docker build --platform=linux/amd64 -t amazon-ecs-gmsa-linux/web-site

图 7:AWS 管理控制台中的 Amazon ECR 推送命令

现在您已将应用程序容器映像推送到 Amazon ECR 中,下一步是配置 Amazon ECS 任务定义以支持使用 gMSA 进行 Windows 身份验证。

配置 Amazon ECS 任务定义

要在 Amazon ECS 任务定义中启用 gMSA 支持,您需要设置 credentialSpec  属性,并附上指向最小化 CredSpec 的链接。CredSpec 可以存储在 Amazon Simple Storage Service(Amazon S3)存储桶、Parameter Store 参数或容器可访问的本地文件中。如果您选择 Amazon S3 或 Parameter Store,则必须将 Amazon ECS 任务执行角色属性设置为具有读取参数或存储桶权限的 AWS Identity and Access Management(AWS IAM)角色。有关更多信息,请参阅关于在 Linux 容器上使用 gMSA 的 Amazon ECS 文档。

Amazon ECS 任务定义如下所示:

"containerDefinitions": [
        {
            ...
            " credentialSpec": [
                " credentialspecdomainless:arn:aws:ssm:us-west-2:111111111111:parameter/testgmsa"
            ]
           ...
            ],
        }
]

注意在加入域模式下请将 “credentialspecdomainless:” 文本替换为” credentialspec:”

credentialSpec 属性只能在 AWS CLI 版本 2.12.7 或更高版本中使用 aws ecs register-task-definition 命令进行设置,或者使用 Amazon ECS 控制台中的 JSON 编辑器进行设置。

该示例解决方案已经使用旧版 dockerSecurityOptions 属性创建了一个基本的 Amazon ECS 任务定义,其中包含所有正确的属性和 IAM 角色。下一步是创建一个改用 credentialSpec 属性的新修订版。使用与部署基础设施相同的终端,如果您使用的是 Bash,请运行以下命令:

../scripts/update-ecs-task-definition-cred-spec.sh -t amazon-ecs-gmsa-linux-web-site-task

或者,如果您使用的是 PowerShell,则运行以下命令:

../scripts/Update-ECSTaskDefinitionCredSpec.ps1 -TaskDefinitionName amazon-ecs-gmsa-linux-web-site-task 

注意在加入域模式下请在运行脚本时设置--domain-joined-mode(PowerShell 中的 -DomainJoinedMode)标志。

查看脚本的输出并记下刚刚创建的修订版编号。您将在下一步中使用它。

将应用程序部署到 Amazon ECS

要将 Amazon ECS 服务与您的应用程序一起部署,请返回用于部署基础设施的终端。如果使用的是 Bash,请运行以下命令:

export DEPLOY_APP=1
export APP_TD_REVISION=[TASK DEFINITION REVISION]
cdk deploy "*" --require-approval "never"

如果使用的是 PowerShell,请运行以下命令:

$Env:DEPLOY_APP = 1
$Env:APP_TD_REVISION = [TASK DEFINITION REVISION]
cdk deploy "*" --require-approval "never"

部署完成后,前往 CloudFormation 控制台,点击名称类似 websiteec2serviceServiceURLXXXXXXXX 的输出值,以导航到 Web 应用程序。Web 应用程序将运行,并使用 gMSA 进行 AD 身份验证。

图 8:运作中的示例 Web 应用程序

ASP.NET Core 应用程序配置与任何其他环境(包括本地环境)相同。唯一需要记住的特殊之处是,连接字符串中的 SQL Server 地址必须使用 AD 域名进行定义。如果您使用 IP 地址或其他 DNS 名称,则身份验证方法默认为 Microsoft NTLM,这将会失败。

故障排除

在部署示例解决方案时,您可能会遇到两种主要类型的错误。第一种(也是最简单的)错误类型是连接到 SQL Server 时出错。要诊断问题,请导航到 Amazon ECS 控制台,选择您的集群名称,然后选择服务名称,最后选择日志以显示该应用程序记录的错误。

图 9:Amazon ECS 任务日志

第二种错误是无法成功启动 Amazon ECS 任务。这些错误的根本原因通常在 Credentials Fetcher 中找到。要浏览 Credentials Fetcher 日志,您需要连接到一个 Amazon ECS 容器 Linux 实例,然后运行以下命令:sudo journalctl -u credentials-fetcher -e。日志可以更准确地告诉您是什么问题阻止了任务的启动。

清理

为避免将来产生费用,请删除这些资源。可以使用 cdk destroy 命令删除堆栈。在终端或 PowerShell 窗口中运行以下命令:

cdk destroy "*" --require-approval "never"

最后,手动删除由 CLI 和 Amazon CloudWatch 日志组创建的 Amazon ECS 任务定义修订版。

结论

在此文章中,我们向您展示了如何将 Credentials Fetcher 与 Amazon ECS 集成。这种集成可让 AWS 客户将应用程序现代化为 Linux 容器,同时通过 gMSA 使用 Windows 身份验证。gMSA 身份验证以前仅在 Windows 容器上可用,其可让应用程序和服务无需手动管理密码即可针对 AD 进行身份验证。这就降低了密码泄露的风险,从而减少复杂性并提高安全性。

文章中的示例应用程序显示了在连接到 SQL Server 数据库时,如何使用 gMSA 在 Linux 容器中运行的.NET 应用程序内支持 Windows 身份验证。这种模式可让企业在不牺牲 AD 所提供安全身份验证的情况下运行现代容器化工作负载,从而节省成本,同时提高 Linux 的可靠性和可扩展性。

本篇作者

Cristobal

Cristobal 是Amazon Web Services 的高级解决方案架构师。他专门帮助客户对在 AWS 上运行的.NET 应用程序进行现代化改造。自 2009 年以来,他帮助组织使用开放 Web 技术、Kubernetes、CI/CD 和云原生服务对其传统 .NET 应用程序进行现代化改造。

Matt Cline

Matt Cline 是 Amazon Web Services 的高级解决方案架构师,在其家乡宾夕法尼亚州匹兹堡为客户提供支持。Matt 拥有全栈开发人员和架构师的背景,他热衷于帮助客户在 AWS 上交付高质量的应用程序。工作之余,Matt 会构建(偶尔还会完成)比例模型,并喜欢与朋友一起玩桌面角色扮演游戏。