diff --git a/specs/08-infrastructure/Infrastructure Setup.md b/specs/08-infrastructure/01_Infrastructure Setup.md similarity index 90% rename from specs/08-infrastructure/Infrastructure Setup.md rename to specs/08-infrastructure/01_Infrastructure Setup.md index 805959c..46ba162 100644 --- a/specs/08-infrastructure/Infrastructure Setup.md +++ b/specs/08-infrastructure/01_Infrastructure Setup.md @@ -41,7 +41,7 @@ services: cpus: '2.0' memory: 1.5G networks: - lcbp3-network: + lcbp3: external: true ``` @@ -120,19 +120,18 @@ services: container_name: lcbp3-backend-1 environment: - NODE_ENV=production - - DB_HOST=mariadb-primary - - REDIS_CLUSTER_NODES=redis-1:6379,redis-2:6379,redis-3:6379 + - DB_HOST=mariadb + - REDIS_HOST=cache + - REDIS_PORT=6379 - NUMBERING_LOCK_TIMEOUT=5000 - NUMBERING_RESERVATION_TTL=300 ports: - "3001:3000" depends_on: - - mariadb-primary - - redis-1 - - redis-2 - - redis-3 + - mariadb + - cache networks: - - lcbp3-network + - lcbp3 restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] @@ -145,19 +144,20 @@ services: container_name: lcbp3-backend-2 environment: - NODE_ENV=production - - DB_HOST=mariadb-primary - - REDIS_CLUSTER_NODES=redis-1:6379,redis-2:6379,redis-3:6379 + - DB_HOST=mariadb + - REDIS_HOST=cache + - REDIS_PORT=6379 ports: - "3002:3000" depends_on: - - mariadb-primary - - redis-1 + - mariadb + - cache networks: - - lcbp3-network + - lcbp3 restart: unless-stopped networks: - lcbp3-network: + lcbp3: external: true ``` @@ -346,7 +346,7 @@ curl -X POST http://admin:admin@localhost:3000/api/dashboards/db \ 2. **Sequence Utilization** - Current usage vs max (alert >90%) 3. **Lock Wait Time (p95)** - Performance indicator 4. **Lock Failures** - System health indicator -5. **Redis Cluster Health** - Node status +5. **Redis Health (Single instance)** - Node status 6. **Database Connection Pool** - Resource usage --- @@ -426,31 +426,31 @@ BACKUP_DIR="/backups/redis" mkdir -p $BACKUP_DIR -for i in 1 2 3; do - echo "Backing up redis-$i..." +echo "Backing up Redis..." - # Trigger BGSAVE - docker exec lcbp3-redis-$i redis-cli -p 6379 BGSAVE +# Trigger BGSAVE +docker exec cache redis-cli BGSAVE - # Wait for save to complete - sleep 10 +# Wait for save to complete +sleep 10 - # Copy RDB file - docker cp lcbp3-redis-$i:/data/dump.rdb \ - $BACKUP_DIR/redis-${i}_${DATE}.rdb +# Copy RDB file +docker cp cache:/data/dump.rdb \ + $BACKUP_DIR/redis_${DATE}.rdb - # Copy AOF file - docker cp lcbp3-redis-$i:/data/appendonly.aof \ - $BACKUP_DIR/redis-${i}_${DATE}.aof -done +# Copy AOF file +docker cp cache:/data/appendonly.aof \ + $BACKUP_DIR/redis_${DATE}.aof # Compress -tar -czf $BACKUP_DIR/redis_cluster_${DATE}.tar.gz $BACKUP_DIR/*_${DATE}.* +tar -czf $BACKUP_DIR/redis_${DATE}.tar.gz \ + $BACKUP_DIR/redis_${DATE}.rdb \ + $BACKUP_DIR/redis_${DATE}.aof # Cleanup -rm $BACKUP_DIR/*_${DATE}.rdb $BACKUP_DIR/*_${DATE}.aof +rm $BACKUP_DIR/redis_${DATE}.rdb $BACKUP_DIR/redis_${DATE}.aof -echo "✅ Redis backup complete" +echo "✅ Redis backup complete: redis_${DATE}.tar.gz" ``` ### 5.3 Recovery Procedures @@ -490,17 +490,19 @@ echo "✅ Restore complete" echo "🔄 Please verify sequence integrity" ``` -#### Scenario 2: Redis Node Failure +#### Scenario 2: Redis Failure ```bash -# Automatically handled by cluster -# Node will rejoin cluster when restarted +# Check Redis status +docker exec cache redis-cli ping -# Check cluster status -docker exec lcbp3-redis-1 redis-cli cluster info +# If Redis is down, restart container +docker restart cache -# If node is failed, remove and add back -docker exec lcbp3-redis-1 redis-cli --cluster del-node -docker exec lcbp3-redis-1 redis-cli --cluster add-node :6379 :6379 +# Verify Redis is running +docker exec cache redis-cli ping + +# If restart fails, restore from backup +./scripts/restore-redis.sh /backups/redis/latest.tar.gz ``` --- @@ -759,16 +761,14 @@ echo "⚠️ Please verify system functionality manually" --- -### 8.3 Redis Cluster Down +### 8.3 Redis Down **Alert**: `RedisUnavailable` **Steps**: -1. Verify all nodes down +1. Verify Redis is down ```bash - for i in {1..3}; do - docker exec lcbp3-redis-$i redis-cli ping || echo "Node $i DOWN" - done + docker exec cache redis-cli ping || echo "Redis DOWN" ``` 2. Check system falls back to DB-only mode @@ -777,11 +777,11 @@ echo "⚠️ Please verify system functionality manually" # Should show: fallback_mode: true ``` -3. Restart Redis cluster +3. Restart Redis container ```bash - docker-compose -f docker-compose-redis.yml restart - sleep 30 - ./scripts/check-redis-cluster.sh + docker restart cache + sleep 10 + docker exec cache redis-cli ping ``` 4. If restart fails, restore from backup @@ -796,6 +796,9 @@ echo "⚠️ Please verify system functionality manually" ``` 6. Review logs for root cause + ```bash + docker logs cache --tail 100 + ``` --- @@ -830,18 +833,18 @@ OPTIMIZE TABLE document_numbering_sequences; ANALYZE TABLE document_numbering_sequences; ``` -### 8.2 Redis Memory Optimization +### 9.2 Redis Memory Optimization ```bash # Check memory usage -docker exec lcbp3-redis-1 redis-cli INFO memory +docker exec cache redis-cli INFO memory # If memory high, check keys -docker exec lcbp3-redis-1 redis-cli --bigkeys +docker exec cache redis-cli --bigkeys # Set maxmemory policy -docker exec lcbp3-redis-1 redis-cli CONFIG SET maxmemory 2gb -docker exec lcbp3-redis-1 redis-cli CONFIG SET maxmemory-policy allkeys-lru +docker exec cache redis-cli CONFIG SET maxmemory 2gb +docker exec cache redis-cli CONFIG SET maxmemory-policy allkeys-lru ``` --- @@ -878,7 +881,7 @@ FLUSH PRIVILEGES; ```yaml # docker-compose-network.yml networks: - lcbp3-network: + lcbp3: driver: bridge ipam: config: diff --git a/specs/08-infrastructure/Network_daigram.md b/specs/08-infrastructure/02_Network_daigram.md similarity index 56% rename from specs/08-infrastructure/Network_daigram.md rename to specs/08-infrastructure/02_Network_daigram.md index af38728..2e33895 100644 --- a/specs/08-infrastructure/Network_daigram.md +++ b/specs/08-infrastructure/02_Network_daigram.md @@ -6,7 +6,7 @@ --- -## 2. Data Flow Diagram +## 1. Data Flow Diagram ```mermaid flowchart TB @@ -52,7 +52,7 @@ flowchart TB --- -## 3. Docker Management View +## 2. Docker Management View ```mermaid flowchart TB @@ -85,7 +85,7 @@ flowchart TB --- -## 4. Security Zones Diagram +## 3. Security Zones Diagram ```mermaid flowchart TB @@ -126,7 +126,7 @@ flowchart TB --- -## 5. แผนผังการเชื่อมต่อเครือข่าย (Network Flow) +## 4. แผนผังการเชื่อมต่อเครือข่าย (Network Flow) ```mermaid graph TD @@ -239,32 +239,14 @@ graph TD --- -## 6. สรุปการตั้งค่า Firewall ACLs (สำหรับ Omada OC200) +## 5. Firewall & Security Configuration -นี่คือรายการกฎ (Rules) ที่คุณต้องสร้างใน **Settings > Network Security > ACL** (เรียงลำดับจากบนลงล่าง): +> 📖 **ดูรายละเอียด Firewall ACLs และ Port Forwarding ได้ที่:** [03_Securities.md](03_Securities.md) -| ลำดับ | Name | Policy | Source | Destination | Ports | -| :---- | :--------------------- | :-------- | :---------------- | :------------------------ | :----------------------------------- | -| **1** | Isolate-Guests | **Deny** | Network → VLAN 70 | Network → VLAN 10, 20, 30 | All | -| **2** | Isolate-Servers | **Deny** | Network → VLAN 10 | Network → VLAN 30 (USER) | All | -| **3** | Block-User-to-Mgmt | **Deny** | Network → VLAN 30 | Network → VLAN 20 (MGMT) | All | -| **4** | Allow-User-to-Services | **Allow** | Network → VLAN 30 | IP → QNAP (192.168.10.8) | Port Group → Web (443, 80, 81, 2222) | -| **5** | Allow-MGMT-to-All | **Allow** | Network → VLAN 20 | Any | All | -| **6** | Allow-Server-Internal | **Allow** | IP → 192.168.10.8 | IP → 192.168.10.9 | All (QNAP ↔ ASUSTOR) | -| **7** | (Default) | Deny | Any | Any | All | - ---- - -## 7. สรุปการตั้งค่า Port Forwarding (สำหรับ Omada ER7206) - -นี่คือรายการกฎที่คุณต้องสร้างใน **Settings > Transmission > Port Forwarding**: - -| Name | External Port | Internal IP | Internal Port | Protocol | -| :-------------- | :------------ | :----------- | :------------ | :------- | -| Allow-NPM-HTTPS | 443 | 192.168.10.8 | 443 | TCP | -| Allow-NPM-HTTP | 80 | 192.168.10.8 | 80 | TCP | - -> **หมายเหตุ**: Port forwarding ไปที่ QNAP (NPM) เท่านั้น, ASUSTOR ไม่ควรเปิดรับ traffic จากภายนอก +ไฟล์ `03_Securities.md` ประกอบด้วย: +- 🌐 VLAN Segmentation +- 🔥 Firewall Rules (IP Groups, Port Groups, Switch ACL, Gateway ACL) +- 🚪 Port Forwarding Configuration --- @@ -274,35 +256,33 @@ graph TD --- -## 9. Backup Flow +## 7. Backup Flow -``` -┌────────────────────────────────────────────────────────────────────────┐ -│ BACKUP STRATEGY │ -├────────────────────────────────────────────────────────────────────────┤ -│ │ -│ QNAP (Source) ASUSTOR (Target) │ -│ ┌──────────────┐ ┌──────────────────────┐ │ -│ │ MariaDB │ ──── Daily 2AM ────▶ │ /volume1/backup/db/ │ │ -│ │ (mysqldump) │ │ (Restic Repository) │ │ -│ └──────────────┘ └──────────────────────┘ │ -│ │ -│ ┌──────────────┐ ┌──────────────────────┐ │ -│ │ Redis RDB │ ──── Daily 3AM ────▶ │ /volume1/backup/ │ │ -│ │ + AOF │ │ redis/ │ │ -│ └──────────────┘ └──────────────────────┘ │ -│ │ -│ ┌──────────────┐ ┌──────────────────────┐ │ -│ │ App Config │ ──── Weekly ───────▶ │ /volume1/backup/ │ │ -│ │ + Volumes │ Sunday 4AM │ config/ │ │ -│ └──────────────┘ └──────────────────────┘ │ -│ │ -│ Retention Policy: │ -│ • Daily: 7 days │ -│ • Weekly: 4 weeks │ -│ • Monthly: 6 months │ -│ │ -└────────────────────────────────────────────────────────────────────────┘ +```mermaid +flowchart LR + subgraph QNAP["💾 QNAP TS-473A (Source)"] + direction TB + DB["🗄️ MariaDB
(mysqldump)"] + Redis["📦 Redis
(RDB + AOF)"] + Config["⚙️ App Config
+ Volumes"] + end + + subgraph ASUSTOR["💾 ASUSTOR AS5403T (Target)"] + direction TB + BackupDB["📁 /volume1/backup/db/
(Restic Repository)"] + BackupRedis["📁 /volume1/backup/redis/"] + BackupConfig["📁 /volume1/backup/config/"] + end + + DB -->|"Daily 2AM"| BackupDB + Redis -->|"Daily 3AM"| BackupRedis + Config -->|"Weekly Sun 4AM"| BackupConfig + + subgraph Retention["📋 Retention Policy"] + R1["Daily: 7 days"] + R2["Weekly: 4 weeks"] + R3["Monthly: 6 months"] + end ``` --- diff --git a/specs/08-infrastructure/03_Securities.md b/specs/08-infrastructure/03_Securities.md new file mode 100644 index 0000000..43e73fa --- /dev/null +++ b/specs/08-infrastructure/03_Securities.md @@ -0,0 +1,149 @@ +# การตั้งค่า Network Segmentation และ Firewall Rules + +สำหรับอุปกรณ์ Omada (ER7206 + OC200) กลยุทธ์หลักคือการใช้ **VLANs (Virtual LANs)** เพื่อแบ่งกลุ่มอุปกรณ์ และใช้ **Firewall ACLs (Access Control Lists)** เพื่อควบคุมการจราจรระหว่างกลุ่มเหล่านั้น + +นี่คือคำแนะนำตามแนวทาง "Zero Trust" ที่ปรับให้เข้ากับสถาปัตยกรรมของคุณครับ + +--- + +## 1. 🌐 การแบ่งส่วนเครือข่าย (VLAN Segmentation) + +ใน Omada Controller (OC200) ให้คุณไปที่ `Settings > Wired Networks > LAN` และสร้างเครือข่ายย่อย (VLANs) ดังนี้: + +* **VLAN 10: SERVER** + * **IP Range:** 192.168.10.x + * **วัตถุประสงค์:** ใช้สำหรับอุปกรณ์ Server (QNAP และ ASUSTOR) + +* **VLAN 20 MGMT(Default): Management** + * **IP Range:** 192.168.20.x + * **วัตถุประสงค์:** ใช้สำหรับอุปกรณ์ Network (ER7206, OC200, Switches) และ PC ของผู้ดูแลระบบ (Admin) เท่านั้น + +* **VLAN 30: USER** + * **IP Range:** 192.168.30.x + * **วัตถุประสงค์:** สำหรับ PC, Notebook, และ Wi-Fi ของพนักงานทั่วไปที่ต้องเข้าใช้งานระบบ (เช่น `lcbp3.np-dms.work`) + +* **VLAN 40: CCTV** + * **IP Range:** 192.168.40.x + * **วัตถุประสงค์:** ใช้สำหรับอุปกรณ์ CCTV เท่านั้น + +* **VLAN 50 VOICEt** + * **IP Range:** 192.168.50.x + * **วัตถุประสงค์:** ใช้สำหรับอุปกรณ์ IP Phone เท่านั้น + +* **VLAN 60 DMZ** + * **IP Range:** 192.168.60.x + * **วัตถุประสงค์:** ใช้สำหรับ Network DMZ เท่านั้น + +* **VLAN 70: GUEST / Untrusted** (สำหรับ Wi-Fi แขก) + * **IP Range:** 192.168.70.x + * **วัตถุประสงค์:** สำหรับ Wi-Fi แขก (Guest) ห้ามเข้าถึงเครือข่ายภายในโดยเด็ดขาด + +**การตั้งค่า Port Switch:** +หลังจากสร้าง VLANs แล้ว ให้ไปที่ `Devices` > เลือก Switch ของคุณ > `Ports` > กำหนด Port Profile: + +* Port ที่เสียบ QNAP NAS: ตั้งค่า Profile เป็น **VLAN 10** +* Port ที่เสียบ PC พนักงาน: ตั้งค่า Profile เป็น **VLAN 20** + +--- + +## 2. 🔥 Firewall Rules (ACLs) + +นี่คือหัวใจสำคัญครับ ไปที่ `Settings > Network Security > ACL (Access Control)` + +กฎของ Firewall จะทำงานจากบนลงล่าง (ข้อ 1 ทำก่อนข้อ 2) + +--- + +### 2.1 IP Groups & Port Groups + +**IP Groups** (สร้างใน `Settings > Network Security > Groups`): + +| Group Name | Members | +| :----------------- | :------------------------------------------------ | +| `Server` | 192.168.10.8, 192.168.10.9, 192.168.10.111 | +| `Omada-Controller` | 192.168.20.250 (OC200 IP) | +| `DHCP-Gateways` | 192.168.30.1, 192.168.70.1 | +| `QNAP_Services` | 192.168.10.8 | +| `Internal` | 192.168.10.0/24, 192.168.20.0/24, 192.168.30.0/24 | +| `Blacklist` | (Add malicious IPs as needed) | + +**Port Groups**: + +| Group Name | Ports | +| :----------- | :-------------------------------------- | +| `Web` | TCP 443, 8443, 80, 81, 2222 | +| `Omada-Auth` | TCP 443, 8043, 8088, 8843, 29810-29814 | +| `VoIP` | UDP 5060, 5061, 10000-20000 (SIP + RTP) | +| `DHCP` | UDP 67, 68 | + +--- + +### 2.2 Switch ACL (สำหรับ Omada OC200) + +| ลำดับ | Name | Policy | Source | Destination | Ports | +| :--- | :------------------------ | :----- | :---------------- | :---------------------------- | :---------------------------------------- | +| 1 | 01 Allow-User-DHCP | Allow | Network → VLAN 30 | IP → 192.168.30.1 | Port Group → DHCP | +| 2 | 02 Allow-Guest-DHCP | Allow | Network → VLAN 70 | IP → 192.168.70.1 | Port Group → DHCP | +| 3 | 03 Allow-WiFi-Auth | Allow | Network → VLAN 30 | IP Group → Omada-Controller | Port Group → Omada-Auth | +| 4 | 04 Allow-Guest-WiFi-Auth | Allow | Network → VLAN 70 | IP Group → Omada-Controller | Port Group → Omada-Auth | +| 5 | 05 Isolate-Guests | Deny | Network → VLAN 70 | Network → VLAN 10, 20, 30, 60 | All | +| 6 | 06 Isolate-Servers | Deny | Network → VLAN 10 | Network → VLAN 30 (USER) | All | +| 7 | 07 Block-User-to-Mgmt | Deny | Network → VLAN 30 | Network → VLAN 20 (MGMT) | All | +| 8 | 08 Allow-User-to-Services | Allow | Network → VLAN 30 | IP → QNAP (192.168.10.8) | Port Group → Web (443,8443, 80, 81, 2222) | +| 9 | 09 Allow-Voice-to-User | Allow | Network → VLAN 50 | Network → VLAN 30,50 | All | +| 10 | 10 Allow-MGMT-to-All | Allow | Network → VLAN 20 | Any | All | +| 11 | 11 Allow-Server-Internal | Allow | IP Group : Server | IP Group : Server | All | +| 12 | 12 Allow-Server → CCTV | Allow | IP Group : Server | Network → VLAN 40 (CCTV) | All | +| 13 | 100 (Default) | Deny | Any | Any | All | + +> ⚠️ **หมายเหตุสำคัญ - ลำดับ ACL:** +> 1. **Allow rules ก่อน** - DHCP (#1-2) และ WiFi-Auth (#3-4) ต้องอยู่ **บนสุด** +> 2. **Isolate/Deny rules ถัดมา** - (#5-7) block traffic ที่ไม่ต้องการ +> 3. **Allow specific rules** - (#8-12) อนุญาต traffic ที่เหลือ +> 4. **Default Deny ล่าสุด** - (#13) block ทุกอย่างที่ไม่ match + +--- + +### 2.3 Gateway ACL (สำหรับ Omada ER7206) + +| ลำดับ | Name | Policy | Direction | PROTOCOLS | Source | Destination | +| :--- | :---------------------- | :----- | :-------- | :-------- | :------------------- | :--------------------------- | +| 1 | 01 Blacklist | Deny | [WAN2] IN | All | IP Group:Blacklist | IP Group:Internal | +| 2 | 02 Geo | Permit | [WAN2] IN | All | Location Group:Allow | IP Group:Internal | +| 3 | 03 Allow-Voice-Internet | Permit | LAN->WAN | UDP | Network → VLAN 50 | Any | +| 4 | 04 Internal → Internet | Permit | LAN->WAN | All | IP Group:Internal | Domain Group:DomainGroup_Any | + +> 💡 **หมายเหตุ:** Rule #3 `Allow-Voice-Internet` อนุญาต IP Phone (VLAN 50) เชื่อมต่อ Cloud PBX ภายนอก ผ่าน Port Group → VoIP (UDP 5060, 5061, 10000-20000) + +--- + +## 3. 🚪 Port Forwarding (การเปิด Service สู่สาธารณะ) + +ส่วนนี้ไม่ใช่ Firewall ACL แต่จำเป็นเพื่อให้คนนอกเข้าใช้งานได้ครับ +ไปที่ `Settings > Transmission > Port Forwarding` + +สร้างกฎเพื่อส่งต่อการจราจรจาก WAN (อินเทอร์เน็ต) ไปยัง Nginx Proxy Manager (NPM) ที่อยู่บน QNAP (VLAN 10) + +* **Name:** Allow-NPM-HTTPS +* **External Port:** 443 +* **Internal Port:** 443 +* **Internal IP:** `192.168.10.8` (IP ของ QNAP) +* **Protocol:** TCP + +* **Name:** Allow-NPM-HTTP (สำหรับ Let's Encrypt) +* **External Port:** 80 +* **Internal Port:** 80 +* **Internal IP:** `192.168.10.8` (IP ของ QNAP) +* **Protocol:** TCP + +### สรุปผังการเชื่อมต่อ + +1. **ผู้ใช้ภายนอก** -> `https://lcbp3.np-dms.work` +2. **ER7206** รับที่ Port 443 +3. **Port Forwarding** ส่งต่อไปยัง `192.168.10.8:443` (QNAP NPM) +4. **NPM** (บน QNAP) ส่งต่อไปยัง `backend:3000` หรือ `frontend:3000` ภายใน Docker +5. **ผู้ใช้ภายใน (Office)** -> `https://lcbp3.np-dms.work` +6. **Firewall ACL** (กฎข้อ 4) อนุญาตให้ VLAN 30 คุยกับ `192.168.10.8:443` +7. (ขั้นตอนที่ 3-4 ทำงานเหมือนเดิม) + +การตั้งค่าตามนี้จะช่วยแยกส่วน Server ของคุณออกจากเครือข่ายพนักงานอย่างชัดเจน ซึ่งปลอดภัยกว่าการวางทุกอย่างไว้ในวง LAN เดียวกันมากครับ diff --git a/specs/08-infrastructure/Service_setting.md b/specs/08-infrastructure/04_Service_setting.md similarity index 98% rename from specs/08-infrastructure/Service_setting.md rename to specs/08-infrastructure/04_Service_setting.md index 666d81d..1577657 100644 --- a/specs/08-infrastructure/Service_setting.md +++ b/specs/08-infrastructure/04_Service_setting.md @@ -1,4 +1,4 @@ -# การติดตั้ง Nginx Proxy Manager (NPM) ใน Docker +# การตั้งค่า Redis และ Elasticsearch --- diff --git a/specs/08-infrastructure/monitoring.md b/specs/08-infrastructure/05_monitoring.md similarity index 100% rename from specs/08-infrastructure/monitoring.md rename to specs/08-infrastructure/05_monitoring.md diff --git a/specs/08-infrastructure/06_backup.md b/specs/08-infrastructure/06_backup.md new file mode 100644 index 0000000..f678a1b --- /dev/null +++ b/specs/08-infrastructure/06_backup.md @@ -0,0 +1,247 @@ +# Backup Strategy สำหรับ LCBP3-DMS + +> 📍 **Deploy on:** ASUSTOR AS5403T (Infrastructure Server) +> 🎯 **Backup Target:** QNAP TS-473A (Application & Database) +> 📄 **Version:** v1.8.0 + +--- + +## Overview + +ระบบ Backup แบบ Pull-based: ASUSTOR ดึงข้อมูลจาก QNAP เพื่อความปลอดภัย +หาก QNAP ถูกโจมตี ผู้โจมตีจะไม่สามารถลบ Backup บน ASUSTOR ได้ + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ BACKUP ARCHITECTURE │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ QNAP (Source) ASUSTOR (Backup Target) │ +│ 192.168.10.8 192.168.10.9 │ +│ │ +│ ┌──────────────┐ SSH/Rsync ┌──────────────────────┐ │ +│ │ MariaDB │ ─────────────▶ │ /volume1/backup/db/ │ │ +│ │ (mysqldump) │ Daily 2AM │ (Restic Repository) │ │ +│ └──────────────┘ └──────────────────────┘ │ +│ │ +│ ┌──────────────┐ ┌──────────────────────┐ │ +│ │ Redis RDB │ ─────────────▶ │ /volume1/backup/ │ │ +│ │ + AOF │ Daily 3AM │ redis/ │ │ +│ └──────────────┘ └──────────────────────┘ │ +│ │ +│ ┌──────────────┐ ┌──────────────────────┐ │ +│ │ App Config │ ─────────────▶ │ /volume1/backup/ │ │ +│ │ + Volumes │ Weekly Sun │ config/ │ │ +│ └──────────────┘ └──────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 1. MariaDB Backup + +### 1.1 Daily Database Backup Script + +```bash +#!/bin/bash +# File: /volume1/np-dms/scripts/backup-mariadb.sh +# Run on: ASUSTOR (Pull from QNAP) + +DATE=$(date +%Y%m%d_%H%M%S) +BACKUP_DIR="/volume1/backup/db" +QNAP_IP="192.168.10.8" +DB_NAME="lcbp3_db" +DB_USER="root" +DB_PASSWORD="${MARIADB_ROOT_PASSWORD}" + +echo "🔄 Starting MariaDB backup at $DATE" + +# Create backup directory +mkdir -p $BACKUP_DIR + +# Remote mysqldump via SSH +ssh admin@$QNAP_IP "docker exec mariadb mysqldump \ + --single-transaction \ + --routines \ + --triggers \ + -u $DB_USER -p$DB_PASSWORD $DB_NAME" > $BACKUP_DIR/lcbp3_$DATE.sql + +# Compress +gzip $BACKUP_DIR/lcbp3_$DATE.sql + +# Add to Restic repository +restic -r $BACKUP_DIR/restic-repo backup $BACKUP_DIR/lcbp3_$DATE.sql.gz + +# Keep only last 30 days of raw files +find $BACKUP_DIR -name "lcbp3_*.sql.gz" -mtime +30 -delete + +echo "✅ MariaDB backup complete: lcbp3_$DATE.sql.gz" +``` + +### 1.2 Cron Schedule (ASUSTOR) + +```cron +# MariaDB daily backup at 2 AM +0 2 * * * /volume1/np-dms/scripts/backup-mariadb.sh >> /var/log/backup-mariadb.log 2>&1 +``` + +--- + +## 2. Redis Backup + +### 2.1 Redis Backup Script + +```bash +#!/bin/bash +# File: /volume1/np-dms/scripts/backup-redis.sh +# Run on: ASUSTOR (Pull from QNAP) + +DATE=$(date +%Y%m%d_%H%M%S) +BACKUP_DIR="/volume1/backup/redis" +QNAP_IP="192.168.10.8" + +echo "🔄 Starting Redis backup at $DATE" + +mkdir -p $BACKUP_DIR + +# Trigger BGSAVE on QNAP Redis +ssh admin@$QNAP_IP "docker exec cache redis-cli BGSAVE" +sleep 10 + +# Copy RDB and AOF files +scp admin@$QNAP_IP:/share/np-dms/services/cache/data/dump.rdb $BACKUP_DIR/redis_$DATE.rdb +scp admin@$QNAP_IP:/share/np-dms/services/cache/data/appendonly.aof $BACKUP_DIR/redis_$DATE.aof + +# Compress +tar -czf $BACKUP_DIR/redis_$DATE.tar.gz \ + $BACKUP_DIR/redis_$DATE.rdb \ + $BACKUP_DIR/redis_$DATE.aof + +# Cleanup raw files +rm $BACKUP_DIR/redis_$DATE.rdb $BACKUP_DIR/redis_$DATE.aof + +echo "✅ Redis backup complete: redis_$DATE.tar.gz" +``` + +### 2.2 Cron Schedule + +```cron +# Redis daily backup at 3 AM +0 3 * * * /volume1/np-dms/scripts/backup-redis.sh >> /var/log/backup-redis.log 2>&1 +``` + +--- + +## 3. Application Config Backup + +### 3.1 Weekly Config Backup Script + +```bash +#!/bin/bash +# File: /volume1/np-dms/scripts/backup-config.sh +# Run on: ASUSTOR (Pull from QNAP) + +DATE=$(date +%Y%m%d) +BACKUP_DIR="/volume1/backup/config" +QNAP_IP="192.168.10.8" + +echo "🔄 Starting config backup at $DATE" + +mkdir -p $BACKUP_DIR + +# Sync Docker compose files and configs +rsync -avz --delete \ + admin@$QNAP_IP:/share/np-dms/ \ + $BACKUP_DIR/np-dms_$DATE/ \ + --exclude='*/data/*' \ + --exclude='*/logs/*' \ + --exclude='node_modules' + +# Compress +tar -czf $BACKUP_DIR/config_$DATE.tar.gz $BACKUP_DIR/np-dms_$DATE + +# Cleanup +rm -rf $BACKUP_DIR/np-dms_$DATE + +echo "✅ Config backup complete: config_$DATE.tar.gz" +``` + +### 3.2 Cron Schedule + +```cron +# Config weekly backup on Sunday at 4 AM +0 4 * * 0 /volume1/np-dms/scripts/backup-config.sh >> /var/log/backup-config.log 2>&1 +``` + +--- + +## 4. Retention Policy + +| Backup Type | Frequency | Retention | Storage Est. | +| :---------- | :-------- | :-------- | :----------- | +| MariaDB | Daily | 30 days | ~5GB/month | +| Redis | Daily | 7 days | ~500MB | +| Config | Weekly | 4 weeks | ~200MB | +| Restic | Daily | 6 months | Deduplicated | + +--- + +## 5. Restic Repository Setup + +```bash +# Initialize Restic repository (one-time) +restic init -r /volume1/backup/restic-repo + +# Set password in environment +export RESTIC_PASSWORD="your-secure-backup-password" + +# Check repository status +restic -r /volume1/backup/restic-repo snapshots + +# Prune old snapshots (keep 30 daily, 4 weekly, 6 monthly) +restic -r /volume1/backup/restic-repo forget \ + --keep-daily 30 \ + --keep-weekly 4 \ + --keep-monthly 6 \ + --prune +``` + +--- + +## 6. Verification Script + +```bash +#!/bin/bash +# File: /volume1/np-dms/scripts/verify-backup.sh + +echo "📋 Backup Verification Report" +echo "==============================" +echo "" + +# Check latest MariaDB backup +LATEST_DB=$(ls -t /volume1/backup/db/*.sql.gz 2>/dev/null | head -1) +if [ -n "$LATEST_DB" ]; then + echo "✅ Latest DB backup: $LATEST_DB" + echo " Size: $(du -h $LATEST_DB | cut -f1)" +else + echo "❌ No DB backup found!" +fi + +# Check latest Redis backup +LATEST_REDIS=$(ls -t /volume1/backup/redis/*.tar.gz 2>/dev/null | head -1) +if [ -n "$LATEST_REDIS" ]; then + echo "✅ Latest Redis backup: $LATEST_REDIS" +else + echo "❌ No Redis backup found!" +fi + +# Check Restic repository +echo "" +echo "📦 Restic Snapshots:" +restic -r /volume1/backup/restic-repo snapshots --latest 5 +``` + +--- + +> 📝 **หมายเหตุ**: เอกสารนี้อ้างอิงจาก Architecture Document **v1.8.0** diff --git a/specs/08-infrastructure/07_disaster_recovery.md b/specs/08-infrastructure/07_disaster_recovery.md new file mode 100644 index 0000000..9e2cbdc --- /dev/null +++ b/specs/08-infrastructure/07_disaster_recovery.md @@ -0,0 +1,209 @@ +# Disaster Recovery Plan สำหรับ LCBP3-DMS + +> 📍 **Version:** v1.8.0 +> 🖥️ **Primary Server:** QNAP TS-473A (Application & Database) +> 💾 **Backup Server:** ASUSTOR AS5403T (Infrastructure & Backup) + +--- + +## RTO/RPO Targets + +| Scenario | RTO | RPO | Priority | +| :-------------------------- | :------ | :----- | :------- | +| Single backend node failure | 0 min | 0 | P0 | +| Redis failure | 5 min | 0 | P0 | +| MariaDB failure | 10 min | 0 | P0 | +| QNAP total failure | 2 hours | 15 min | P1 | +| Data corruption | 4 hours | 1 day | P2 | + +--- + +## 1. Quick Recovery Procedures + +### 1.1 Service Not Responding + +```bash +# Check container status +docker ps -a | grep + +# Restart specific service +docker restart + +# Check logs for errors +docker logs --tail 100 +``` + +### 1.2 Redis Failure + +```bash +# Check status +docker exec cache redis-cli ping + +# Restart +docker restart cache + +# Verify +docker exec cache redis-cli ping +``` + +### 1.3 MariaDB Failure + +```bash +# Check status +docker exec mariadb mysql -u root -p -e "SELECT 1" + +# Restart +docker restart mariadb + +# Wait for startup +sleep 30 + +# Verify +docker exec mariadb mysql -u root -p -e "SHOW DATABASES" +``` + +--- + +## 2. Full System Recovery + +### 2.1 Recovery Prerequisites (ASUSTOR) + +ตรวจสอบว่า Backup files พร้อมใช้งาน: + +```bash +# SSH to ASUSTOR +ssh admin@192.168.10.9 + +# List available backups +ls -la /volume1/backup/db/ +ls -la /volume1/backup/redis/ +ls -la /volume1/backup/config/ + +# Check Restic snapshots +restic -r /volume1/backup/restic-repo snapshots +``` + +### 2.2 QNAP Recovery Script + +```bash +#!/bin/bash +# File: /volume1/np-dms/scripts/disaster-recovery.sh +# Run on: ASUSTOR (Push to QNAP) + +QNAP_IP="192.168.10.8" +BACKUP_DIR="/volume1/backup" + +echo "🚨 Starting Disaster Recovery..." +echo "================================" + +# 1. Restore Docker Network +echo "1️⃣ Creating Docker network..." +ssh admin@$QNAP_IP "docker network create lcbp3 || true" + +# 2. Restore config files +echo "2️⃣ Restoring configuration files..." +LATEST_CONFIG=$(ls -t $BACKUP_DIR/config/*.tar.gz | head -1) +tar -xzf $LATEST_CONFIG -C /tmp/ +rsync -avz /tmp/np-dms/ admin@$QNAP_IP:/share/np-dms/ + +# 3. Start infrastructure services +echo "3️⃣ Starting MariaDB..." +ssh admin@$QNAP_IP "cd /share/np-dms/mariadb && docker-compose up -d" +sleep 30 + +# 4. Restore database +echo "4️⃣ Restoring database..." +LATEST_DB=$(ls -t $BACKUP_DIR/db/*.sql.gz | head -1) +gunzip -c $LATEST_DB | ssh admin@$QNAP_IP "docker exec -i mariadb mysql -u root -p\$MYSQL_ROOT_PASSWORD lcbp3_db" + +# 5. Start Redis +echo "5️⃣ Starting Redis..." +ssh admin@$QNAP_IP "cd /share/np-dms/services && docker-compose up -d cache" + +# 6. Restore Redis data (if needed) +echo "6️⃣ Restoring Redis data..." +LATEST_REDIS=$(ls -t $BACKUP_DIR/redis/*.tar.gz | head -1) +tar -xzf $LATEST_REDIS -C /tmp/ +scp /tmp/redis_*.rdb admin@$QNAP_IP:/share/np-dms/services/cache/data/dump.rdb +ssh admin@$QNAP_IP "docker restart cache" + +# 7. Start remaining services +echo "7️⃣ Starting application services..." +ssh admin@$QNAP_IP "cd /share/np-dms/services && docker-compose up -d" +ssh admin@$QNAP_IP "cd /share/np-dms/npm && docker-compose up -d" + +# 8. Health check +echo "8️⃣ Running health checks..." +sleep 60 +curl -f https://lcbp3.np-dms.work/health || echo "⚠️ Frontend not ready" +curl -f https://backend.np-dms.work/health || echo "⚠️ Backend not ready" + +echo "" +echo "✅ Disaster Recovery Complete" +echo "⚠️ Please verify system functionality manually" +``` + +--- + +## 3. Data Corruption Recovery + +### 3.1 Point-in-Time Recovery (Database) + +```bash +# List available Restic snapshots +restic -r /volume1/backup/restic-repo snapshots + +# Restore specific snapshot +restic -r /volume1/backup/restic-repo restore --target /tmp/restore/ + +# Apply restored backup +gunzip -c /tmp/restore/lcbp3_*.sql.gz | \ + ssh admin@192.168.10.8 "docker exec -i mariadb mysql -u root -p\$MYSQL_ROOT_PASSWORD lcbp3_db" +``` + +### 3.2 Selective Table Recovery + +```bash +# Extract specific tables from backup +gunzip -c /volume1/backup/db/lcbp3_YYYYMMDD.sql.gz | \ + grep -A1000 "CREATE TABLE \`documents\`" | \ + grep -B1000 "UNLOCK TABLES" > /tmp/documents_table.sql + +# Restore specific table +ssh admin@192.168.10.8 "docker exec -i mariadb mysql -u root -p\$MYSQL_ROOT_PASSWORD lcbp3_db" < /tmp/documents_table.sql +``` + +--- + +## 4. Communication & Escalation + +### 4.1 Incident Response + +| Severity | Response Time | Notify | +| :------- | :------------ | :----------------------------- | +| P0 | Immediate | Admin Team + Management | +| P1 | 30 minutes | Admin Team | +| P2 | 2 hours | Admin Team (next business day) | + +### 4.2 Post-Incident Checklist + +- [ ] Identify root cause +- [ ] Document timeline of events +- [ ] Verify all services restored +- [ ] Check data integrity +- [ ] Update monitoring alerts if needed +- [ ] Create incident report + +--- + +## 5. Testing Schedule + +| Test Type | Frequency | Last Tested | Next Due | +| :---------------------- | :-------- | :---------- | :------- | +| Backup Verification | Weekly | - | - | +| Single Service Recovery | Monthly | - | - | +| Full DR Test | Quarterly | - | - | + +--- + +> 📝 **หมายเหตุ**: เอกสารนี้อ้างอิงจาก Architecture Document **v1.8.0** diff --git a/specs/08-infrastructure/08_secrets_management.md b/specs/08-infrastructure/08_secrets_management.md new file mode 100644 index 0000000..d87b8b7 --- /dev/null +++ b/specs/08-infrastructure/08_secrets_management.md @@ -0,0 +1,201 @@ +# Secrets Management สำหรับ LCBP3-DMS + +> 📍 **Version:** v1.8.0 +> ⚠️ **Security Level:** CONFIDENTIAL + +--- + +## Overview + +เอกสารนี้อธิบายวิธีการจัดการ Secrets และ Sensitive Data สำหรับ LCBP3-DMS + +--- + +## 1. Secret Categories + +| Category | Examples | Storage Location | +| :------------------- | :----------------------------- | :----------------------- | +| Database Credentials | `MYSQL_ROOT_PASSWORD` | `.env` file (gitignored) | +| API Keys | `JWT_SECRET`, `REDIS_PASSWORD` | `.env` file (gitignored) | +| SSL Certificates | Let's Encrypt certs | NPM volume | +| SSH Keys | Backup access keys | ASUSTOR secure storage | + +--- + +## 2. Environment File Structure + +### 2.1 Main Environment File + +```bash +# File: /share/np-dms/.env (QNAP) +# ⚠️ This file MUST be in .gitignore + +# === Database === +MYSQL_ROOT_PASSWORD= +MYSQL_DATABASE=lcbp3_db +MYSQL_USER=lcbp3_user +MYSQL_PASSWORD= + +# === Redis === +REDIS_PASSWORD= + +# === Application === +JWT_SECRET= +SESSION_SECRET= + +# === Monitoring === +GRAFANA_PASSWORD= + +# === External Services === +LINE_CHANNEL_SECRET= +LINE_CHANNEL_ACCESS_TOKEN= +SMTP_PASSWORD= +``` + +### 2.2 Docker Compose Override (Optional) + +```yaml +# File: /share/np-dms/docker-compose.override.yml +# For additional local development secrets + +services: + backend: + environment: + - DEBUG_MODE=true + - LOG_LEVEL=debug +``` + +--- + +## 3. Secret Generation + +### 3.1 Generate Strong Passwords + +```bash +# Generate random 32-character password +openssl rand -base64 32 + +# Generate random hex string (for JWT) +openssl rand -hex 64 +``` + +### 3.2 Recommended Password Policy + +| Type | Length | Characters | Example Tool | +| :--------- | :----- | :--------------------- | :------------------------ | +| Database | 24+ | Alphanumeric + symbols | `openssl rand -base64 32` | +| JWT Secret | 64+ | Hex | `openssl rand -hex 64` | +| API Keys | 32+ | Alphanumeric | `openssl rand -base64 32` | + +--- + +## 4. Secret Rotation + +### 4.1 Rotation Schedule + +| Secret Type | Rotation Period | Impact on Services | +| :---------------- | :------------------- | :--------------------- | +| JWT Secret | 90 days | Users need to re-login | +| Database Password | 180 days | Requires restart | +| Redis Password | 180 days | Requires restart | +| SSL Certificates | Auto (Let's Encrypt) | None | + +### 4.2 Rotation Procedure + +```bash +# 1. Update .env file with new secret +nano /share/np-dms/.env + +# 2. Restart affected services +docker-compose up -d --force-recreate backend + +# 3. Verify services are running +docker ps +curl https://backend.np-dms.work/health +``` + +--- + +## 5. Access Control + +### 5.1 Who Has Access + +| Role | .env Access | Server SSH | Backup Access | +| :----------- | :---------- | :--------- | :------------ | +| System Admin | ✅ Full | ✅ Full | ✅ Full | +| DevOps | ✅ Read | ✅ Limited | ❌ None | +| Developer | ❌ None | ❌ None | ❌ None | + +### 5.2 Audit Logging + +```bash +# View SSH login attempts +tail -100 /var/log/auth.log + +# Monitor file access +auditctl -w /share/np-dms/.env -p rwa -k secrets_access +``` + +--- + +## 6. Emergency Procedures + +### 6.1 Secret Compromised + +1. **Immediately** rotate the compromised secret +2. **Check** access logs for unauthorized access +3. **Notify** security team +4. **Document** incident + +### 6.2 Lost Access to Secrets + +1. Contact QNAP Admin for direct access +2. Use backup `.env` from ASUSTOR (encrypted) +3. If both unavailable, regenerate all secrets and reset passwords + +--- + +## 7. Backup of Secrets + +```bash +# Encrypted backup of .env (run on QNAP) +gpg --symmetric --cipher-algo AES256 \ + /share/np-dms/.env -o /tmp/env.gpg + +# Copy to ASUSTOR +scp /tmp/env.gpg admin@192.168.10.9:/volume1/backup/secrets/ + +# Clean up +rm /tmp/env.gpg +``` + +### 7.1 Restore from Backup + +```bash +# Copy from ASUSTOR +scp admin@192.168.10.9:/volume1/backup/secrets/env.gpg /tmp/ + +# Decrypt +gpg --decrypt /tmp/env.gpg > /share/np-dms/.env + +# Clean up +rm /tmp/env.gpg +``` + +--- + +## 8. Checklist + +- [ ] `.env` file exists and is configured +- [ ] `.env` is in `.gitignore` +- [ ] All passwords are strong (24+ characters) +- [ ] JWT secret is 64+ hex characters +- [ ] Encrypted backup of secrets exists on ASUSTOR +- [ ] Access control is properly configured +- [ ] Rotation schedule is documented + +--- + +> ⚠️ **Security Warning**: ห้ามเก็บ secrets ใน version control หรือ commit ไปยัง Git repository +> +> 📝 **หมายเหตุ**: เอกสารนี้อ้างอิงจาก Architecture Document **v1.8.0** diff --git a/specs/08-infrastructure/README.md b/specs/08-infrastructure/README.md index 2d8ef4a..820b3fd 100644 --- a/specs/08-infrastructure/README.md +++ b/specs/08-infrastructure/README.md @@ -6,7 +6,7 @@ > 🌐 **Domain:** `*.np-dms.work` (IP: 159.192.126.103) > 🔒 **Network:** `lcbp3` (Docker External Network) > 📄 **Version:** v1.8.0 (aligned with 01-02-architecture.md) -dckr_pat_VzAvAsjeHB3TORZ7vX0kSABIeKI + --- ## 🏢 Hardware Infrastructure @@ -89,16 +89,17 @@ dckr_pat_VzAvAsjeHB3TORZ7vX0kSABIeKI ### Static IP Allocation (Key Devices) -| VLAN | Device | IP Address | Role | -| :--------- | :------ | :------------- | :------------------ | -| SERVER(10) | QNAP | 192.168.10.8 | App/DB Server | -| SERVER(10) | ASUSTOR | 192.168.10.9 | Infra/Backup Server | -| MGMT(20) | ER7206 | 192.168.20.1 | Gateway/Router | -| MGMT(20) | SG2428P | 192.168.20.2 | Core Switch | -| MGMT(20) | AMPCOM | 192.168.20.3 | Server Switch | -| MGMT(20) | OC200 | 192.168.20.250 | Omada Controller | -| USER(30) | Printer | 192.168.30.222 | Kyocera CS3554ci | -| CCTV(40) | NVR | 192.168.40.100 | HikVision NVR | +| VLAN | Device | IP Address | Role | +| :--------- | :------- | :---------------------------- | :------------------ | +| SERVER(10) | QNAP | 192.168.10.8 | App/DB Server | +| SERVER(10) | ASUSTOR | 192.168.10.9 | Infra/Backup Server | +| MGMT(20) | ER7206 | 192.168.20.1 | Gateway/Router | +| MGMT(20) | SG2428P | 192.168.20.2 | Core Switch | +| MGMT(20) | AMPCOM | 192.168.20.3 | Server Switch | +| MGMT(20) | OC200 | 192.168.20.250 | Omada Controller | +| USER(30) | Printer | 192.168.30.222 | Kyocera CS3554ci | +| CCTV(40) | NVR | 192.168.40.200 | HikVision NVR | +| VOICE(50) | IP Phone | 192.168.50.211-192.168.50.221 | IP Phone | ### Network Equipment @@ -146,21 +147,22 @@ graph TB end subgraph OtherSwitches["Distribution"] - CCTV_SW[("🔲 TL-SL1226P
CCTV")] - PHONE_SW[("🔲 SG1210P
IP Phone")] - ADMIN_SW[("🔲 ES205G
Admin")] + OC200[("🔲OC200
Omada Controller")] + CCTV_SW[("🔲 TL-SL1226P
CCTV Switch")] + PHONE_SW[("🔲 SG1210P
IP Phone Switch")] + ADMIN_SW[("🔲 ES205G
Admin Switch")] end WAN --> R - R -->|Port 3| CS - CS -->|LAG Port 3-4| SS + R -->|Port 3 - Port 1| CS + CS -->|LAG Port 3| SS SS -->|Port 3-4 LACP| QNAP SS -->|Port 5-6 LACP| ASUSTOR - SS -->|Port 8| ADMIN_SW CS -->|Port 5-20| AP + CS -->|Port 2| OC200 CS -->|SFP 25| CCTV_SW CS -->|SFP 26| PHONE_SW - CS -->|Port 24| ADMIN_SW + CS -->|Port 21| ADMIN_SW ``` --- @@ -189,12 +191,12 @@ graph TB ### Infrastructure Services (ASUSTOR) -| ไฟล์ | Application | Services | Path บน ASUSTOR | -| :----------------------------- | :----------------- | :--------------------------------------------------- | :---------------------------- | -| [monitoring.md](monitoring.md) | `lcbp3-monitoring` | `prometheus`, `grafana`, `node-exporter`, `cadvisor` | `/volume1/np-dms/monitoring/` | -| *(NEW)* backup.md | `lcbp3-backup` | `restic`, `borg` | `/volume1/np-dms/backup/` | -| *(NEW)* registry.md | `lcbp3-registry` | `registry` | `/volume1/np-dms/registry/` | -| *(NEW)* uptime-kuma.md | `lcbp3-uptime` | `uptime-kuma` | `/volume1/np-dms/uptime/` | +| ไฟล์ | Application | Services | Path บน ASUSTOR | +| :--------------------------------------------------- | :----------------- | :--------------------------------------------------- | :---------------------------- | +| [05_monitoring.md](05_monitoring.md) | `lcbp3-monitoring` | `prometheus`, `grafana`, `node-exporter`, `cadvisor` | `/volume1/np-dms/monitoring/` | +| [06_backup.md](06_backup.md) | `lcbp3-backup` | `restic`, Pull-based strategy | `/volume1/np-dms/backup/` | +| [07_disaster_recovery.md](07_disaster_recovery.md) | - | DR Plan, RTO/RPO Targets | - | +| [08_secrets_management.md](08_secrets_management.md) | - | Secrets & Credentials Management | - | --- diff --git a/specs/08-infrastructure/Securities.md b/specs/08-infrastructure/Securities.md deleted file mode 100644 index 92de76f..0000000 --- a/specs/08-infrastructure/Securities.md +++ /dev/null @@ -1,119 +0,0 @@ -สวัสดีครับ! การตั้งค่า Network Segmentation และ Firewall Rules เป็นขั้นตอนที่ฉลาดมากครับ โดยเฉพาะเมื่อคุณมี Services ที่ต้องเปิดสู่ Public (เช่น `lcbp3.np-dms.work`) และ Services ภายใน (เช่น `db.np-dms.work`) - -สำหรับอุปกรณ์ Omada (ER7206 + OC200) กลยุทธ์หลักคือการใช้ **VLANs (Virtual LANs)** เพื่อแบ่งกลุ่มอุปกรณ์ และใช้ **Firewall ACLs (Access Control Lists)** เพื่อควบคุมการจราจรระหว่างกลุ่มเหล่านั้น - -นี่คือคำแนะนำตามแนวทาง "Zero Trust" ที่ปรับให้เข้ากับสถาปัตยกรรมของคุณครับ - ---- - -## 1. 🌐 การแบ่งส่วนเครือข่าย (VLAN Segmentation) - -ใน Omada Controller (OC200) ให้คุณไปที่ `Settings > Wired Networks > LAN` และสร้างเครือข่ายย่อย (VLANs) ดังนี้: - -* **VLAN 1 (Default): Management** - * **IP Range:** 192.168.1.x - * **วัตถุประสงค์:** ใช้สำหรับอุปกรณ์ Network (ER7206, OC200, Switches) และ PC ของผู้ดูแลระบบ (Admin) เท่านั้น - -* **VLAN 10: Servers (DMZ)** - * **IP Range:** 192.168.10.x - * **วัตถุประสงค์:** นี่คือ VLAN ที่คุณจะเสียบสาย LAN ของ **QNAP NAS** ครับ QNAP จะได้รับ IP ในกลุ่มนี้ (เช่น `192.168.10.100`) - -* **VLAN 20: Office / Trusted** - * **IP Range:** 192.168.20.x - * **วัตถุประสงค์:** สำหรับ PC, Notebook, และ Wi-Fi ของพนักงานทั่วไปที่ต้องเข้าใช้งานระบบ (เช่น `lcbp3.np-dms.work`) - -* **VLAN 30: Guests / Untrusted** - * **IP Range:** 192.168.30.x - * **วัตถุประสงค์:** สำหรับ Wi-Fi แขก (Guest) ห้ามเข้าถึงเครือข่ายภายในโดยเด็ดขาด - -**การตั้งค่า Port Switch:** -หลังจากสร้าง VLANs แล้ว ให้ไปที่ `Devices` > เลือก Switch ของคุณ > `Ports` > กำหนด Port Profile: - -* Port ที่เสียบ QNAP NAS: ตั้งค่า Profile เป็น **VLAN 10** -* Port ที่เสียบ PC พนักงาน: ตั้งค่า Profile เป็น **VLAN 20** - ---- - -## 2. 🔥 Firewall Rules (ACLs) - -นี่คือหัวใจสำคัญครับ ไปที่ `Settings > Network Security > ACL (Access Control)` - -กฎของ Firewall จะทำงานจากบนลงล่าง (ข้อ 1 ทำก่อนข้อ 2) - -### A. กฎการห้าม (Deny Rules) - สำคัญที่สุด - -**กฎข้อ 1: บล็อก Guest (VLAN 30) ไม่ให้ยุ่งกับใคร** - -* **Name:** Isolate-Guests -* **Policy:** Deny -* **Source:** `Network` -> `VLAN 30` -* **Destination:** `Network` -> `VLAN 1`, `VLAN 10`, `VLAN 20` -* *(กฎนี้จะทำให้ Guest ออกอินเทอร์เน็ตได้อย่างเดียว แต่คุยข้าม VLAN ไม่ได้)* - -**กฎข้อ 2: บล็อก Server (VLAN 10) ไม่ให้โจมตีคนอื่น** - -* **Name:** Isolate-Servers -* **Policy:** Deny -* **Source:** `Network` -> `VLAN 10` -* **Destination:** `Network` -> `VLAN 20` -* *(กฎนี้ป้องกันไม่ให้ Server (QNAP) ที่อาจถูกแฮก เริ่มเชื่อมต่อไปยัง PC ของพนักงาน (VLAN 20) เพื่อแพร่กระจาย Malware)* - -**กฎข้อ 3: บล็อก Office ไม่ให้เข้าหน้า Admin** - -* **Name:** Block-Office-to-Management -* **Policy:** Deny -* **Source:** `Network` -> `VLAN 20` -* **Destination:** `Network` -> `VLAN 1` -* *(ป้องกันไม่ให้พนักงานทั่วไปเข้าหน้าตั้งค่า Router หรือ Controller)* - -### B. กฎการอนุญาต (Allow Rules) - -**กฎข้อ 4: อนุญาตให้ Office (VLAN 20) ใช้งาน Services ที่จำเป็น** - -* **Name:** Allow-Office-to-Services -* **Policy:** Allow -* **Source:** `Network` -> `VLAN 20` -* **Destination:** `IP Group` -> (สร้าง Group ชื่อ `QNAP_Services` ชี้ไปที่ `192.168.10.100` (IP ของ QNAP)) -* **Port:** `Service` -> (สร้าง Port Group ชื่อ `Web_Services`): - * TCP 443 (HTTPS - สำหรับทุก Service เช่น lcbp3, git, pma) - * TCP 80 (HTTP - สำหรับ NPM redirect) - * TCP 81 (NPM Admin UI) - * TCP 2222 (Gitea SSH) - * (ไม่จำเป็นต้องเปิด Port 3000, 3003, 5678, 89 เพราะ NPM จัดการให้แล้ว) - -### C. กฎสุดท้าย (Default) - -Omada มักจะมีกฎ "Allow All" อยู่ล่างสุด ให้ปล่อยไว้ หรือถ้าคุณต้องการความปลอดภัยสูงสุด (Zero Trust) ให้เปลี่ยนกฎสุดท้ายเป็น "Deny All" (แต่ต้องมั่นใจว่ากฎ Allow ของคุณครอบคลุมทั้งหมดแล้ว) - ---- - -## 3. 🚪 Port Forwarding (การเปิด Service สู่สาธารณะ) - -ส่วนนี้ไม่ใช่ Firewall ACL แต่จำเป็นเพื่อให้คนนอกเข้าใช้งานได้ครับ -ไปที่ `Settings > Transmission > Port Forwarding` - -สร้างกฎเพื่อส่งต่อการจราจรจาก WAN (อินเทอร์เน็ต) ไปยัง Nginx Proxy Manager (NPM) ที่อยู่บน QNAP (VLAN 10) - -* **Name:** Allow-NPM-HTTPS -* **External Port:** 443 -* **Internal Port:** 443 -* **Internal IP:** `192.168.10.100` (IP ของ QNAP) -* **Protocol:** TCP - -* **Name:** Allow-NPM-HTTP (สำหรับ Let's Encrypt) -* **External Port:** 80 -* **Internal Port:** 80 -* **Internal IP:** `192.168.10.100` (IP ของ QNAP) -* **Protocol:** TCP - -### สรุปผังการเชื่อมต่อ - -1. **ผู้ใช้ภายนอก** -> `https://lcbp3.np-dms.work` -2. **ER7206** รับที่ Port 443 -3. **Port Forwarding** ส่งต่อไปยัง `192.168.10.100:443` (QNAP NPM) -4. **NPM** (บน QNAP) ส่งต่อไปยัง `backend:3000` หรือ `frontend:3000` ภายใน Docker -5. **ผู้ใช้ภายใน (Office)** -> `https://lcbp3.np-dms.work` -6. **Firewall ACL** (กฎข้อ 4) อนุญาตให้ VLAN 20 คุยกับ `192.168.10.100:443` -7. (ขั้นตอนที่ 3-4 ทำงานเหมือนเดิม) - -การตั้งค่าตามนี้จะช่วยแยกส่วน Server ของคุณออกจากเครือข่ายพนักงานอย่างชัดเจน ซึ่งปลอดภัยกว่าการวางทุกอย่างไว้ในวง LAN เดียวกันมากครับ