Add 5 more runbooks

- aws-ses-setup.md: AWS SES email configuration
- anvil-ses-final-setup.md: Anvil SES final setup
- n8n-setup-guide.md: n8n workflow automation
- gitea-setup.md: Gitea server installation
- cloudflare-vault-integration.md: Cloudflare + Vault integration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-16 00:39:06 +09:00
parent 5c6b5382f6
commit 49fe96775a
6 changed files with 1374 additions and 0 deletions

View File

@@ -14,6 +14,11 @@ DevOps 운영 매뉴얼 및 기술 가이드 모음
| [kitty-setup-guide.md](kitty-setup-guide.md) | Kitty 터미널 설정 가이드 |
| [claude_communication_flow.md](claude_communication_flow.md) | Claude 통신 흐름 문서 |
| [anvil-load-test-report.md](anvil-load-test-report.md) | Anvil 부하 테스트 리포트 |
| [aws-ses-setup.md](aws-ses-setup.md) | AWS SES 이메일 설정 |
| [anvil-ses-final-setup.md](anvil-ses-final-setup.md) | Anvil SES 최종 설정 |
| [n8n-setup-guide.md](n8n-setup-guide.md) | n8n 워크플로우 자동화 설정 |
| [gitea-setup.md](gitea-setup.md) | Gitea Git 서버 설치 및 설정 |
| [cloudflare-vault-integration.md](cloudflare-vault-integration.md) | Cloudflare + Vault 연동 |
## 카테고리
@@ -26,6 +31,15 @@ DevOps 운영 매뉴얼 및 기술 가이드 모음
### 인프라
- incus-meilisearch-manual.md
- anvil-load-test-report.md
- gitea-setup.md
### 이메일
- aws-ses-setup.md
- anvil-ses-final-setup.md
### 자동화
- n8n-setup-guide.md
- cloudflare-vault-integration.md
### 개발 환경
- kitty-setup-guide.md

195
anvil-ses-final-setup.md Normal file
View File

@@ -0,0 +1,195 @@
# AWS SES 완벽 설정 완료 - anvil.it.com
## 🎉 설정 완료 상태
**Mail-Tester 점수: 10/10 (만점)** ✅
설정 완료일: 2025-09-06
도메인: anvil.it.com
리전: ap-northeast-2 (Seoul)
## 📧 SMTP 설정 정보
```yaml
SMTP_HOST: email-smtp.ap-northeast-2.amazonaws.com
SMTP_PORT: 587
SMTP_TLS: true
SMTP_USERNAME: [Vault: secret/aws/ses/smtp -> smtp_username]
SMTP_PASSWORD: [Vault: secret/aws/ses/smtp -> smtp_password]
SMTP_FROM: noreply@anvil.it.com
```
### Vault에서 인증 정보 조회
```bash
# 전체 설정 조회
vault kv get secret/aws/ses/smtp
# SMTP Password만 조회
vault kv get -field=smtp_password secret/aws/ses/smtp
```
## 🔐 DNS 레코드 설정
### 1. SPF 레코드 (TXT)
```
anvil.it.com TXT "v=spf1 include:_spf.mx.cloudflare.net include:amazonses.com ~all"
```
### 2. DKIM 레코드 (CNAME × 3)
```
dgcehnldehfmfgpvrrbc6drwasiibhnp._domainkey.anvil.it.com CNAME dgcehnldehfmfgpvrrbc6drwasiibhnp.dkim.amazonses.com
spopdscdt2sxngqzl5ir66k3ed6og7ut._domainkey.anvil.it.com CNAME spopdscdt2sxngqzl5ir66k3ed6og7ut.dkim.amazonses.com
55l5wnmktvacgyfpt6sovcgb2rqexrpy._domainkey.anvil.it.com CNAME 55l5wnmktvacgyfpt6sovcgb2rqexrpy.dkim.amazonses.com
```
### 3. DMARC 레코드 (TXT)
```
_dmarc.anvil.it.com TXT "v=DMARC1;p=quarantine;pct=25;rua=mailto:908761dcafa547a981e283a21768d69f@dmarc-reports.cloudflare.net,mailto:dmarc-reports@anvil.it.com;ruf=mailto:dmarc-failures@anvil.it.com"
```
### 4. MAIL FROM 도메인 설정
```
# MX 레코드
bounce.anvil.it.com MX 10 feedback-smtp.ap-northeast-2.amazonses.com
# SPF 레코드
bounce.anvil.it.com TXT "v=spf1 include:amazonses.com ~all"
```
### 5. 도메인 검증 (TXT)
```
_amazonses.anvil.it.com TXT "0cuw9v32N+aeFiNlTh2Poxglgzf3BlmFRjVOjeLEdy4="
```
## ✅ AWS SES 상태
### 도메인 검증
- **Status**: Success ✅
- **DKIM**: Success & Enabled ✅
- **MAIL FROM**: bounce.anvil.it.com (Success) ✅
### 발송 한도
- **일일 최대**: 50,000통
- **초당 최대**: 14통
- **현재 환경**: Production Ready
## 🎯 컴플라이언스 달성 상태
| 요구사항 | 상태 | 점수 |
|---------|------|------|
| SPF Authentication | ✅ Compliant | Pass |
| DKIM Authentication | ✅ Compliant | Pass |
| DMARC Authentication | ✅ Quarantine (25%) | Pass |
| From Header Alignment | ✅ Compliant | Pass |
| DNS Records | ✅ Compliant | Pass |
| Encryption | ✅ TLS Required | Pass |
| One-click Unsubscribe | ✅ RFC 8058 | Pass |
| Honor Unsubscribe | ✅ Compliant | Pass |
**최종 점수: 8/8 (완전 준수)** 🏆
## 📱 애플리케이션 통합 예시
### Node.js (nodemailer)
```javascript
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransporter({
host: 'email-smtp.ap-northeast-2.amazonaws.com',
port: 587,
secure: false,
auth: {
user: process.env.SMTP_USERNAME, // SMTP Username
pass: process.env.SMTP_PASSWORD // SMTP Password (변환된 값)
}
});
// RFC 8058 호환 메일 발송
const mailOptions = {
from: 'noreply@anvil.it.com',
to: 'user@example.com',
subject: 'Welcome!',
html: '<h1>Welcome!</h1>',
headers: {
'List-Unsubscribe': '<mailto:unsubscribe@anvil.it.com>, <https://anvil.it.com/unsubscribe>',
'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click'
}
};
```
### Python (smtplib)
```python
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
msg = MIMEMultipart()
msg['From'] = 'noreply@anvil.it.com'
msg['To'] = 'user@example.com'
msg['Subject'] = 'Welcome!'
msg['List-Unsubscribe'] = '<mailto:unsubscribe@anvil.it.com>, <https://anvil.it.com/unsubscribe>'
msg['List-Unsubscribe-Post'] = 'List-Unsubscribe=One-Click'
server = smtplib.SMTP('email-smtp.ap-northeast-2.amazonaws.com', 587)
server.starttls()
server.login(smtp_username, smtp_password)
server.send_message(msg)
server.quit()
```
## 🚀 성과 요약
1. **완벽한 인증 체계**: SPF, DKIM, DMARC 모두 통과
2. **최고 수준 컴플라이언스**: RFC 8058 One-click Unsubscribe 구현
3. **Gmail 최적화**: Postmaster Tools 준비 완료
4. **확장 가능한 구조**: 일일 50K 발송 지원
5. **보안 강화**: 단계적 DMARC 정책 (quarantine 25%)
## 📊 모니터링 및 관리
### Gmail Postmaster Tools
- URL: https://postmaster.google.com
- 도메인 등록: anvil.it.com
- 데이터 확인: 1-2일 후부터
### DMARC 리포트
- **집계 리포트**: dmarc-reports@anvil.it.com
- **실패 리포트**: dmarc-failures@anvil.it.com
- **Cloudflare**: 자동 대시보드 제공
### AWS SES 모니터링
```bash
# 발송 통계
aws ses get-send-statistics --region ap-northeast-2
# 발송 한도 확인
aws ses get-send-quota --region ap-northeast-2
```
## 🔄 향후 개선 계획
### 1단계 (완료): 기본 설정
- ✅ 도메인 검증
- ✅ DKIM 활성화
- ✅ SPF 설정
- ✅ DMARC 모니터링
### 2단계 (완료): 컴플라이언스
- ✅ DMARC quarantine (25%)
- ✅ One-click Unsubscribe
- ✅ RFC 8058 준수
### 3단계 (2주 후): 정책 강화
- DMARC: p=quarantine;pct=100
- 평판 안정화 후 p=reject 고려
- Gmail Postmaster 데이터 분석
### 4단계 (1개월 후): 고도화
- 발송량 점진적 증가 (Warm-up)
- A/B 테스트 기반 최적화
- 고급 모니터링 구축
---
**🎯 결과: anvil.it.com 메일 시스템은 업계 최고 수준으로 설정되었습니다!**
Mail-Tester 10/10 점수는 모든 주요 메일 서비스에서 anvil.it.com을 신뢰할 수 있는 발신자로 인식한다는 의미입니다.

271
aws-ses-setup.md Normal file
View File

@@ -0,0 +1,271 @@
# AWS SES 메일 발송 설정 가이드
> 작성일: 2026-01-12
> 리전: ap-northeast-2 (서울)
## 개요
AWS SES(Simple Email Service)를 사용하여 서버에서 메일을 발송하기 위한 설정 가이드입니다.
---
## 계정 상태
| 항목 | 값 |
|------|-----|
| 리전 | ap-northeast-2 (서울) |
| 모드 | **프로덕션** (샌드박스 해제됨) |
| 24시간 발송 한도 | 50,000통 |
| 초당 발송률 | 14통/초 |
---
## 인증된 도메인
| 도메인 | 도메인 인증 | DKIM |
|--------|------------|------|
| `anvil.it.com` | ✅ Success | ✅ Success |
| `ironclad.it.com` | ✅ Success | ✅ Success |
### 사용 가능한 발신자 주소
```
*@anvil.it.com (예: noreply@anvil.it.com, support@anvil.it.com)
*@ironclad.it.com (예: noreply@ironclad.it.com, admin@ironclad.it.com)
```
---
## DNS 설정 (Cloudflare)
### anvil.it.com
**TXT 레코드 (도메인 소유권 확인)**
| Type | Name | Content |
|------|------|---------|
| TXT | `_amazonses.anvil.it.com` | `0cuw9v32N+aeFiNlTh2Poxglgzf3BlmFRjVOjeLEdy4=` |
**CNAME 레코드 (DKIM)**
| Type | Name | Target |
|------|------|--------|
| CNAME | `dgcehnldehfmfgpvrrbc6drwasiibhnp._domainkey` | `dgcehnldehfmfgpvrrbc6drwasiibhnp.dkim.amazonses.com` |
| CNAME | `spopdscdt2sxngqzl5ir66k3ed6og7ut._domainkey` | `spopdscdt2sxngqzl5ir66k3ed6og7ut.dkim.amazonses.com` |
| CNAME | `55l5wnmktvacgyfpt6sovcgb2rqexrpy._domainkey` | `55l5wnmktvacgyfpt6sovcgb2rqexrpy.dkim.amazonses.com` |
### ironclad.it.com
**TXT 레코드 (도메인 소유권 확인)**
| Type | Name | Content |
|------|------|---------|
| TXT | `_amazonses.ironclad.it.com` | `C+RMHyLd/U2H5WSCu8L2avRn8NmuwEll0xxYjTyvoEY=` |
**CNAME 레코드 (DKIM)**
| Type | Name | Target |
|------|------|--------|
| CNAME | `5sycmzu364y2rgefnqaloptgodymasct._domainkey` | `5sycmzu364y2rgefnqaloptgodymasct.dkim.amazonses.com` |
| CNAME | `qm7d7qgkdikpcbrbgo7bmgqfmuulrbah._domainkey` | `qm7d7qgkdikpcbrbgo7bmgqfmuulrbah.dkim.amazonses.com` |
| CNAME | `tu6oey5tktoqub753cxpayhhwrlzcskk._domainkey` | `tu6oey5tktoqub753cxpayhhwrlzcskk.dkim.amazonses.com` |
---
## IAM 권한 설정
### 최소 권한 정책 (권장)
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SESSendEmail",
"Effect": "Allow",
"Action": [
"ses:SendEmail",
"ses:SendRawEmail"
],
"Resource": "*"
}
]
}
```
### 특정 발신자 제한 정책
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SESSendFromVerified",
"Effect": "Allow",
"Action": [
"ses:SendEmail",
"ses:SendRawEmail"
],
"Resource": [
"arn:aws:ses:ap-northeast-2:*:identity/anvil.it.com",
"arn:aws:ses:ap-northeast-2:*:identity/ironclad.it.com"
]
}
]
}
```
---
## 사용 방법
### AWS CLI
```bash
aws ses send-email \
--from "noreply@anvil.it.com" \
--to "recipient@example.com" \
--subject "메일 제목" \
--text "메일 본문" \
--region ap-northeast-2
```
### Python (boto3)
```python
import boto3
from botocore.exceptions import ClientError
def send_email(to_email: str, subject: str, body: str, sender: str = "noreply@anvil.it.com"):
client = boto3.client('ses', region_name='ap-northeast-2')
try:
response = client.send_email(
Source=sender,
Destination={'ToAddresses': [to_email]},
Message={
'Subject': {'Data': subject, 'Charset': 'UTF-8'},
'Body': {
'Text': {'Data': body, 'Charset': 'UTF-8'},
# HTML 본문 사용 시:
# 'Html': {'Data': '<h1>Hello</h1>', 'Charset': 'UTF-8'}
}
}
)
print(f"발송 완료: {response['MessageId']}")
return response
except ClientError as e:
print(f"발송 실패: {e.response['Error']['Message']}")
raise
# 사용 예시
send_email(
to_email='recipient@example.com',
subject='테스트 메일',
body='안녕하세요!'
)
```
### Python (HTML 메일)
```python
def send_html_email(to_email: str, subject: str, html_body: str, text_body: str = None):
client = boto3.client('ses', region_name='ap-northeast-2')
body = {'Html': {'Data': html_body, 'Charset': 'UTF-8'}}
if text_body:
body['Text'] = {'Data': text_body, 'Charset': 'UTF-8'}
response = client.send_email(
Source='noreply@anvil.it.com',
Destination={'ToAddresses': [to_email]},
Message={
'Subject': {'Data': subject, 'Charset': 'UTF-8'},
'Body': body
}
)
return response
# 사용 예시
html = """
<html>
<body>
<h1>안녕하세요!</h1>
<p>AWS SES로 발송된 <strong>HTML 메일</strong>입니다.</p>
</body>
</html>
"""
send_html_email('recipient@example.com', '테스트', html)
```
---
## 환경변수 설정 (컨테이너용)
```bash
export AWS_ACCESS_KEY_ID="your_access_key"
export AWS_SECRET_ACCESS_KEY="your_secret_key"
export AWS_DEFAULT_REGION="ap-northeast-2"
```
환경변수 설정 시 코드에서 자격증명 생략 가능:
```python
client = boto3.client('ses') # 환경변수에서 자동 로드
```
---
## 상태 확인 명령어
```bash
# 발송 활성화 상태
aws ses get-account-sending-enabled
# 발송 한도 확인
aws ses get-send-quota
# 등록된 Identity 목록
aws ses list-identities
# 도메인 인증 상태
aws ses get-identity-verification-attributes --identities anvil.it.com ironclad.it.com
# DKIM 상태
aws ses get-identity-dkim-attributes --identities anvil.it.com ironclad.it.com
```
---
## 새 도메인 추가 방법
### 1. SES 도메인 등록
```bash
aws ses verify-domain-identity --domain newdomain.com
aws ses verify-domain-dkim --domain newdomain.com
```
### 2. Cloudflare DNS 레코드 추가
출력된 토큰으로 TXT, CNAME 레코드 추가 (Cloudflare API 또는 대시보드)
### 3. 인증 확인
```bash
aws ses get-identity-verification-attributes --identities newdomain.com
aws ses get-identity-dkim-attributes --identities newdomain.com
```
---
## 참고 사항
- **프로덕션 모드**: 수신자 제한 없음
- **DKIM**: 메일 신뢰도 향상 (스팸 방지)
- **발송 한도**: 필요시 AWS에 증가 요청 가능
- **비용**: $0.10 / 1,000통
---
## 관련 링크
- [AWS SES 콘솔](https://ap-northeast-2.console.aws.amazon.com/ses/)
- [AWS SES 개발자 가이드](https://docs.aws.amazon.com/ses/latest/dg/)
- [Cloudflare DNS](https://dash.cloudflare.com/)

View File

@@ -0,0 +1,69 @@
# Cloudflare Vault 통합 가이드
## 저장 방식 권장사항
### 1. **API Token 방식 (권장)**
```bash
# Vault에 저장
vault kv put secret/cloudflare/tokens \
api_token="your-api-token" \
account_id="your-account-id"
# 사용
export CLOUDFLARE_API_TOKEN=$(vault kv get -field=api_token secret/cloudflare/tokens)
```
### 2. **Global API Key 방식 (현재)**
```bash
# 현재 Vault 구조
secret/cloudflare/
├── email
└── api_key
# 사용
export CF_API_EMAIL=$(vault kv get -field=email secret/cloudflare)
export CF_API_KEY=$(vault kv get -field=api_key secret/cloudflare)
```
### 3. **Tunnel 별 크레덴셜**
```bash
# Vault에 터널별로 저장
vault kv put secret/cloudflare/tunnels/production \
tunnel_id="uuid" \
tunnel_secret="base64-secret" \
account_id="account-id"
# JSON 파일로 추출
vault kv get -format=json secret/cloudflare/tunnels/production | \
jq '.data.data' > ~/.cloudflared/production.json
```
## 자동화 스크립트
### Fish Shell 함수
```fish
function cf-auth
set -gx CF_API_EMAIL (vault kv get -field=email secret/cloudflare)
set -gx CF_API_KEY (vault kv get -field=api_key secret/cloudflare)
echo "Cloudflare 인증 설정 완료"
end
```
### Bash 별칭
```bash
alias cf-auth='eval $(vault kv get -format=json secret/cloudflare | jq -r ".data.data | to_entries[] | \"export CF_\" + (.key | ascii_upcase) + \"=\" + .value")'
```
## 보안 고려사항
1. **최소 권한 원칙**: API Token 사용 시 필요한 권한만 부여
2. **임시 인증**: 환경변수는 세션 종료 시 자동 삭제
3. **파일 권한**: `chmod 600 ~/.cloudflared/*`
4. **Vault 통합**: 모든 크레덴셜은 Vault에서 중앙 관리
## 추천 워크플로우
1. Vault에 API Token 저장 (Global Key 대신)
2. 필요시 환경변수로 로드
3. 작업 완료 후 환경변수 정리
4. CI/CD에서는 Vault Agent 사용

552
gitea-setup.md Normal file
View File

@@ -0,0 +1,552 @@
# Gitea Docker Compose Setup
A production-ready Gitea deployment using Docker Compose with PostgreSQL, optimized for NAS systems and self-hosted environments.
## 🚀 Quick Start
1. **Clone or download this repository**
2. **Run the setup script:**
```bash
./setup-gitea.sh
```
3. **Start Gitea:**
```bash
docker-compose up -d
```
4. **Access Gitea at:** `http://localhost:3000`
## 📋 Prerequisites
### Required Software
- **Docker Engine** (20.10+)
- **Docker Compose** (2.0+)
- **OpenSSL** (for generating secure keys)
- **Bash** (for setup scripts)
### System Requirements
- **RAM:** Minimum 2GB, recommended 4GB+
- **Storage:** Minimum 10GB free space
- **Network:** Ports 3000 (HTTP) and 2222 (SSH) available
### Installation Commands
**Ubuntu/Debian:**
```bash
# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
# Install Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# Log out and back in to apply group changes
```
**macOS:**
```bash
# Install Docker Desktop from https://docker.com/products/docker-desktop
# Or via Homebrew:
brew install --cask docker
```
## 🏗️ Architecture
### Services
- **gitea**: Main Gitea application (rootless container)
- **gitea-db**: PostgreSQL 15 database
- **gitea-runner**: Optional Gitea Actions runner
### Volumes
- **gitea-data**: Application data and repositories
- **gitea-config**: Configuration files
- **gitea-db-data**: PostgreSQL data
- **gitea-runner-data**: Actions runner data
### Network
- **gitea-network**: Isolated bridge network with custom subnet
## ⚙️ Configuration
### Environment Variables
The setup is configured through a `.env` file. Key settings include:
```bash
# Domain Configuration
GITEA_DOMAIN=your-domain.com
GITEA_ROOT_URL=https://your-domain.com
# Port Configuration
GITEA_HTTP_PORT=3000
GITEA_SSH_PORT=2222
# Security (automatically generated)
GITEA_SECRET_KEY=your_secret_key
GITEA_INTERNAL_TOKEN=your_internal_token
# Database
POSTGRES_PASSWORD=secure_password
# Admin Account
GITEA_ADMIN_USER=admin
GITEA_ADMIN_EMAIL=admin@your-domain.com
GITEA_ADMIN_PASSWORD=secure_password
```
### Advanced Configuration
For advanced settings, modify:
- **docker-compose.yml**: Service configuration, resource limits, environment variables
- **gitea-app.ini.template**: Detailed Gitea configuration reference
- **.env**: Environment-specific settings
## 🚀 Installation Guide
### Method 1: Automated Setup (Recommended)
```bash
# 1. Download the setup files
git clone <repository-url> gitea-setup
cd gitea-setup
# 2. Run interactive setup
./setup-gitea.sh
# 3. Start services
docker-compose up -d
# 4. Check status
docker-compose ps
docker-compose logs -f gitea
```
### Method 2: Manual Setup
```bash
# 1. Create directories
mkdir -p gitea-{data,config,db-data,runner-data} backups
# 2. Copy environment file
cp .env.example .env
# 3. Edit configuration
nano .env # Update all required values
# 4. Generate secure keys
openssl rand -base64 32 # Use for GITEA_SECRET_KEY
openssl rand -base64 32 # Use for GITEA_INTERNAL_TOKEN
# 5. Start services
docker-compose up -d
```
## 🔐 Security Configuration
### SSL/TLS Setup with Reverse Proxy
**Nginx Configuration:**
```nginx
server {
listen 80;
server_name your-domain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name your-domain.com;
ssl_certificate /path/to/certificate.crt;
ssl_certificate_key /path/to/private.key;
client_max_body_size 512M;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
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;
}
}
```
**Traefik Configuration:**
```yaml
# docker-compose.override.yml
services:
gitea:
labels:
- "traefik.enable=true"
- "traefik.http.routers.gitea.rule=Host(`your-domain.com`)"
- "traefik.http.routers.gitea.tls.certresolver=letsencrypt"
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
```
### SSH Configuration
**Host SSH Configuration (recommended):**
```bash
# Add to /etc/ssh/sshd_config
Match User git
AllowTcpForwarding no
AllowAgentForwarding no
PermitTTY no
X11Forwarding no
```
**Container SSH (current setup):**
- SSH server runs inside Gitea container
- Exposed on port 2222
- User authentication via Gitea SSH keys
### Firewall Configuration
```bash
# Ubuntu/Debian (ufw)
sudo ufw allow 3000/tcp comment 'Gitea HTTP'
sudo ufw allow 2222/tcp comment 'Gitea SSH'
# CentOS/RHEL (firewalld)
sudo firewall-cmd --permanent --add-port=3000/tcp
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
```
## 🗄️ Backup & Restore
### Automated Backup
```bash
# Full backup (recommended)
./backup-gitea.sh --full
# Database only
./backup-gitea.sh --database-only
# Custom retention and compression
./backup-gitea.sh --full --retention 90 --compress 9
```
### Backup Schedule
**Crontab example:**
```bash
# Daily backup at 2 AM, keep for 30 days
0 2 * * * /path/to/gitea-setup/backup-gitea.sh --full --retention 30
# Weekly full backup, keep for 1 year
0 2 * * 0 /path/to/gitea-setup/backup-gitea.sh --full --retention 365
```
### Restore from Backup
```bash
# Restore from full backup
./restore-gitea.sh backups/gitea_backup_20240101_120000.tar.gz
# Restore database only
./restore-gitea.sh --database-only backup_directory/
# Restore with current data backup
./restore-gitea.sh --backup-current latest_backup.tar.gz
```
## 🔄 Maintenance
### Update Gitea
```bash
# Check for updates
./update-gitea.sh --check-only
# Update to latest version
./update-gitea.sh
# Update to specific version
./update-gitea.sh 1.21.5
```
### Monitor Services
```bash
# Check service status
docker-compose ps
# View logs
docker-compose logs -f gitea
docker-compose logs -f gitea-db
# Monitor resources
docker-compose top
docker stats
```
### Database Maintenance
```bash
# Access database
docker-compose exec gitea-db psql -U gitea -d gitea
# Database backup
docker-compose exec gitea-db pg_dump -U gitea -d gitea > backup.sql
# Database restore
docker-compose exec -T gitea-db psql -U gitea -d gitea < backup.sql
```
## 🔧 Troubleshooting
### Common Issues
**1. Permission Errors**
```bash
# Fix directory permissions
sudo chown -R 1000:1000 gitea-data gitea-config
sudo chmod -R 755 gitea-data gitea-config
```
**2. Database Connection Issues**
```bash
# Check database logs
docker-compose logs gitea-db
# Test database connection
docker-compose exec gitea-db pg_isready -U gitea -d gitea
```
**3. SSH Access Issues**
```bash
# Check SSH configuration
docker-compose exec gitea cat /etc/gitea/app.ini | grep -A 5 "\[server\]"
# Test SSH connection
ssh -T git@localhost -p 2222
```
**4. Memory/Resource Issues**
```bash
# Check resource usage
docker stats
# Adjust resource limits in docker-compose.yml
services:
gitea:
deploy:
resources:
limits:
memory: 2G
cpus: '1.0'
```
### Log Analysis
```bash
# Application logs
docker-compose logs --tail=100 -f gitea
# Database logs
docker-compose logs --tail=100 -f gitea-db
# System logs (Ubuntu/Debian)
sudo journalctl -u docker --tail=100 -f
```
### Health Checks
```bash
# Service health
docker-compose exec gitea curl -f http://localhost:3000/api/healthz
# Database health
docker-compose exec gitea-db pg_isready -U gitea -d gitea
```
## 🎯 Performance Optimization
### NAS-Specific Optimizations
**1. Storage Configuration:**
```yaml
# Use external SSD for better performance
volumes:
gitea-data:
driver: local
driver_opts:
type: none
o: bind
device: /mnt/ssd/gitea-data
```
**2. Resource Limits:**
```yaml
services:
gitea:
deploy:
resources:
limits:
memory: 1G
cpus: '1.0'
reservations:
memory: 512M
cpus: '0.5'
```
**3. Database Tuning:**
```bash
# Add to docker-compose.yml under gitea-db environment
POSTGRES_INITDB_ARGS: "--encoding=UTF8 --lc-collate=C --lc-ctype=C"
```
### Network Optimization
```yaml
# Custom network configuration
networks:
gitea-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
```
## 📊 Monitoring
### Basic Monitoring
```bash
# Resource usage
docker stats --no-stream
# Disk usage
du -sh gitea-data/ gitea-db-data/
# Service health
curl -f http://localhost:3000/api/healthz
```
### Advanced Monitoring with Prometheus
```yaml
# Add to docker-compose.yml
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana:latest
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
```
## 🔌 Gitea Actions (CI/CD)
### Enable Actions Runner
```bash
# Start with actions profile
docker-compose --profile actions up -d
# Or enable in existing deployment
docker-compose up -d gitea-runner
```
### Runner Configuration
1. **Generate Registration Token:**
- Go to Gitea Admin → Site Administration → Actions → Runners
- Click "Create new Runner"
- Copy the registration token
2. **Add Token to Environment:**
```bash
echo "GITEA_RUNNER_TOKEN=your_token_here" >> .env
docker-compose restart gitea-runner
```
### Action Examples
**.gitea/workflows/ci.yml:**
```yaml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run tests
run: |
echo "Running tests..."
# Add your test commands here
```
## 📚 Additional Resources
### Official Documentation
- [Gitea Documentation](https://docs.gitea.com/)
- [Docker Compose Reference](https://docs.docker.com/compose/)
- [PostgreSQL Documentation](https://www.postgresql.org/docs/)
### Community Resources
- [Gitea Community](https://github.com/go-gitea/gitea/discussions)
- [Docker Community](https://forums.docker.com/)
### Migration Guides
- [GitHub to Gitea Migration](https://docs.gitea.com/usage/migrate-from-github/)
- [GitLab to Gitea Migration](https://docs.gitea.com/usage/migrate-from-gitlab/)
## 🆘 Support
### Getting Help
1. **Check logs first:**
```bash
docker-compose logs -f gitea
```
2. **Review common issues** in this README
3. **Search existing issues:**
- [Gitea Issues](https://github.com/go-gitea/gitea/issues)
- [Community Discussions](https://github.com/go-gitea/gitea/discussions)
4. **Create detailed bug report** with:
- Gitea version
- Docker version
- Operating system
- Error logs
- Steps to reproduce
### Script Help
All scripts include built-in help:
```bash
./setup-gitea.sh --help
./backup-gitea.sh --help
./restore-gitea.sh --help
./update-gitea.sh --help
```
## 📄 License
This setup configuration is provided under the MIT License. Gitea itself is licensed under the MIT License.
## 🙏 Acknowledgments
- [Gitea Team](https://gitea.io/) for creating an excellent Git service
- [Docker Community](https://docker.com/) for containerization platform
- [PostgreSQL Team](https://postgresql.org/) for the reliable database
---
**Happy Self-Hosting!** 🎉
For questions or improvements, please open an issue or pull request.

273
n8n-setup-guide.md Normal file
View File

@@ -0,0 +1,273 @@
# 🤖 n8n AI Agent: Cloudflare Tunnel 자동화 가이드
n8n에서 Cloudflare Tunnel과 Nginx Proxy Manager를 자동으로 설정하는 AI 에이전트 워크플로우입니다.
## 🏗️ 아키텍처
```mermaid
graph TD
A[HTTP Request] --> B[n8n Webhook]
B --> C[AI Agent 검증]
C --> D[Cloudflare API]
C --> E[NPM API]
D --> F[DNS CNAME 설정]
E --> G[프록시 호스트 생성]
F --> H[완료 응답]
G --> H
```
## 📋 사전 요구사항
### 1. n8n 설치 및 실행
```bash
# Docker Compose로 실행 (권장)
docker run -it --rm --name n8n -p 5678:5678 n8nio/n8n
# 또는 npm으로 설치
npm install n8n -g
n8n start
```
### 2. Credentials 설정
n8n에서 다음 Credentials를 생성해야 합니다:
#### Cloudflare Credential (`cloudflare`)
```json
{
"email": "your-email@example.com",
"api_key": "your-global-api-key"
}
```
#### NPM Credential (`npm`) - 선택사항
```json
{
"url": "http://localhost:81",
"email": "admin@example.com",
"password": "changeme"
}
```
## 🚀 워크플로우 설치
### 1. 메인 AI Agent 워크플로우 임포트
1. n8n 대시보드에서 **"Import from file"** 선택
2. `n8n-cf-tunnel-workflow.json` 파일 업로드
3. Credentials 연결:
- **cloudflare**: Cloudflare credential 선택
- **npm**: NPM credential 선택 (있는 경우)
### 2. Webhook API 워크플로우 임포트
1. `n8n-webhook-workflow.json` 파일 임포트
2. **Execute CF Agent** 노드에서:
- `CF-TUNNEL-AI-AGENT-WORKFLOW-ID`를 실제 워크플로우 ID로 변경
## 📡 API 사용법
### HTTP POST 요청
```bash
curl -X POST "http://localhost:5678/webhook/cf-tunnel" \
-H "Content-Type: application/json" \
-d '{
"domain": "example.com",
"subdomain": "app",
"service_ip": "192.168.1.100",
"service_port": "8080"
}'
```
### 성공 응답 (200)
```json
{
"success": true,
"domain": "app.example.com",
"tunnel_id": "0adb287c-10e2-4f1d-af4c-8e083ed878d4",
"tunnel_domain": "0adb287c-10e2-4f1d-af4c-8e083ed878d4.cfargotunnel.com",
"service": "192.168.1.100:8080",
"dns_configured": true,
"npm_configured": true,
"npm_host_id": "123",
"timestamp": "2024-09-07T01:00:00.000Z",
"message": "🚀 AI Agent 완료: app.example.com → 192.168.1.100:8080"
}
```
### 에러 응답 (400/500)
```json
{
"error": true,
"message": "Missing required fields: domain, subdomain",
"required_fields": ["domain", "subdomain", "service_ip", "service_port"],
"example": {
"domain": "example.com",
"subdomain": "app",
"service_ip": "192.168.1.100",
"service_port": "8080"
}
}
```
## 🔧 커스터마이징
### 1. 터널 ID 변경
각 워크플로우의 **Initialize Config** 노드에서:
```javascript
tunnel_id: 'YOUR-TUNNEL-ID-HERE',
tunnel_domain: 'YOUR-TUNNEL-ID-HERE.cfargotunnel.com',
```
### 2. 계정 ID 변경
```javascript
account_id: 'YOUR-ACCOUNT-ID-HERE'
```
### 3. NPM 설정 수정
**Create NPM Proxy** 노드에서 SSL, 캐싱 등 설정 변경 가능:
```json
{
"ssl_forced": true,
"caching_enabled": true,
"certificate_id": 1,
"meta": {
"letsencrypt_agree": true,
"dns_challenge": true
}
}
```
## 🔒 보안 고려사항
### 1. API 키 보호
- Vault 연동 사용 권장
- n8n Credentials에 직접 저장 시 암호화 확인
- 최소 권한 원칙 적용
### 2. 웹훅 보안
```javascript
// Webhook 노드에 인증 추가
const authHeader = $input.all()[0].headers.authorization;
if (!authHeader || authHeader !== 'Bearer YOUR-SECRET-TOKEN') {
return [{ json: { error: true, message: 'Unauthorized' } }];
}
```
### 3. Rate Limiting
n8n에서 실행 제한 설정:
- **Execution Timeout**: 300초
- **Max Executions**: 시간당 제한 설정
## 📊 모니터링 및 로깅
### 1. 실행 로그 확인
n8n 대시보드 → **Executions** → 워크플로우 실행 기록 확인
### 2. 에러 알림 설정
Error Workflow 생성하여 실패 시 알림 발송:
```json
{
"name": "CF-Tunnel-Error-Handler",
"nodes": [
{
"name": "Send Slack Alert",
"type": "n8n-nodes-base.slack"
}
]
}
```
### 3. 성능 메트릭
- 평균 실행 시간: ~30-60초
- 성공률: >95% 목표
- API 응답 시간 모니터링
## 🧪 테스트 시나리오
### 1. 기본 테스트
```bash
# 새 도메인 생성
curl -X POST "http://localhost:5678/webhook/cf-tunnel" \
-H "Content-Type: application/json" \
-d '{
"domain": "test.com",
"subdomain": "api",
"service_ip": "127.0.0.1",
"service_port": "3000"
}'
```
### 2. 기존 도메인 업데이트
```bash
# 같은 subdomain.domain으로 다른 서비스로 업데이트
curl -X POST "http://localhost:5678/webhook/cf-tunnel" \
-H "Content-Type: application/json" \
-d '{
"domain": "test.com",
"subdomain": "api",
"service_ip": "127.0.0.1",
"service_port": "4000"
}'
```
### 3. 에러 테스트
```bash
# 잘못된 IP 주소
curl -X POST "http://localhost:5678/webhook/cf-tunnel" \
-H "Content-Type: application/json" \
-d '{
"domain": "test.com",
"subdomain": "api",
"service_ip": "999.999.999.999",
"service_port": "3000"
}'
```
## 🚀 프로덕션 배포
### 1. Docker Compose 설정
```yaml
version: '3.8'
services:
n8n:
image: n8nio/n8n
ports:
- "5678:5678"
environment:
- N8N_HOST=0.0.0.0
- N8N_PORT=5678
- N8N_PROTOCOL=https
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=secure-password
volumes:
- n8n_data:/home/node/.n8n
restart: unless-stopped
volumes:
n8n_data:
```
### 2. 리버스 프록시 설정
```nginx
server {
listen 80;
server_name n8n.yourdomain.com;
location / {
proxy_pass http://localhost:5678;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
```
## 📝 추가 기능 아이디어
1. **Bulk API**: 여러 도메인 한번에 설정
2. **Status Check**: 터널 상태 모니터링 API
3. **SSL Certificate**: Let's Encrypt 자동 설정
4. **Health Check**: 서비스 상태 확인 및 알림
5. **Rollback**: 설정 되돌리기 기능
---
이제 n8n에서 Cloudflare 터널을 자동화하는 AI 에이전트가 준비되었습니다! 🤖✨