MySQL 계정 관련 장애 사례

2024. 6. 20. 21:34데이터베이스

반응형

 

 

여러가지 비즈니스적 결정 사유로 인해 크고 작은 마이그레이션 작업은 DBA로서 피할 수 없는 일상 업무입니다.

 

이번에도 언제나 그렇듯, 클러스터 마이그레이션이 진행되었습니다.

서비스 규모는 조금 있었으나 다행히 중단이 가능한 서비스여서 부담은 크지 않았습니다.

 

특이점이라고 한다면 AWS 계정이 달라서 CROSS-ACCOUNT 스냅샷 공유를 통해 스냅샷을 공유받아 복원한다는 점이었으나, 몇 번 해보니 KMS 관련 세팅만 잘 하면 별 문제는 없었습니다.

 

 

이 종류의 마이그레이션을 진행하면서 MySQL 계정 관련 문제를 일으킨 서로 다른 두 가지 사례를 소개합니다. 귀여운 경험치를 가진 글입니다 호호

 

 

 


 

 

1. validate_password_policy 관련 이슈

 

실제 시나리오로 살펴보겠습니다.

 

크로스 어카운트 DB 스냅샷 공유를 마치고, restore까지 성공적으로 수행했습니다.

이제 우리 환경에 맞게 세팅을 해야할 차례인데, master 계정의 비밀번호를 전달받기보다 새로 리셋해서 사용하는 것이 좋습니다.

 

패스워드를 리셋하려면, 콘솔에서 클러스터 선택 - modify 후

아래 이미지와 같이 password를 self-managed 로 설정하고, AWS에서 제공하는 auto generate password를 사용합니다.

 

 

정말 적용할건지 변경내역을 최종적으로 확인시켜주는 콘솔 웹페이지입니다.

Master password의 Current value는 보여주지 않지만, 새로 설정될 패스워드 New Value는 보여줍니다.

`7MMkbYUAz0oqrP7r2ht6` 입니다.

7MMkbYUAz0oqrP7r2ht6

 

Modify cluster 를 선택하면 이제 aws 내부적으로 마스터 계정의 암호 재설정 작업이 진행됩니다. 

리소스를 많이 쓰는 작업이 아니기 때문에 통상적으로 1분 이내에 모두 완료되는 태스크입니다.

 

 

 

그런데.....

당시 상황을 재연한 스크린샷, Status = [Resetting-master-credentials]

 

restore 후 reset 작업을 수행했는데 통상적으로 1분 내로 수행되는 작업이 10분, 20분, 30분이 지나도 클러스터가 Available 상태로 넘어가지 않습니다. Available 상태가 아니다보니 작업을 취소하거나 다른 추가 작업을 할 수도 없습니다..

혹시 AWS측의 버그인가 싶어서 동일한 스냅샷에서 다른 클러스터를 복원해서 중복 시도 해봤지만 동일하게 상태가 멈추는 현상이 발생했습니다.

AWS 버그로 확신하고 긴급하게 AWS 서포트 티켓을 오픈했지만... 수차례 라이브 전화에도 AWS측에서는 잘 모르겠다는 눈치였습니다...

 

 

이런저런 해프닝이 있었지만 근본 원인을 말씀드리면, validate_password 플러그인이 패스워드 리세팅을 튕겨내는 것이 원인입니다.

 

 

validate_password
MySQL에서는 비밀번호 강도 및 보안 정책을 설정하고 검사하기 위한 플러그인인 `validate_password` 를 지원합니다. 이 플러그인을 사용하면 사용자 비밀번호가 강력하고 안전한지 확인할 수 있습니다. 최소 글자 수, 특수문자가 포함되어있는지 검사한다거나, 숫자 및 정책 수준을 미리 설정할 수 있습니다.

 

 

제가 일하는 환경에서는 validate_password 플러그인(8버전에서는 컴포넌트)을 DB 서버 세팅 시점에 INSTALL 해서 운영하는 것이 전사 표준입니다.

이슈의 주요 비극은 제가 그걸 모른 채 입사했다는 점이었습니다. 이미 운영중인 DB에는 해당 플러그인이 대부분 설치되어있었고, 앞으로 스냅샷 이관받을 DB에도 모두 세팅된 상태였습니다. 아래는 세팅 방법입니다.

-- MySQL 8.0 이전 버전 (aurora)
mysql> INSTALL PLUGIN validate_password SONAME 'validate_password.so';

-- MySQL 8.0 버전 (aurora)
mysql> INSTALL COMPONENT 'file://component_validate_password';

-- 정상 설치 확인
mysql> show global variables like '%validate_%';
+--------------------------------------+--------+
| Variable_name                        | Value  |
+--------------------------------------+--------+
| innodb_validate_tablespace_paths     | OFF    |
| validate_password_check_user_name    | ON     |
| validate_password_dictionary_file    |        |
| validate_password_length             | 8      |
| validate_password_mixed_case_count   | 1      |
| validate_password_number_count       | 1      |
| validate_password_policy             | MEDIUM |
| validate_password_special_char_count | 1      |
+--------------------------------------+--------+
8 rows in set (0.08 sec)


validate_password 관련 변수를 조회해보면, 각종 정책이 정의되어있는 변수를 볼 수 있습니다.

 

그 중, `validate_password_policy` 변수가 오늘 이슈의 주인공이었습니다.

https://dev.mysql.com/doc/refman/8.4/en/validate-password-options-variables.html#sysvar_validate_password.policy

 

 

변수의 값인 `MEDIUM` policy는 아래와 같습니다.

1. 최소 길이: 비밀번호는 최소 X자 이상이어야 합니다.
2. 문자 유형: 비밀번호는 다음 네 가지 유형을 포함해야 합니다.
• 소문자 (a-z)
• 대문자 (A-Z)
• 숫자 (0-9)
• 특수 문자 (!, @, #, $, 등)
3. 사전 기반 비밀번호 사용 금지: 비밀번호는 사전에 있는 단어를 포함하지 않아야 합니다. 

 

한마디로 기본 설정으로는 8글자 이상의 대소문자, 숫자, 특수문자가 포함된 문자열이면서 사전 단어를 포함하면 안됩니다.

 

예시를 들자면, ABcd123@ 같은 문자열입니다. 

 

아까 AWS에서 auto-generate 해준 패스워드를 다시 끌고오자면 `7MMkbYUAz0oqrP7r2ht6` 입니다.

MEDIUM 강도에 적합하지 않아 내부적으로 resetting 태스크가 실패하는 것이었습니다.

결국 auto-generate 암호를 사용하지 않고 따로 커스텀하게 특수문자, 대소문자, 숫자를 포함한 암호를 적용하는 것으로 해결합니다.

 

 

막상 원인을 찾고 들여다보니 해결하기 참 쉬운 이슈죠?!

특수문자만 하나 추가했어도 작업이 몇시간씩 지연되는 일이 없었을텐데...

aws를 너무 믿었고 회사 DB환경에 대한 이해가 적었어서 생긴 이슈입니다.

 

 

 


 

2. 마스터유저 ip관련 이슈

 

다음 이슈도 다시 돌아보면 별 것 아닌 일이었습니다.

 

 

AWS RDS, Aurora MySQL에서는 처음 서버를 생성하면 MasterUser가 존재하고 host(ip) 대역은 %로 설정되어있습니다.

-- 계정 조회
mysql> select user,host from mysql.user;

+------------------------+------------------------+
| user                   | host                   |
+------------------------+------------------------+
| AWS_COMPREHEND_ACCESS  | %                      |
| AWS_LAMBDA_ACCESS      | %                      |
| AWS_LOAD_S3_ACCESS     | %                      |
| AWS_SAGEMAKER_ACCESS   | %                      |
| AWS_SELECT_S3_ACCESS   | %                      |
| mysql.infoschema       | localhost              |
| mysql.session          | localhost              |
| mysql.sys              | localhost              |
| rdsadmin               | localhost              |
| root                   | %                      | <<<<<
+------------------------+------------------------+

 

host가 % 라면 DB 서버 입장에서 어느 네트워크에서나 접근할 수 있다는 의미입니다.

운영이나 기타 다른 이유로 root 계정을 삭제하는 경우에는 앞서 살펴봤던 것 처럼 콘솔에서 마스터 계정의 패스워드를 리셋하는 과정을 거치면 자동으로 복원됩니다.

 

 

1번 시나리오와 동일하게 크로스 어카운트 DB 스냅샷 공유를 마치고, restore까지 성공적으로 수행했습니다.

 

이관 전 미리 전달받은 특이한 점은 기존에 root 계정을 백엔드 서버의 어플리케이션 용 계정으로 사용하고 있다는 점이었습니다.

그래서 root 계정의 host가 `%`가 아닌 `123.%` 와 같이 커스텀하게 설정되어있었습니다. app에 속한 네트워크 망에서만 접근을 허용해야 하기 때문에 이렇게 사용한 것입니다.

-- 이관 전 예상되는 계정 조회 결과
mysql> select user,host from mysql.user;

+------------------------+------------------------+
| user                   | host                   |
+------------------------+------------------------+
| AWS_COMPREHEND_ACCESS  | %                      |
| AWS_LAMBDA_ACCESS      | %                      |
| AWS_LOAD_S3_ACCESS     | %                      |
| AWS_SAGEMAKER_ACCESS   | %                      |
| AWS_SELECT_S3_ACCESS   | %                      |
| mysql.infoschema       | localhost              |
| mysql.session          | localhost              |
| mysql.sys              | localhost              |
| rdsadmin               | localhost              |
| root                   | 123.%                  | <<<<<
+------------------------+------------------------+

 

이관하면서 어차피 app의 네트워크 환경에서만 접근가능한 상황이기에 마스터계정 패스워드 리세팅으로 root 계정을 초기화 시도합니다.

 

...

 

콘솔에서 정상적으로 리세팅을 완료했습니다.

 

이제 DB 서버에 접근을 시도했는데 'Access Denied' 오류가 발생합니다.

-- 당시 상황 재연
% mysql -uroot -hDB엔드포인트 -p
Enter password:
ERROR 1045 (28000): Access denied for user 'root'@'123.123.123.123' (using password: YES)

 

 


 

 

access denied 는 크게 세 가지 상황에서 발생합니다.

  1. 계정의 패스워드와 입력한 패스워드가 일치하지 않을 때
  2. 계정의 host와 접속하는 환경의 host가 일치하지 않을 때
  3. 입력한 계정이 존재하지 않을 때

 

방금 마스터유저를 복구했으니 2번과 3번은 제외합니다.

 

비밀번호를 잘못 입력했겠거니 하고 마스터유저 패스워드를 재설정합니다.

그래도 접근이 안돼서 키보드에 문제가 있나 싶었습니다. 수차례 재시도 및 Secret Manager 를 통한 암호 초기화도 수행해봅니다.

암호 관리 서비스 secret manager

 

물론 여전히 안됩니다...

 

스냅샷을 다시 복구하고, 여러 차례 재시도했는데도 안되는 즈음에는

어김없이 AWS 버그를 의심합니다...^^ 

 

이미 이관작업에 약속된 시간은 많이 지났습니다.

계정 재설정이 내부 버그로 인해 정상적으로 설정되지 않는 것 같아,

혹여나 하는 마음에 기존 aws 계정을 관리하시던 분께 root 계정 비밀번호를 전달받아 접근을 시도하니 접근이 가능했습니다.

 

 

접속 후 계정목록을 다시 조회해봤습니다.

-- 접근 성공 후 계정 조회
mysql> select user,host from mysql.user;

+------------------------+------------------------+
| user                   | host                   |
+------------------------+------------------------+
| AWS_COMPREHEND_ACCESS  | %                      |
| AWS_LAMBDA_ACCESS      | %                      |
| AWS_LOAD_S3_ACCESS     | %                      |
| AWS_SAGEMAKER_ACCESS   | %                      |
| AWS_SELECT_S3_ACCESS   | %                      |
| mysql.infoschema       | localhost              |
| mysql.session          | localhost              |
| mysql.sys              | localhost              |
| rdsadmin               | localhost              |
| root                   | 123.%                  | <<<<<
| root                   | %                      | <<<<<
+------------------------+------------------------+

 

root 계정이 2건 조회됩니다. 

 

아차 싶었습니다. 

MySQL에서는 계정을 user 로만 구분하지 않고, user+host 형태의 `root`@`123.%` 의 단위로 구분짓습니다.
`root` 라는 문자열로만 id를 구성하는 것이 아니라 host까지 합친 형태로 id를 구성하는 것이죠 (https://dev.mysql.com/doc/refman/8.4/en/account-names.html)

 

 

 

 

이번에도 싱거운 결말이지만

 

저는 AWS 측에서 마법처럼 `root`@`123.%` 계정을 초기화 해서 `root`@`%` 계정으로 만들어주는 줄로 알고 있었습니다.

하지만 `root`@`123.%` 계정은 username은 같지만 사실상 master 계정이 아니기 때문에 기존 계정은 그대로 두고 새로운 계정을 생성해 패스워드를 삽입하고 있었던 것입니다.

 

하지만 MySQL 로그인시 username이 같다면 해당 네트워크에서 host가 더 자세한 것으로 붙기 때문에 새로 생성된 root 계정은 붙을 수 없었던 것입니다.

 

문제를 파악하고 나니 해결 자체는 쉬웠습니다. 

(미리 `root`@`123.%` 계정을 삭제한 후에 스냅샷을 다시 이관받는 흐름으로 일을 진행했습니다)

 

끝~~

 

 

 

 

반응형