CloudFormation 템플릿으로 SAML 자격 증명 공급자(IdP) 생성하기

CloudFormation 템플릿으로 SAML 자격 증명 공급자(IdP) 생성하기 (튜토리얼)

AWS 계정에 SAML 페더레이션을 설정하려면 SAML 자격 증명 공급자(IdP)를 만들어야 해요. 이 튜토리얼은 CloudFormation 템플릿을 사용해 AWS와 외부 IdP 간의 신뢰를 설정하는 SAML IdP를 만드는 방법을 보여줘요.

출처: 문서

본문

템플릿은 IdP의 메타데이터 문서로 구성된 SAML IdP를 만들어요. 그러면 페더레이션 IAM 역할이 이 IdP를 참조해 외부 IdP에서 인증된 사용자가 AWS 리소스에 접근할 수 있게 해줘요.

배포된 리소스는 IdP의 메타데이터 문서와 선택적 암호화 설정으로 구성된 SAML IdP로 이루어져요.

사전 요구 사항 (Prerequisites)

이 튜토리얼은 다음이 이미 준비되어 있다고 가정해요.

  • 이 튜토리얼에서 IdP의 SAML 메타데이터 XML 파일을 포맷하는 데 사용하는 Python 명령을 실행할 로컬 머신에 설치된 Python 3.6 이상
  • 외부 IdP의 SAML 메타데이터 문서를 XML 파일로 저장한 것

CloudFormation으로 SAML IdP 생성 (Create a SAML IdP using CloudFormation)

SAML IdP를 만들려면 CloudFormation 템플릿을 만들고 이를 사용해 IdP 리소스를 포함하는 스택을 만들어요.

템플릿 생성 (Create the template)

먼저 CloudFormation 템플릿을 만들어요.

  1. Template 섹션에서 JSON 또는 YAML 탭의 복사 아이콘을 선택해 템플릿 내용을 복사해요.
  2. 템플릿 내용을 새 파일에 붙여넣어요.
  3. 파일을 로컬에 저장해요.

스택 생성 (Create the stack)

다음으로 저장한 템플릿을 사용해 CloudFormation 스택을 프로비저닝해요.

  1. https://console.aws.amazon.com/cloudformation 에서 CloudFormation 콘솔을 열어요.
  2. Stacks 페이지에서 Create stack 메뉴에서 **with new resources (standard)**를 선택해요.
  3. 템플릿을 지정해요:
    • Prerequisite 아래에서 Choose an existing template을 선택해요.
    • Specify template 아래에서 Upload a template file을 선택해요.
    • Choose file을 선택하고 템플릿 파일로 이동한 뒤 선택해요.
    • Next를 선택해요.
  4. 다음 스택 세부 정보를 지정해요:
    • 스택 이름을 입력해요.
    • IdentityProviderName은 비워 두면 스택 이름을 기반으로 이름이 자동 생성되거나, SAML IdP에 사용자 지정 이름을 입력할 수 있어요. 사용자 지정 이름은 영숫자 문자, 마침표, 밑줄, 하이픈만 포함해야 해요.
    • 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>
      
    • 다른 매개변수는 요구 사항에 따라 기본값을 수락하거나 직접 입력해요:
      • IdentityProviderAddPrivateKey – SAML 어서션을 복호화하기 위한 선택적 개인 키
      • IdentityProviderAssertionEncryptionMode – 선택적, SAML 어서션의 암호화 모드를 설정해요 (Allowed, Required 또는 비어 있음)
    • Next를 선택해요.
  5. 스택 옵션을 구성해요:
    • Stack failure options 아래에서 Delete all newly created resources를 선택해요.

      참고 – 이 옵션을 선택하면 스택 생성이 실패해도 삭제 정책이 리소스를 보관하도록 지정된 리소스에 대해 요금이 부과되는 것을 방지할 수 있어요.

    • 다른 모든 기본값을 수락해요.
    • Capabilities 아래에서 CloudFormation이 계정에 IAM 리소스를 만들 수 있음을 인정하는 확인란을 선택해요.
    • Next를 선택해요.
  6. 스택 세부 정보를 검토하고 Submit을 선택해요.
  7. CloudFormation이 스택을 만들어요. 스택 생성이 완료되면 스택 리소스를 사용할 준비가 돼요. 스택 상세 페이지의 Resources 탭을 사용해 계정에 프로비저닝된 리소스를 볼 수 있어요.

스택은 다음 값을 출력하며 Outputs 탭에서 볼 수 있어요.

  • ProviderARN: 생성된 SAML IdP의 ARN(예: arn:aws:iam::123456789012:saml-provider/CompanyIdP). 이 공급자를 신뢰하는 역할을 만들 때 이 ARN이 필요해요.
  • ProviderName: 생성된 SAML IdP의 이름(예: 사용자 지정 이름을 지정했다면 CompanyIdP, 기본 이름을 사용했다면 my-saml-stack-saml-provider).

이 출력도 내보내지므로 Fn::ImportValue 함수로 다른 CloudFormation 스택에 가져올 수 있어요.

SAML IdP 확인 (Verify the SAML IdP)

SAML IdP를 만든 후에는 구성을 확인하고 페더레이션 역할에 사용할 ARN을 기록할 수 있어요.

  1. https://console.aws.amazon.com/iam/ 에서 IAM 콘솔을 열어요.
  2. 탐색 창에서 Identity providers를 선택해요.
  3. 목록에 새로 만든 SAML IdP가 보여야 해요.
  4. IdP 이름을 선택해 세부 정보를 확인해요.
  5. IdP 상세 페이지에서 SAML 메타데이터 문서와 기타 구성 세부 정보를 볼 수 있어요.
  6. 세부 정보 페이지에 표시된 Provider ARN을 기록해요. 이 IdP를 신뢰하는 페더레이션 IAM 역할을 만들 때 이 ARN이 필요해요.
  7. 메타데이터 문서를 검토해 외부 IdP에서 제공한 것과 일치하는지 확인해요.

이제 SAML IdP를 페더레이션 IAM 역할에서 사용할 준비가 됐어요. 이 IdP를 신뢰하는 역할을 만들어 외부 IdP에서 인증된 사용자가 해당 역할을 맡아 AWS 리소스에 접근할 수 있게 할 수 있어요.

정리: 리소스 삭제 (Clean up: delete resources)

마지막 단계로 스택과 그 안에 포함된 리소스를 삭제해요.

  1. CloudFormation 콘솔을 열어요.
  2. Stacks 페이지에서 템플릿으로 만든 스택을 선택하고 Delete를 선택한 뒤 Delete를 확인해요.
  3. CloudFormation이 스택과 여기에 포함된 모든 리소스의 삭제를 시작해요.

CloudFormation 템플릿 세부 정보 (CloudFormation template details)

리소스 (Resources)

이 튜토리얼의 CloudFormation 템플릿은 계정에 다음 리소스를 만들어요.

  • AWS::IAM::SAMLProvider: AWS와 외부 IdP 간의 신뢰를 설정하는 SAML IdP

구성 (Configuration)

템플릿은 다음 구성 가능한 매개변수를 포함해요.

  • IdentityProviderName – SAML IdP의 이름 (자동 생성 이름을 위해 비워 두기) 예: CompanyIdP 또는 EnterpriseSSO
  • IdentityProviderSAMLMetadataDocument – 외부 IdP의 SAML 메타데이터 문서 (단일 줄로 포맷)
  • IdentityProviderAddPrivateKey – SAML 어서션을 복호화하기 위한 선택적 개인 키
  • IdentityProviderAssertionEncryptionMode – 선택적, SAML 어서션의 암호화 모드를 설정

CloudFormation 템플릿 (CloudFormation template)

이 튜토리얼의 CloudFormation 템플릿으로 사용할 다음 JSON 또는 YAML 코드를 별도 파일로 저장해요.

JSON

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "[AWSDocs] IAM: tutorial_saml-idp",
  "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"]
    }
  },
  "Conditions": {
    "HasPrivateKey": {"Fn::Not": [{"Fn::Equals": [{"Ref": "IdentityProviderAddPrivateKey"}, ""]}]},
    "HasEncryptionMode": {"Fn::Not": [{"Fn::Equals": [{"Ref": "IdentityProviderAssertionEncryptionMode"}, ""]}]},
    "HasCustomName": {"Fn::Not": [{"Fn::Equals": [{"Ref": "IdentityProviderName"}, ""]}]}
  },
  "Resources": {
    "SAMLProvider": {
      "Type": "AWS::IAM::SAMLProvider",
      "Properties": {
        "Name": {"Fn::If": ["HasCustomName", {"Ref": "IdentityProviderName"}, {"Ref": "AWS::NoValue"}]},
        "SamlMetadataDocument": {"Ref": "IdentityProviderSAMLMetadataDocument"},
        "Tags": [
          {
            "Key": "Name",
            "Value": {"Fn::If": ["HasCustomName", {"Ref": "IdentityProviderName"}, {"Fn::Sub": "${AWS::StackName}-saml-provider"}]}
          }
        ],
        "AddPrivateKey": {"Fn::If": ["HasPrivateKey", {"Ref": "IdentityProviderAddPrivateKey"}, {"Ref": "AWS::NoValue"}]},
        "AssertionEncryptionMode": {"Fn::If": ["HasEncryptionMode", {"Ref": "IdentityProviderAssertionEncryptionMode"}, {"Ref": "AWS::NoValue"}]}
      }
    }
  },
  "Outputs": {
    "ProviderARN": {
      "Description": "ARN of the created SAML Identity Provider",
      "Value": {"Ref": "SAMLProvider"},
      "Export": {
        "Name": {"Fn::Sub": "${AWS::StackName}-ProviderARN"}
      }
    },
    "ProviderName": {
      "Description": "Name of the SAML Identity Provider",
      "Value": {"Fn::If": ["HasCustomName", {"Ref": "IdentityProviderName"}, {"Fn::Sub": "${AWS::StackName}-saml-provider"}]},
      "Export": {
        "Name": {"Fn::Sub": "${AWS::StackName}-ProviderName"}
      }
    }
  }
}

YAML

AWSTemplateFormatVersion: '2010-09-09'
Description: '[AWSDocs] IAM: tutorial_saml-idp'

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"

Conditions:
  HasPrivateKey: !Not [!Equals [!Ref IdentityProviderAddPrivateKey, ""]]
  HasEncryptionMode: !Not [!Equals [!Ref IdentityProviderAssertionEncryptionMode, ""]]
  HasCustomName: !Not [!Equals [!Ref IdentityProviderName, ""]]

Resources:
  SAMLProvider:
    Type: 'AWS::IAM::SAMLProvider'
    Properties:
      Name: !If
        - HasCustomName
        - !Ref IdentityProviderName
        - !Ref AWS::NoValue
      SamlMetadataDocument: !Ref IdentityProviderSAMLMetadataDocument
      Tags:
        - Key: Name
          Value: !If
            - HasCustomName
            - !Ref IdentityProviderName
            - !Sub '${AWS::StackName}-saml-provider'
      AddPrivateKey: !If
        - HasPrivateKey
        - !Ref IdentityProviderAddPrivateKey
        - !Ref AWS::NoValue
      AssertionEncryptionMode: !If
        - HasEncryptionMode
        - !Ref IdentityProviderAssertionEncryptionMode
        - !Ref AWS::NoValue

Outputs:
  ProviderARN:
    Description: 'ARN of the created SAML Identity Provider'
    Value: !Ref SAMLProvider
    Export:
      Name: !Sub '${AWS::StackName}-ProviderARN'

  ProviderName:
    Description: 'Name of the SAML Identity Provider'
    Value: !If
      - HasCustomName
      - !Ref IdentityProviderName
      - !Sub '${AWS::StackName}-saml-provider'
    Export:
      Name: !Sub '${AWS::StackName}-ProviderName'

더 알아보기 (Learn more)