당신의 DB는 안전한가요?: 안전하게 RDS에 접근하는 방법 (with IAM)

Dec 24, 2023

RDS security

    실수로 프로덕션 DB를 입사 첫날에 날려버렸고, 퇴사를 명령받았습니다. 여기에 더해 CTO로부터 법률적인 검토가 필요하다는 말을 들었는데 이게 얼마나 큰일인가요?

    2017년 레딧에 올라온 이 글은 바이럴을 탔고 한 기술 뉴스에서 누가 잘려야되는지 투표를 올렸습니다. 여기에서 45%의 사람들이 CTO를 잘라야 된다고 투표했습니다. (출처)

    즉, 새로운 사람들이나 기존 인원들이 시스템에 장애를 일으킬 수 있고, 이를 방지하는 것이 CTO나 테크 리드의 역할이라는 것을 알 수 있었습니다. 해당 글을 읽으면서 제가 CTO로 근무하고 있는 코드트리에서는 어떤 방식으로 DB에 접근하고 있었는지 돌아보게 되었습니다.

    코드트리에서는 AWS RDS를 사용해서 Database를 관리하고 있습니다. DB 접속을 하는 인원이 많지 않던 시절에는 RDS에 (1) public access를 허용하고 (2) 마스터 유저 네임과 패스워드로 접속하는 방법을 사용했었는데, 이 부분이 보안적으로 개선할 필요가 있다고 생각했습니다. 안전하게 접근하는 방법을 고민하게 되었고, (1) RDS instance를 private subnet에 두고, (2) IAM을 이용한 접근 방법을 사용하기로 했습니다.

    RDS public access

    AWS VPC를 사용하면, public subnetprivate subnet을 구분해서 사용할 수 있습니다. public subnet은 어떠한 VPC에서도 접근이 가능한 subnet이고, private subnet은 같은 VPC 내에서만 접근이 가능한 subnet입니다. 그래서 보통 VPC를 구성할 때 public subnet에는 외부에서 접근해야 하는 리소스(ALB, EC2 등)를 두고, private subnet에는 외부에서 접근할 필요가 없는 리소스(RDS, Elasticache 등)를 두게 됩니다.

    RDS public access

    다음과 같이 RDS instance를 생성할 때 Public access를 허용할지 말지를 결정할 수 있는데, 이때 public access를 허용하지 않고 RDB instance 또한 private subnet에 두게 되면, VPC 외부에서는 RDS에 아예 접근하지 못하는 안전한 환경을 구성할 수 있습니다.

    RDS instance를 private subnet에 두게 되면, VPC 외부에서는 RDS에 접근할 수 없게 됩니다. 하지만 개발을 하거나 운영을 하기 위해서 해당 유저의 로컬 환경에서 RDS에 접근할 수 있어야 합니다. 그래서 RDS에 접근하기 위한 방법을 고민해야 하는데, 방법은 크게 2가지가 있습니다.

    1. Bastion host를 통한 접근
    2. AWS Session Manager를 통한 접근

    각각의 방법에 대해 알아보도록 하겠습니다.

    Bastion host는 VPC 외부에서 VPC 내부에 있는 리소스에 접근하기 위한 EC2를 의미합니다. Architecture를 그림으로 표현하면 다음과 같습니다.

    RDS public access

    다음과 같이 Public subnet에 Bastion host를 두고, SSH port forwarding(tunneling)을 통해 private subnet에 있는 RDS에 접근할 수 있습니다.

    Bastion host 방식으로 어떻게 접근하는지는 이 포스팅에서 자세히 다루고 있기 때문에 생략하도록 하겠습니다. :)

    Bastion host로 접근하는 방식의 장단점은 다음과 같습니다.

    1. RDS에 접근하는 방법이 간단하다.
    2. 대부분의 MySQL GUI tool에서 SSH tunneling을 지원하기 때문에, MySQL GUI tool을 사용해서 간편하게 RDS에 접근할 수 있다.
    3. Bastion host의 보안 그룹을 통해 IP별 접근을 제어할 수 있다.
    1. Bastion host에 접근할 때 SSH key를 사용하기 때문에, 편리함을 위해 SSH key를 공유할 경우 보안적으로 취약해질 수 있고, 따로 SSH key를 관리할 경우 SSH key 관리가 번거롭다.
    2. 보안 그룹으로 접근을 제어할 경우, IP가 동적으로 변경되는 경우에 대응하기 어렵다.

    보안을 위해 RDS를 private subnet에 두었기 때문에 위의 단점을 보완할 필요가 있었습니다. 그래서 아래의 AWS Session Manager를 통한 접근 방법을 사용하기로 했습니다.

    AWS Session Manager는 private 인스턴스에 SSH 또는 RDP를 사용하지 않고도 해당 인스턴스에 접근할 수 있는 서비스입니다. (AWS Session Manager도 또한 bastion host를 사용합니다.)

    Session Manager를 통해 RDS에 접근하는 방법은 해당 포스트에서 자세히 다루고 있기 때문에 생략하도록 하겠습니다. :)

    Session Manager로 접근하는 방식의 장단점은 다음과 같습니다.

    1. SSH key를 사용하지 않기 때문에, SSH key 관리가 필요 없다.
    2. Systems Manager의 Session Manager 콘솔에서 현재 접속 중인 세션을 확인할 수 있다.
    3. IAM으로 접근을 제어하기 때문에 IP가 동적으로 변경되는 경우에 대응할 수 있다.
    1. MySQL GUI tool에서 Session Manager를 통한 연결을 자체적으로 지원하지 않기 때문에, Session Manager를 통해 접근하기 위해서는 AWS CLI를 사용해서 연결한 뒤 로컬 호스트로 접근해야한다.
    2. Session Manager를 통해 열어놓은 세션에 활동이 없으면 세션이 자동으로 종료된다.

    Bastion host보다 다소 복잡하긴 하지만 보안적으로 더 안전하기 때문에 Session Manager를 사용하기로 했습니다.

    일반적으로 DB에 접근할 때는 DB에 접근하기 위한 계정을 생성하고, password를 사용해서 접근하는 방식을 사용합니다.

    DB server에 직접 user를 생성하고 이에 대한 권한을 조정하는 방식은 인원이 늘어나거나 DB server가 여러개인 경우 관리하기가 어렵습니다.

    RDS에서는 IAM Authentication을 지원하기 때문에, 각 IAM user에게 RDS에 접근할 수 있는 권한을 부여하고, 각 IAM 유저는 토큰을 발급받아 RDS에 접근할 수 있습니다.

    다음과 같은 방법으로 IAM Authentication을 사용할 수 있습니다.

    RDS console에서 RDS instance를 선택하고, Modify 버튼을 누릅니다.

    RDS public access

    Database authentication 섹션에서 Password and IAM database authentication을 체크하고 Continue 버튼을 누릅니다.

    RDS public access

    RDS instance로 접근하여 DB user를 생성합니다.

    MySQL or MariaDB

    CREATE USER jane_doe IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS';
    

    PostgreSQL

    CREATE USER jane_doe; 
    GRANT rds_iam TO jane_doe;
    

    IAM console에서 권한을 부여할 IAM user 또는 group을 선택하고, Add inline policy 버튼을 누릅니다.

    RDS public access
    RDS public access

    JSON을 클릭한 후 다음과 같은 권한을 변수명을 원하는대로 바꾸어 입력합니다.

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "rds-db:connect"
                ],
                "Resource": [
                    "arn:aws:rds-db:<AWS_REGION>:<AWS_ACCOUNT_ID>:dbuser:<dbiRESOURCE_ID>/<DB_USERNAME>"
                ]
            }
        ]
    }
    

    <AWS_REGION>은 RDS instance가 생성된 region을 입력합니다. <AWS_ACCOUNT_ID>는 AWS account id를 입력합니다. <dbiRESOURCE_ID>는 RDS instance의 resource id를 입력합니다. <DB_USERNAME>은 위에서 생성한 DB user의 username을 입력합니다. (위의 예시의 경우 jane_doe)

    3의 IAM user로 인증이 되어 있는(aws configure를 통해 인증을 해줄 수 있습니다.) 상태에서 아래의 명령어를 통해 token을 생성합니다.

    TOKEN="$(aws rds generate-db-auth-token --hostname <RDS hostname> --port <port> --region <region> --username <db-user-name>)"
    

    <RDS hostname>은 RDS instance의 hostname을 입력합니다. <region>은 RDS instance가 생성된 region을 입력합니다. <db-user-name>은 위에서 생성한 DB user의 username을 입력합니다. (위의 예시의 경우 jane_doe) <port>는 RDS instance의 port를 입력합니다. (MySQL의 경우 3306, PostgreSQL의 경우 5432)

    Session Manager를 통해 연결을 한 상태라면 다음 명령어를 통해 RDS에 접근할 수 있습니다. (MySQL의 경우)

    mysql -h 127.0.0.1 --enable-cleartext-plugin -u <DB_USERNAME> -p $TOKEN
    

    MySQL의 경우 --enable-cleartext-plugin 옵션을 추가해주어야 합니다. (mariaDB의 경우 --enable-cleartext-plugin 옵션을 추가해주지 않아도 됩니다.)

    이렇게 RDS에 안전하게 접근하기 위한 방법을 정리해보았습니다. 개발을 위한 로컬에서의 접근은 허용해주되, master user의 정보 등을 공유하지 않고도 안전하게 접근할 수 있게 되었습니다. (그렇게 되면서 퇴사의 위험도 줄어들게 되었습니다.)

    또한 IAM으로 인증 방식을 일원화하면서 입사, 퇴사 시에 IAM 계정을 추가, 삭제해주는 것만으로 권한을 간편하게 관리할 수 있게 되었습니다.

    비슷한 고민을 가지고 계신 CTO/테크리드분들께 도움이 되었으면 좋겠습니다.

    감사합니다. :)

    1. Hiding a DB instance in a VPC from the internet
    2. IAM authentication with Amazon RDS for MariaDB
    3. A DB instance in a VPC accessed by an EC2 instance in the same VPC
    4. GUI 환경을 이용하여 안전하게 Amazon RDS 또는 Amazon EC2 DB 인스턴스에 원격 접근하기
    5. IAM database authentication for MariaDB, MySQL, and PostgreSQL