AWS 기술 블로그

Amazon Managed Grafana에서 Keycloak SAML 기반 IdP로 사용자 인증하기

배경

많은 기업에서 AWS 서비스뿐만 아니라 다양한 오픈 소스, 타사 및 기타 클라우드 데이터 소스의 통합 모니터링을 위해 Amazon Managed Grafana 서비스를 활용하고 있습니다. Amazon Managed Grafana는 사용자 인증 및 권한 부여를 위해 AWS IAM Identity Center 이외에도 SAML 2.0을 지원하는 IdP(자격 증명 제공자)와의 통합을 지원합니다. 따라서 기존에 사용 중인 IdP(자격 증명 제공자)를 활용하여 Amazon Managed Grafana 워크 스페이스의 Grafana 콘솔에 로그인하고, 액세스 제어를 관리하여, 데이터를 검색하고, 시각화를 구축하기 위한 SSO(single sign-on)를 구성할 수 있습니다. 이 블로그에서는 Amazon Managed Grafana 서비스에서 오픈 소스 Identity and Access Management(IAM)인 Keycloak서비스를 활용하여 SSO(single sign-on)를 구성하는 방법을 알아보고자 합니다.

SAML 2.0(Security Assertion Markup Language 2.0)

주로 웹 기반 애플리케이션에서 IdP(자격 증명 제공자)와 SP(서비스 제공자) 간 인증 및 권한 부여를 위한 개방형 표준 프로토콜 중 하나입니다. 즉, 사용자가 SP(서비스 제공자)의 권한을 얻기 위해서, IdP(자격 증명 제공자)로부터 사용자의 신원 정보를 안전하게 전달받고 권한을 부여하기 위해 사용됩니다. 따라서 SAML은 로그인을 시도할 때마다 사용자 이름과 비밀번호 같은 자격 증명을 요구하지 않아도 사용자 신원과 권한 수준을 확인하여 액세스를 허용하거나 거부할 수 있다는 점에서 매우 유용합니다. 주요 구성 요소로는 다음과 같습니다.

SP(서비스 제공자)는 일반적으로 서비스를 제공하는 주체를 말합니다. 사용자에게 서비스를 제공하고 IdP(자격 증명 제공자)로부터 인증을 받아서 사용자에게 권한을 부여합니다. Amazon Managed Grafana 워크스페이스는 SP(서비스 제공자) 역할을 하며 IdP(자격 증명 제공자) 와 상호 작용하여 사용자 정보를 얻게 됩니다.

IdP(자격 증명 제공자)는 SP(서비스 제공자)에 엑세스하려는 사용자의 인증을 제공하고 인증 데이터를 함께 보내는 공급자입니다. IdP(자격 증명 제공자)와 SP(서비스 제공자)가 서로 협의한 구성을 기반으로 SAML 응답을 생성합니다. SP(서비스 제공자)는 SAML assertion을 받은 후 assertion이 유효한 IdP(자격 증명 제공자)에서 온 것인지 확인한 다음 assertion에서 필요한 정보(사용자 이름, 속성 등)를 파싱합니다. 이 블로그에서는 Keycloak을 사용할 예정이며 이외에도 Azure Active Directory, CyberArk, Okta, OneLogin, Ping ID 등 SAML2.0을 지원하는 IdP(자격 증명 제공자)를 선택하여 사용하실 수 있습니다.

Keycloak은 Red Hat에서 개발한 오픈 소스 Identity and Access Management (IAM) 솔루션으로 사용자 인증, 권한 부여 및 보안 기능을 통합적으로 제공합니다. SSO(Single-Sign On) 기능과 함께 국제 인증/인가 표준 프로토콜(SAML, OIDC, OAuth 2.0 등) 을 모두 지원합니다

솔루션 개요

Amazon Managed Grafana는 SP에서 시작한 요청(SP-initiated requests)을 지원하며 Keycloak 을 활용한 Amazon Managed Grafana 인증 흐름은 다음과 같습니다.

  1. 최종 사용자가 브라우저에서 SP(서비스 제공자)에 액세스합니다.
  2. SP(서비스 제공자)는 SAML 요청을 다시 브라우저로 리다이렉션 합니다.
  3. 브라우저가 SAML 요청을 IdP(자격 증명 제공자)에 전달합니다.
  4. 사용자를 식별 및 인증합니다.
  5. IdP(자격 증명 제공자)가 SAML assertion을 생성하여 브라우저로 다시 전송합니다.
  6. 브라우저가 SAML assertion을 다시 SP(서비스 제공자)로 전달합니다.
  7. 사용자가 인증된 경우 SP(서비스 제공자)는 요청한 컨텍스트를 브라우저로 보냅니다.

단계 요약

Amazon EC2에서 Keycloak을 설치 후 Amazon Managed Grafana 서비스와 연동하여 Keycloak을 통한 사용자 생성 및 권한 관리 설정을 함께 다룹니다.

  • 단계 1 : Keycloak docker 설치하기
  • 단계 2 : HTTPS 통신을 위해 ALB에 SSL 설정하기
  • 단계 3 : realm 및 사용자 생성하기
  • 단계 4 : AWS SAML 연동 및 Amazon Managed Grafana 로그인하기

사전 준비사항

솔루션을 배포하기 위해서는 아래와 같은 사항을 미리 준비해야 합니다.

  • AWS 계정
  • 실습 환경 : 서울(ap-northeast-2) 리전, EC2 Amazon Linux 2023 AMI
  • Amazon EC2, Amazon Route 53, AWS Certificate Manager, Amazon Managed Grafana 서비스 접근 권한
  • HTTPS 통신을 위해 AWS Certificate Manager 또는 타 도메인 호스팅 업체를 통한 도메인 발급
  • AWS CLI 설치

단계 1 : Amazon EC2 내 Keycloak docker 설치하기

  1. Amazon EC2 생성 후 SSH 접속하여 Docker를 설치합니다.
    sudo yum update -y
    sudo yum install docker -y
    sudo service docker start
  2. 아래 명령어에서 사용할 Keycloak admin패스워드를 수정한 후 Keycloak docker container를 실행합니다. (50-60초 소요됨)
    sudo docker run -d -p 8080:8080 --name keycloak -e KC_PROXY_HEADERS=xforwarded -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=YOUR_ADMIN_PASSWORD quay.io/keycloak/keycloak "start-dev"
  3. Keycloak 로그를 확인합니다.
    sudo docker logs keycloak | grep started
    
    2024-02-26 01:34:21,369 INFO  [io.quarkus] (main) Keycloak 23.0.7 on JVM (powered by Quarkus 3.2.10.Final) started in 38.928s. Listening on: http://0.0.0.0:8080

단계2 : HTTPS 통신을 위해 ALB에 SSL 설정하기

  1. Keycloak Administration Console은 보안을 위해 기본적으로 HTTPS를 통한 접속을 허용합니다. HTTPS 통신을 위한 SSL 인증서 설정이 필요하므로 ACM(AWS Certificate Manager) 퍼블릭 인증서 발급DNS 검증을 수행합니다.
  2. AWS CLI 를 접속할 환경에서 환경변수(KC_VPC_ID : Keycloak EC2 생성한 VPC와 동일한 VPC ID, SUBNET_ID_1/SUBNET_ID_2: Keycloak 로드밸런서가 존재할 public 서브넷 ID, KC_EC2_INSTANDE_ID: keycloak EC2 인스턴스 ID, KC_EC2_SG_ID: keycloak EC2 보안그룹 ID, HOSTED_ZONE_ID: Route53에 등록한 호스팅 영역 ID, CERTIFICATE_ARN: ACM 인증서 ID)를 지정합니다.
    export KC_VPC_ID=vpc-xxxxxxxxxxxxxxxxx
    export SUBNET_ID_1=subnet-xxxxxxxxxxxxxxxxx
    export SUBNET_ID_2=subnet-xxxxxxxxxxxxxxxxx
    export KC_EC2_INSTANDE_ID=i-xxxxxxxxxxxxxxxxx
    export KC_EC2_SG_ID=sg-xxxxxxxxxxxxxxxxx
    export HOSTED_ZONE_ID=Zxxxxxxxxxxxxxxxxxxxx
    export CERTIFICATE_ARN= arn:aws:acm:ap-northeast-2:xxxxxxxx:certificate/xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx

    로드밸런서를 생성하기 위해 보안 그룹(443 포트 허용), 대상 그룹, 로드밸런서를 차례대로 생성합니다.

    # 로드밸런서 보안 그룹 생성 
    aws ec2 create-security-group --group-name kc-lb-sg \
    --description "Security group for ALB allowing HTTPS traffic" --vpc-id $KC_VPC_ID
    
    # 결과로 출력된 보안 GroupID 복사 후 환경변수 추가
    export KC_LB_SG_ID=sg-xxxxxxxxxxxxxxxxx
    
    aws ec2 authorize-security-group-ingress --group-id $KC_LB_SG_ID \
    --protocol tcp --port 443 --cidr 0.0.0.0/0
    
    # 로드밸런서 생성
    aws elbv2 create-load-balancer --name kc-lb \
    --subnets $SUBNET_ID_1 $SUBNET_ID_2 --security-groups $KC_LB_SG_ID --scheme internet-facing --type application
    
    # 결과로 출력된 LoadBalancerArn 복사 후 환경변수 추가
    export LOADBALANCER_ARN=arn:aws:elasticloadbalancing:ap-northeast-2:xxxxxxxxxxxx:loadbalancer/app/kc-lb/xxxxxxxxxxxxxxxx
    
    # 대상 그룹 생성
    aws elbv2 create-target-group --name kc-tg \
    --protocol HTTP --port 8080 --vpc-id $KC_VPC_ID --health-check-protocol HTTP --health-check-path /realms/master/ --target-type instance
    
    # 결과로 출력된 TargetGroupArn 복사 후 환경변수 추가
    export TARGETGROUP_ARN=arn:aws:elasticloadbalancing:ap-northeast-2:xxxxxxxxxxxx:targetgroup/kc-tg/xxxxxxxxxxxxxxxx
    
    # 대상 그룹으로 Keycloak EC2 연결 
    aws elbv2 register-targets --target-group-arn $TARGETGROUP_ARN \
    --targets Id=$KC_EC2_INSTANDE_ID
    
    # 로드밸런서 443 listener 생성 후 Keycloak EC2는 로드밸런서에서 들어온 트래픽만 허용하도록 보안 그룹 수정
    aws elbv2 create-listener --load-balancer-arn $LOADBALANCER_ARN \
    --protocol HTTPS --port 443 --certificates CertificateArn=$CERTIFICATE_ARN --default-actions Type=forward,TargetGroupArn=$TARGETGROUP_ARN
    
    aws ec2 authorize-security-group-ingress --group-id $KC_EC2_SG_ID \
    --protocol tcp --port 8080 --source-group $KC_LB_SG_ID 
    
  3. 사전에 등록한 Route53 호스팅 영역에서 Keycloak 레코드 생성을 위해 Keycloak Administration 콘솔 접속을 위한 URL과 Keycloak LB 도메인을 수정한 후 kc-record.json 파일을 생성합니다. 그 다음 레코드를 추가하는 명령어를 수행합니다.
    vi kc-record.json
    {
      "Changes": [
        {
          "Action": "UPSERT",
          "ResourceRecordSet": {
            "Name": "mykeycloak.YOUR_DOMAIN_NAME",
            "Type": "CNAME",
            "TTL": 300,
            "ResourceRecords": [
              {
                "Value": "YOUR_LOADBALANCER_DNS_NAME"
              }
            ]
          }
        }
      ]
    }
    
    aws route53 change-resource-record-sets --hosted-zone-id $HOSTED_ZONE_ID --change-batch file://kc-record.json
    
  4. Administration Console ( https://YOUR_DOMAIN_NAME)에 접근합니다. 다음과 같이 admin으로 로그인 합니다. (Username : admin, Password: 지정한 keycloak admin 패스워드 입력)

단계 3 : realm 및 사용자 생성하기

  1. 새로운 Realm을 생성하기 위해 Create Realm 버튼을 클릭합니다.
  2. Realm name으로 amg-keycloak-saml을 입력하고, Create 버튼을 클릭합니다.
  3. 생성된 Realm 을 확인하고 Realm settings 메뉴를 선택합니다.
  4. 모든 요청에 HTTPS 를 사용하도록 Require SSL을 All requests로 선택한 후 Save버튼을 클릭합니다. 하단에 있는 Endpoints에서 SAML 2.0 Identity Provider Metadata 링크 주소를 복사하여 메모합니다.
  5. amg-keyclock-saml realm에 grafana 사용자를 생성하기 위해 Users 메뉴를 선택하고, Add User 버튼을 클릭합니다. Username으로 grafana를 입력한 후 Email, First name, Last name을 입력하고 Create 버튼을 클릭합니다.
  6. grafana 사용자의 패스워드 변경을 위해 상단에 Credentials 메뉴를 선택합니다.
  7. 다음으로 Set password 버튼을 클릭합니다. 설정할 패스워드를 입력하고, Temporary는 Off로 설정한 후 Save 버튼을 클릭합니다.
  8. grafana 사용자의 패스워드 설정이 완료되었습니다. grafana 사용자로 로그인하기 위해 https://mykeycloak.생성한 도메인 이름/keycloak-saml/account/#/ 로 이동하여 grafana 사용자 패스워드 설정 시 입력했던 로그인 정보를 입력합니다.

단계 4 : AWS SAML 연동 및 Amazon Managed Grafana 로그인하기

  1. Amazon Managed Grafana(AMG) 워크스페이스 생성을 클릭합니다. amg-blog-workspace 이름으로 Security Assertion Markup Language(SAML) 인증 방법을 선택하여 워크스페이스를 생성합니다.
  2. 생성한 워크스페이스를 선택한 후 하단에 Security Assertion Markup Language(SAML) 설정 완료 버튼을 클릭합니다.
  3. Metadata URL은 Keycloak realm 의 SAML Metadata 주소로 (단계 2 : realm 및 사용자 생성하기)에서 메모했던 SAML 2.0 Identity Provider Metadata 링크 주소를 입력합니다. Assertion attribute role 에는 role을 입력합니다. Admin role values 에 admin, editor, viewer를 입력할 수 있으며, editor를 입력하고 SAML 구성 저장 버튼을 클릭합니다. 각 role에 대한 상세한 권한은 다음 링크에서 확인할 수 있습니다. https://docs.thinkwithwp.com/ko_kr/grafana/latest/userguide/dashboard-and-folder-permissions.html
    이후 Grafana 워크스페이스 URL를 메모합니다.
  4. Client(AMG)를 생성하는 JSON파일을 생성하겠습니다. 아래 파일 중에서 ${WORKSPACE_ENDPOINT} 값은 실제 Grafana 워크스페이스 URL 값으로 수정 후 파일을 json 이름으로 저장합니다. 실제 사용자를 식별하는 데 email 을 사용하고자 합니다. 또한 Client Scope는 Client 권한을 설정하기 위해 Mapper를 생성하여 설정할 수 있습니다. Client Scope가 등록되면 해당 Client에 대한 Protocol Mapper와 Role Mapping을 정의해야 합니다. 상세한 내용은 다음 링크에서 확인할 수 있습니다.
    {
      "clientId": "https://${WORKSPACE_ENDPOINT}/saml/metadata",
      "name": "amazon-managed-grafana",
      "enabled": true,
      "protocol": "saml",
      "adminUrl": "https://${WORKSPACE_ENDPOINT}/login/saml",
      "redirectUris": [
        "https://${WORKSPACE_ENDPOINT}/saml/acs"
      ],
      "attributes": {
        "saml.authnstatement": "true",
        "saml.server.signature": "true",
        "saml_name_id_format": "email",
        "saml_force_name_id_format": "true",
        "saml.assertion.signature": "true",
        "saml.client.signature": "false"
      },
      "defaultClientScopes": [],
      "protocolMappers": [
        {
          "name": "name",
          "protocol": "saml",
          "protocolMapper": "saml-user-property-mapper",
          "consentRequired": false,
          "config": {
            "attribute.nameformat": "Unspecified",
            "user.attribute": "firstName",
            "attribute.name": "displayName"
          }
        },
        {
          "name": "email",
          "protocol": "saml",
          "protocolMapper": "saml-user-property-mapper",
          "consentRequired": false,
          "config": {
            "attribute.nameformat": "Unspecified",
            "user.attribute": "email",
            "attribute.name": "mail"
          }
        },
        {
          "name": "role list",
          "protocol": "saml",
          "protocolMapper": "saml-role-list-mapper",
          "config": {
            "single": "true",
            "attribute.nameformat": "Unspecified",
            "attribute.name": "role"
          }
        }
      ]
    }
    
  5. admin 로그인하여 amg-keyclock-saml reaml를 선택합니다. Clients 메뉴에서 Import client 버튼을 클릭합니다. Browse버튼을 선택한 후 앞서 생성한 json파일을 선택합니다. Keycloak 입장에서 Client는 Amazon Managed Grafana(AMG) 가 됩니다.
  6. Realm roles 메뉴를 선택합니다. Create role 버튼을 클릭하여 다음과 같이 사용자에게 부여할 Role 3개 admin, editor, viewer를 생성합니다.
  7. Users 메뉴에서 grafana를 선택한 후 Role mapping 메뉴에서 Assign role 버튼을 클릭하여 editor role을 지정합니다.
  8. Grafana 워크스페이스 URL 을 접속한 후 grafana 사용자로 SAML로그인 합니다.

리소스 정리하기

  1. Amazon EC2(keycloak) 중지
  2. 생성한 보안그룹, 대상 그룹, 로드밸런서 삭제
  3. 등록된 Route53 도메인, ACM 인증서 삭제
  4. Amazon Managed Grafana(AMG) 워크스페이스 삭제

결론

지금까지 Amazon Managed Grafana 서비스에서 Keycloak을 활용하여 SSO(single sign-on)를 구성하는 방법을 알아보았습니다. 여러 가지 방법 중에서도 Keycloak Administration Console을 활용하여 쉽게 설정할 수 있습니다. 테스트 이후 상용에서 보다 안정적인 운영을 위해 운영 모드로 설치하여 Amazon Relational Database Service (RDS)와 같은 외부 Database를 추가로 구성해 볼 수 있습니다.

JeongHee Chae

JeongHee Chae

채정희 솔루션즈 아키텍트는 인프라 운영 경험을 바탕으로 클라우드가 익숙하지 않는 고객들에게 클라우드를 효율적으로 시작할 수 있도록 도움을 드리는 역할을 하고 있습니다.