- 09-security: 서버 계정 관리 섹션 신규 추가 (Linux/MySQL 계정 현황, 생성 절차, 잠금/해제) - 09-security: SSH 인증 정책 정리 (패스워드 허용, root 외부 접근 차단) - 01-server-overview: 3개 서버 사용자 정보 업데이트 (hskwon/pro/kkk) - 01-server-overview: 운영 DB 사용자 테이블에 pro, kkk 추가
336 lines
9.5 KiB
Markdown
336 lines
9.5 KiB
Markdown
# 9. 보안 관리
|
|
|
|
[목차로 돌아가기](./README.md)
|
|
|
|
---
|
|
|
|
## 서버 계정 관리
|
|
|
|
### Linux 계정 현황
|
|
|
|
| 계정 | 이름 | sam-dev | sam-prod | sam-cicd | 비고 |
|
|
|------|------|---------|----------|----------|------|
|
|
| hskwon | 권혁성 | sudo, users | sudo, users, webservice | sudo, users, webservice | 개발팀장 |
|
|
| pro | 김보곤 | develop, sudo, users | pro, sudo, users, webservice (잠금) | pro, sudo, users, webservice (잠금) | 개발실장 |
|
|
| kkk | 강영보 | develop, sudo, users | pro, sudo, users, webservice (잠금) | pro, sudo, users, webservice (잠금) | 개발자 (2026-03-13 생성) |
|
|
|
|
### MySQL 계정 현황
|
|
|
|
| 계정 | sam-dev | sam-prod | sam-cicd | 비고 |
|
|
|------|---------|----------|----------|------|
|
|
| hskwon | ALL *.* (auth_socket) | ALL *.* (auth_socket) | ALL *.* (auth_socket) | 개발팀장 |
|
|
| codebridge | - | sam, sam_stage, sam_stat, codebridge | - | 앱 사용자 |
|
|
| pro | chandj, sam, samdb | codebridge, sam, sam_stage, sam_stat (잠금) | codebridge, gitea, sam, sam_stage, sam_stat (잠금) | 개발실장 |
|
|
| kkk | chandj, sam, samdb | codebridge, sam, sam_stage, sam_stat (잠금) | codebridge, gitea, sam, sam_stage, sam_stat (잠금) | 개발자 (2026-03-13 생성) |
|
|
| sam_backup | - | - (운영 DB에서 원격 접근) | sam, sam_stat (원격 백업) | CI/CD 백업 |
|
|
|
|
### 신규 계정 생성 절차
|
|
|
|
```bash
|
|
# 1. 개발서버 (전체 권한)
|
|
ssh sam-dev "sudo useradd -m -s /bin/bash -G develop,sudo,users -c '이름' 계정명"
|
|
ssh sam-dev "echo '계정명:비밀번호' | sudo chpasswd"
|
|
|
|
# 2. 운영/CICD (잠금 상태로 생성)
|
|
ssh sam-prod "sudo useradd -m -s /bin/bash -G pro,sudo,users,webservice -c '이름' 계정명"
|
|
ssh sam-prod "echo '계정명:비밀번호' | sudo chpasswd"
|
|
ssh sam-prod "sudo usermod -L 계정명" # Linux 잠금
|
|
|
|
ssh sam-cicd "sudo useradd -m -s /bin/bash -G pro,sudo,users,webservice -c '이름' 계정명"
|
|
ssh sam-cicd "echo '계정명:비밀번호' | sudo chpasswd"
|
|
ssh sam-cicd "sudo usermod -L 계정명" # Linux 잠금
|
|
|
|
# 3. DB 계정 — 개발 (활성)
|
|
ssh sam-dev "mysql -u root -p'<비밀번호>' -e \"
|
|
CREATE USER '계정명'@'localhost' IDENTIFIED BY '비밀번호';
|
|
GRANT ALL PRIVILEGES ON chandj.* TO '계정명'@'localhost';
|
|
GRANT ALL PRIVILEGES ON sam.* TO '계정명'@'localhost';
|
|
GRANT ALL PRIVILEGES ON samdb.* TO '계정명'@'localhost';
|
|
FLUSH PRIVILEGES;\""
|
|
|
|
# 4. DB 계정 — 운영/CICD (잠금)
|
|
ssh sam-prod "sudo mysql -e \"
|
|
CREATE USER '계정명'@'localhost' IDENTIFIED BY '비밀번호';
|
|
GRANT ALL PRIVILEGES ON codebridge.* TO '계정명'@'localhost';
|
|
GRANT ALL PRIVILEGES ON sam.* TO '계정명'@'localhost';
|
|
GRANT ALL PRIVILEGES ON sam_stage.* TO '계정명'@'localhost';
|
|
GRANT ALL PRIVILEGES ON sam_stat.* TO '계정명'@'localhost';
|
|
ALTER USER '계정명'@'localhost' ACCOUNT LOCK;
|
|
FLUSH PRIVILEGES;\""
|
|
|
|
ssh sam-cicd "sudo mysql -e \"
|
|
CREATE USER '계정명'@'localhost' IDENTIFIED BY '비밀번호';
|
|
GRANT ALL PRIVILEGES ON codebridge.* TO '계정명'@'localhost';
|
|
GRANT ALL PRIVILEGES ON gitea.* TO '계정명'@'localhost';
|
|
GRANT ALL PRIVILEGES ON sam.* TO '계정명'@'localhost';
|
|
GRANT ALL PRIVILEGES ON sam_stage.* TO '계정명'@'localhost';
|
|
GRANT ALL PRIVILEGES ON sam_stat.* TO '계정명'@'localhost';
|
|
ALTER USER '계정명'@'localhost' ACCOUNT LOCK;
|
|
FLUSH PRIVILEGES;\""
|
|
```
|
|
|
|
### 계정 잠금/해제
|
|
|
|
```bash
|
|
# Linux 잠금 해제
|
|
sudo usermod -U 계정명
|
|
|
|
# Linux 잠금
|
|
sudo usermod -L 계정명
|
|
|
|
# MySQL 잠금 해제
|
|
sudo mysql -e "ALTER USER '계정명'@'localhost' ACCOUNT UNLOCK;"
|
|
|
|
# MySQL 잠금
|
|
sudo mysql -e "ALTER USER '계정명'@'localhost' ACCOUNT LOCK;"
|
|
```
|
|
|
|
---
|
|
|
|
## SSH 키 관리
|
|
|
|
### SSH 인증 정책
|
|
|
|
| 서버 | 패스워드 인증 | root 외부 접근 | SSH 키 | 비고 |
|
|
|------|:----------:|:----------:|:------:|------|
|
|
| sam-dev | ✅ 허용 | ❌ 차단 | ✅ 허용 | 개발서버 |
|
|
| sam-prod | ✅ 허용 | ❌ 차단 | ✅ 허용 | 운영서버 |
|
|
| sam-cicd | ✅ 허용 | ❌ 차단 | ✅ 허용 | CICD서버 |
|
|
|
|
> 일반 계정은 패스워드 접근 허용, root는 외부 접근 완전 차단 (`PermitRootLogin no`)
|
|
|
|
```bash
|
|
# SSH 설정 확인
|
|
sudo grep -E "^(PasswordAuthentication|PermitRootLogin|PubkeyAuthentication)" /etc/ssh/sshd_config
|
|
# 현재 설정:
|
|
# PasswordAuthentication yes
|
|
# PermitRootLogin no # root 외부 접근 완전 차단
|
|
# PubkeyAuthentication yes
|
|
```
|
|
|
|
### [운영] 공개키 관리
|
|
|
|
```bash
|
|
cat /home/hskwon/.ssh/authorized_keys
|
|
|
|
# 새 공개키 추가
|
|
echo "새_공개키_내용" >> /home/hskwon/.ssh/authorized_keys
|
|
|
|
# SSH 설정 변경 후 반드시 재시작
|
|
sudo systemctl restart sshd
|
|
```
|
|
|
|
### [CI/CD] 공개키 관리
|
|
|
|
```bash
|
|
cat /home/hskwon/.ssh/authorized_keys
|
|
echo "ssh-ed25519 AAAA... user@host" >> /home/hskwon/.ssh/authorized_keys
|
|
chmod 600 /home/hskwon/.ssh/authorized_keys
|
|
```
|
|
|
|
### [CI/CD] Jenkins SSH 키
|
|
|
|
```bash
|
|
# 경로: /var/lib/jenkins/.ssh/id_ed25519
|
|
# 공개키는 운영서버/개발서버 hskwon authorized_keys에 등록됨
|
|
sudo cat /var/lib/jenkins/.ssh/id_ed25519.pub
|
|
|
|
# 연결 테스트
|
|
sudo -u jenkins ssh -i /var/lib/jenkins/.ssh/id_ed25519 hskwon@211.117.60.189 "hostname && date"
|
|
sudo -u jenkins ssh -i /var/lib/jenkins/.ssh/id_ed25519 hskwon@114.203.209.83 "hostname && date"
|
|
|
|
# known_hosts 갱신 (호스트 키 변경 시)
|
|
sudo -u jenkins ssh-keygen -R 211.117.60.189
|
|
sudo -u jenkins ssh-keyscan -H 211.117.60.189 >> /var/lib/jenkins/.ssh/known_hosts
|
|
```
|
|
|
|
---
|
|
|
|
## UFW (방화벽) 관리
|
|
|
|
### [운영] 규칙
|
|
|
|
| 포트 | 허용 범위 | 용도 |
|
|
|------|-----------|------|
|
|
| 22 | Anywhere | SSH |
|
|
| 80 | Anywhere | HTTP |
|
|
| 443 | Anywhere | HTTPS |
|
|
| 9100 | 110.10.147.46 only | node_exporter |
|
|
| 3306 | 110.10.147.46 only | MySQL 백업 |
|
|
|
|
### [CI/CD] 규칙
|
|
|
|
| 포트 | 허용 범위 | 용도 |
|
|
|------|-----------|------|
|
|
| 22 | Anywhere | SSH |
|
|
| 80 | Anywhere | HTTP |
|
|
| 443 | Anywhere | HTTPS |
|
|
|
|
### [개발] 규칙
|
|
|
|
| 포트 | 허용 범위 | 용도 |
|
|
|------|-----------|------|
|
|
| 22 | Anywhere | SSH |
|
|
| 80 | Anywhere | HTTP |
|
|
| 443 | Anywhere | HTTPS |
|
|
| 3000 | Anywhere | Gitea |
|
|
|
|
> MySQL(3306), Apache(8080), Next.js(3001) 등은 외부 차단됨
|
|
|
|
### 공통 명령어
|
|
|
|
```bash
|
|
# 규칙 확인
|
|
sudo ufw status numbered
|
|
|
|
# 규칙 추가
|
|
sudo ufw allow from IP주소 to any port 포트번호
|
|
|
|
# 규칙 삭제
|
|
sudo ufw delete 규칙_번호
|
|
|
|
# 변경사항은 즉시 적용 (재시작 불필요)
|
|
```
|
|
|
|
**주의:** SSH (22/tcp) 규칙 삭제 금지
|
|
|
|
```bash
|
|
# 변경 전 백업 (CI/CD)
|
|
sudo ufw status numbered > /tmp/ufw-backup-$(date +%Y%m%d).txt
|
|
```
|
|
|
|
---
|
|
|
|
## SSL 인증서 관리
|
|
|
|
```bash
|
|
# 인증서 만료일 전체 확인
|
|
sudo certbot certificates
|
|
|
|
# 자동 갱신 타이머 확인
|
|
sudo systemctl status certbot.timer
|
|
|
|
# 새 도메인 인증서 발급
|
|
sudo certbot --nginx -d 새도메인 --email develop@codebridge-x.com
|
|
|
|
# 수동 갱신
|
|
sudo certbot renew
|
|
|
|
# 인증서 삭제
|
|
sudo certbot delete --cert-name 도메인명
|
|
```
|
|
|
|
---
|
|
|
|
## fail2ban 관리
|
|
|
|
```bash
|
|
# jail 상태 확인
|
|
sudo fail2ban-client status
|
|
sudo fail2ban-client status sshd
|
|
|
|
# IP 차단 해제
|
|
sudo fail2ban-client set sshd unbanip IP주소
|
|
|
|
# jail 재시작
|
|
sudo fail2ban-client restart sshd
|
|
```
|
|
|
|
### 화이트리스트 설정
|
|
|
|
**현재 설정:**
|
|
|
|
| 서버 | ignoreip |
|
|
|------|----------|
|
|
| 운영 | 127.0.0.1/8, 110.10.147.46 (CI/CD) |
|
|
| CI/CD | 127.0.0.1/8, 211.117.60.189 (운영) |
|
|
| 개발 | 127.0.0.1/8, 110.10.147.46 (CI/CD), 211.117.60.189 (운영) |
|
|
|
|
```bash
|
|
# /etc/fail2ban/jail.local
|
|
[DEFAULT]
|
|
ignoreip = 127.0.0.1/8 110.10.147.46 211.117.60.189
|
|
|
|
# 변경 후
|
|
sudo systemctl restart fail2ban
|
|
```
|
|
|
|
---
|
|
|
|
## [운영] .env 파일 보안
|
|
|
|
> **주의:** `.env` 권한은 반드시 `640` (`-rw-r-----`)이어야 합니다.
|
|
> PHP-FPM은 `www-data` 사용자(webservice 그룹)로 실행되므로 그룹 읽기 권한이 필요합니다.
|
|
> `600`으로 설정하면 PHP-FPM이 .env를 읽지 못해 **전체 서비스 500 에러**가 발생합니다.
|
|
> (실제 장애 사례: 2026-03-03, 08-troubleshooting.md 참조)
|
|
|
|
```bash
|
|
# 권한 확인 (640이어야 함 — 소유자 rw + 그룹 r)
|
|
ls -la /home/webservice/api/shared/.env
|
|
ls -la /home/webservice/mng/shared/.env
|
|
ls -la /home/webservice/sales/.env
|
|
|
|
# 권한 수정
|
|
chmod 640 /home/webservice/api/shared/.env
|
|
chmod 640 /home/webservice/mng/shared/.env
|
|
chmod 640 /home/webservice/sales/.env
|
|
```
|
|
|
|
### vi 편집 시 권한 변경 방지
|
|
|
|
`vi`로 파일을 편집하면 새 파일로 교체되면서 권한이 `600`으로 초기화될 수 있습니다.
|
|
이를 방지하기 위해 서버 계정의 `~/.vimrc`에 아래 설정을 추가합니다:
|
|
|
|
```bash
|
|
# 원본 파일에 직접 덮어쓰기 (권한 유지)
|
|
echo "set backupcopy=yes" >> ~/.vimrc
|
|
```
|
|
|
|
**적용 현황 (2026-03-03):**
|
|
- sam-prod: hskwon, pro 계정 적용 완료
|
|
- sam-cicd: hskwon, pro 계정 적용 완료
|
|
|
|
---
|
|
|
|
## [운영] Redis 보안
|
|
|
|
Redis는 127.0.0.1에만 바인딩되어 외부 접근 불가.
|
|
|
|
```bash
|
|
redis-cli config get bind # "127.0.0.1 ::1"
|
|
grep "^bind" /etc/redis/redis.conf
|
|
```
|
|
|
|
---
|
|
|
|
## [운영] MySQL 사용자 관리
|
|
|
|
```bash
|
|
# 사용자 목록
|
|
sudo mysql -e "SELECT user, host, plugin FROM mysql.user;"
|
|
|
|
# 비밀번호 변경
|
|
sudo mysql -e "ALTER USER 'codebridge'@'localhost' IDENTIFIED BY '새_비밀번호'; FLUSH PRIVILEGES;"
|
|
|
|
# 외부 접근 사용자 확인
|
|
sudo mysql -e "SELECT user, host FROM mysql.user WHERE host != 'localhost';"
|
|
```
|
|
|
|
---
|
|
|
|
## [CI/CD] Jenkins 보안
|
|
|
|
- Jenkins Credentials에서만 민감 정보 관리
|
|
- Jenkinsfile에 직접 비밀번호 기재 금지
|
|
- 관리자: hskwon
|
|
- 사용자 추가: Jenkins 관리 > Users > Create User
|
|
|
|
---
|
|
|
|
## [CI/CD] Gitea 접근 제어
|
|
|
|
- 회원가입 비활성화 (DISABLE_REGISTRATION = true)
|
|
- 로그인 필수 (REQUIRE_SIGNIN_VIEW = true)
|
|
- API 토큰 기반 인증
|
|
- 사용자 추가: CLI 또는 관리자 웹 UI
|