IAM 教程:使用 AWS CloudFormation 模板创建 SAML 身份提供者 (IdP) 和 SAML 联合 IAM 角色 - AWS Identity and Access Management

IAM 教程:使用 AWS CloudFormation 模板创建 SAML 身份提供者 (IdP) 和 SAML 联合 IAM 角色

要熟悉 SAML 联合身份验证及其功能,则需使用 AWS CloudFormation 模板设置 SAML 身份提供者 (IdP) 和相关的联合 IAM 角色。本教程介绍如何在单个堆栈中同时创建两种资源。

该模板创建一个可用于联合访问 AWS 资源的 SAML IdP,以及一个信任 SAML 提供者的 IAM 角色。经外部 IdP 进行身份验证的用户可代入此角色访问 AWS 资源。

所部署的资源包括以下部分:

  • 使用 IdP 的元数据文档配置的 SAML IdP。

  • 信任 SAML IdP 的联合 IAM 角色,可由经过身份验证的用户代入此角色。

  • 可配置的托管策略,可以附加到该角色以授予特定权限。

先决条件

本教程假定您已准备好以下各项:

  • 本地计算机上安装了 Python 3.6 或更高版本,可运行本教程中用于对 IdP 的 SAML 元数据 XML 文件进行格式化的 Python 命令。

  • 来自外部 IdP 的 SAML 元数据文档,另存为 XML 文件。

使用 AWS CloudFormation 创建 SAML IdP 和角色

要创建 SAML IdP 和联合角色,您需要创建 CloudFormation 模板,并使用此模板创建包含这两种资源的堆栈。

创建 模板

首先,创建 CloudFormation 模板。

  1. 模板 部分中,单击 JSONYAML 选项卡上的复制图标,以复制模板内容。

  2. 将模板内容粘贴到新文件中。

  3. 将该文件保存在本地。

创建堆栈

接下来,使用已保存的模板来预置 CloudFormation 堆栈。

  1. 通过以下网址打开 AWS CloudFormation 控制台:https://console.aws.amazon.com/cloudformation

  2. 堆栈页面,从创建堆栈菜单中选择(采用新资源(标准))

  3. 指定模板:

    1. 先决条件下,选择选择现有模板

    2. 指定模板下,选择上传模板文件

    3. 选择选择文件,导航到模板文件并将其选中。

    4. 选择下一步

  4. 指定以下堆栈详细信息:

    1. 输入堆栈名称。

    2. 对于 IdentityProviderName,可将其留空以根据堆栈名称自动生成名称,也可以为 SAML IdP 输入自定义名称。

      示例:CompanyIdPEnterpriseSSO

    3. 对于 IdentityProvidersamlMetadataDocument,在将 SAML 元数据 XML 文件粘贴到此字段之前,需要将其格式化为单行。这是必要的,因为 CloudFormation 控制台要求 XML 内容在通过控制台参数传递时需格式化为单行。

      使用以下 Python 命令重新格式化 XML 文件:

      python3 -c "import sys, re; content=open(sys.argv[1]).read(); print(re.sub(r'>\s+<', '><', content.replace('\n', '').replace('\r', '').strip()))" saml-metadata.xml
      注意

      IdP 的 SAML 元数据文档必须格式化为单行才能输入控制台参数。Python 命令删除了换行符和多余空格以创建所需的格式,同时保留所有原始内容和结构。

      复制 Python 命令的输出并将其粘贴到 IdentityProviderSAMLMetadataDocument 字段中。

      格式化的 SAML 元数据文档示例(缩减版):

      <?xml version="1.0" encoding="UTF-8"?><md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://portal.sso.example.com/saml/assertion/CompanyIdP"><md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"><md:KeyDescriptor use="signing"><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificate>MIIDXTCCAkWgAwIBAgIJAJC1HiIAZAiIMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV...</ds:X509Certificate></ds:X509Data></ds:KeyInfo></md:KeyDescriptor><md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://portal.sso.example.com/saml/logout/CompanyIdP"/><md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat><md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://portal.sso.example.com/saml/assertion/CompanyIdP"/></md:IDPSSODescriptor></md:EntityDescriptor>
    4. 对于 RoleName,可将其留空以根据堆栈名称自动生成名称,也可以为联合 IAM 角色输入自定义名称。

      示例:SAML-Developer-AccessSAML-ReadOnly-Role

    5. 对于其他参数,请接受默认值或根据需要输入自己的值:

      • IdentityProviderAddPrivateKey - 解密 SAML 断言的可选私钥

      • IdentityProviderAssertionEncryptionMode - SAML 断言加密模式

        示例值:AllowedRequired,或者留空表示不加密

      • RoleSessionDuration - 最大会话持续时间,以秒为单位(3600-43200,默认为 7200)

        示例:14400(4 小时)

      • RolePermissionsBoundary - 权限边界策略的可选 ARN

        示例:arn:aws:iam::123456789012:policy/DeveloperBoundary

      • RolePath - IAM 角色的路径(默认为 /)

        示例:/saml-roles/

      • RoleManagedPolicy1-5 - 最多 5 个待附加托管策略的可选 ARN

        RoleManagedPolicy1 示例:arn:aws:iam::aws:policy/ReadOnlyAccess

        RoleManagedPolicy2 示例:arn:aws:iam::123456789012:policy/CustomPolicy

    6. 选择下一步

  5. 配置堆栈选项:

    1. 堆栈故障选项下,选择删除所有新创建的资源

      注意

      如果选择此选项,即使堆栈创建失败,您可能也不会因为删除策略规定保留资源而向资源付费。

    2. 接受其他所有默认值。

    3. 功能下勾选复选框,确认 CloudFormation 可能在您的账户中创建 IAM 资源。

    4. 选择下一步

  6. 查看堆栈详细信息并选择提交

AWS CloudFormation 将创建堆栈。一旦堆栈创建完成之后,堆栈资源就即可投入使用。可使用堆栈详细信息页面上的资源选项卡来查看账户中预置的资源。

堆栈将输出以下值,可参见输出选项卡:

  • RoleARN:已创建的 IAM 角色的 ARN(例如,arn:aws:iam::123456789012:role/SAML-Developer-Accessarn:aws:iam::123456789012:role/stack-name-a1b2c3d4,如果使用自动生成的名称)。

  • IdentityProviderARN:已创建的 SAML IdP 的 ARN(例如,arn:aws:iam::123456789012:saml-provider/CompanyIdP)。

在配置 IdP 以发送相应 SAML 属性进行角色代入时,需要这两个 ARN。

测试 SAML 联合身份验证

一旦创建 SAML IdP 和联合角色后,可测试联合身份验证设置。

  1. 通过 https://console.aws.amazon.com/iam/ 打开 IAM 控制台。

  2. 在导航窗格中,选择 Identity providers(身份提供程序)。

    您应该能在列表中看到新创建的 SAML IdP。

  3. 选择 IdP 名称以查看其详细信息。

    在 IdP 详细信息页面上,可以看到 SAML 元数据文档和其他配置详细信息。

  4. 在导航窗格中,选择角色

  5. 找到并选择新创建的联合角色。

    在角色详细信息页面上,可以看到允许 SAML IdP 代入此角色的信任策略。

  6. 选择信任关系选项卡,查看信任策略。

    信任策略应表明在 SAML 受众 (SAML:aud) 匹配 https://signin.aws.amazon.com/saml 的条件下,信任该 SAML IdP 代入此角色。

清理:删除资源

最后一步,您将删除该堆栈及其包含的资源。

  1. 打开 AWS CloudFormation 控制台。

  2. 堆栈页面上,选择根据该模板创建的堆栈,选择删除,然后确认删除

    CloudFormation 启动删除堆栈及其包含的所有资源。

CloudFormation 模板详细信息

资源

本教程的 AWS CloudFormation 模板将在您的账户中创建以下资源:

  • AWS::IAM::SAMLProvider:一个在 AWS 和外部 IdP 之间建立信任的 SAML IdP。

  • AWS::IAM::Role:一个可以由通过 SAML IdP 进行身份验证的用户代入的联合 IAM 角色。

配置

模板包含如下可配置参数:

  • IdentityProviderName - SAML IdP 的名称(自动生成的名称请留空)

  • IdentityProviderSAMLMetadataDocument - 来自 IdP 的 SAML 元数据文档(必需)

  • IdentityProviderAddPrivateKey - 解密 SAML 断言的可选私钥

  • IdentityProviderAssertionEncryptionMode - SAML 断言加密模式

  • RoleName - IAM 角色的名称(自动生成的名称请留空)

  • RolePath - IAM 角色的路径(默认为 /)

  • RolePermissionsBoundary - 权限边界策略的可选 ARN

  • RoleSessionDuration - 最大会话持续时间,以秒为单位(3600-43200,默认为 7200)

  • RoleManagedPolicy1-5 - 最多 5 个待附加托管策略的可选 ARN

CloudFormation 模板

将以下 JSON 或 YAML 代码另存为单独文件,以用作本教程的 CloudFormation 模板。

JSON
{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "[AWSDocs] IAM: tutorial_saml-idp-and-federated-role", "Parameters": { "IdentityProviderName": { "Type": "String", "Description": "Name of the SAML Identity Provider (leave empty for auto-generated name like '{StackName}-{UniqueId}')", "Default": "", "AllowedPattern": "^$|^[a-zA-Z0-9._-]+$", "ConstraintDescription": "Must be empty or contain only alphanumeric characters, periods, underscores, and hyphens" }, "IdentityProviderSAMLMetadataDocument": { "Type": "String", "Description": "SAML metadata document from identity provider" }, "IdentityProviderAddPrivateKey": { "Type": "String", "Description": "Optional private key for decrypting SAML assertions. The private key must be a .pem file that uses AES-GCM or AES-CBC encryption algorithm to decrypt SAML assertions.", "Default": "" }, "IdentityProviderAssertionEncryptionMode": { "Type": "String", "Description": "Optional, sets encryption mode for SAML assertions", "Default": "", "AllowedValues": ["", "Allowed", "Required"] }, "RoleName": { "Type": "String", "Description": "Name of the IAM Role (leave empty for auto-generated name like '{StackName}-{UniqueId}')", "Default": "", "AllowedPattern": "^$|^[\\w+=,.@-]{1,64}$", "ConstraintDescription": "Must be empty or 1-64 characters and can contain alphanumeric characters and +=,.@-" }, "RolePath": { "Type": "String", "Description": "Path for the IAM Role", "AllowedPattern": "(^\\/$)|(^\\/.*\\/$)", "Default": "/" }, "RolePermissionsBoundary": { "Type": "String", "Description": "Optional ARN of the permissions boundary policy (leave empty for none)", "Default": "" }, "RoleSessionDuration": { "Description": "The maximum session duration (in seconds) that you want to set for the specified role (3600-43200)", "Type": "Number", "MinValue": 3600, "MaxValue": 43200, "Default": 7200 }, "RoleManagedPolicy1": { "Type": "String", "Description": "Optional managed policy ARN 1", "Default": "" }, "RoleManagedPolicy2": { "Type": "String", "Description": "Optional managed policy ARN 2", "Default": "" }, "RoleManagedPolicy3": { "Type": "String", "Description": "Optional managed policy ARN 3", "Default": "" }, "RoleManagedPolicy4": { "Type": "String", "Description": "Optional managed policy ARN 4", "Default": "" }, "RoleManagedPolicy5": { "Type": "String", "Description": "Optional managed policy ARN 5", "Default": "" } }, "Conditions": { "HasCustomProviderName": {"Fn::Not": [{"Fn::Equals": [{"Ref": "IdentityProviderName"}, ""]}]}, "HasCustomRoleName": {"Fn::Not": [{"Fn::Equals": [{"Ref": "RoleName"}, ""]}]}, "HasPermissionsBoundary": {"Fn::Not": [{"Fn::Equals": [{"Ref": "RolePermissionsBoundary"}, ""]}]}, "HasPolicy1": {"Fn::Not": [{"Fn::Equals": [{"Ref": "RoleManagedPolicy1"}, ""]}]}, "HasPolicy2": {"Fn::Not": [{"Fn::Equals": [{"Ref": "RoleManagedPolicy2"}, ""]}]}, "HasPolicy3": {"Fn::Not": [{"Fn::Equals": [{"Ref": "RoleManagedPolicy3"}, ""]}]}, "HasPolicy4": {"Fn::Not": [{"Fn::Equals": [{"Ref": "RoleManagedPolicy4"}, ""]}]}, "HasPolicy5": {"Fn::Not": [{"Fn::Equals": [{"Ref": "RoleManagedPolicy5"}, ""]}]}, "HasPrivateKey": {"Fn::Not": [{"Fn::Equals": [{"Ref": "IdentityProviderAddPrivateKey"}, ""]}]}, "HasAssertionEncryptionMode": {"Fn::Not": [{"Fn::Equals": [{"Ref": "IdentityProviderAssertionEncryptionMode"}, ""]}]} }, "Resources": { "SAMLProvider": { "Type": "AWS::IAM::SAMLProvider", "Properties": { "Name": {"Fn::If": ["HasCustomProviderName", {"Ref": "IdentityProviderName"}, {"Ref": "AWS::NoValue"}]}, "SamlMetadataDocument": {"Ref": "IdentityProviderSAMLMetadataDocument"}, "AddPrivateKey": {"Fn::If": ["HasPrivateKey", {"Ref": "IdentityProviderAddPrivateKey"}, {"Ref": "AWS::NoValue"}]}, "AssertionEncryptionMode": {"Fn::If": ["HasAssertionEncryptionMode", {"Ref": "IdentityProviderAssertionEncryptionMode"}, {"Ref": "AWS::NoValue"}]} } }, "SAMLFederatedRole": { "Type": "AWS::IAM::Role", "Properties": { "RoleName": {"Fn::If": ["HasCustomRoleName", {"Ref": "RoleName"}, {"Ref": "AWS::NoValue"}]}, "Path": {"Ref": "RolePath"}, "Description": "SAML federated IAM role for SSO access with specified permissions", "MaxSessionDuration": {"Ref": "RoleSessionDuration"}, "PermissionsBoundary": {"Fn::If": ["HasPermissionsBoundary", {"Ref": "RolePermissionsBoundary"}, {"Ref": "AWS::NoValue"}]}, "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": {"Ref": "SAMLProvider"} }, "Action": [ "sts:AssumeRole", "sts:SetSourceIdentity", "sts:TagSession" ], "Condition": { "StringEquals": { "SAML:aud": "https://signin.aws.amazon.com/saml" } } } ] }, "ManagedPolicyArns": { "Fn::Split": [ ",", { "Fn::Join": [ ",", [ {"Fn::If": ["HasPolicy1", {"Ref": "RoleManagedPolicy1"}, {"Ref": "AWS::NoValue"}]}, {"Fn::If": ["HasPolicy2", {"Ref": "RoleManagedPolicy2"}, {"Ref": "AWS::NoValue"}]}, {"Fn::If": ["HasPolicy3", {"Ref": "RoleManagedPolicy3"}, {"Ref": "AWS::NoValue"}]}, {"Fn::If": ["HasPolicy4", {"Ref": "RoleManagedPolicy4"}, {"Ref": "AWS::NoValue"}]}, {"Fn::If": ["HasPolicy5", {"Ref": "RoleManagedPolicy5"}, {"Ref": "AWS::NoValue"}]} ] ] } ] } } } }, "Outputs": { "RoleARN": { "Description": "ARN of the created IAM Role", "Value": {"Fn::GetAtt": ["SAMLFederatedRole", "Arn"]}, "Export": { "Name": {"Fn::Sub": "${AWS::StackName}-RoleARN"} } }, "IdentityProviderARN": { "Description": "ARN of the created SAML Identity Provider", "Value": {"Ref": "SAMLProvider"}, "Export": { "Name": {"Fn::Sub": "${AWS::StackName}-IdentityProviderARN"} } } } }
YAML
AWSTemplateFormatVersion: '2010-09-09' Description: '[AWSDocs] IAM: tutorial_saml-idp-and-federated-role' Parameters: IdentityProviderName: Type: String Description: Name of the SAML Identity Provider (leave empty for auto-generated name like '{StackName}-{UniqueId}') Default: "" AllowedPattern: '^$|^[a-zA-Z0-9._-]+$' ConstraintDescription: Must be empty or contain only alphanumeric characters, periods, underscores, and hyphens IdentityProviderSAMLMetadataDocument: Type: String Description: SAML metadata document from identity provider IdentityProviderAddPrivateKey: Type: String Description: Optional private key for decrypting SAML assertions. The private key must be a .pem file that uses AES-GCM or AES-CBC encryption algorithm to decrypt SAML assertions. Default: "" IdentityProviderAssertionEncryptionMode: Type: String Description: Optional, sets encryption mode for SAML assertions Default: "" AllowedValues: - "" - "Allowed" - "Required" RoleName: Type: String Description: Name of the IAM Role (leave empty for auto-generated name like '{StackName}-{UniqueId}') Default: "" AllowedPattern: '^$|^[\w+=,.@-]{1,64}$' ConstraintDescription: "Must be empty or 1-64 characters and can contain alphanumeric characters and +=,.@-" RolePath: Type: String Description: Path for the IAM Role AllowedPattern: (^\/$)|(^\/.*\/$) Default: "/" RolePermissionsBoundary: Type: String Description: Optional ARN of the permissions boundary policy (leave empty for none) Default: "" RoleSessionDuration: Description: The maximum session duration (in seconds) that you want to set for the specified role (3600-43200) Type: Number MinValue: 3600 MaxValue: 43200 Default: 7200 RoleManagedPolicy1: Type: String Description: Optional managed policy ARN 1 Default: "" RoleManagedPolicy2: Type: String Description: Optional managed policy ARN 2 Default: "" RoleManagedPolicy3: Type: String Description: Optional managed policy ARN 3 Default: "" RoleManagedPolicy4: Type: String Description: Optional managed policy ARN 4 Default: "" RoleManagedPolicy5: Type: String Description: Optional managed policy ARN 5 Default: "" Conditions: HasCustomProviderName: !Not [!Equals [!Ref IdentityProviderName, ""]] HasCustomRoleName: !Not [!Equals [!Ref RoleName, ""]] HasPermissionsBoundary: !Not [!Equals [!Ref RolePermissionsBoundary, ""]] HasPolicy1: !Not [!Equals [!Ref RoleManagedPolicy1, ""]] HasPolicy2: !Not [!Equals [!Ref RoleManagedPolicy2, ""]] HasPolicy3: !Not [!Equals [!Ref RoleManagedPolicy3, ""]] HasPolicy4: !Not [!Equals [!Ref RoleManagedPolicy4, ""]] HasPolicy5: !Not [!Equals [!Ref RoleManagedPolicy5, ""]] HasPrivateKey: !Not [!Equals [!Ref IdentityProviderAddPrivateKey, ""]] HasAssertionEncryptionMode: !Not [!Equals [!Ref IdentityProviderAssertionEncryptionMode, ""]] Resources: SAMLProvider: Type: AWS::IAM::SAMLProvider Properties: Name: !If - HasCustomProviderName - !Ref IdentityProviderName - !Ref AWS::NoValue SamlMetadataDocument: !Ref IdentityProviderSAMLMetadataDocument AddPrivateKey: !If - HasPrivateKey - !Ref IdentityProviderAddPrivateKey - !Ref AWS::NoValue AssertionEncryptionMode: !If - HasAssertionEncryptionMode - !Ref IdentityProviderAssertionEncryptionMode - !Ref AWS::NoValue SAMLFederatedRole: Type: AWS::IAM::Role Properties: RoleName: !If - HasCustomRoleName - !Ref RoleName - !Ref AWS::NoValue Path: !Ref RolePath Description: "SAML federated IAM role for SSO access with specified permissions" MaxSessionDuration: !Ref RoleSessionDuration PermissionsBoundary: !If - HasPermissionsBoundary - !Ref RolePermissionsBoundary - !Ref AWS::NoValue AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Federated: !Ref SAMLProvider Action: - 'sts:AssumeRole' - 'sts:SetSourceIdentity' - 'sts:TagSession' Condition: StringEquals: 'SAML:aud': 'https://signin.aws.amazon.com/saml' ManagedPolicyArns: !Split - ',' - !Join - ',' - - !If [HasPolicy1, !Ref RoleManagedPolicy1, !Ref "AWS::NoValue"] - !If [HasPolicy2, !Ref RoleManagedPolicy2, !Ref "AWS::NoValue"] - !If [HasPolicy3, !Ref RoleManagedPolicy3, !Ref "AWS::NoValue"] - !If [HasPolicy4, !Ref RoleManagedPolicy4, !Ref "AWS::NoValue"] - !If [HasPolicy5, !Ref RoleManagedPolicy5, !Ref "AWS::NoValue"] Outputs: RoleARN: Description: ARN of the created IAM Role Value: !GetAtt SAMLFederatedRole.Arn Export: Name: !Sub '${AWS::StackName}-RoleARN' IdentityProviderARN: Description: ARN of the created SAML Identity Provider Value: !Ref SAMLProvider Export: Name: !Sub '${AWS::StackName}-IdentityProviderARN'