Improve security documentation based on Context7 review
aws-ses-setup.md: - Add SPF records for email authentication - Add DMARC policy configuration - Add bounce/complaint handling with SNS - Add DNS verification commands n8n-setup-guide.md: - Use official Docker registry (docker.n8n.io) - Add N8N_ENCRYPTION_KEY requirement - Add N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS - Add N8N_PUBLIC_API_DISABLED option - Add security headers to nginx config - Add healthcheck configuration gitea-setup.md: - Add password policy (MIN_PASSWORD_LENGTH, PASSWORD_COMPLEXITY) - Add argon2 password hashing - Add DISABLE_GIT_HOOKS for security - Add Docker Secrets configuration - Add file-based secret management (SECRET_KEY_URI) - Add REVERSE_PROXY_TRUSTED_PROXIES setting Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
106
aws-ses-setup.md
106
aws-ses-setup.md
@@ -36,6 +36,20 @@ AWS SES(Simple Email Service)를 사용하여 서버에서 메일을 발송하
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 이메일 인증 (SPF, DKIM, DMARC)
|
||||||
|
|
||||||
|
> DMARC 준수를 위해 SPF와 DKIM 모두 설정 필요. 두 가지 중 하나라도 통과하면 DMARC 통과.
|
||||||
|
|
||||||
|
### 인증 방식 비교
|
||||||
|
|
||||||
|
| 방식 | 역할 | 검증 대상 |
|
||||||
|
|------|------|----------|
|
||||||
|
| **SPF** | 발신 서버 허용 목록 | MAIL FROM 도메인 |
|
||||||
|
| **DKIM** | 메일 서명 검증 | 메일 헤더 서명 |
|
||||||
|
| **DMARC** | SPF/DKIM 정책 통합 | From 도메인 정렬 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## DNS 설정 (Cloudflare)
|
## DNS 설정 (Cloudflare)
|
||||||
|
|
||||||
### anvil.it.com
|
### anvil.it.com
|
||||||
@@ -45,6 +59,16 @@ AWS SES(Simple Email Service)를 사용하여 서버에서 메일을 발송하
|
|||||||
|------|------|---------|
|
|------|------|---------|
|
||||||
| TXT | `_amazonses.anvil.it.com` | `0cuw9v32N+aeFiNlTh2Poxglgzf3BlmFRjVOjeLEdy4=` |
|
| TXT | `_amazonses.anvil.it.com` | `0cuw9v32N+aeFiNlTh2Poxglgzf3BlmFRjVOjeLEdy4=` |
|
||||||
|
|
||||||
|
**TXT 레코드 (SPF)** ⭐ 신규
|
||||||
|
| Type | Name | Content |
|
||||||
|
|------|------|---------|
|
||||||
|
| TXT | `anvil.it.com` | `v=spf1 include:amazonses.com ~all` |
|
||||||
|
|
||||||
|
**TXT 레코드 (DMARC)** ⭐ 신규
|
||||||
|
| Type | Name | Content |
|
||||||
|
|------|------|---------|
|
||||||
|
| TXT | `_dmarc.anvil.it.com` | `v=DMARC1; p=quarantine; rua=mailto:dmarc@anvil.it.com; pct=100` |
|
||||||
|
|
||||||
**CNAME 레코드 (DKIM)**
|
**CNAME 레코드 (DKIM)**
|
||||||
| Type | Name | Target |
|
| Type | Name | Target |
|
||||||
|------|------|--------|
|
|------|------|--------|
|
||||||
@@ -59,6 +83,16 @@ AWS SES(Simple Email Service)를 사용하여 서버에서 메일을 발송하
|
|||||||
|------|------|---------|
|
|------|------|---------|
|
||||||
| TXT | `_amazonses.ironclad.it.com` | `C+RMHyLd/U2H5WSCu8L2avRn8NmuwEll0xxYjTyvoEY=` |
|
| TXT | `_amazonses.ironclad.it.com` | `C+RMHyLd/U2H5WSCu8L2avRn8NmuwEll0xxYjTyvoEY=` |
|
||||||
|
|
||||||
|
**TXT 레코드 (SPF)** ⭐ 신규
|
||||||
|
| Type | Name | Content |
|
||||||
|
|------|------|---------|
|
||||||
|
| TXT | `ironclad.it.com` | `v=spf1 include:amazonses.com ~all` |
|
||||||
|
|
||||||
|
**TXT 레코드 (DMARC)** ⭐ 신규
|
||||||
|
| Type | Name | Content |
|
||||||
|
|------|------|---------|
|
||||||
|
| TXT | `_dmarc.ironclad.it.com` | `v=DMARC1; p=quarantine; rua=mailto:dmarc@ironclad.it.com; pct=100` |
|
||||||
|
|
||||||
**CNAME 레코드 (DKIM)**
|
**CNAME 레코드 (DKIM)**
|
||||||
| Type | Name | Target |
|
| Type | Name | Target |
|
||||||
|------|------|--------|
|
|------|------|--------|
|
||||||
@@ -66,6 +100,14 @@ AWS SES(Simple Email Service)를 사용하여 서버에서 메일을 발송하
|
|||||||
| CNAME | `qm7d7qgkdikpcbrbgo7bmgqfmuulrbah._domainkey` | `qm7d7qgkdikpcbrbgo7bmgqfmuulrbah.dkim.amazonses.com` |
|
| CNAME | `qm7d7qgkdikpcbrbgo7bmgqfmuulrbah._domainkey` | `qm7d7qgkdikpcbrbgo7bmgqfmuulrbah.dkim.amazonses.com` |
|
||||||
| CNAME | `tu6oey5tktoqub753cxpayhhwrlzcskk._domainkey` | `tu6oey5tktoqub753cxpayhhwrlzcskk.dkim.amazonses.com` |
|
| CNAME | `tu6oey5tktoqub753cxpayhhwrlzcskk._domainkey` | `tu6oey5tktoqub753cxpayhhwrlzcskk.dkim.amazonses.com` |
|
||||||
|
|
||||||
|
### DMARC 정책 옵션
|
||||||
|
|
||||||
|
| 정책 | 설명 | 권장 단계 |
|
||||||
|
|------|------|----------|
|
||||||
|
| `p=none` | 모니터링만 (조치 없음) | 초기 테스트 |
|
||||||
|
| `p=quarantine` | 스팸함으로 이동 | 중간 단계 |
|
||||||
|
| `p=reject` | 수신 거부 | 최종 단계 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## IAM 권한 설정
|
## IAM 권한 설정
|
||||||
@@ -255,10 +297,72 @@ aws ses get-identity-dkim-attributes --identities newdomain.com
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 반송/불만 처리 (SNS 연동)
|
||||||
|
|
||||||
|
> 반송(Bounce)과 불만(Complaint) 처리는 SES 평판 유지에 필수
|
||||||
|
|
||||||
|
### SNS 토픽 생성
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 반송 알림용 토픽
|
||||||
|
aws sns create-topic --name ses-bounces --region ap-northeast-2
|
||||||
|
|
||||||
|
# 불만 알림용 토픽
|
||||||
|
aws sns create-topic --name ses-complaints --region ap-northeast-2
|
||||||
|
```
|
||||||
|
|
||||||
|
### SES 알림 설정
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 반송 알림 연결
|
||||||
|
aws ses set-identity-notification-topic \
|
||||||
|
--identity anvil.it.com \
|
||||||
|
--notification-type Bounce \
|
||||||
|
--sns-topic arn:aws:sns:ap-northeast-2:ACCOUNT_ID:ses-bounces
|
||||||
|
|
||||||
|
# 불만 알림 연결
|
||||||
|
aws ses set-identity-notification-topic \
|
||||||
|
--identity anvil.it.com \
|
||||||
|
--notification-type Complaint \
|
||||||
|
--sns-topic arn:aws:sns:ap-northeast-2:ACCOUNT_ID:ses-complaints
|
||||||
|
```
|
||||||
|
|
||||||
|
### 반송 유형
|
||||||
|
|
||||||
|
| 유형 | 설명 | 조치 |
|
||||||
|
|------|------|------|
|
||||||
|
| **Hard Bounce** | 영구적 실패 (주소 없음) | 즉시 목록에서 제거 |
|
||||||
|
| **Soft Bounce** | 일시적 실패 (용량 초과) | 재시도 후 제거 |
|
||||||
|
| **Complaint** | 스팸 신고 | 즉시 목록에서 제거 |
|
||||||
|
|
||||||
|
### 권장 임계값
|
||||||
|
|
||||||
|
- **반송률**: 5% 미만 유지 (10% 초과 시 계정 정지 위험)
|
||||||
|
- **불만률**: 0.1% 미만 유지
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 인증 검증 명령어
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# SPF 레코드 확인
|
||||||
|
dig TXT anvil.it.com +short
|
||||||
|
|
||||||
|
# DMARC 레코드 확인
|
||||||
|
dig TXT _dmarc.anvil.it.com +short
|
||||||
|
|
||||||
|
# DKIM 레코드 확인
|
||||||
|
dig CNAME dgcehnldehfmfgpvrrbc6drwasiibhnp._domainkey.anvil.it.com +short
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 참고 사항
|
## 참고 사항
|
||||||
|
|
||||||
- **프로덕션 모드**: 수신자 제한 없음
|
- **프로덕션 모드**: 수신자 제한 없음
|
||||||
- **DKIM**: 메일 신뢰도 향상 (스팸 방지)
|
- **DKIM**: 메일 서명으로 위변조 방지
|
||||||
|
- **SPF**: 허용된 발신 서버 목록 정의
|
||||||
|
- **DMARC**: SPF/DKIM 정책 통합 및 리포트 수신
|
||||||
- **발송 한도**: 필요시 AWS에 증가 요청 가능
|
- **발송 한도**: 필요시 AWS에 증가 요청 가능
|
||||||
- **비용**: $0.10 / 1,000통
|
- **비용**: $0.10 / 1,000통
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,12 @@ GITEA_ROOT_URL=https://your-domain.com
|
|||||||
GITEA_HTTP_PORT=3000
|
GITEA_HTTP_PORT=3000
|
||||||
GITEA_SSH_PORT=2222
|
GITEA_SSH_PORT=2222
|
||||||
|
|
||||||
# Security (automatically generated)
|
# Security - 파일 기반 Secret 권장 (환경변수 노출 방지)
|
||||||
|
# docker-compose에서 secrets 사용 시:
|
||||||
|
# GITEA__security__SECRET_KEY__FILE=/run/secrets/gitea_secret_key
|
||||||
|
# GITEA__security__INTERNAL_TOKEN__FILE=/run/secrets/gitea_internal_token
|
||||||
|
|
||||||
|
# 환경변수 방식 (개발용)
|
||||||
GITEA_SECRET_KEY=your_secret_key
|
GITEA_SECRET_KEY=your_secret_key
|
||||||
GITEA_INTERNAL_TOKEN=your_internal_token
|
GITEA_INTERNAL_TOKEN=your_internal_token
|
||||||
|
|
||||||
@@ -95,6 +100,75 @@ GITEA_ADMIN_EMAIL=admin@your-domain.com
|
|||||||
GITEA_ADMIN_PASSWORD=secure_password
|
GITEA_ADMIN_PASSWORD=secure_password
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 보안 강화 설정 (app.ini)
|
||||||
|
|
||||||
|
프로덕션 환경에서 아래 설정을 `app.ini` 또는 환경변수로 추가:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[security]
|
||||||
|
; 비밀번호 정책 강화
|
||||||
|
MIN_PASSWORD_LENGTH = 10
|
||||||
|
PASSWORD_COMPLEXITY = lower,upper,digit
|
||||||
|
PASSWORD_HASH_ALGO = argon2
|
||||||
|
|
||||||
|
; Git Hooks 비활성화 (보안 강화)
|
||||||
|
DISABLE_GIT_HOOKS = true
|
||||||
|
|
||||||
|
; 리버스 프록시 신뢰 설정
|
||||||
|
REVERSE_PROXY_TRUSTED_PROXIES = 127.0.0.0/8,::1/128
|
||||||
|
|
||||||
|
; Secret 파일 기반 관리 (권장)
|
||||||
|
SECRET_KEY_URI = file:/etc/gitea/secret_key
|
||||||
|
INTERNAL_TOKEN_URI = file:/etc/gitea/internal_token
|
||||||
|
|
||||||
|
[service]
|
||||||
|
; 회원가입 비활성화 (필요시)
|
||||||
|
DISABLE_REGISTRATION = true
|
||||||
|
; 로그인 필수
|
||||||
|
REQUIRE_SIGNIN_VIEW = true
|
||||||
|
; 이메일 비공개 기본값
|
||||||
|
DEFAULT_KEEP_EMAIL_PRIVATE = true
|
||||||
|
|
||||||
|
[repository.signing]
|
||||||
|
; 커밋 서명 설정
|
||||||
|
SIGNING_KEY = default
|
||||||
|
INITIAL_COMMIT = always
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Secrets 사용 (프로덕션 권장)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml
|
||||||
|
services:
|
||||||
|
gitea:
|
||||||
|
image: gitea/gitea:latest-rootless
|
||||||
|
environment:
|
||||||
|
- GITEA__security__SECRET_KEY__FILE=/run/secrets/gitea_secret_key
|
||||||
|
- GITEA__security__INTERNAL_TOKEN__FILE=/run/secrets/gitea_internal_token
|
||||||
|
- GITEA__database__PASSWD__FILE=/run/secrets/db_password
|
||||||
|
secrets:
|
||||||
|
- gitea_secret_key
|
||||||
|
- gitea_internal_token
|
||||||
|
- db_password
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
gitea_secret_key:
|
||||||
|
file: ./secrets/secret_key
|
||||||
|
gitea_internal_token:
|
||||||
|
file: ./secrets/internal_token
|
||||||
|
db_password:
|
||||||
|
file: ./secrets/db_password
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Secret 파일 생성
|
||||||
|
mkdir -p secrets
|
||||||
|
openssl rand -base64 32 > secrets/secret_key
|
||||||
|
openssl rand -base64 32 > secrets/internal_token
|
||||||
|
openssl rand -base64 24 > secrets/db_password
|
||||||
|
chmod 600 secrets/*
|
||||||
|
```
|
||||||
|
|
||||||
### Advanced Configuration
|
### Advanced Configuration
|
||||||
|
|
||||||
For advanced settings, modify:
|
For advanced settings, modify:
|
||||||
|
|||||||
@@ -20,14 +20,16 @@ graph TD
|
|||||||
|
|
||||||
### 1. n8n 설치 및 실행
|
### 1. n8n 설치 및 실행
|
||||||
```bash
|
```bash
|
||||||
# Docker Compose로 실행 (권장)
|
# Docker Compose로 실행 (권장) - 공식 이미지 사용
|
||||||
docker run -it --rm --name n8n -p 5678:5678 n8nio/n8n
|
docker run -it --rm --name n8n -p 5678:5678 docker.n8n.io/n8nio/n8n
|
||||||
|
|
||||||
# 또는 npm으로 설치
|
# 또는 npm으로 설치
|
||||||
npm install n8n -g
|
npm install n8n -g
|
||||||
n8n start
|
n8n start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **주의**: `n8nio/n8n` 대신 공식 레지스트리 `docker.n8n.io/n8nio/n8n` 사용 권장
|
||||||
|
|
||||||
### 2. Credentials 설정
|
### 2. Credentials 설정
|
||||||
n8n에서 다음 Credentials를 생성해야 합니다:
|
n8n에서 다음 Credentials를 생성해야 합니다:
|
||||||
|
|
||||||
@@ -223,39 +225,93 @@ curl -X POST "http://localhost:5678/webhook/cf-tunnel" \
|
|||||||
|
|
||||||
## 🚀 프로덕션 배포
|
## 🚀 프로덕션 배포
|
||||||
|
|
||||||
### 1. Docker Compose 설정
|
### 1. Docker Compose 설정 (보안 강화)
|
||||||
```yaml
|
```yaml
|
||||||
version: '3.8'
|
version: '3.8'
|
||||||
services:
|
services:
|
||||||
n8n:
|
n8n:
|
||||||
image: n8nio/n8n
|
image: docker.n8n.io/n8nio/n8n
|
||||||
ports:
|
ports:
|
||||||
- "5678:5678"
|
- "127.0.0.1:5678:5678"
|
||||||
environment:
|
environment:
|
||||||
|
# 기본 설정
|
||||||
- N8N_HOST=0.0.0.0
|
- N8N_HOST=0.0.0.0
|
||||||
- N8N_PORT=5678
|
- N8N_PORT=5678
|
||||||
- N8N_PROTOCOL=https
|
- N8N_PROTOCOL=https
|
||||||
|
- NODE_ENV=production
|
||||||
|
# 보안 설정 (필수)
|
||||||
|
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY} # openssl rand -hex 32 로 생성
|
||||||
|
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
|
||||||
|
# 인증 설정
|
||||||
- N8N_BASIC_AUTH_ACTIVE=true
|
- N8N_BASIC_AUTH_ACTIVE=true
|
||||||
- N8N_BASIC_AUTH_USER=admin
|
- N8N_BASIC_AUTH_USER=admin
|
||||||
- N8N_BASIC_AUTH_PASSWORD=secure-password
|
- N8N_BASIC_AUTH_PASSWORD=${N8N_ADMIN_PASSWORD}
|
||||||
|
# 보안 강화 옵션
|
||||||
|
- N8N_PUBLIC_API_DISABLED=true # Public API 비활성화
|
||||||
|
- N8N_RUNNERS_ENABLED=true # Task Runner 활성화
|
||||||
|
- N8N_PROXY_HOPS=1 # 리버스 프록시 사용 시
|
||||||
|
# Webhook 설정
|
||||||
|
- WEBHOOK_URL=https://n8n.yourdomain.com/
|
||||||
|
# 타임존
|
||||||
|
- GENERIC_TIMEZONE=Asia/Seoul
|
||||||
|
- TZ=Asia/Seoul
|
||||||
volumes:
|
volumes:
|
||||||
- n8n_data:/home/node/.n8n
|
- n8n_data:/home/node/.n8n
|
||||||
|
- ./local-files:/files
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-q", "--spider", "http://localhost:5678/healthz"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
n8n_data:
|
n8n_data:
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 리버스 프록시 설정
|
### 환경변수 파일 (.env)
|
||||||
|
```bash
|
||||||
|
# 암호화 키 생성 (최초 1회)
|
||||||
|
openssl rand -hex 32
|
||||||
|
|
||||||
|
# .env 파일 예시
|
||||||
|
N8N_ENCRYPTION_KEY=your_generated_key_here
|
||||||
|
N8N_ADMIN_PASSWORD=secure_password_here
|
||||||
|
```
|
||||||
|
|
||||||
|
> **중요**: `N8N_ENCRYPTION_KEY`는 DB에 저장되는 credential을 암호화합니다. 분실 시 모든 credential 재설정 필요.
|
||||||
|
|
||||||
|
### 2. 리버스 프록시 설정 (보안 헤더 포함)
|
||||||
```nginx
|
```nginx
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name n8n.yourdomain.com;
|
server_name n8n.yourdomain.com;
|
||||||
|
return 301 https://$server_name$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name n8n.yourdomain.com;
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/n8n.yourdomain.com/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/n8n.yourdomain.com/privkey.pem;
|
||||||
|
|
||||||
|
# 보안 헤더
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://localhost:5678;
|
proxy_pass http://127.0.0.1:5678;
|
||||||
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_read_timeout 300s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user