260209:1642 Update Infrastructure 3
This commit is contained in:
@@ -41,7 +41,7 @@ services:
|
|||||||
cpus: '2.0'
|
cpus: '2.0'
|
||||||
memory: 1.5G
|
memory: 1.5G
|
||||||
networks:
|
networks:
|
||||||
lcbp3-network:
|
lcbp3:
|
||||||
external: true
|
external: true
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -120,19 +120,18 @@ services:
|
|||||||
container_name: lcbp3-backend-1
|
container_name: lcbp3-backend-1
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
- DB_HOST=mariadb-primary
|
- DB_HOST=mariadb
|
||||||
- REDIS_CLUSTER_NODES=redis-1:6379,redis-2:6379,redis-3:6379
|
- REDIS_HOST=cache
|
||||||
|
- REDIS_PORT=6379
|
||||||
- NUMBERING_LOCK_TIMEOUT=5000
|
- NUMBERING_LOCK_TIMEOUT=5000
|
||||||
- NUMBERING_RESERVATION_TTL=300
|
- NUMBERING_RESERVATION_TTL=300
|
||||||
ports:
|
ports:
|
||||||
- "3001:3000"
|
- "3001:3000"
|
||||||
depends_on:
|
depends_on:
|
||||||
- mariadb-primary
|
- mariadb
|
||||||
- redis-1
|
- cache
|
||||||
- redis-2
|
|
||||||
- redis-3
|
|
||||||
networks:
|
networks:
|
||||||
- lcbp3-network
|
- lcbp3
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
||||||
@@ -145,19 +144,20 @@ services:
|
|||||||
container_name: lcbp3-backend-2
|
container_name: lcbp3-backend-2
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
- DB_HOST=mariadb-primary
|
- DB_HOST=mariadb
|
||||||
- REDIS_CLUSTER_NODES=redis-1:6379,redis-2:6379,redis-3:6379
|
- REDIS_HOST=cache
|
||||||
|
- REDIS_PORT=6379
|
||||||
ports:
|
ports:
|
||||||
- "3002:3000"
|
- "3002:3000"
|
||||||
depends_on:
|
depends_on:
|
||||||
- mariadb-primary
|
- mariadb
|
||||||
- redis-1
|
- cache
|
||||||
networks:
|
networks:
|
||||||
- lcbp3-network
|
- lcbp3
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
lcbp3-network:
|
lcbp3:
|
||||||
external: true
|
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%)
|
2. **Sequence Utilization** - Current usage vs max (alert >90%)
|
||||||
3. **Lock Wait Time (p95)** - Performance indicator
|
3. **Lock Wait Time (p95)** - Performance indicator
|
||||||
4. **Lock Failures** - System health 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
|
6. **Database Connection Pool** - Resource usage
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -426,31 +426,31 @@ BACKUP_DIR="/backups/redis"
|
|||||||
|
|
||||||
mkdir -p $BACKUP_DIR
|
mkdir -p $BACKUP_DIR
|
||||||
|
|
||||||
for i in 1 2 3; do
|
echo "Backing up Redis..."
|
||||||
echo "Backing up redis-$i..."
|
|
||||||
|
|
||||||
# Trigger BGSAVE
|
# Trigger BGSAVE
|
||||||
docker exec lcbp3-redis-$i redis-cli -p 6379 BGSAVE
|
docker exec cache redis-cli BGSAVE
|
||||||
|
|
||||||
# Wait for save to complete
|
# Wait for save to complete
|
||||||
sleep 10
|
sleep 10
|
||||||
|
|
||||||
# Copy RDB file
|
# Copy RDB file
|
||||||
docker cp lcbp3-redis-$i:/data/dump.rdb \
|
docker cp cache:/data/dump.rdb \
|
||||||
$BACKUP_DIR/redis-${i}_${DATE}.rdb
|
$BACKUP_DIR/redis_${DATE}.rdb
|
||||||
|
|
||||||
# Copy AOF file
|
# Copy AOF file
|
||||||
docker cp lcbp3-redis-$i:/data/appendonly.aof \
|
docker cp cache:/data/appendonly.aof \
|
||||||
$BACKUP_DIR/redis-${i}_${DATE}.aof
|
$BACKUP_DIR/redis_${DATE}.aof
|
||||||
done
|
|
||||||
|
|
||||||
# Compress
|
# 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
|
# 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
|
### 5.3 Recovery Procedures
|
||||||
@@ -490,17 +490,19 @@ echo "✅ Restore complete"
|
|||||||
echo "🔄 Please verify sequence integrity"
|
echo "🔄 Please verify sequence integrity"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Scenario 2: Redis Node Failure
|
#### Scenario 2: Redis Failure
|
||||||
```bash
|
```bash
|
||||||
# Automatically handled by cluster
|
# Check Redis status
|
||||||
# Node will rejoin cluster when restarted
|
docker exec cache redis-cli ping
|
||||||
|
|
||||||
# Check cluster status
|
# If Redis is down, restart container
|
||||||
docker exec lcbp3-redis-1 redis-cli cluster info
|
docker restart cache
|
||||||
|
|
||||||
# If node is failed, remove and add back
|
# Verify Redis is running
|
||||||
docker exec lcbp3-redis-1 redis-cli --cluster del-node <node-id>
|
docker exec cache redis-cli ping
|
||||||
docker exec lcbp3-redis-1 redis-cli --cluster add-node <new-node-ip>:6379 <cluster-ip>:6379
|
|
||||||
|
# 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`
|
**Alert**: `RedisUnavailable`
|
||||||
|
|
||||||
**Steps**:
|
**Steps**:
|
||||||
1. Verify all nodes down
|
1. Verify Redis is down
|
||||||
```bash
|
```bash
|
||||||
for i in {1..3}; do
|
docker exec cache redis-cli ping || echo "Redis DOWN"
|
||||||
docker exec lcbp3-redis-$i redis-cli ping || echo "Node $i DOWN"
|
|
||||||
done
|
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Check system falls back to DB-only mode
|
2. Check system falls back to DB-only mode
|
||||||
@@ -777,11 +777,11 @@ echo "⚠️ Please verify system functionality manually"
|
|||||||
# Should show: fallback_mode: true
|
# Should show: fallback_mode: true
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Restart Redis cluster
|
3. Restart Redis container
|
||||||
```bash
|
```bash
|
||||||
docker-compose -f docker-compose-redis.yml restart
|
docker restart cache
|
||||||
sleep 30
|
sleep 10
|
||||||
./scripts/check-redis-cluster.sh
|
docker exec cache redis-cli ping
|
||||||
```
|
```
|
||||||
|
|
||||||
4. If restart fails, restore from backup
|
4. If restart fails, restore from backup
|
||||||
@@ -796,6 +796,9 @@ echo "⚠️ Please verify system functionality manually"
|
|||||||
```
|
```
|
||||||
|
|
||||||
6. Review logs for root cause
|
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;
|
ANALYZE TABLE document_numbering_sequences;
|
||||||
```
|
```
|
||||||
|
|
||||||
### 8.2 Redis Memory Optimization
|
### 9.2 Redis Memory Optimization
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check memory usage
|
# Check memory usage
|
||||||
docker exec lcbp3-redis-1 redis-cli INFO memory
|
docker exec cache redis-cli INFO memory
|
||||||
|
|
||||||
# If memory high, check keys
|
# If memory high, check keys
|
||||||
docker exec lcbp3-redis-1 redis-cli --bigkeys
|
docker exec cache redis-cli --bigkeys
|
||||||
|
|
||||||
# Set maxmemory policy
|
# Set maxmemory policy
|
||||||
docker exec lcbp3-redis-1 redis-cli CONFIG SET maxmemory 2gb
|
docker exec cache 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-policy allkeys-lru
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -878,7 +881,7 @@ FLUSH PRIVILEGES;
|
|||||||
```yaml
|
```yaml
|
||||||
# docker-compose-network.yml
|
# docker-compose-network.yml
|
||||||
networks:
|
networks:
|
||||||
lcbp3-network:
|
lcbp3:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
ipam:
|
ipam:
|
||||||
config:
|
config:
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Data Flow Diagram
|
## 1. Data Flow Diagram
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TB
|
flowchart TB
|
||||||
@@ -52,7 +52,7 @@ flowchart TB
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Docker Management View
|
## 2. Docker Management View
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TB
|
flowchart TB
|
||||||
@@ -85,7 +85,7 @@ flowchart TB
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Security Zones Diagram
|
## 3. Security Zones Diagram
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TB
|
flowchart TB
|
||||||
@@ -126,7 +126,7 @@ flowchart TB
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. แผนผังการเชื่อมต่อเครือข่าย (Network Flow)
|
## 4. แผนผังการเชื่อมต่อเครือข่าย (Network Flow)
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TD
|
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 |
|
ไฟล์ `03_Securities.md` ประกอบด้วย:
|
||||||
| :---- | :--------------------- | :-------- | :---------------- | :------------------------ | :----------------------------------- |
|
- 🌐 VLAN Segmentation
|
||||||
| **1** | Isolate-Guests | **Deny** | Network → VLAN 70 | Network → VLAN 10, 20, 30 | All |
|
- 🔥 Firewall Rules (IP Groups, Port Groups, Switch ACL, Gateway ACL)
|
||||||
| **2** | Isolate-Servers | **Deny** | Network → VLAN 10 | Network → VLAN 30 (USER) | All |
|
- 🚪 Port Forwarding Configuration
|
||||||
| **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 จากภายนอก
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -274,35 +256,33 @@ graph TD
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 9. Backup Flow
|
## 7. Backup Flow
|
||||||
|
|
||||||
```
|
```mermaid
|
||||||
┌────────────────────────────────────────────────────────────────────────┐
|
flowchart LR
|
||||||
│ BACKUP STRATEGY │
|
subgraph QNAP["💾 QNAP TS-473A (Source)"]
|
||||||
├────────────────────────────────────────────────────────────────────────┤
|
direction TB
|
||||||
│ │
|
DB["🗄️ MariaDB<br/>(mysqldump)"]
|
||||||
│ QNAP (Source) ASUSTOR (Target) │
|
Redis["📦 Redis<br/>(RDB + AOF)"]
|
||||||
│ ┌──────────────┐ ┌──────────────────────┐ │
|
Config["⚙️ App Config<br/>+ Volumes"]
|
||||||
│ │ MariaDB │ ──── Daily 2AM ────▶ │ /volume1/backup/db/ │ │
|
end
|
||||||
│ │ (mysqldump) │ │ (Restic Repository) │ │
|
|
||||||
│ └──────────────┘ └──────────────────────┘ │
|
subgraph ASUSTOR["💾 ASUSTOR AS5403T (Target)"]
|
||||||
│ │
|
direction TB
|
||||||
│ ┌──────────────┐ ┌──────────────────────┐ │
|
BackupDB["📁 /volume1/backup/db/<br/>(Restic Repository)"]
|
||||||
│ │ Redis RDB │ ──── Daily 3AM ────▶ │ /volume1/backup/ │ │
|
BackupRedis["📁 /volume1/backup/redis/"]
|
||||||
│ │ + AOF │ │ redis/ │ │
|
BackupConfig["📁 /volume1/backup/config/"]
|
||||||
│ └──────────────┘ └──────────────────────┘ │
|
end
|
||||||
│ │
|
|
||||||
│ ┌──────────────┐ ┌──────────────────────┐ │
|
DB -->|"Daily 2AM"| BackupDB
|
||||||
│ │ App Config │ ──── Weekly ───────▶ │ /volume1/backup/ │ │
|
Redis -->|"Daily 3AM"| BackupRedis
|
||||||
│ │ + Volumes │ Sunday 4AM │ config/ │ │
|
Config -->|"Weekly Sun 4AM"| BackupConfig
|
||||||
│ └──────────────┘ └──────────────────────┘ │
|
|
||||||
│ │
|
subgraph Retention["📋 Retention Policy"]
|
||||||
│ Retention Policy: │
|
R1["Daily: 7 days"]
|
||||||
│ • Daily: 7 days │
|
R2["Weekly: 4 weeks"]
|
||||||
│ • Weekly: 4 weeks │
|
R3["Monthly: 6 months"]
|
||||||
│ • Monthly: 6 months │
|
end
|
||||||
│ │
|
|
||||||
└────────────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
149
specs/08-infrastructure/03_Securities.md
Normal file
149
specs/08-infrastructure/03_Securities.md
Normal 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 เดียวกันมากครับ
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# การติดตั้ง Nginx Proxy Manager (NPM) ใน Docker
|
# การตั้งค่า Redis และ Elasticsearch
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
247
specs/08-infrastructure/06_backup.md
Normal file
247
specs/08-infrastructure/06_backup.md
Normal 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**
|
||||||
209
specs/08-infrastructure/07_disaster_recovery.md
Normal file
209
specs/08-infrastructure/07_disaster_recovery.md
Normal 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**
|
||||||
201
specs/08-infrastructure/08_secrets_management.md
Normal file
201
specs/08-infrastructure/08_secrets_management.md
Normal 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**
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
> 🌐 **Domain:** `*.np-dms.work` (IP: 159.192.126.103)
|
> 🌐 **Domain:** `*.np-dms.work` (IP: 159.192.126.103)
|
||||||
> 🔒 **Network:** `lcbp3` (Docker External Network)
|
> 🔒 **Network:** `lcbp3` (Docker External Network)
|
||||||
> 📄 **Version:** v1.8.0 (aligned with 01-02-architecture.md)
|
> 📄 **Version:** v1.8.0 (aligned with 01-02-architecture.md)
|
||||||
dckr_pat_VzAvAsjeHB3TORZ7vX0kSABIeKI
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🏢 Hardware Infrastructure
|
## 🏢 Hardware Infrastructure
|
||||||
@@ -90,7 +90,7 @@ dckr_pat_VzAvAsjeHB3TORZ7vX0kSABIeKI
|
|||||||
### Static IP Allocation (Key Devices)
|
### Static IP Allocation (Key Devices)
|
||||||
|
|
||||||
| VLAN | Device | IP Address | Role |
|
| VLAN | Device | IP Address | Role |
|
||||||
| :--------- | :------ | :------------- | :------------------ |
|
| :--------- | :------- | :---------------------------- | :------------------ |
|
||||||
| SERVER(10) | QNAP | 192.168.10.8 | App/DB Server |
|
| SERVER(10) | QNAP | 192.168.10.8 | App/DB Server |
|
||||||
| SERVER(10) | ASUSTOR | 192.168.10.9 | Infra/Backup Server |
|
| SERVER(10) | ASUSTOR | 192.168.10.9 | Infra/Backup Server |
|
||||||
| MGMT(20) | ER7206 | 192.168.20.1 | Gateway/Router |
|
| 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) | AMPCOM | 192.168.20.3 | Server Switch |
|
||||||
| MGMT(20) | OC200 | 192.168.20.250 | Omada Controller |
|
| MGMT(20) | OC200 | 192.168.20.250 | Omada Controller |
|
||||||
| USER(30) | Printer | 192.168.30.222 | Kyocera CS3554ci |
|
| 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
|
### Network Equipment
|
||||||
|
|
||||||
@@ -146,21 +147,22 @@ graph TB
|
|||||||
end
|
end
|
||||||
|
|
||||||
subgraph OtherSwitches["Distribution"]
|
subgraph OtherSwitches["Distribution"]
|
||||||
CCTV_SW[("🔲 TL-SL1226P<br/>CCTV")]
|
OC200[("🔲OC200<br/>Omada Controller")]
|
||||||
PHONE_SW[("🔲 SG1210P<br/>IP Phone")]
|
CCTV_SW[("🔲 TL-SL1226P<br/>CCTV Switch")]
|
||||||
ADMIN_SW[("🔲 ES205G<br/>Admin")]
|
PHONE_SW[("🔲 SG1210P<br/>IP Phone Switch")]
|
||||||
|
ADMIN_SW[("🔲 ES205G<br/>Admin Switch")]
|
||||||
end
|
end
|
||||||
|
|
||||||
WAN --> R
|
WAN --> R
|
||||||
R -->|Port 3| CS
|
R -->|Port 3 - Port 1| CS
|
||||||
CS -->|LAG Port 3-4| SS
|
CS -->|LAG Port 3| SS
|
||||||
SS -->|Port 3-4 LACP| QNAP
|
SS -->|Port 3-4 LACP| QNAP
|
||||||
SS -->|Port 5-6 LACP| ASUSTOR
|
SS -->|Port 5-6 LACP| ASUSTOR
|
||||||
SS -->|Port 8| ADMIN_SW
|
|
||||||
CS -->|Port 5-20| AP
|
CS -->|Port 5-20| AP
|
||||||
|
CS -->|Port 2| OC200
|
||||||
CS -->|SFP 25| CCTV_SW
|
CS -->|SFP 25| CCTV_SW
|
||||||
CS -->|SFP 26| PHONE_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)
|
### Infrastructure Services (ASUSTOR)
|
||||||
|
|
||||||
| ไฟล์ | Application | Services | Path บน ASUSTOR |
|
| ไฟล์ | Application | Services | Path บน ASUSTOR |
|
||||||
| :----------------------------- | :----------------- | :--------------------------------------------------- | :---------------------------- |
|
| :--------------------------------------------------- | :----------------- | :--------------------------------------------------- | :---------------------------- |
|
||||||
| [monitoring.md](monitoring.md) | `lcbp3-monitoring` | `prometheus`, `grafana`, `node-exporter`, `cadvisor` | `/volume1/np-dms/monitoring/` |
|
| [05_monitoring.md](05_monitoring.md) | `lcbp3-monitoring` | `prometheus`, `grafana`, `node-exporter`, `cadvisor` | `/volume1/np-dms/monitoring/` |
|
||||||
| *(NEW)* backup.md | `lcbp3-backup` | `restic`, `borg` | `/volume1/np-dms/backup/` |
|
| [06_backup.md](06_backup.md) | `lcbp3-backup` | `restic`, Pull-based strategy | `/volume1/np-dms/backup/` |
|
||||||
| *(NEW)* registry.md | `lcbp3-registry` | `registry` | `/volume1/np-dms/registry/` |
|
| [07_disaster_recovery.md](07_disaster_recovery.md) | - | DR Plan, RTO/RPO Targets | - |
|
||||||
| *(NEW)* uptime-kuma.md | `lcbp3-uptime` | `uptime-kuma` | `/volume1/np-dms/uptime/` |
|
| [08_secrets_management.md](08_secrets_management.md) | - | Secrets & Credentials Management | - |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -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 เดียวกันมากครับ
|
|
||||||
Reference in New Issue
Block a user