260209:1642 Update Infrastructure 3
Some checks failed
Spec Validation / validate-markdown (push) Has been cancelled
Spec Validation / validate-diagrams (push) Has been cancelled
Spec Validation / check-todos (push) Has been cancelled

This commit is contained in:
admin
2026-02-09 16:42:27 +07:00
parent fe0f8aade7
commit d5e37d986f
10 changed files with 926 additions and 254 deletions

View File

@@ -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
docker exec cache redis-cli BGSAVE
# Wait for save to complete
sleep 10
# Copy RDB file
docker cp lcbp3-redis-$i:/data/dump.rdb \
$BACKUP_DIR/redis-${i}_${DATE}.rdb
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
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 <node-id>
docker exec lcbp3-redis-1 redis-cli --cluster add-node <new-node-ip>:6379 <cluster-ip>: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:

View File

@@ -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<br/>(mysqldump)"]
Redis["📦 Redis<br/>(RDB + AOF)"]
Config["⚙️ App Config<br/>+ Volumes"]
end
subgraph ASUSTOR["💾 ASUSTOR AS5403T (Target)"]
direction TB
BackupDB["📁 /volume1/backup/db/<br/>(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
```
---

View File

@@ -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 เดียวกันมากครับ

View File

@@ -1,4 +1,4 @@
# การติดตั้ง Nginx Proxy Manager (NPM) ใน Docker
# การตั้งค่า Redis และ Elasticsearch
---

View File

@@ -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**

View File

@@ -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 <service-name>
# Restart specific service
docker restart <container-name>
# Check logs for errors
docker logs <container-name> --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 <snapshot-id> --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**

View File

@@ -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=<strong-password>
MYSQL_DATABASE=lcbp3_db
MYSQL_USER=lcbp3_user
MYSQL_PASSWORD=<strong-password>
# === Redis ===
REDIS_PASSWORD=<strong-password>
# === Application ===
JWT_SECRET=<random-256-bit-string>
SESSION_SECRET=<random-256-bit-string>
# === Monitoring ===
GRAFANA_PASSWORD=<admin-password>
# === External Services ===
LINE_CHANNEL_SECRET=<line-secret>
LINE_CHANNEL_ACCESS_TOKEN=<line-token>
SMTP_PASSWORD=<email-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**

View File

@@ -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
@@ -90,7 +90,7 @@ 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 |
@@ -98,7 +98,8 @@ dckr_pat_VzAvAsjeHB3TORZ7vX0kSABIeKI
| 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 |
| 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<br/>CCTV")]
PHONE_SW[("🔲 SG1210P<br/>IP Phone")]
ADMIN_SW[("🔲 ES205G<br/>Admin")]
OC200[("🔲OC200<br/>Omada Controller")]
CCTV_SW[("🔲 TL-SL1226P<br/>CCTV Switch")]
PHONE_SW[("🔲 SG1210P<br/>IP Phone Switch")]
ADMIN_SW[("🔲 ES205G<br/>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
```
---
@@ -190,11 +192,11 @@ 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/` |
| :--------------------------------------------------- | :----------------- | :--------------------------------------------------- | :---------------------------- |
| [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 | - |
---

View File

@@ -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 เดียวกันมากครับ