Remote Access หรือ การเข้าถึงระบบระยะไกล คือความสามารถในการเชื่อมต่อและควบคุมคอมพิวเตอร์หรือเครือข่ายจากสถานที่ห่างไกล โดยไม่ต้องอยู่หน้าเครื่องจริง ๆ ในยุคปัจจุบัน Remote Access กลายเป็นองค์ประกอบสำคัญของโครงสร้างพื้นฐานด้านไอทีสำหรับองค์กรทุกขนาด
ความสำคัญของ Remote Access ในเชิงความปลอดภัย:
ภาพรวมระบบ Remote Access ที่ปลอดภัย:
flowchart TD
subgraph Internet["🌐 อินเทอร์เน็ต (Internet)"]
U[("👤 ผู้ใช้งาน - Remote User")]
end
subgraph DMZ["🟡 DMZ Zone"]
BH["🛡️ Bastion Host - Jump Server - 203.0.113.10"]
VPN["🔐 VPN Gateway - 203.0.113.20"]
end
subgraph Internal["🟢 Internal Network (10.0.0.0/24)"]
WEB["🖥️ Web Server - 10.0.0.10"]
DB["🗄️ Database Server - 10.0.0.20"]
MGMT["⚙️ Management Server - 10.0.0.30"]
COCKPIT["🌐 Cockpit - 10.0.0.40:9090"]
end
subgraph FW["🔴 Firewall Rules"]
FW1["Port 22 SSH - Allowed from Any"]
FW2["Port 443 HTTPS - Allowed from Any"]
FW3["Port 3389 RDP - Blocked from Internet"]
end
U -->|"SSH + MFA"| BH
U -->|"WireGuard / IPSec"| VPN
BH -->|"ProxyJump"| WEB
BH -->|"ProxyJump"| DB
BH -->|"ProxyJump"| MGMT
VPN -->|"Encrypted Tunnel"| COCKPIT
style Internet fill:#cc241d,color:#ebdbb2,stroke:#fb4934
style DMZ fill:#d79921,color:#1d2021,stroke:#fabd2f
style Internal fill:#98971a,color:#1d2021,stroke:#b8bb26
style FW fill:#458588,color:#ebdbb2,stroke:#83a598
การเปรียบเทียบ Remote Access Solutions:
| เทคโนโลยี | โปรโตคอล | Port เริ่มต้น | การเข้ารหัส | ความซับซ้อน | Use Case หลัก |
|---|---|---|---|---|---|
| SSH | TCP | 22 | AES-256 | ต่ำ | CLI Administration |
| VNC | TCP | 5900 | ไม่มีโดยตรง* | ปานกลาง | GUI Remote Desktop |
| RDP | TCP/UDP | 3389 | TLS 1.2+ | ปานกลาง | Windows Desktop |
| Cockpit | TCP | 9090 | TLS 1.3 | ต่ำ | Web-based Admin |
| VPN+SSH | TCP | 22+Custom | Multi-layer | สูง | Enterprise Access |
* VNC ต้องใช้ร่วมกับ SSH Tunnel หรือ VPN เพื่อการเข้ารหัส
SSH (Secure Shell) คือโปรโตคอลเครือข่ายที่ให้การเชื่อมต่อที่ปลอดภัยระหว่างเครื่องคอมพิวเตอร์ผ่านเครือข่ายที่ไม่ปลอดภัย โดยในสัปดาห์นี้เราจะเรียนรู้ฟีเจอร์ขั้นสูงที่ช่วยเพิ่มความปลอดภัยและประสิทธิภาพ
SSH Agent Forwarding คือกลไกที่ช่วยให้คุณสามารถใช้ SSH Key จากเครื่องต้นทาง (Local) เพื่อยืนยันตัวตนบนเครื่องปลายทางที่สาม โดยไม่ต้องเก็บ Private Key บนเครื่องกลาง
ข้อควรระวัง: Agent Forwarding มีความเสี่ยงด้านความปลอดภัย ควรใช้เฉพาะกับเซิร์ฟเวอร์ที่เชื่อถือได้เท่านั้น
วิธีการทำงานของ SSH Agent Forwarding:
sequenceDiagram
participant L as 💻 Local Machine
participant B as 🛡️ Bastion Host
participant T as 🖥️ Target Server
Note over L: ssh-agent ทำงานอยู่
Note over L: มี Private Key: id_rsa
L->>B: ssh -A user@bastion (เชื่อมต่อพร้อม Forward Agent)
B-->>L: ขอยืนยันตัวตน (Challenge)
L-->>B: Agent ลงนาม Challenge ด้วย Private Key
B-->>L: เชื่อมต่อสำเร็จ ✓
Note over B: Agent Socket ถูก Forward
Note over B: SSH_AUTH_SOCK=/tmp/ssh-xxx/agent.123
B->>T: ssh user@target-server (จาก Bastion)
T-->>L: ขอยืนยันตัวตน (ผ่าน Agent)
L-->>T: Agent ลงนาม (ผ่าน Bastion)
T-->>B: เชื่อมต่อสำเร็จ ✓
การตั้งค่า SSH Agent Forwarding:
# 1. เริ่มต้น SSH Agent
eval "$(ssh-agent -s)"
# Output: Agent pid 12345
# 2. เพิ่ม Private Key เข้า Agent
ssh-add ~/.ssh/id_rsa
# Output: Identity added: /home/user/.ssh/id_rsa
# 3. ตรวจสอบ Key ที่โหลดอยู่
ssh-add -l
# Output: 3072 SHA256:xxxxxxxxxxx user@local (RSA)
# 4. เชื่อมต่อพร้อม Agent Forwarding
ssh -A user@bastion.example.com
ตัวอย่างไฟล์ ~/.ssh/config สำหรับการจัดการ Remote Access:
# ~/.ssh/config - ตั้งค่า SSH Client
# === Bastion Host ===
Host bastion
HostName 203.0.113.10
User admin
Port 22
IdentityFile ~/.ssh/id_ed25519
ForwardAgent yes
ServerAliveInterval 60
ServerAliveCountMax 3
# === Web Server (ผ่าน Bastion) ===
Host webserver
HostName 10.0.0.10
User deploy
Port 22
IdentityFile ~/.ssh/id_ed25519
ProxyJump bastion
ForwardAgent no
# === Database Server (ผ่าน Bastion) ===
Host dbserver
HostName 10.0.0.20
User dba
Port 22
IdentityFile ~/.ssh/id_ed25519
ProxyJump bastion
ForwardAgent no
# === ค่าเริ่มต้นสำหรับทุก Host ===
Host *
AddKeysToAgent yes
IdentitiesOnly yes
StrictHostKeyChecking ask
HashKnownHosts yes
ProxyJump เป็นวิธีที่ทันสมัยและปลอดภัยกว่า Agent Forwarding สำหรับการเชื่อมต่อผ่านเซิร์ฟเวอร์กลาง (Bastion/Jump Host)
# วิธีที่ 1: ใช้ command line
ssh -J user@bastion.example.com user@internal-server.local
# วิธีที่ 2: ใช้หลาย Jump Hosts
ssh -J user@bastion1,user@bastion2 user@target
# วิธีที่ 3: ใช้ ~/.ssh/config (แนะนำ)
# เพิ่มใน config: ProxyJump bastion
# แล้วเชื่อมต่อด้วย:
ssh webserver
ตัวอย่างการใช้งานจริง — คำนวณ Connection Overhead:
สมมติว่าเรามีการเชื่อมต่อดังนี้:
แทนค่า:
ตัวแปร: T_total = เวลารวมสำหรับ Handshake, RTT = Round Trip Time, N_handshake = จำนวน Handshake Rounds (ปกติ 3 RTT สำหรับ SSH + TLS)
สคริปต์ Python สำหรับทดสอบ SSH Connectivity:
#!/usr/bin/env python3
"""
ทดสอบการเชื่อมต่อ SSH ผ่าน ProxyJump
สคริปต์สำหรับตรวจสอบ latency และความพร้อมใช้งานของ SSH hosts
"""
import subprocess
import time
import json
from datetime import datetime
def measure_ssh_latency(host: str, port: int = 22, timeout: int = 10) -> dict:
"""
วัด latency การเชื่อมต่อ SSH ไปยัง host ที่กำหนด
Parameters:
host (str): ชื่อโฮสต์หรือ IP address
port (int): พอร์ต SSH (ค่าเริ่มต้น: 22)
timeout (int): เวลาหมดอายุในวินาที
Returns:
dict: ผลลัพธ์การทดสอบ
"""
start_time = time.time()
try:
result = subprocess.run(
["ssh-keyscan", "-T", str(timeout), "-p", str(port), host],
capture_output=True,
text=True,
timeout=timeout
)
end_time = time.time()
latency_ms = (end_time - start_time) * 1000
return {
"host": host,
"port": port,
"status": "accessible" if result.returncode == 0 else "error",
"latency_ms": round(latency_ms, 2),
"timestamp": datetime.now().isoformat(),
"key_types": [
line.split()[1]
for line in result.stdout.strip().split(' - ') if line
]
}
except subprocess.TimeoutExpired:
return {
"host": host,
"port": port,
"status": "timeout",
"latency_ms": timeout * 1000,
"timestamp": datetime.now().isoformat()
}
except Exception as e:
return {
"host": host,
"port": port,
"status": "error",
"error": str(e),
"timestamp": datetime.now().isoformat()
}
def check_proxyjump_path(hosts: list) -> dict:
"""
ตรวจสอบเส้นทาง ProxyJump ทั้งหมด
Parameters:
hosts (list): รายการ hosts ในลำดับ [bastion, target1, target2, ...]
Returns:
dict: สรุปผลการตรวจสอบทั้งหมด
"""
results = []
total_latency = 0
print(f" - {'='*60}")
print("🔍 SSH ProxyJump Path Analysis")
print(f"{'='*60}")
for host in hosts:
print(f" - ⏳ กำลังทดสอบ: {host['name']} ({host['address']})")
result = measure_ssh_latency(host["address"], host.get("port", 22))
result["name"] = host["name"]
results.append(result)
status_icon = "✅" if result["status"] == "accessible" else "❌"
print(f" {status_icon} Status: {result['status']}")
print(f" ⏱️ Latency: {result['latency_ms']} ms")
if result["status"] == "accessible":
total_latency += result["latency_ms"]
accessible = sum(1 for r in results if r["status"] == "accessible")
print(f" - {'='*60}")
print(f"📊 สรุปผล:")
print(f" Total Estimated Latency: {total_latency:.2f} ms")
print(f" Hosts Checked: {len(hosts)}")
print(f" Accessible: {accessible}/{len(hosts)}")
print(f"{'='*60} - ")
return {
"path": results,
"total_latency_ms": round(total_latency, 2),
"summary": {
"total_hosts": len(hosts),
"accessible": accessible,
"path_healthy": accessible == len(hosts)
}
}
# ===== ตัวอย่างการใช้งาน =====
if __name__ == "__main__":
jump_path = [
{"name": "Bastion Host", "address": "203.0.113.10", "port": 22},
{"name": "Web Server", "address": "10.0.0.10", "port": 22},
{"name": "DB Server", "address": "10.0.0.20", "port": 22},
]
report = check_proxyjump_path(jump_path)
with open("ssh_path_report.json", "w", encoding="utf-8") as f:
json.dump(report, f, ensure_ascii=False, indent=2)
print("💾 บันทึกรายงานที่: ssh_path_report.json")
ผลลัพธ์ตัวอย่าง:
============================================================
🔍 SSH ProxyJump Path Analysis
============================================================
⏳ กำลังทดสอบ: Bastion Host (203.0.113.10)
✅ Status: accessible
⏱️ Latency: 18.42 ms
⏳ กำลังทดสอบ: Web Server (10.0.0.10)
✅ Status: accessible
⏱️ Latency: 4.87 ms
⏳ กำลังทดสอบ: DB Server (10.0.0.20)
✅ Status: accessible
⏱️ Latency: 5.12 ms
============================================================
📊 สรุปผล:
Total Estimated Latency: 28.41 ms
Hosts Checked: 3
Accessible: 3/3
============================================================
VNC (Virtual Network Computing) คือระบบ Graphical Desktop Sharing ที่ใช้โปรโตคอล RFB (Remote Framebuffer Protocol) ในการส่งภาพหน้าจอและรับคำสั่งจาก Keyboard/Mouse ผ่านเครือข่าย
flowchart LR
subgraph Client["💻 VNC Client"]
VI["🖥️ VNC Viewer - ติดตั้งบน Local"]
KB["⌨️ Keyboard/Mouse - Input Events"]
end
subgraph Network["🌐 เครือข่าย"]
TUN["🔐 SSH Tunnel - Port 22 → 5901"]
end
subgraph Server["🖥️ VNC Server (Remote)"]
VS["📡 VNC Server - Port 5900/5901"]
FB["🖼️ Framebuffer - (ภาพหน้าจอ)"]
DE["🗂️ Desktop Environment - GNOME/KDE/XFCE"]
end
KB -->|"Input Events (RFB)"| TUN
TUN -->|"Forwarded to :5901"| VS
VS --> DE
DE --> FB
FB -->|"Screen Updates (Compressed)"| TUN
TUN -->|"Pixel Data"| VI
style Client fill:#458588,color:#ebdbb2,stroke:#83a598
style Network fill:#d79921,color:#1d2021,stroke:#fabd2f
style Server fill:#689d6a,color:#1d2021,stroke:#8ec07c
ขั้นตอนการติดตั้ง TigerVNC บน Ubuntu Server:
# 1. ติดตั้ง TigerVNC Server
sudo apt update
sudo apt install -y tigervnc-standalone-server tigervnc-common
# 2. ติดตั้ง Desktop Environment (เลือกแบบเบา)
sudo apt install -y xfce4 xfce4-goodies dbus-x11
# 3. ตั้งรหัสผ่าน VNC
vncpasswd
# Password: [กรอกรหัสผ่าน >= 6 ตัวอักษร]
# Verify: [ยืนยันรหัสผ่าน]
# Would you like to enter a view-only password (y/n)? n
# 4. สร้าง startup script
mkdir -p ~/.vnc
cat > ~/.vnc/xstartup << 'EOF'
#!/bin/bash
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
exec startxfce4
EOF
chmod +x ~/.vnc/xstartup
# 5. เริ่ม VNC Server (Display :1 = Port 5901)
vncserver :1 -geometry 1920x1080 -depth 24 -localhost yes
# -localhost yes = รับเฉพาะจาก localhost (ต้องใช้ SSH Tunnel)
# 6. ตรวจสอบสถานะ
vncserver -list
# Output:
# TigerVNC server sessions:
# X DISPLAY # PROCESS ID
# :1 23456
VNC ไม่มีการเข้ารหัสในตัว ดังนั้นต้องใช้ SSH Tunnel เสมอ:
# บนเครื่อง Client - สร้าง SSH Tunnel
ssh -L 5901:localhost:5901 -N -f user@203.0.113.10
# คำอธิบาย:
# -L 5901:localhost:5901 = Forward port 5901 local → 5901 บน server
# -N = ไม่รัน command บน remote (tunnel only)
# -f = รันใน background
# จากนั้นเชื่อมต่อ VNC Viewer ไปที่:
# localhost:5901 หรือ 127.0.0.1::5901
คำนวณ Bandwidth ที่ต้องการสำหรับ VNC:
สมมติการใช้งานแบบ Standard Desktop:
Uncompressed Bandwidth:
หลังบีบอัด (Tight Encoding, ~90% reduction):
ตัวแปร: W = ความกว้าง (pixels), H = ความสูง (pixels), D_color = ขนาดสีต่อ pixel (bytes), FPS = จำนวนเฟรมต่อวินาที, r_compression = อัตราการบีบอัด (0.0–1.0)
สรุป Bandwidth Requirements:
| ความละเอียด | ไม่บีบอัด | Tight Encoding | ZRLE Encoding |
|---|---|---|---|
| 1024×768 | 70.8 MB/s | ~7 MB/s | ~3 MB/s |
| 1920×1080 | 186.6 MB/s | ~18.7 MB/s | ~8 MB/s |
| 2560×1440 | 331.8 MB/s | ~33.2 MB/s | ~14 MB/s |
| 3840×2160 | 746.5 MB/s | ~74.7 MB/s | ~32 MB/s |
RDP (Remote Desktop Protocol) คือโปรโตคอลที่พัฒนาโดย Microsoft สำหรับการเชื่อมต่อ Remote Desktop ที่ใช้ Port 3389 โดยค่าเริ่มต้น
flowchart LR
subgraph E1["📅 ยุคแรก (1996-2005)"]
R1["RDP 4.0 - Windows NT 4.0 - เข้ารหัสอ่อนแอ"]
R2["RDP 5.0 - Windows 2000 - 128-bit RC4"]
end
subgraph E2["⚠️ ยุคช่องโหว่ (2006-2018)"]
R3["RDP 6.0 - Vista/2008 - NLA ครั้งแรก"]
V1["MS12-020 - CVE-2012-0002 - RCE Critical"]
V2["BlueKeep - CVE-2019-0708 - Wormable RCE"]
end
subgraph E3["🔐 ยุคทันสมัย (2019-ปัจจุบัน)"]
R4["RDP 10.0 - Windows 10 - TLS 1.3, NLA บังคับ"]
V3["DejaBlue - CVE-2019-1182 - RCE on Win 8+"]
R5["RDP Hardening - CredSSP, TLS 1.2+"]
end
R1 --> R2
R2 --> R3
R3 --> V1
V1 --> V2
V2 --> R4
R4 --> V3
V3 --> R5
style E1 fill:#504945,color:#ebdbb2,stroke:#7c6f64
style E2 fill:#cc241d,color:#ebdbb2,stroke:#fb4934
style E3 fill:#98971a,color:#1d2021,stroke:#b8bb26
ขั้นตอนการเพิ่มความปลอดภัย RDP:
เปิดใช้งาน Network Level Authentication (NLA)
Computer Configuration →
Administrative Templates →
Windows Components →
Remote Desktop Services →
Remote Desktop Session Host →
Security →
"Require use of specific security layer" = SSL (TLS 1.0)
"Require NLA" = Enabled
เปลี่ยน Port จาก 3389
$rdpPort = 49152
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' `
-name 'PortNumber' -Value $rdpPort
New-NetFirewallRule -DisplayName "RDP Custom Port" `
-Direction Inbound -Protocol TCP `
-LocalPort $rdpPort -Action Allow
Restart-Service TermService
จำกัดผู้ใช้ที่เข้าถึงได้
Add-LocalGroupMember -Group "Remote Desktop Users" -Member "rdp_user"
Remove-LocalGroupMember -Group "Remote Desktop Users" -Member "Administrator"
# สร้าง SSH Tunnel
ssh -L 13389:windows-server.internal:3389 \
-N -f \
-o StrictHostKeyChecking=yes \
user@bastion.example.com
# เชื่อมต่อด้วย xfreerdp (Linux RDP Client)
xfreerdp \
/v:localhost:13389 \
/u:WindowsUser \
/d:DOMAIN \
/sec:tls \
/cert:tofu \
/dynamic-resolution \
/f
Cockpit คือ Web-based Server Management Tool ที่พัฒนาโดย Red Hat ช่วยให้ผู้ดูแลระบบสามารถจัดการเซิร์ฟเวอร์ Linux ผ่าน Web Browser ผ่าน Port 9090
mindmap
root((🌐 Cockpit Port 9090))
📊 System Overview
CPU/Memory/Disk
Network Traffic
System Logs
🔧 System Management
Services Control
Software Updates
User Management
🌐 Networking
Interfaces
Firewall Rules
VPN
🐳 Containers
Podman
Image Management
💾 Storage
Disk/RAID/LVM
LUKS Encryption
🔐 Security
SSH Keys
TLS Certificates
Audit Logs
# ติดตั้งบน Ubuntu 22.04/24.04
sudo apt update
sudo apt install -y cockpit cockpit-packagekit
# เพิ่ม plugin ที่มีประโยชน์
sudo apt install -y \
cockpit-storaged \
cockpit-networkmanager \
cockpit-podman
# เปิดใช้งาน Service
sudo systemctl enable --now cockpit.socket
# ตรวจสอบสถานะ
sudo systemctl status cockpit.socket
# ตั้งค่า Firewall
sudo ufw allow 9090/tcp comment "Cockpit Web Admin"
sudo ufw reload
echo "เข้าถึงได้ที่: https://$(hostname -I | awk '{print $1}'):9090"
# ตั้งค่า /etc/cockpit/cockpit.conf
sudo mkdir -p /etc/cockpit
sudo tee /etc/cockpit/cockpit.conf << 'EOF'
[WebService]
Origins = https://admin.example.com:9090
AllowUnencrypted = false
[Session]
IdleTimeout = 15
[Log]
Fatal = criticals
EOF
# ใช้ Custom TLS Certificate
sudo cp /etc/letsencrypt/live/admin.example.com/fullchain.pem \
/etc/cockpit/ws-certs.d/admin.example.com.cert
sudo cp /etc/letsencrypt/live/admin.example.com/privkey.pem \
/etc/cockpit/ws-certs.d/admin.example.com.key
sudo chmod 640 /etc/cockpit/ws-certs.d/*.key
sudo chown root:cockpit-ws /etc/cockpit/ws-certs.d/*.key
sudo systemctl restart cockpit
# ตรวจสอบ TLS
openssl s_client -connect localhost:9090 -brief 2>/dev/null | \
grep -E "Protocol|Cipher"
สคริปต์ตรวจสอบสุขภาพ Cockpit:
#!/usr/bin/env python3
"""
ตรวจสอบสถานะ Cockpit Web Admin
สคริปต์สำหรับ monitoring และ health check ของ Cockpit service
"""
import requests
import ssl
import socket
from datetime import datetime
def check_cockpit_health(host: str, port: int = 9090) -> dict:
"""
ตรวจสอบสถานะ Cockpit server
Parameters:
host (str): hostname หรือ IP ของ Cockpit server
port (int): port ของ Cockpit (ค่าเริ่มต้น: 9090)
Returns:
dict: ผลการตรวจสอบสุขภาพ
"""
result = {
"host": host,
"port": port,
"timestamp": datetime.now().isoformat(),
"checks": {}
}
# ตรวจสอบ 1: TCP Connectivity
try:
sock = socket.create_connection((host, port), timeout=5)
sock.close()
result["checks"]["tcp_connectivity"] = {
"status": "pass",
"message": f"Port {port} is open"
}
except Exception as e:
result["checks"]["tcp_connectivity"] = {
"status": "fail",
"message": str(e)
}
# ตรวจสอบ 2: TLS Certificate
try:
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
with socket.create_connection((host, port), timeout=5) as sock:
with context.wrap_socket(sock, server_hostname=host) as ssock:
cipher = ssock.cipher()
version = ssock.version()
result["checks"]["tls"] = {
"status": "pass",
"protocol": version,
"cipher": cipher[0],
"bits": cipher[2]
}
except Exception as e:
result["checks"]["tls"] = {"status": "fail", "message": str(e)}
# ตรวจสอบ 3: HTTP Response
try:
response = requests.get(
f"https://{host}:{port}/",
verify=False, timeout=10, allow_redirects=True
)
result["checks"]["http_response"] = {
"status": "pass" if response.status_code in [200, 401, 403] else "warn",
"status_code": response.status_code,
"response_time_ms": round(response.elapsed.total_seconds() * 1000, 2)
}
except Exception as e:
result["checks"]["http_response"] = {"status": "fail", "message": str(e)}
# สรุปผล
check_results = [c["status"] for c in result["checks"].values()]
if all(s == "pass" for s in check_results):
result["overall_status"] = "healthy"
elif any(s == "fail" for s in check_results):
result["overall_status"] = "unhealthy"
else:
result["overall_status"] = "degraded"
return result
# ===== ตัวอย่างการใช้งาน =====
if __name__ == "__main__":
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
servers = [
{"host": "10.0.0.30", "port": 9090, "name": "Management Server"},
{"host": "10.0.0.40", "port": 9090, "name": "Backup Server"},
]
print(" - 🏥 Cockpit Health Check Report")
print("=" * 50)
for server in servers:
health = check_cockpit_health(server["host"], server["port"])
status_icon = {"healthy": "✅", "degraded": "⚠️", "unhealthy": "❌"}.get(
health["overall_status"], "❓"
)
print(f" - {status_icon} {server['name']} ({server['host']}:{server['port']})")
print(f" Overall: {health['overall_status'].upper()}")
for check_name, check_data in health["checks"].items():
icon = "✅" if check_data["status"] == "pass" else "❌"
detail = check_data.get("message", check_data.get("protocol", "OK"))
print(f" {icon} {check_name}: {detail}")
Bastion Host หรือ Jump Server คือเซิร์ฟเวอร์พิเศษที่ทำหน้าที่เป็น จุดเข้าถึงเดียว (Single Point of Entry) สู่เครือข่ายภายใน โดยได้รับการ Hardening อย่างเข้มข้นและมีการ Monitor ทุกการกระทำ
flowchart TD
subgraph Public["🌐 Public Internet"]
U1["👤 Admin 1 - (Thailand)"]
U2["👤 Admin 2 - (Remote)"]
ATK["☠️ Attacker - (Blocked)"]
end
subgraph DMZ["🟡 DMZ - Screened Subnet"]
FW1{"🔴 External - Firewall"}
BH["🛡️ Bastion Host - 203.0.113.10 - Port 22 เท่านั้น - MFA Required"]
FW2{"🔴 Internal - Firewall"}
end
subgraph Prod["🟢 Production Network 10.0.0.0/24"]
APP["🖥️ App Server - 10.0.0.10"]
DB["🗄️ DB Server - 10.0.0.20"]
MON["📊 Monitoring - 10.0.0.30"]
end
subgraph Log["📋 Audit and Logging"]
SIEM["🔍 SIEM - Log Collection"]
AUDIT["📜 Session Recording - ttyrec / asciinema"]
end
U1 -->|"SSH + MFA"| FW1
U2 -->|"SSH + MFA"| FW1
ATK -.->|"❌ Blocked"| FW1
FW1 -->|"Port 22 Only"| BH
BH -->|"Authenticated"| FW2
FW2 --> APP
FW2 --> DB
FW2 --> MON
BH --> AUDIT
BH --> SIEM
style Public fill:#cc241d,color:#ebdbb2,stroke:#fb4934
style DMZ fill:#d79921,color:#1d2021,stroke:#fabd2f
style Prod fill:#98971a,color:#1d2021,stroke:#b8bb26
style Log fill:#458588,color:#ebdbb2,stroke:#83a598
# ===== Bastion Host Hardening Script =====
# 1. อัปเดตระบบ
sudo apt update && sudo apt upgrade -y
# 2. ตั้งค่า SSH อย่างเข้มงวด
sudo tee /etc/ssh/sshd_config.d/99-bastion-hardening.conf << 'EOF'
Protocol 2
Port 22
# Authentication
PermitRootLogin no
MaxAuthTries 3
LoginGraceTime 30
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
ChallengeResponseAuthentication yes
# Features ที่ปิด
X11Forwarding no
AllowTcpForwarding yes
AllowAgentForwarding no
PermitTunnel no
GatewayPorts no
# ผู้ใช้ที่อนุญาต
AllowGroups sshusers
# Timeout
ClientAliveInterval 300
ClientAliveCountMax 2
# Logging
LogLevel VERBOSE
SyslogFacility AUTH
EOF
# 3. สร้าง group สำหรับ SSH users
sudo groupadd sshusers
sudo usermod -aG sshusers admin1
sudo usermod -aG sshusers admin2
# 4. ติดตั้งและตั้งค่า Fail2Ban
sudo apt install -y fail2ban
sudo tee /etc/fail2ban/jail.local << 'EOF'
[sshd]
enabled = true
port = 22
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
EOF
sudo systemctl enable --now fail2ban
# 5. Session Recording
sudo tee /etc/profile.d/session-recording.sh << 'EOF'
if [ "$SSH_TTY" != "" ]; then
SESSION_FILE="/var/log/sessions/$(date +%Y%m%d_%H%M%S)_${USER}_$$.log"
mkdir -p /var/log/sessions
exec script -q -f "$SESSION_FILE"
fi
EOF
# 6. ติดตั้ง auditd
sudo apt install -y auditd
sudo systemctl enable --now auditd
sudo auditctl -w /etc/ssh/sshd_config -p wa -k ssh_config
sudo auditctl -a always,exit -F arch=b64 -S execve -k cmd_exec
ตัวอย่างการคำนวณ: สมมติว่ามีเซิร์ฟเวอร์ 20 เครื่อง แต่ละเครื่องเปิด Port 22 ตรงสู่ Internet
Attack Surface ก่อนติดตั้ง Bastion:
หลังติดตั้ง Bastion Host:
อัตราการลดความเสี่ยง:
ตัวแปร: A_surface = พื้นที่การโจมตี (Attack Surface), N_servers = จำนวนเซิร์ฟเวอร์, P_exposed = จำนวน Port ที่เปิดต่อ Internet
สคริปต์ Python สำหรับ Bastion Host Audit:
#!/usr/bin/env python3
"""
Bastion Host Security Audit Script
ตรวจสอบการตั้งค่าความปลอดภัยของ Bastion Host
"""
import subprocess
import re
from dataclasses import dataclass
from typing import List, Tuple
@dataclass
class AuditCheck:
"""ผลการตรวจสอบแต่ละรายการ"""
name: str
status: str # "pass", "fail", "warn"
message: str
severity: str # "critical", "high", "medium", "low"
def run_command(cmd: str) -> Tuple[int, str, str]:
"""รัน shell command และคืนค่า (returncode, stdout, stderr)"""
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
return result.returncode, result.stdout, result.stderr
def audit_bastion_host() -> List[AuditCheck]:
"""ตรวจสอบความปลอดภัยของ Bastion Host"""
checks = []
# 1. ตรวจสอบว่าปิด Root Login
rc, out, _ = run_command("sshd -T 2>/dev/null | grep permitrootlogin")
if "no" in out.lower():
checks.append(AuditCheck(
"SSH Root Login", "pass", "PermitRootLogin = no ✓", "critical"
))
else:
checks.append(AuditCheck(
"SSH Root Login", "fail",
f"PermitRootLogin ไม่ปลอดภัย: {out.strip()}", "critical"
))
# 2. ตรวจสอบ Password Authentication
rc, out, _ = run_command("sshd -T 2>/dev/null | grep passwordauthentication")
if "no" in out.lower():
checks.append(AuditCheck(
"Password Authentication", "pass",
"PasswordAuthentication = no ✓", "high"
))
else:
checks.append(AuditCheck(
"Password Authentication", "fail",
"PasswordAuthentication เปิดอยู่ — เสี่ยงต่อ Brute Force", "high"
))
# 3. ตรวจสอบ Fail2Ban
rc, out, _ = run_command("systemctl is-active fail2ban 2>/dev/null")
if "active" in out:
checks.append(AuditCheck(
"Fail2Ban Service", "pass", "Fail2Ban กำลังทำงาน ✓", "high"
))
else:
checks.append(AuditCheck(
"Fail2Ban Service", "fail",
"Fail2Ban ไม่ได้ทำงาน — เสี่ยงต่อ Brute Force", "high"
))
# 4. ตรวจสอบ Open Ports
rc, out, _ = run_command("ss -tlnp 2>/dev/null | grep LISTEN")
open_ports = re.findall(r':(\d+)\s', out)
suspicious_ports = [p for p in open_ports if p not in ['22', '9090']]
if not suspicious_ports:
checks.append(AuditCheck(
"Open Ports", "pass",
f"Port ที่เปิด: {', '.join(set(open_ports))} ✓", "medium"
))
else:
checks.append(AuditCheck(
"Open Ports", "warn",
f"Port ที่น่าสงสัย: {', '.join(set(suspicious_ports))}", "medium"
))
# 5. ตรวจสอบ System Updates
rc, out, _ = run_command("apt list --upgradable 2>/dev/null | wc -l")
pending_updates = max(0, int(out.strip()) - 1)
if pending_updates <= 5:
checks.append(AuditCheck(
"System Updates", "pass",
f"Updates รอการติดตั้ง: {pending_updates} ✓", "medium"
))
else:
checks.append(AuditCheck(
"System Updates", "warn",
f"มี {pending_updates} updates รอการติดตั้ง", "medium"
))
return checks
def generate_report(checks: List[AuditCheck]) -> None:
"""แสดงรายงานการตรวจสอบ"""
status_icons = {"pass": "✅", "fail": "❌", "warn": "⚠️"}
print(" - " + "="*60)
print("🛡️ Bastion Host Security Audit Report")
print("="*60)
passed = sum(1 for c in checks if c.status == "pass")
failed = sum(1 for c in checks if c.status == "fail")
warned = sum(1 for c in checks if c.status == "warn")
for check in checks:
icon = status_icons.get(check.status, "❓")
print(f" - {icon} [{check.severity.upper()}] {check.name}")
print(f" → {check.message}")
score = (passed / len(checks)) * 100 if checks else 0
print(f" - {'='*60}")
print(f"📊 สรุปผล: {passed} ผ่าน | {failed} ไม่ผ่าน | {warned} เตือน")
print(f"🏆 Security Score: {score:.1f}/100")
if score >= 90:
print("🎉 สถานะ: EXCELLENT")
elif score >= 70:
print("👍 สถานะ: GOOD - ควรแก้ไขรายการที่ไม่ผ่าน")
else:
print("⚠️ สถานะ: NEEDS IMPROVEMENT - ต้องแก้ไขด่วน!")
# ===== ตัวอย่างการใช้งาน =====
if __name__ == "__main__":
checks = audit_bastion_host()
generate_report(checks)
Multi-factor Authentication (MFA) หรือ การยืนยันตัวตนหลายปัจจัย คือกระบวนการที่ต้องการหลักฐานยืนยันตัวตนมากกว่าหนึ่งประเภทก่อนอนุญาตให้เข้าถึงระบบ
flowchart LR
subgraph F1["🧠 Something You Know"]
P1["🔑 Password"]
P2["📌 PIN"]
P3["❓ Security Questions"]
end
subgraph F2["📱 Something You Have"]
H1["📲 TOTP App - Google Authenticator"]
H2["🔐 Hardware Token - YubiKey"]
H3["💳 Smart Card"]
H4["📱 SMS OTP"]
end
subgraph F3["👁️ Something You Are"]
B1["🖐️ Fingerprint"]
B2["👁️ Iris Scan"]
B3["😊 Face Recognition"]
end
subgraph MFA["🛡️ MFA (ต้องใช้ >= 2 Factors)"]
COMBO["✅ Password + TOTP - - ✅ SSH Key + TOTP - - ✅ Password + YubiKey"]
end
F1 --> MFA
F2 --> MFA
F3 --> MFA
style F1 fill:#458588,color:#ebdbb2,stroke:#83a598
style F2 fill:#689d6a,color:#1d2021,stroke:#8ec07c
style F3 fill:#d65d0e,color:#ebdbb2,stroke:#fe8019
style MFA fill:#98971a,color:#1d2021,stroke:#b8bb26
TOTP (Time-based One-Time Password) ใช้สมการ:
โดยที่:
ตัวอย่างการคำนวณ TOTP Step:
สมมติว่า:
OTP นี้จะเปลี่ยนทุก 30 วินาที และ Valid เฉพาะใน Window ±1 step (เพื่อชดเชย Clock Skew)
ตัวแปร: K = Secret Key (แชร์ระหว่าง Client และ Server), T = เวลาปัจจุบัน (Unix), T₀ = Unix Epoch (0), T_interval = ช่วงเวลา OTP (ปกติ 30 วินาที)
การติดตั้ง Google Authenticator PAM:
# 1. ติดตั้ง PAM Module
sudo apt install -y libpam-google-authenticator
# 2. ตั้งค่าสำหรับผู้ใช้
google-authenticator
# Make tokens time-based: y
# Update .google_authenticator file: y
# Disallow multiple uses: y
# Permit up to 4 minutes clock skew: n
# Enable rate-limiting: y
# 3. ตั้งค่า PAM
sudo tee /etc/pam.d/sshd << 'EOF'
@include common-auth
auth required pam_google_authenticator.so nullok
EOF
# 4. ตั้งค่า SSH daemon
sudo tee -a /etc/ssh/sshd_config << 'EOF'
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
EOF
# 5. Restart SSH
sudo systemctl restart sshd
# ===== ทดสอบการเชื่อมต่อ =====
# ssh user@bastion.example.com
# Enter passphrase for key: [SSH Key passphrase]
# Verification code: [6 หลักจาก Authenticator App]
YubiKey เป็น Hardware Security Key ที่รองรับ FIDO2/WebAuthn และ PIV (Personal Identity Verification)
# ติดตั้ง YubiKey software
sudo apt install -y yubikey-manager libpam-yubico
# ตรวจสอบ YubiKey
ykman info
# Device type: YubiKey 5 NFC
# Serial number: 12345678
# Firmware version: 5.4.3
# Generate SSH Key บน YubiKey (FIDO2)
ssh-keygen -t ed25519-sk -C "yubikey@bastion" \
-f ~/.ssh/id_ed25519_sk
# เพิ่ม Public Key ไปยัง Server
ssh-copy-id -i ~/.ssh/id_ed25519_sk.pub user@bastion.example.com
# เชื่อมต่อ (จะขอให้แตะ YubiKey)
ssh -i ~/.ssh/id_ed25519_sk user@bastion.example.com
# Please touch your authenticator device now.
| วิธีการ | ความปลอดภัย | ความสะดวก | Phishing Resistant | ต้นทุน |
|---|---|---|---|---|
| SMS OTP | ต่ำ | สูง | ❌ | ฟรี |
| Email OTP | ต่ำ | สูง | ❌ | ฟรี |
| TOTP App | ปานกลาง | ปานกลาง | ❌ | ฟรี |
| Push Notification | ปานกลาง | สูง | บางส่วน | มี |
| FIDO2/WebAuthn | สูงมาก | ปานกลาง | ✅ | สูง |
| Hardware Token (YubiKey) | สูงมาก | ปานกลาง | ✅ | สูง |
| Smart Card + PIN | สูงมาก | ต่ำ | ✅ | สูงมาก |
#!/usr/bin/env python3
"""
ตรวจสอบสถานะ MFA ของผู้ใช้ในระบบ
ช่วยให้ผู้ดูแลระบบเห็นว่าใครที่ยังไม่ได้ตั้งค่า MFA
"""
import subprocess
from pathlib import Path
def check_user_mfa_status(username: str) -> dict:
"""
ตรวจสอบสถานะ MFA ของผู้ใช้
Parameters:
username (str): ชื่อผู้ใช้ที่ต้องการตรวจสอบ
Returns:
dict: สถานะ MFA ของผู้ใช้
"""
home_dir = Path(f"/home/{username}")
status = {"username": username, "mfa_methods": [], "compliant": False}
# ตรวจสอบ Google Authenticator
ga_file = home_dir / ".google_authenticator"
if ga_file.exists():
status["mfa_methods"].append("TOTP (Google Authenticator)")
# ตรวจสอบ SSH Keys
auth_keys = home_dir / ".ssh" / "authorized_keys"
if auth_keys.exists():
with open(auth_keys, 'r') as f:
content = f.read()
key_count = len([
l for l in content.split(' - ')
if l.strip() and not l.startswith('#')
])
fido2_count = content.count('sk-')
if key_count > 0:
status["mfa_methods"].append(
f"SSH Keys ({key_count} keys, {fido2_count} FIDO2)"
)
has_ssh = any("SSH Keys" in m for m in status["mfa_methods"])
has_totp = any("TOTP" in m for m in status["mfa_methods"])
has_fido2 = any("FIDO2" in m for m in status["mfa_methods"])
status["compliant"] = has_ssh and (has_totp or has_fido2)
return status
def audit_mfa_compliance() -> None:
"""ตรวจสอบ MFA Compliance ของทุกผู้ใช้"""
result = subprocess.run(["getent", "passwd"], capture_output=True, text=True)
real_users = []
for line in result.stdout.strip().split(' - '):
parts = line.split(':')
username = parts[0]
uid = int(parts[2])
shell = parts[6] if len(parts) > 6 else ''
if uid >= 1000 and '/sbin/nologin' not in shell and '/bin/false' not in shell:
real_users.append(username)
print(" - 🔐 MFA Compliance Audit Report")
print("=" * 60)
print(f"{'Username':<20} {'MFA Status':<15} {'Methods'}")
print("-" * 60)
compliant_count = 0
for user in sorted(real_users):
s = check_user_mfa_status(user)
if s["compliant"]:
compliant_count += 1
icon, label = "✅", "COMPLIANT"
else:
icon, label = "❌", "NON-COMPLIANT"
methods = ", ".join(s["mfa_methods"]) or "ไม่มี MFA"
print(f"{icon} {user:<18} {label:<15} {methods}")
rate = (compliant_count / len(real_users) * 100) if real_users else 0
print("=" * 60)
print(f" - 📊 MFA Compliance Rate: {compliant_count}/{len(real_users)} ({rate:.1f}%)")
# ===== ตัวอย่างการใช้งาน =====
if __name__ == "__main__":
status = check_user_mfa_status("admin1")
print(f" - ผู้ใช้: {status['username']}")
print(f"Compliant: {'ใช่' if status['compliant'] else 'ไม่ใช่'}")
print(f"Methods: {', '.join(status['mfa_methods']) or 'ไม่มี'}")
audit_mfa_compliance()
วัตถุประสงค์: ตั้งค่าระบบ Remote Access ที่ปลอดภัยด้วย Bastion Host + TOTP
สภาพแวดล้อม Lab:
┌─────────────────────────────────────────┐
│ Local Machine (ผู้เรียน) │
│ IP: 192.168.56.1 │
├─────────────────────────────────────────┤
│ Bastion Host (VM1) │
│ IP: 192.168.56.10 (External) │
│ IP: 10.0.0.1 (Internal) │
├─────────────────────────────────────────┤
│ Target Server (VM2) │
│ IP: 10.0.0.10 (Internal เท่านั้น) │
└─────────────────────────────────────────┘
ขั้นตอนที่ 1: ตั้งค่า Bastion Host
# บน Bastion Host (VM1)
sudo apt update && sudo apt install -y \
openssh-server libpam-google-authenticator fail2ban ufw
# ตั้งค่า SSH MFA
sudo tee /etc/ssh/sshd_config.d/10-mfa.conf << 'EOF'
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
EOF
echo "auth required pam_google_authenticator.so" | \
sudo tee -a /etc/pam.d/sshd
# ตั้งค่า Google Authenticator
google-authenticator -t -d -f -r 3 -R 30 -w 3
# ตั้งค่า Firewall และ restart
sudo ufw allow 22/tcp && sudo ufw enable
sudo systemctl restart sshd
ขั้นตอนที่ 2: ตั้งค่า Target Server
# บน Target Server (VM2)
echo "ssh-ed25519 AAAA...bastion_key..." >> ~/.ssh/authorized_keys
sudo ufw default deny incoming
sudo ufw allow from 10.0.0.1 to any port 22
sudo ufw enable
ขั้นตอนที่ 3: ทดสอบการเชื่อมต่อ
# บน Local Machine
ssh-keygen -t ed25519 -C "student@lab"
ssh-copy-id -i ~/.ssh/id_ed25519.pub student@192.168.56.10
# สร้าง SSH Config
cat >> ~/.ssh/config << 'EOF'
Host bastion-lab
HostName 192.168.56.10
User student
IdentityFile ~/.ssh/id_ed25519
Host target-lab
HostName 10.0.0.10
User student
IdentityFile ~/.ssh/id_ed25519
ProxyJump bastion-lab
EOF
# ทดสอบ - จะถาม OTP
ssh bastion-lab
# Enter verification code: [6 หลักจาก App]
ssh target-lab
# บน Target Server (VM2)
sudo apt install -y cockpit
sudo systemctl enable --now cockpit.socket
sudo ufw allow from 10.0.0.0/24 to any port 9090
# สร้าง SSH Tunnel จาก Local
ssh -L 9090:10.0.0.10:9090 -N -f bastion-lab
# เข้าถึง Cockpit
echo "เปิด Browser: https://localhost:9090"
Remote Access Solutions ที่ปลอดภัยต้องอาศัยการผสมผสานของเทคโนโลยีและแนวปฏิบัติที่ดีหลายอย่างเข้าด้วยกัน
mindmap
root((🔐 Remote Access Security))
🖥️ Remote Access Tools
SSH Advanced
ProxyJump
Agent Forwarding
SSH Config
VNC + SSH Tunnel
TigerVNC
Bandwidth Optimization
RDP Security
NLA Enforcement
TLS 1.2+
Cockpit
Web-based Admin
TLS Hardening
🛡️ Infrastructure
Bastion Host
Single Entry Point
95% Attack Surface Reduction
Session Recording
Network Segmentation
DMZ Zone
Firewall Rules
🔑 Authentication
MFA
TOTP App
FIDO2/YubiKey
SSH Keys
Ed25519 (แนะนำ)
FIDO2-SK
PAM Integration
หลักการสำคัญที่ต้องจำ:
การเปรียบเทียบ Security Posture:
| สถานการณ์ | Attack Surface | ความปลอดภัย | คะแนน |
|---|---|---|---|
| ไม่มีการป้องกัน (Port เปิดหมด) | สูงมาก | ต่ำมาก | 1/10 |
| SSH + Password เท่านั้น | สูง | ต่ำ | 3/10 |
| SSH + Key Authentication | ปานกลาง | ปานกลาง | 6/10 |
| Bastion + SSH Key | ต่ำ | สูง | 8/10 |
| Bastion + SSH Key + MFA + Logging | ต่ำมาก | สูงมาก | 10/10 |
OpenSSH Manual Pages — The OpenSSH Project
https://www.openssh.com/manual.html
TigerVNC Documentation — TigerVNC Project
https://tigervnc.org/doc/
NIST Special Publication 800-63B — Digital Identity Guidelines
https://pages.nist.gov/800-63-3/sp800-63b.html
Red Hat Cockpit Documentation — Red Hat, Inc.
https://cockpit-project.org/documentation.html
CIS Benchmarks — SSH — Center for Internet Security
https://www.cisecurity.org/benchmark/ssh
RFC 6238 — TOTP: Time-Based One-Time Password Algorithm — IETF
https://www.rfc-editor.org/rfc/rfc6238
FIDO Alliance Specifications — FIDO Alliance
https://fidoalliance.org/specifications/
Bastion Host Best Practices — AWS Security Blog
https://aws.amazon.com/blogs/security/how-to-record-ssh-sessions-established-through-a-bastion-host/
Microsoft RDP Security — Microsoft Docs
https://learn.microsoft.com/en-us/windows-server/remote/remote-desktop-services/
Linux PAM Documentation — The Linux-PAM Project
http://www.linux-pam.org/Linux-PAM-html/