- 서버 환경 : Ubuntu 24.04 LTS
- MySQL 버전 : 8.0.40
MySQL 설치
설치
MySQL을 먼저 설치한다.
sudo apt update && sudo apt upgrade -y
sudo apt install mysql-server -y
설치된 버전을 확인하여 정상적으로 설치되었는지 확인한다.
mysql --version
나는 위 명령어를 실행했을 때 mysql Ver 8.0.40-0ubuntu0.24.04.1 for Linux on x86_64 ((Ubuntu)) 이 결과가 나왔다.
보안 스크립트 설정
보안을 강화하기 위해 아래의 보안 스크립트를 실행한다.
sudo mysql_secure_installation
Securing the MySQL server deployment. Connecting to MySQL using a blank password. VALIDATE PASSWORD COMPONENT can be used to test passwords and improve security. It checks the strength of password and allows the users to set only those passwords which are secure enough. Would you like to setup VALIDATE PASSWORD component? Press y|Y for Yes, any other key for No
- 복잡한 비밀번호를 하려면 Yes, 아니라면 No
- 나는 보안을 강화하기위해 y를 눌렀다.
y를 눌렀다면 다음과 같은 비밀번호 강도 레벨을 선택해야한다.
There are three levels of password validation policy: LOW Length >= 8 MEDIUM Length >= 8, numeric, mixed case, and special characters STRONG Length >= 8, numeric, mixed case, special characters and dictionary file Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 1
- Level 0 (LOW):비밀번호의 길이만 체크한다.
- Level 1 (MEDIUM):비밀번호 길이와 함께 숫자, 대문자, 소문자, 특수문자 중 일부를 포함하도록 요구한다.
- Level 2 (STRONG):비밀번호 길이와 문자 조합을 더 엄격하게 요구하며, 사전에 있는 단어도 허용하지 않는다.
나는 2레벨로 설정하였다.
By default, a MySQL installation has an anonymous user, allowing anyone to log into MySQL without having to have a user account created for them. This is intended only for testing, and to make the installation go a bit smoother. You should remove them before moving into a production environment. Remove anonymous users? (Press y|Y for Yes, any other key for No)
- 익명 사용자 계정을 제거할지 묻는 것이다.
- 익명 사용자 계정은 별도의 인증 없이 데이터베이스에 접속할 수 있게 해주므로, 프로덕션 환경에서는 보안을 위해 반드시 제거하는 것이 권장된다.
- y를 입력하여 익명 사용자 계정을 제거한다.
Normally, root should only be allowed to connect from 'localhost'. This ensures that someone cannot guess at the root password from the network. Disallow root login remotely? (Press y|Y for Yes, any other key for No)
- root 사용자 계정에 대해 원격 접속을 비활성화할지를 묻는 것이다.
- 보안 강화를 위해 원격 root 접속은 비활성화하는 것이 권장된다.
- y를 선택하면 mysql -u root -p를 통한 접근도 불가능하다.
- root 계정명을 이용해 비밀번호를 브루트포스 방법으로 공격당할 수 있으니 당연히 y을 입력하여 원격 root 접속을 거부한다.
By default, MySQL comes with a database named 'test' that anyone can access. This is also intended only for testing, and should be removed before moving into a production environment. Remove test database and access to it? (Press y|Y for Yes, any other key for No)
메시지는 MySQL 초기 설치 시 생성되는 기본 test 데이터베이스를 삭제할지 묻는 것이다.
이 데이터베이스는 보안상 취약점이 될 수 있으므로, 프로덕션 환경에서는 삭제하는 것이 권장된다.
MySQL의 test 데이터베이스는 기본적으로 GRANT ALL 권한이 설정되어 있어 인증된 사용자가 다음 작업을 수행할 수 있다.
- 데이터베이스에 데이터를 삽입.
- 테이블 생성 및 삭제.
- 임의의 쿼리 실행.
y를 눌러 테스트 데이터베이스 삭제
Reloading the privilege tables will ensure that all changes made so far will take effect immediately. Reload privilege tables now? (Press y|Y for Yes, any other key for No)
- 현재까지 적용된 보안 설정(사용자 삭제, 권한 변경, 데이터베이스 삭제 등)을 즉시 활성화할지 묻는 것이다.
- 이 작업은 모든 변경 사항을 실시간으로 반영하기 때문에 y를 선택하는 것이 권장된다.
- y를 눌러 보안 설정 즉시 활성화
방화벽 및 네트워크 보안
UFW 를 이용한 포트 개방
UFW를 이용해 3306번 포트는 열어둔다.(나는 외부에서 DataGrip이나 그런 툴을 이용할것이기 때문에)
sudo ufw allow 3306/tcp
sudo ufw reload
SSL/TLS 활성화
MySQL에서 SSL/TLS를 활성화해야 하는 이유는 주로 보안 때문이다.
SSL/TLS는 클라이언트와 MySQL 서버 간 통신을 암호화하여 데이터 전송 중 발생할 수 있는 여러 보안 문제를 방지한다.
1. 인증서 발급
나는 가비아를 이용해 도메인을 발급받았다.
- @ : 기본 도메인
- api : api용 도메인
이렇게 등록을 하고 약 5분동안 DNS가 전파되도록 기다린 뒤 SSL/TLS 인증서 발급을 진행한다.
이 방식은 nginx와 Certbot를 이용한다.
sudo apt update
sudo apt install nginx
sudo apt install certbot
sudo apt install python3-certbot-nginx -y
sudo certbot --nginx -d <기본 도메인> -d <api 도메인>
인증받을 도메인이 2개라서 나는 위 처럼되어있는것이고, 하나라면 줄이고, 더 많다면 더 넣으면 된다.
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices)
(Enter 'c' to cancel):
- 자신의 이메일 입력
Please read the Terms of Service at https://letsencrypt.org/documents/LE-SA-v1.4-April-3-2024.pdf. You must agree in order to register with the ACME server. Do you agree?
- 약관에 동의하냐는 말 -> y 입력
Would you be willing, once your first certificate is successfully issued, to share your email address with the Electronic Frontier Foundation, a founding partner of the Let's Encrypt project and the non-profit organization that develops Certbot? We'd like to send you email about our work encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- 이메일 주소를 EFF와 공유하겠냐는 말, 공유를 하지 않아도 인증서 발급에 전혀 문제가 되지 않음 -> n 입력
2. 인증서 파일 이동 및 권한 설정
인증서 파일을 복사 및 권한 설정을 진행한다.
sudo cp /etc/letsencrypt/live/도메인/{chain.pem,fullchain.pem,privkey.pem} /etc/mysql/
sudo chmod 640 /etc/letsencrypt/live/도메인/{chain.pem,fullchain.pem,privkey.pem}
sudo chmod 750 /etc/letsencrypt/live/도메인
sudo chmod 640 /etc/mysql/{chain.pem,fullchain.pem,privkey.pem}
sudo chown mysql:mysql /etc/mysql/{chain.pem,fullchain.pem,privkey.pem}
- chmod 640은 파일의 권한을 소유자만 읽기/쓰기 가능하도록 설정한다. 소유 그룹은 읽기만 가능하다.
- chmod 750은 파일의 권한을 소유자만 읽기/쓰기/실행 가능하도록 설정한다. 소유 그룹은 읽기 및 실행만 가능하다.
- chown mysql:mysql: 파일의 소유자를 mysql, 그룹을 mysql로 변경
3. 인증서 자동 갱신 설정
인증서가 자동 갱신되면 mysql은 기존 인증서를 참조하기 때문에 문제가 발생한다
이를 위해 자동화가 필요하다.
Certbot이 SSL 인증서를 갱신한 후 MySQL을 재시작하기 위해 스크립트를 작성한다.
sudo nano /etc/letsencrypt/renewal-hooks/deploy/renew_action.sh
아래의 내용을 입력한다.
#!/bin/bash
{
echo "===== $(date '+%Y-%m-%d %H:%M:%S') : Renew action started ====="
systemctl restart nginx
rm -f /etc/mysql/{chain.pem,fullchain.pem,privkey.pem}
cp /etc/letsencrypt/live/seungwook.com/{chain.pem,fullchain.pem,privkey.pem} /etc/mysql/
chown mysql:mysql /etc/mysql/{chain.pem,fullchain.pem,privkey.pem}
chmod 640 /etc/mysql/{chain.pem,fullchain.pem,privkey.pem}
systemctl restart mysql
echo "===== $(date '+%Y-%m-%d %H:%M:%S') : Renew action completed ====="
} &>> /var/log/renew_action.log
실행 권한을 추가한다.
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/renew_action.sh
로그를 확인하려면 아래의 명령어를 입력
sudo cat /var/log/renew_action.log
4. mysql의 세부 설정 정보를 변경
(개인 사정에 맞게 설정)
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
아래의 내용을 입력한다
[mysqld]
# SSL/TLS 관련
ssl-ca=/etc/mysql/chain.pem
ssl-cert=/etc/mysql/fullchain.pem
ssl-key=/etc/mysql/privkey.pem
#모든 통신을 SSL/TSL 을 통하도록 강제
require_secure_transport=ON
# 원격 접속 아이피 설정(모든 아이피 접속 가능)
bind-address = 0.0.0.0
# UTC 시간대보다 9시간을 추가함(한국 시간대로 변경)
default-time-zone="+09:00"
# "log_warnings=1" 이 내용 삭제하고 아래의 내용 삽입(이 설정은 8버전 이상부턴 적용안됨)
log_error_verbosity = 2
아래의 명령어로 시스템의 시간대를 한국으로 변경한다.
sudo timedatectl set-timezone Asia/Seoul
MySQL을 재시작한다.
sudo systemctl restart mysql
정상적으로 변경되었는지 확인한다.
system_time_zone이 KST면 잘 된것이다.
SELECT @@global.time_zone,@@session.time_zone,@@system_time_zone;
5. MySQL의 메인 설정 파일을 변경
sudo nano /etc/mysql/my.cnf
아래의 내용을 입력한다.
[mysqld]
# 로그 타임 스탬프의 시간대를 시스템 기본값으로 설정
log_timestamps = SYSTEM
모든 설정을 완료했으면 mysql을 재시작해서 마무리한다.
sudo systemctl restart mysql
SSL/TSL 변경 확인
SHOW VARIABLES LIKE 'have_ssl';
yes가 설정되어있어야 한다.
SHOW VARIABLES LIKE '%ssl%';
표시된 3개의 항목이 있어야 한다.
서버 내부 말고 외부에서 mysql을 접속한 뒤 아래의 쿼리를 실행해본다
SHOW STATUS LIKE 'Ssl_cipher';
외부에서 mysql에 접속하고 위 쿼리를 실행하면 위와 같이 결과가 나오면 정상적으로 모든 과정을 마친 것이다.
이는 SSL/TLS 연결이 성공적으로 활성화되었고 안전한 암호화 알고리즘이 사용되고 있음을 의미한다.
root 계정 보안 강화
이전에 root 계정을 원격으로 접속하는 것을 막아뒀다.
하지만 기본 보안 플러그인이 auth_socket이기 때문에 sudo mysql 이라는 명령어만 입력하면 바로 접속할 수 있다.
이를 caching_sha2_password로 변경하면 좋다.
ALTER USER 'root'@'localhost' IDENTIFIED WITH 'caching_sha2_password' BY '루트비밀번호' REQUIRE SSL;
FLUSH PRIVILEGES;
이후 root 권한으로 로컬호스트에서 접속하려면 아래의 명령어를 입력하고, 비밀번호를 입력해야 접속할 수 있다.
mysql -u root -p --ssl-mode=REQUIRED
그리고 특정 데이터베이스만 관리하는 계정을 따로 생성해놓는 것이 좋다.
CREATE DATABASE 특정데이터베이스;
CREATE USER '계정명'@'%' IDENTIFIED WITH 'caching_sha2_password' BY '비밀번호' REQUIRE SSL;
GRANT SELECT, INSERT, UPDATE, DELETE ON 특정데이터베이스.* TO '계정명'@'%';
FLUSH PRIVILEGES;
레퍼런스
'서버 구축기' 카테고리의 다른 글
[GitHub Actions] Self-Hosted Runner (0) | 2025.01.09 |
---|---|
[Ubuntu] GeoIP 를 이용한 해외 IP SSH 접속 차단 (0) | 2025.01.02 |
[Nginx] Nginx 기본 보안 설정 (1) | 2024.12.28 |
[Ubuntu] 외장 SSD ext4 초기화 + 마운트 (0) | 2024.12.26 |
[Ubuntu] ssh 원격접속 보안 강화 (0) | 2024.11.29 |