
การเข้าถึงระบบจากระยะไกล (Remote Access) เป็นทักษะพื้นฐานที่จำเป็นสำหรับผู้ดูแลระบบ (System Administrator) และนักพัฒนา (Developer) ในยุคที่เซิร์ฟเวอร์ส่วนใหญ่อยู่บน Cloud หรือ Data Center บทความนี้นำเสนอแนวคิด สถาปัตยกรรม การตั้งค่า และเทคนิคขั้นสูงของเครื่องมือ Remote Access ยอดนิยมในโลก Linux ได้แก่ SSH, Cockpit และ VNC พร้อมตัวอย่างการใช้งานจริงและแนวทางด้านความปลอดภัย (Security Best Practices)
Remote Access หมายถึง การเข้าถึงและควบคุมคอมพิวเตอร์หรือระบบจากตำแหน่งที่อยู่ห่างไกลผ่านเครือข่าย (Network) โดยไม่จำเป็นต้องอยู่หน้าเครื่องจริง ๆ ซึ่งเป็นองค์ประกอบสำคัญของการบริหารจัดการระบบสมัยใหม่ ทั้งในงาน DevOps, Cloud Computing และ Edge Computing
การเข้าถึงระยะไกลแบ่งออกเป็นสองรูปแบบหลักตามลักษณะ Interface ที่ผู้ใช้ปฏิสัมพันธ์ด้วย:
flowchart LR
User[("👤 ผู้ใช้
(User)")] --> Choice{ต้องการอะไร?
What is needed?}
Choice -->|Text เท่านั้น
Text only| Shell["Remote Shell
(SSH, Mosh)"]
Choice -->|กราฟิก
Graphics| Desktop["Remote Desktop
(VNC, RDP, SPICE)"]
Choice -->|จัดการระบบผ่านเว็บ
Web management| Web["Web-based
(Cockpit, Webmin)"]
Shell --> LowBW["แบนด์วิดธ์ต่ำ
Low Bandwidth
~ KB/s"]
Desktop --> HighBW["แบนด์วิดธ์สูง
High Bandwidth
~ MB/s"]
Web --> MidBW["แบนด์วิดธ์ปานกลาง
Medium Bandwidth"]
Protocol ที่ใช้สำหรับ Remote Access มีหลากหลาย แต่ละแบบมีจุดแข็ง-จุดอ่อนและ Use Case ที่ต่างกัน
| Protocol | ประเภท | Encryption | Default Port | จุดเด่น | Use Case |
|---|---|---|---|---|---|
| SSH | Shell | ใช่ (AES, ChaCha20) | 22/tcp | ปลอดภัย, มาตรฐาน Unix/Linux | Server admin, file transfer |
| Telnet | Shell | ไม่ใช่ (cleartext) | 23/tcp | เก่าแก่, เบามาก | Legacy device, lab เท่านั้น |
| Mosh | Shell | ใช่ (UDP-based) | 60000-61000/udp | ทนต่อเครือข่ายไม่เสถียร | มือถือ, Wi-Fi เปลี่ยนบ่อย |
| RDP | Desktop | ใช่ (TLS) | 3389/tcp | Native ใน Windows | Remote Windows |
| VNC | Desktop | ขึ้นกับ implementation | 5900-5999/tcp | ข้ามแพลตฟอร์ม | Remote Linux GUI |
| SPICE | Desktop | ใช่ (TLS) | 5900+/tcp | ประสิทธิภาพสูง, multimedia | KVM/QEMU VM |
| X11 Forwarding | App | ผ่าน SSH tunnel | ผ่าน 22/tcp | ส่งเฉพาะหน้าต่างแอป | รันโปรแกรม GUI ทีละตัว |
⚠️ Telnet ส่งข้อมูลทุกอย่าง (รวม username/password) เป็น cleartext ห้ามใช้บน Public Network เด็ดขาด ปัจจุบันถูกแทนที่ด้วย SSH โดยสมบูรณ์
ในการเข้าถึงระยะไกล มีองค์ประกอบสามประการที่ต้องคำนึงถึงเสมอ ตามหลัก CIA Triad:
ความแข็งแรงของการเข้ารหัสสามารถวัดได้จากจำนวน Operations ที่ผู้โจมตีต้องใช้ใน Brute-force Attack ซึ่งสำหรับ key ขนาด n บิต จะมีค่าเท่ากับ:
โดยที่ W คือจำนวน Operations เฉลี่ยที่ต้องใช้ในการเดา Key สำเร็จ และ n คือขนาดของ Key (เป็นบิต) — ตัวอย่างเช่น AES-256 ต้องใช้ Operations ซึ่งเป็นไปไม่ได้ในทางปฏิบัติแม้ใช้ supercomputer ที่เร็วที่สุดในโลก
SSH (Secure Shell) เป็น Protocol และโปรแกรมที่ออกแบบมาแทนที่ Telnet, rlogin, rsh ที่ไม่ปลอดภัย โดย Tatu Ylönen สร้างขึ้นในปี 1995 ปัจจุบันมาตรฐานที่ใช้คือ SSH-2 (RFC 4251-4254) และ implementation ที่นิยมที่สุดคือ OpenSSH
SSH ทำงานในรูปแบบ Client-Server Architecture ดังนี้:
/etc/ssh/sshd_config (server), ~/.ssh/config (client per-user), /etc/ssh/ssh_config (client system-wide)
flowchart TB
subgraph Client["💻 SSH Client (ฝั่งผู้ใช้)"]
SC[ssh command]
CK[("Client Keys
~/.ssh/id_ed25519")]
KH[("Known Hosts
~/.ssh/known_hosts")]
end
subgraph Network["🌐 Network (Internet/LAN)"]
Pkt["Encrypted Packets
(AES, ChaCha20)
Port 22/tcp"]
end
subgraph Server["🖥️ SSH Server (ฝั่งเซิร์ฟเวอร์)"]
SD[sshd Daemon]
HK[("Host Keys
/etc/ssh/ssh_host_*")]
AK[("authorized_keys
~/.ssh/authorized_keys")]
Cfg[("sshd_config
/etc/ssh/sshd_config")]
end
SC --> Pkt --> SD
SD --> Pkt --> SC
CK -.อ่าน.-> SC
KH -.ตรวจสอบ.-> SC
HK -.อ่าน.-> SD
AK -.ตรวจสอบ.-> SD
Cfg -.อ่าน.-> SD
การเชื่อมต่อ SSH ใช้การเข้ารหัสสามแบบรวมกันเพื่อความปลอดภัย:
ขั้นตอน Handshake (แบบสรุป) มีดังนี้:
sequenceDiagram
participant C as 💻 Client
participant S as 🖥️ Server
C->>S: 1. TCP SYN → port 22
S->>C: 2. SSH Version String (SSH-2.0-OpenSSH_9.x)
C->>S: 3. SSH Version String
Note over C,S: 4. Algorithm Negotiation
(KEX, Cipher, MAC)
C->>S: 5. KEXINIT (รายการ algorithms)
S->>C: 6. KEXINIT (เลือก algorithms ร่วม)
Note over C,S: 7. Key Exchange
(ECDH/Curve25519)
C->>S: 8. Public Key (ephemeral)
S->>C: 9. Public Key + Host Key + Signature
Note over C,S: 10. คำนวณ Shared Secret K
C->>S: 11. NEWKEYS (เปลี่ยนเป็น symmetric)
S->>C: 12. NEWKEYS
Note over C,S: 🔒 ช่องสัญญาณเข้ารหัสพร้อม
C->>S: 13. User Authentication
S->>C: 14. Auth Success → Open Shell/Channel
หลักการ Diffie-Hellman Key Exchange ที่ใช้ใน SSH สามารถอธิบายด้วยสมการได้ว่า ทั้งสองฝ่ายตกลงเลขสาธารณะ (จำนวนเฉพาะ) และ (generator) จากนั้น Client เลือก secret และ Server เลือก secret แล้วต่างคำนวณ:
จากนั้นแลกเปลี่ยน และ และคำนวณ Shared Secret :
โดยที่ K คือ Shared Secret ที่ทั้งสองฝ่ายได้ค่าเดียวกันโดยไม่เคยส่ง หรือ ผ่านเครือข่าย — ผู้ดักฟังจะเห็นเพียง แต่ไม่สามารถคำนวณ ได้ในเวลาที่สมเหตุสมผล (Discrete Logarithm Problem) ปัจจุบัน SSH นิยมใช้ Elliptic Curve Diffie-Hellman (ECDH) บน Curve25519 แทนเพื่อความเร็วและความปลอดภัยที่สูงขึ้น
# การเชื่อมต่อแบบพื้นฐาน — รูปแบบ user@host
ssh moo@server.rmutsv.ac.th
# ระบุพอร์ตที่ไม่ใช่ default 22 ด้วย -p
ssh -p 2222 moo@10.0.0.50
# ระบุ identity file (private key) ที่จะใช้
ssh -i ~/.ssh/id_ed25519_lab moo@10.0.0.50
# เปิด verbose mode สำหรับ debug การเชื่อมต่อ (-v, -vv, -vvv)
ssh -vvv moo@10.0.0.50
# รันคำสั่งเดียวบนเซิร์ฟเวอร์แล้วปิดทันที (ไม่ได้ shell)
ssh moo@10.0.0.50 'uptime && df -h'
# ใช้ ssh เป็น "pipe" — รันคำสั่งบน remote แล้วเอาผลกลับมา process ฝั่ง local
ssh moo@10.0.0.50 'cat /var/log/syslog' | grep ERROR | wc -l
ตัวอย่างผลลัพธ์ที่จะเห็นเมื่อเชื่อมต่อสำเร็จ:
$ ssh moo@10.0.0.50
The authenticity of host '10.0.0.50 (10.0.0.50)' can't be established.
ED25519 key fingerprint is SHA256:abc123def456...xyz
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.0.0.50' (ED25519) to the list of known hosts.
moo@10.0.0.50's password: ********
Welcome to Ubuntu 24.04 LTS (GNU/Linux 6.8.0-generic x86_64)
moo@server:~$
เมื่อเชื่อมต่อ SSH ครั้งแรก Client จะบันทึก Host Key (กุญแจสาธารณะของ Server) ลงในไฟล์ ~/.ssh/known_hosts ครั้งต่อไป Client จะตรวจสอบว่า Host Key ตรงกับครั้งก่อนหรือไม่ — กลไกนี้ป้องกัน Man-in-the-Middle Attack
# ดูเนื้อหาไฟล์ known_hosts
cat ~/.ssh/known_hosts
# ตัวอย่าง entry หนึ่งบรรทัด:
# 10.0.0.50 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO...
# ลบ entry ของ host เดียว (เช่น เปลี่ยนเซิร์ฟเวอร์แล้วได้ key ใหม่)
ssh-keygen -R 10.0.0.50
# ตรวจสอบ fingerprint ของ host key (รันบนเซิร์ฟเวอร์)
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
# ตรวจสอบ fingerprint ของ host จากระยะไกล (ก่อนเชื่อมต่อจริง)
ssh-keyscan -t ed25519 server.rmutsv.ac.th | ssh-keygen -lf -
💡 Best Practice: เมื่อเห็นข้อความ
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!ห้าม กด yes ทันที ให้ตรวจสอบกับผู้ดูแลระบบเสมอว่ามีการเปลี่ยน host key จริงหรือไม่ มิฉะนั้นอาจตกเป็นเหยื่อ MitM Attack
SSH รองรับการพิสูจน์ตัวตน (Authentication) หลายแบบ ซึ่งสามารถใช้ผสมกัน (Multi-method) ได้ตามนโยบายความปลอดภัย
วิธีที่ง่ายที่สุดคือใช้ Username/Password ของระบบ (ตรงกับใน /etc/passwd และ /etc/shadow) แต่มีข้อเสียคือ:
# ปิด Password Auth ใน sshd_config (แนะนำสำหรับ production)
sudo sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl reload sshd
วิธีที่ปลอดภัยและสะดวกที่สุดคือ Public Key Authentication ซึ่งใช้คู่กุญแจ (Key Pair):
~/.ssh/authorized_keysหลักการคือ Server ส่ง Challenge ที่เข้ารหัสด้วย Public Key ของผู้ใช้ Client ต้องใช้ Private Key ในการ decrypt และตอบกลับ ถ้าตอบถูก แสดงว่าเป็นเจ้าของ key จริง
sequenceDiagram
participant C as 💻 Client
(มี Private Key)
participant S as 🖥️ Server
(มี authorized_keys)
C->>S: 1. ขอ Login เป็น user "moo"
S->>S: 2. อ่าน ~moo/.ssh/authorized_keys
S->>C: 3. ส่ง Challenge (random data)
encrypted ด้วย Public Key
C->>C: 4. Decrypt Challenge ด้วย Private Key
C->>S: 5. ส่ง Hash ของ Challenge กลับ
S->>S: 6. ตรวจสอบ Hash
S->>C: 7. ✅ Auth Success → เปิด Shell
# สร้าง Ed25519 key (แนะนำ — เร็ว, ปลอดภัย, key สั้น)
ssh-keygen -t ed25519 -C "moo@rmutsv-laptop" -f ~/.ssh/id_ed25519
# สร้าง RSA 4096-bit (compatibility กับระบบเก่า)
ssh-keygen -t rsa -b 4096 -C "moo@rmutsv-laptop" -f ~/.ssh/id_rsa
# สร้าง ECDSA (ทางเลือก — แต่นิยมน้อยกว่า Ed25519)
ssh-keygen -t ecdsa -b 521 -C "moo@rmutsv-laptop"
# ดู public key ที่สร้าง
cat ~/.ssh/id_ed25519.pub
# ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO... moo@rmutsv-laptop
# ดู fingerprint ของ key
ssh-keygen -lf ~/.ssh/id_ed25519.pub
# 256 SHA256:abc123... moo@rmutsv-laptop (ED25519)
# เปลี่ยน passphrase ของ private key เดิม
ssh-keygen -p -f ~/.ssh/id_ed25519
ตารางเปรียบเทียบประเภทของ Key:
| ประเภท | ขนาดที่แนะนำ | ความเร็ว | ขนาด Public Key | สถานะปัจจุบัน |
|---|---|---|---|---|
| Ed25519 | 256-bit (fixed) | ⚡⚡⚡ เร็วที่สุด | สั้น (~80 chars) | ✅ แนะนำ |
| RSA | ≥ 3072-bit | ⚡ ช้าที่สุด | ยาวมาก (~700+ chars) | ⚠️ ใช้ได้ ต้อง ≥ 3072 |
| ECDSA | 256/384/521-bit | ⚡⚡ เร็ว | ปานกลาง | ⚠️ ใช้ได้ แต่นิยมน้อย |
| DSA | 1024-bit | ช้า | ปานกลาง | ❌ ห้ามใช้ (deprecated) |
# วิธีง่ายที่สุด: ใช้ ssh-copy-id ส่ง public key ไปยัง server
ssh-copy-id -i ~/.ssh/id_ed25519.pub moo@10.0.0.50
# วิธี manual (ถ้าไม่มี ssh-copy-id เช่น บน Windows)
cat ~/.ssh/id_ed25519.pub | ssh moo@10.0.0.50 \
'mkdir -p ~/.ssh && chmod 700 ~/.ssh && \
cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'
# ทดสอบเชื่อมต่อ — ไม่ควรถาม password อีกแล้ว
ssh moo@10.0.0.50
# ตรวจสอบเนื้อหา authorized_keys บนเซิร์ฟเวอร์
ssh moo@10.0.0.50 'cat ~/.ssh/authorized_keys'
โครงสร้างของ authorized_keys แต่ละบรรทัดมีรูปแบบ:
[options] keytype base64-encoded-key comment
ตัวอย่างพร้อม options เพื่อจำกัดสิทธิ์ของ key:
# จำกัดให้ key นี้รันได้เฉพาะคำสั่ง backup เท่านั้น (ห้าม shell)
command="/usr/local/bin/backup.sh",no-port-forwarding,no-pty,no-X11-forwarding ssh-ed25519 AAAA... backup-job
# จำกัด IP ที่อนุญาตให้ใช้ key นี้
from="10.0.0.0/24,192.168.1.5" ssh-ed25519 AAAA... office-only
# key สำหรับ Git deploy (อ่านอย่างเดียว ไม่ต้องการ shell)
command="git-shell",no-pty,no-X11-forwarding ssh-rsa AAAA... deploy-key
ssh-agent เป็นโปรแกรมที่จดจำ Private Key (หลังถอด passphrase แล้ว) ไว้ในหน่วยความจำ ทำให้ผู้ใช้ไม่ต้องพิมพ์ passphrase ทุกครั้ง
# เริ่ม ssh-agent ใน shell ปัจจุบัน (ส่วนใหญ่ desktop env เริ่มให้อยู่แล้ว)
eval "$(ssh-agent -s)"
# เพิ่ม key เข้า agent (ถาม passphrase ครั้งเดียว)
ssh-add ~/.ssh/id_ed25519
# เพิ่ม key พร้อมหมดอายุใน 1 ชั่วโมง (ปลอดภัยกว่า)
ssh-add -t 3600 ~/.ssh/id_ed25519
# ดู keys ที่อยู่ใน agent ขณะนี้
ssh-add -l
# ลบ key ออกจาก agent
ssh-add -d ~/.ssh/id_ed25519
# ลบทุก keys ออกจาก agent
ssh-add -D
Agent Forwarding ทำให้สามารถใช้ key จากเครื่อง local ผ่านเซิร์ฟเวอร์กลางได้ (เช่น hop ไปอีกเครื่อง):
# เปิด agent forwarding ด้วย -A
ssh -A moo@bastion.rmutsv.ac.th
# เมื่อเข้าไปแล้ว สามารถ ssh ต่อไปยังเครื่องอื่นโดยใช้ key เดิมได้
moo@bastion:~$ ssh moo@internal-server
⚠️ Agent Forwarding มีความเสี่ยง — ถ้าเซิร์ฟเวอร์กลางถูกแฮ็ก ผู้โจมตีสามารถใช้ key ของคุณได้ ควรใช้
ProxyJump(-J) แทนเป็นทางเลือกที่ปลอดภัยกว่า
Passphrase คือรหัสผ่านที่ใช้เข้ารหัส Private Key อีกชั้น — แม้ Private Key รั่วไหล ผู้โจมตีก็ยังต้องเดา passphrase อีก
แนวทางจัดการ Key อย่างปลอดภัย:
id_ed25519_personal, id_ed25519_work, id_ed25519_lab~/.ssh = 700, private key = 600, public key = 644# ตรวจสอบและแก้ไข permission อัตโนมัติ
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519 ~/.ssh/authorized_keys ~/.ssh/config
chmod 644 ~/.ssh/id_ed25519.pub ~/.ssh/known_hosts
การตั้งค่า SSH แบ่งเป็นสองฝั่ง: Client config สำหรับผู้ใช้ที่เชื่อมต่อออก และ Server config สำหรับเครื่องที่รับการเชื่อมต่อ
ไฟล์ ~/.ssh/config ช่วยให้กำหนด alias และ option ของแต่ละ host ทำให้พิมพ์คำสั่งสั้นลงและจำง่ายขึ้น
# ~/.ssh/config — ตัวอย่าง config ที่ใช้งานจริง
# === Default settings สำหรับทุก host ===
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
Compression yes
HashKnownHosts yes
AddKeysToAgent yes
IdentitiesOnly yes
# === เซิร์ฟเวอร์มหาวิทยาลัย ===
Host rmutsv-server
HostName 202.xx.xx.xx
User moo
Port 2222
IdentityFile ~/.ssh/id_ed25519_rmutsv
# === เซิร์ฟเวอร์ใน Lab (เข้าผ่าน bastion) ===
Host lab-bastion
HostName bastion.lab.rmutsv.ac.th
User moo
IdentityFile ~/.ssh/id_ed25519_lab
Host lab-*
User student
IdentityFile ~/.ssh/id_ed25519_lab
ProxyJump lab-bastion
Host lab-web
HostName 10.10.0.10
Host lab-db
HostName 10.10.0.20
# === GitHub (สำหรับ git over SSH) ===
Host github.com
User git
IdentityFile ~/.ssh/id_ed25519_github
IdentitiesOnly yes
# === Wildcard pattern ===
Host *.rmutsv.ac.th
User moo
ForwardAgent no
ผลลัพธ์: แทนที่จะพิมพ์ ssh -i ~/.ssh/id_ed25519_rmutsv -p 2222 moo@202.xx.xx.xx ก็พิมพ์เพียง ssh rmutsv-server ได้ทันที และ ssh lab-web จะ ProxyJump ผ่าน bastion ให้อัตโนมัติ
ตัวอย่าง sshd_config ที่ปลอดภัยและพร้อมใช้งานจริง:
# /etc/ssh/sshd_config — Production-ready hardened config
# === Network ===
Port 2222 # เปลี่ยนจาก 22 เพื่อลด noise จาก bot
AddressFamily inet # ใช้ IPv4 เท่านั้น (หรือ any สำหรับ dual-stack)
ListenAddress 0.0.0.0
Protocol 2 # ห้าม SSH-1 เด็ดขาด
# === Host Keys ===
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
# === Authentication ===
LoginGraceTime 30
PermitRootLogin no # ห้าม login เป็น root โดยตรง
StrictModes yes
MaxAuthTries 3
MaxSessions 5
PubkeyAuthentication yes
PasswordAuthentication no # ปิด password auth (ใช้ key เท่านั้น)
PermitEmptyPasswords no
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
UsePAM yes
# === User/Group restrictions ===
AllowUsers moo admin deploy
# AllowGroups ssh-users
DenyUsers root guest
# === Forwarding (เปิดเฉพาะที่จำเป็น) ===
AllowTcpForwarding yes
AllowAgentForwarding no # ปิดเป็น default — เปิดต่อเมื่อจำเป็น
X11Forwarding no
PermitTunnel no
GatewayPorts no
# === Misc ===
ClientAliveInterval 300
ClientAliveCountMax 2
PrintMotd no
PrintLastLog yes
TCPKeepAlive yes
UseDNS no # speed up login
Banner /etc/issue.net
# === Crypto Hardening (ตัด weak algorithms) ===
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256
# === Subsystems ===
Subsystem sftp /usr/lib/openssh/sftp-server
หลังแก้ไข ต้องตรวจสอบ syntax และ reload service:
# ตรวจสอบ syntax ของ config (ห้าม restart ก่อนตรวจ!)
sudo sshd -t
# ถ้าไม่มี error ค่อย reload
sudo systemctl reload sshd
# ถ้า reload ไม่ได้ผล (เช่นเปลี่ยน port) ใช้ restart
sudo systemctl restart sshd
# ตรวจสอบสถานะ
sudo systemctl status sshd
# ดู log ล่าสุด
sudo journalctl -u sshd -n 50 --no-pager
⚠️ คำเตือน: ก่อน restart sshd ให้เปิด session ที่สองทิ้งไว้เสมอ! ถ้า config ผิด session ปัจจุบันจะยังอยู่ แต่ session ใหม่จะเข้าไม่ได้ — ต้องใช้ session ที่สองในการแก้
แนวทาง Hardening พื้นฐานที่ควรทำกับทุก SSH Server:
PermitRootLogin no แล้ว sudo แทนAllowUsers หรือ AllowGroupsตัวอย่างการตั้ง firewall เพื่อจำกัด SSH:
# ufw (Ubuntu/Debian)
sudo ufw allow from 10.0.0.0/24 to any port 2222 proto tcp
sudo ufw deny 2222/tcp
# firewalld (Fedora/RHEL)
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" port port="2222" protocol="tcp" accept'
sudo firewall-cmd --reload
# nftables
sudo nft add rule inet filter input tcp dport 2222 ip saddr 10.0.0.0/24 accept
sudo nft add rule inet filter input tcp dport 2222 drop
Fail2Ban เป็นโปรแกรมที่อ่าน log และ ban IP ที่พยายาม login ผิดเกินจำนวนครั้งที่กำหนด
# ติดตั้ง
sudo apt install fail2ban -y # Debian/Ubuntu
sudo dnf install fail2ban -y # Fedora/RHEL
# สร้างไฟล์ jail.local (ห้ามแก้ jail.conf โดยตรง)
sudo tee /etc/fail2ban/jail.local <<'EOF'
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd
[sshd]
enabled = true
port = 2222
logpath = %(sshd_log)s
EOF
# เริ่มและ enable
sudo systemctl enable --now fail2ban
# ดู IP ที่ถูก ban
sudo fail2ban-client status sshd
# unban IP เฉพาะ (กรณีตัวเองโดน)
sudo fail2ban-client set sshd unbanip 1.2.3.4
CrowdSec เป็นทางเลือกใหม่ที่ทันสมัยกว่า — มี community blocklist แชร์กันทั่วโลก
# ดู log ของ sshd แบบ real-time
sudo journalctl -u sshd -f
# ดู login ที่สำเร็จในวันนี้
sudo journalctl -u sshd --since today | grep "Accepted"
# ดู login ที่ล้มเหลว
sudo journalctl -u sshd --since "1 hour ago" | grep -E "Failed|Invalid"
# คำสั่ง last — ดูประวัติ login ทั้งหมด
last -a | head -20
# lastb — ดู login ที่ล้มเหลว (ต้อง root)
sudo lastb -a | head -20
# ดู IP ที่พยายาม brute-force มากที่สุด
sudo journalctl -u sshd --since "1 day ago" \
| grep "Failed password" \
| grep -oE 'from [0-9.]+' \
| sort | uniq -c | sort -rn | head -10
SSH ไม่ได้เป็นเพียงแค่ Remote Shell — แต่ยังเป็นเครื่องมือ Tunneling ที่ทรงพลังที่สุดตัวหนึ่ง สามารถใช้สร้างช่องทางเข้ารหัสสำหรับ Protocol อื่น ๆ ได้
Local Port Forwarding เปิดพอร์ตบนเครื่อง Client แล้วส่ง traffic ผ่าน SSH ไปยังเป้าหมายฝั่ง Server หรือเครื่องที่ Server เห็นได้ — ใช้สำหรับ เข้าถึงบริการที่อยู่หลัง firewall
รูปแบบ: ssh -L [bind_addr:]local_port:target_host:target_port user@ssh_server
flowchart LR
subgraph Local["🏠 เครื่อง Local (ของเรา)"]
Browser[("Browser/App
localhost:8080")]
end
subgraph SSHC["🔒 SSH Tunnel (Encrypted)"]
Tunnel["ssh -L 8080:db:5432
user@bastion"]
end
subgraph Remote["🌐 Remote Network (ที่ทำงาน)"]
Bastion["Bastion
(SSH Server)"]
DB[("Database Server
db:5432")]
end
Browser -->|1. connect| Tunnel
Tunnel -->|2. encrypted| Bastion
Bastion -->|"3. plaintext
(internal)"| DB
# ตัวอย่าง: เข้าถึง PostgreSQL บน internal server ผ่าน bastion
# - เปิด port 5432 บน localhost
# - ผ่าน SSH ไปที่ bastion.example.com
# - แล้ว forward ไปที่ db.internal:5432
ssh -L 5432:db.internal:5432 moo@bastion.example.com
# จากนั้นเชื่อมต่อ DB ได้เลยที่ localhost:5432
psql -h localhost -p 5432 -U appuser mydb
# Forward หลายพอร์ตในคราวเดียว
ssh -L 5432:db:5432 -L 6379:redis:6379 -L 9200:elasticsearch:9200 \
moo@bastion.example.com
# Background mode (-f) + ไม่ execute remote command (-N)
ssh -fN -L 5432:db:5432 moo@bastion.example.com
Remote Port Forwarding เปิดพอร์ตบนเครื่อง Server แล้วส่ง traffic กลับมายังเครื่อง Client — ใช้สำหรับ expose บริการบนเครื่อง local ให้คนภายนอกเข้าถึงได้
รูปแบบ: ssh -R [bind_addr:]remote_port:target_host:target_port user@ssh_server
# ตัวอย่าง: เปิด web dev server บน localhost:3000 ของเรา
# ให้คนเข้าถึงได้ที่ public-server:8080
ssh -R 8080:localhost:3000 moo@public-server.com
# ถ้าต้องการให้ bind 0.0.0.0 ของ remote (ไม่ใช่แค่ localhost)
# ต้องตั้ง GatewayPorts yes ใน sshd_config ของ remote ก่อน
ssh -R 0.0.0.0:8080:localhost:3000 moo@public-server.com
💡 ทางเลือกสมัยใหม่สำหรับงานนี้: ใช้ ngrok, cloudflared, localtunnel, frp หรือ rathole ที่ออกแบบมาเฉพาะ
Dynamic Port Forwarding เปลี่ยน SSH ให้กลายเป็น SOCKS5 Proxy สามารถใช้ proxy traffic ของแอปต่าง ๆ ผ่าน SSH ได้ทั้งหมด
# เปิด SOCKS5 proxy ที่ localhost:1080 ผ่าน SSH
ssh -D 1080 moo@bastion.example.com
# ใช้ curl ผ่าน SOCKS proxy
curl --socks5 localhost:1080 https://internal-app.company.com
# ตั้ง Firefox/Chrome ให้ใช้ proxy 127.0.0.1:1080 (SOCKS5)
# จะสามารถเปิดเว็บภายในได้ทั้งหมด
# Background mode
ssh -fND 1080 moo@bastion.example.com
ProxyJump ทำให้สามารถ SSH ทะลุผ่านเครื่องกลาง (Bastion / Jump Host) ไปยังเครื่องปลายทางได้ในคำสั่งเดียว — ปลอดภัยกว่า Agent Forwarding
# Hop เดียว: local → bastion → target
ssh -J moo@bastion.example.com student@10.10.0.20
# Multiple hops: local → bastion1 → bastion2 → target
ssh -J moo@bastion1,moo@bastion2 student@10.10.0.20
# กำหนดใน ~/.ssh/config (สะดวกกว่า)
# Host lab-target
# HostName 10.10.0.20
# User student
# ProxyJump moo@bastion.example.com
ssh lab-target
ตัวอย่างการใช้ SSH Tunnel เข้าถึง MySQL/PostgreSQL บน server ที่ไม่เปิด port ออกเน็ต:
# === MySQL ===
# Terminal 1: เปิด tunnel
ssh -fN -L 3307:localhost:3306 moo@db.rmutsv.ac.th
# Terminal 2: เชื่อมต่อ MySQL ผ่าน tunnel
mysql -h 127.0.0.1 -P 3307 -u dbuser -p mydatabase
# === PostgreSQL ===
# Tunnel
ssh -fN -L 5433:localhost:5432 moo@db.rmutsv.ac.th
# เชื่อมต่อ
psql "host=127.0.0.1 port=5433 user=dbuser dbname=mydb sslmode=disable"
# === MongoDB ===
ssh -fN -L 27018:localhost:27017 moo@db.rmutsv.ac.th
mongosh mongodb://127.0.0.1:27018/mydb
# ปิด tunnel ที่รันแบบ background
pkill -f "ssh -fN -L"
# หรือ
ps aux | grep "ssh -fN" | awk '{print $2}' | xargs kill
X11 Forwarding ทำให้สามารถรันโปรแกรม GUI บน server แล้วแสดงผลที่หน้าจอของ client ได้ — เหมาะสำหรับงาน lab หรือใช้ tool GUI เฉพาะตัวที่ติดตั้งบนเซิร์ฟเวอร์
# -X: Untrusted X11 forwarding (ปลอดภัยกว่า)
ssh -X moo@server
# -Y: Trusted X11 forwarding (เร็วกว่า แต่ปลอดภัยน้อยลง)
ssh -Y moo@server
# ทดสอบ
moo@server:~$ xclock # นาฬิกาควรขึ้นบนหน้าจอ local
moo@server:~$ wireshark # ตัวอย่าง GUI app
# ฝั่ง server ต้องเปิดใน sshd_config:
# X11Forwarding yes
# X11DisplayOffset 10
# X11UseLocalhost yes
Multiplexing ทำให้ SSH หลาย session ใช้ TCP connection เดียวกัน — เร็วขึ้นมากเพราะไม่ต้อง handshake ใหม่ทุกครั้ง
# ~/.ssh/config
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h:%p
ControlPersist 10m
# สร้างโฟลเดอร์ sockets ก่อน
mkdir -p ~/.ssh/sockets
# ครั้งแรก: เชื่อมต่อปกติ — สร้าง master connection
ssh moo@server # ใช้เวลา ~2 วินาที (handshake ครบ)
# ครั้งต่อ ๆ ไป: ใช้ master connection เดิม
ssh moo@server # ใช้เวลา < 0.1 วินาที!
scp file.txt moo@server:~/ # เร็วเพราะไม่ต้อง handshake ใหม่
# ดู master connection ที่เปิดอยู่
ls -la ~/.ssh/sockets/
# ปิด master connection
ssh -O exit moo@server
sshuttle เป็นเครื่องมือที่ทำ "VPN-like" tunnel ผ่าน SSH โดยไม่ต้องตั้ง VPN จริง — เหมาะสำหรับเข้าถึง subnet ภายในองค์กรชั่วคราว
# ติดตั้ง
sudo apt install sshuttle -y # Debian/Ubuntu
sudo dnf install sshuttle -y # Fedora
# เปิด tunnel ทั้ง subnet 10.10.0.0/24 ผ่าน server
sudo sshuttle -r moo@bastion.example.com 10.10.0.0/24
# Tunnel ทุก traffic (เป็น VPN เต็ม)
sudo sshuttle -r moo@bastion.example.com 0/0
# Exclude DNS หรือ subnet บางอัน
sudo sshuttle -r moo@bastion.example.com 10.10.0.0/24 -x 10.10.0.50
# ใช้ DNS ของ remote ด้วย
sudo sshuttle --dns -r moo@bastion.example.com 10.10.0.0/24
💡 sshuttle ไม่ใช่ VPN จริง ไม่ส่ง UDP, ICMP — ถ้าต้องการ full VPN ใช้ WireGuard หรือ OpenVPN แทน
SSH ไม่ได้ใช้แค่ Remote Shell — แต่ยังเป็นช่องทางถ่ายโอนไฟล์ที่ปลอดภัยที่สุดวิธีหนึ่ง
scp เป็นคำสั่งคัดลอกไฟล์ผ่าน SSH ที่ใช้ syntax คล้าย cp ปกติ
# Local → Remote: คัดลอกไฟล์ขึ้น server
scp /home/moo/report.pdf moo@server:/home/moo/
# Remote → Local: ดึงไฟล์ลงมา
scp moo@server:/var/log/syslog ./
# คัดลอกทั้งโฟลเดอร์ (recursive)
scp -r ./project moo@server:/home/moo/
# ระบุ port ที่ไม่ใช่ default
scp -P 2222 file.txt moo@server:~/
# ระบุ identity file
scp -i ~/.ssh/id_ed25519_lab file.txt moo@server:~/
# คงสิทธิ์ไฟล์ + timestamp + verbose
scp -pv file.txt moo@server:~/
# Server-to-Server: คัดลอกระหว่างสอง remote โดย client เป็นตัวกลาง
scp moo@server1:/data/file.txt moo@server2:/backup/
# ใช้ option -3 ให้ traffic ผ่าน client (กรณี server-to-server โดยตรงไม่ได้)
scp -3 moo@server1:/data/file.txt moo@server2:/backup/
⚠️ scp กำลังถูก deprecate ใน OpenSSH รุ่นใหม่ — แนะนำให้ใช้
sftpหรือrsyncแทน
sftp เป็น protocol ระดับสูงกว่า scp ที่รองรับ resume, listing, permission management
# เริ่ม interactive session
sftp moo@server
# คำสั่งภายใน sftp (interactive mode)
sftp> pwd # show remote pwd
sftp> lpwd # show local pwd
sftp> ls # list remote
sftp> lls # list local
sftp> cd /var/log # cd remote
sftp> lcd /tmp # cd local
sftp> get syslog # download
sftp> put report.pdf # upload
sftp> get -r /etc/nginx # download recursive
sftp> mkdir backup # create remote dir
sftp> rm oldfile # delete remote file
sftp> chmod 644 file # change permission
sftp> bye # exit
# Batch mode: รัน script จากไฟล์
cat > batch.txt <<EOF
cd /var/log
get -r nginx
bye
EOF
sftp -b batch.txt moo@server
rsync เป็นเครื่องมือ sync ที่ฉลาดกว่า — คัดลอกเฉพาะส่วนที่เปลี่ยน (delta transfer) เหมาะสำหรับ backup และ sync โฟลเดอร์ใหญ่
# === Options พื้นฐาน ===
# -a : archive mode (= -rlptgoD: recursive, links, perms, times, group, owner, devices)
# -v : verbose
# -z : compress
# -P : show progress + resume partial
# --delete : ลบไฟล์ฝั่งปลายทางที่ไม่มีในต้นทาง
# Local → Remote
rsync -avzP /home/moo/project/ moo@server:/home/moo/project/
# Remote → Local
rsync -avzP moo@server:/var/www/site/ ./site_backup/
# ⚠️ ข้อสำคัญ: ตามด้วย / หรือไม่ตาม / ความหมายต่างกัน!
# rsync -av /src/ /dst/ → คัดลอกเนื้อหาภายใน src ไปไว้ใน dst
# rsync -av /src /dst/ → คัดลอก folder src ทั้งก้อนไปไว้ใน dst (กลายเป็น dst/src)
# Mirror (ทำให้ปลายทางเหมือนต้นทางทุกอย่าง)
rsync -avz --delete /src/ moo@server:/dst/
# Exclude บาง pattern
rsync -avz --exclude='*.log' --exclude='node_modules/' \
./project/ moo@server:/home/moo/project/
# ใช้ exclude file
cat > .rsyncignore <<EOF
*.log
*.tmp
node_modules/
__pycache__/
.venv/
.git/
EOF
rsync -avz --exclude-from=.rsyncignore ./project/ moo@server:/home/moo/project/
# ระบุ SSH option (port, identity)
rsync -avz -e "ssh -p 2222 -i ~/.ssh/id_ed25519_lab" \
./project/ moo@server:/home/moo/project/
# Dry run (--dry-run / -n) ทดสอบก่อนรันจริง
rsync -avzn --delete /src/ moo@server:/dst/
# Bandwidth limit (KB/s)
rsync -avz --bwlimit=1024 /src/ moo@server:/dst/
ตัวอย่าง backup script ที่ใช้ในงานจริง:
#!/usr/bin/env bash
# backup.sh — สำรองข้อมูลรายวัน
set -euo pipefail
REMOTE_USER="moo"
REMOTE_HOST="backup.rmutsv.ac.th"
REMOTE_PATH="/srv/backup/$(hostname)"
LOCAL_PATH="/home/moo/important_data"
LOG_FILE="/var/log/backup-$(date +%F).log"
# Sync ด้วย rsync — log ทั้งหมด, ใช้ port 2222
rsync -avzP --delete \
--exclude-from="$HOME/.rsyncignore" \
-e "ssh -p 2222 -i $HOME/.ssh/id_ed25519_backup" \
"$LOCAL_PATH/" \
"${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH}/" \
2>&1 | tee "$LOG_FILE"
echo "Backup completed at $(date)" | tee -a "$LOG_FILE"
sshfs ใช้ FUSE mount โฟลเดอร์ remote ให้เป็นเหมือนโฟลเดอร์ใน filesystem ของเรา — ใช้งานเหมือนไฟล์ local ทุกประการ
# ติดตั้ง
sudo apt install sshfs -y # Debian/Ubuntu
sudo dnf install fuse-sshfs -y # Fedora
# Mount: remote /home/moo → local ~/server
mkdir -p ~/server
sshfs moo@server.rmutsv.ac.th:/home/moo ~/server
# พร้อม option ที่ใช้บ่อย
sshfs -o reconnect,ServerAliveInterval=15,ServerAliveCountMax=3,allow_other \
moo@server:/home/moo ~/server
# ใช้งานได้เหมือนโฟลเดอร์ local
ls ~/server
nano ~/server/script.sh
cp ~/Downloads/file.zip ~/server/
# Unmount
fusermount -u ~/server
# หรือบน macOS
umount ~/server
ตารางเปรียบเทียบเครื่องมือถ่ายโอนไฟล์:
| เครื่องมือ | การใช้งาน | Resume | Delta | Mount | เหมาะกับ |
|---|---|---|---|---|---|
| scp | One-shot copy | ❌ | ❌ | ❌ | ไฟล์เดียว, เล็ก |
| sftp | Interactive/Batch | ✅ | ❌ | ❌ | จัดการไฟล์หลายชนิด |
| rsync | Sync/Backup | ✅ | ✅ | ❌ | Backup, mirror, ใหญ่ ๆ |
| sshfs | Filesystem mount | ✅ | ❌ | ✅ | ใช้งานต่อเนื่องเหมือนโฟลเดอร์ local |
Cockpit เป็น Web-based Server Management Tool ที่ Red Hat พัฒนา — ออกแบบให้เป็น "เครื่องมือเสริม sshd" ไม่ใช่แทน SSH ทำให้ผู้ดูแลระบบจัดการเซิร์ฟเวอร์ผ่านเบราว์เซอร์ได้สะดวกขึ้น โดยเฉพาะสำหรับงานที่ต้องการ visualization
ข้อดีของ Web-based Management เทียบกับ CLI ล้วน:
แต่ก็มีข้อเสีย:
flowchart TB
subgraph Browser["🌐 Browser (Client)"]
UI["Cockpit Web UI
https://server:9090"]
end
subgraph Server["🖥️ Server"]
CWS["cockpit-ws
(Web Server, port 9090)"]
CB["cockpit-bridge
(per-user session)"]
subgraph Modules["Cockpit Modules"]
M1[System]
M2[Logs]
M3[Storage]
M4[Network]
M5[Services]
M6[Containers]
M7[VMs]
end
SYS[(Linux Kernel
systemd, NetworkManager,
firewalld, podman, libvirt)]
end
UI <-->|HTTPS + WebSocket| CWS
CWS --> CB
CB --> M1 & M2 & M3 & M4 & M5 & M6 & M7
M1 & M2 & M3 & M4 & M5 & M6 & M7 --> SYS
# === Debian/Ubuntu ===
sudo apt update
sudo apt install -y cockpit
# (ทางเลือก) ติดตั้ง module เพิ่ม
sudo apt install -y cockpit-podman cockpit-machines cockpit-networkmanager cockpit-storaged
# === Fedora/RHEL/Rocky/AlmaLinux ===
sudo dnf install -y cockpit
sudo dnf install -y cockpit-podman cockpit-machines cockpit-networkmanager
# === Arch Linux ===
sudo pacman -S cockpit
sudo pacman -S cockpit-podman cockpit-machines cockpit-networkmanager cockpit-storaged
# === openSUSE ===
sudo zypper install cockpit cockpit-machines cockpit-networkmanager
# Enable + Start ผ่าน systemd socket (เปิดบริการตอนมี request เท่านั้น)
sudo systemctl enable --now cockpit.socket
# ตรวจสอบสถานะ
sudo systemctl status cockpit.socket
# เปิด firewall port 9090
sudo firewall-cmd --permanent --add-service=cockpit
sudo firewall-cmd --reload
# หรือ ufw
sudo ufw allow 9090/tcp
หลังจากนั้นเปิดเบราว์เซอร์ไปที่ https://server-ip:9090 แล้ว login ด้วยบัญชี Linux ของระบบ
หน้า Dashboard หลักของ Cockpit ประกอบด้วย module ดังนี้:
journalctl ผ่านเว็บ — filter ตาม service, severity, timeCockpit รองรับการจัดการหลายเซิร์ฟเวอร์จากหน้าเดียว — เครื่องที่เป็น "primary" จะ SSH ไปยังเครื่องอื่น
# ติดตั้ง dashboard module เพิ่ม (เฉพาะบน primary)
sudo apt install -y cockpit-dashboard # Debian/Ubuntu
sudo dnf install -y cockpit-dashboard # Fedora/RHEL
# ติดตั้ง Cockpit บนทุกเครื่อง (server1, server2, server3)
# จากนั้นเปิด primary แล้วคลิก "Add new host" ใน dashboard
# กรอก hostname/IP ของเครื่องอื่น + login credentials
# Cockpit จะ SSH ไปจัดการเครื่องนั้น ๆ ให้
โดย default Cockpit ใช้ self-signed certificate — ในการใช้งานจริงควรใช้ certificate ของจริง
# === ใช้ Let's Encrypt cert ===
# ตรวจสอบ path ของ cert ปัจจุบัน
sudo ls /etc/cockpit/ws-certs.d/
# วาง cert + key ที่ได้จาก Let's Encrypt
sudo cat /etc/letsencrypt/live/server.example.com/fullchain.pem \
/etc/letsencrypt/live/server.example.com/privkey.pem \
| sudo tee /etc/cockpit/ws-certs.d/0-server.cert
sudo chmod 600 /etc/cockpit/ws-certs.d/0-server.cert
# Restart Cockpit
sudo systemctl restart cockpit
# === Hardening: จำกัด IP ที่เข้าถึงได้ ===
# /etc/systemd/system/cockpit.socket.d/listen.conf
sudo mkdir -p /etc/systemd/system/cockpit.socket.d
sudo tee /etc/systemd/system/cockpit.socket.d/listen.conf <<EOF
[Socket]
ListenStream=
ListenStream=10.0.0.10:9090
EOF
sudo systemctl daemon-reload
sudo systemctl restart cockpit.socket
# === ปิด root login ใน Cockpit ===
# /etc/cockpit/disallowed-users
echo "root" | sudo tee /etc/cockpit/disallowed-users
💡 Best Practice: ไม่เปิด Cockpit ออก Public Internet เด็ดขาด — ให้เข้าผ่าน VPN หรือ SSH Tunnel เท่านั้น เช่น
ssh -L 9090:localhost:9090 serverแล้วเปิดhttps://localhost:9090
VNC เป็น Remote Desktop Protocol แบบ cross-platform ที่ใช้ RFB (Remote Frame Buffer) Protocol ออกแบบโดย Olivetti & Oracle Research Lab ในปี 1998 (ปัจจุบันเป็น open standard)
VNC ทำงานโดยส่ง Frame Buffer (พิกเซลของหน้าจอ) จาก Server ไปยัง Client ผ่านเครือข่าย และส่ง keyboard/mouse event กลับ
หลักการสำคัญของ RFB:
ปริมาณข้อมูลที่ต้องส่ง (Bandwidth) สามารถประมาณได้จาก:
โดยที่ B คือ Bandwidth (bits/sec), W = ความกว้างหน้าจอ (pixels), H = ความสูง (pixels), D = bit depth (เช่น 24 bits/pixel), F = frame rate (fps), R = อัตราส่วนพื้นที่ที่เปลี่ยน (0–1), C = compression ratio (เช่น 5–10 สำหรับ Tight encoding)
ตัวอย่าง: 1920×1080×24×30×0.1 / 8 ≈ 18.7 MB/s ต่อ frame เปลี่ยน 10% — แสดงว่า VNC กิน bandwidth สูงพอสมควร
| VNC Server | ลักษณะ | License | จุดเด่น |
|---|---|---|---|
| TigerVNC | Standalone X server | GPL | นิยมที่สุดบน Linux, รองรับ multi-user |
| TightVNC | Standalone | GPL | บีบอัดดี, ข้ามแพลตฟอร์ม |
| x11vnc | Attach to existing X | GPL | แชร์ session ที่ใช้อยู่ |
| RealVNC | Standalone | Commercial + free tier | UI สวย, มี cloud relay |
| TurboVNC | Optimized for 3D | GPL | เน้นงาน CAD, OpenGL |
| Xrdp | RDP-to-X bridge | Apache 2.0 | ใช้ RDP client (Windows) ได้ |
ตัวอย่างการติดตั้ง TigerVNC พร้อม XFCE เป็น desktop environment:
# === ติดตั้ง TigerVNC + XFCE ===
sudo apt install -y tigervnc-standalone-server xfce4 xfce4-goodies
# === ตั้ง password VNC (เก็บใน ~/.vnc/passwd) ===
vncpasswd
# Password: ********
# Verify: ********
# Would you like to enter a view-only password (y/n)? n
# === สร้าง startup script ===
mkdir -p ~/.vnc
cat > ~/.vnc/xstartup <<'EOF'
#!/bin/bash
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
xrdb $HOME/.Xresources
startxfce4 &
EOF
chmod +x ~/.vnc/xstartup
# === เริ่ม VNC server บน display :1 (port 5901) ===
vncserver :1 -geometry 1920x1080 -depth 24 -localhost no
# === ดู VNC session ที่รันอยู่ ===
vncserver -list
# === หยุด session ===
vncserver -kill :1
สำหรับการใช้งานในระยะยาว ควรตั้ง VNC เป็น systemd service:
# /etc/systemd/system/vncserver@.service
sudo tee /etc/systemd/system/vncserver@.service <<'EOF'
[Unit]
Description=Remote desktop service (VNC) for user %i
After=syslog.target network.target
[Service]
Type=simple
User=%i
Group=%i
WorkingDirectory=/home/%i
PIDFile=/home/%i/.vnc/%H:%i.pid
ExecStartPre=-/usr/bin/vncserver -kill :%i > /dev/null 2>&1
ExecStart=/usr/bin/vncserver :%i -geometry 1920x1080 -depth 24 -fg -localhost yes
ExecStop=/usr/bin/vncserver -kill :%i
[Install]
WantedBy=multi-user.target
EOF
# enable + start สำหรับ user moo บน display :1
sudo systemctl daemon-reload
sudo systemctl enable --now vncserver@moo:1.service
# ดูสถานะ
sudo systemctl status vncserver@moo:1.service
⚠️ VNC Protocol โดย default ไม่ปลอดภัย — password authentication ใช้แค่ challenge-response แบบอ่อน, frame buffer ส่งเป็น plaintext! ห้ามเปิด VNC port (5900-5999) ออก Public Internet
วิธีที่ถูกต้องคือเปิด VNC ให้ฟังเฉพาะ localhost แล้วใช้ SSH Tunnel:
# === ฝั่ง Server ===
# เริ่ม VNC ที่ฟังเฉพาะ localhost (-localhost yes)
vncserver :1 -geometry 1920x1080 -depth 24 -localhost yes
# port 5901 จะฟังเฉพาะ 127.0.0.1
# === ฝั่ง Client ===
# สร้าง SSH tunnel: local 5901 → remote 5901
ssh -L 5901:localhost:5901 moo@server.example.com
# จากนั้นเปิด VNC viewer แล้วเชื่อมต่อ localhost:5901
remmina -c vnc://localhost:5901
# หรือ
vncviewer localhost:5901
# === One-liner: tunnel + launch viewer ===
ssh -fNL 5901:localhost:5901 moo@server.example.com && \
vncviewer localhost:5901
ใช้ noVNC ผ่าน HTTPS Reverse Proxy (Nginx) สำหรับเข้าถึงผ่านเบราว์เซอร์:
# ติดตั้ง noVNC + websockify
sudo apt install -y novnc websockify
# Run websockify เป็น proxy ระหว่าง WebSocket → VNC
websockify --web=/usr/share/novnc/ 6080 localhost:5901
# จากนั้นเข้าผ่าน https://server:6080/vnc.html
# (ควรอยู่หลัง Nginx + Let's Encrypt + Auth)
นอกจาก SSH, Cockpit, VNC ยังมีเครื่องมือ Remote Access อื่น ๆ ที่เหมาะกับ Use Case แตกต่างกัน
RDP (Remote Desktop Protocol) เป็น protocol ของ Microsoft แต่บน Linux มี implementation ฟรีคือ xrdp (server) และ FreeRDP (client) — ข้อดีคือ RDP ปลอดภัยกว่า VNC โดย default และมี client ในทุกระบบปฏิบัติการ
# === ฝั่ง Server: ติดตั้ง xrdp ===
sudo apt install -y xrdp xfce4
# ตั้งให้ใช้ XFCE
echo xfce4-session > ~/.xsession
# Add user xrdp เข้ากลุ่ม ssl-cert (สำหรับ TLS)
sudo adduser xrdp ssl-cert
# Enable + Start
sudo systemctl enable --now xrdp
# เปิด firewall (port 3389)
sudo ufw allow 3389/tcp
# === ฝั่ง Client: ใช้ FreeRDP หรือ Remmina ===
# FreeRDP CLI
xfreerdp /v:server.example.com /u:moo /p:password /size:1920x1080 /dynamic-resolution
# Remmina GUI
remmina -c rdp://moo@server.example.com
NoMachine เป็น proprietary remote desktop ที่ใช้ NX protocol — มีประสิทธิภาพดีกว่า VNC มาก เหมาะกับงาน multimedia, แต่เป็น closed-source (มี free tier สำหรับ personal use)
Apache Guacamole เป็น clientless remote desktop gateway — เปิดผ่านเบราว์เซอร์ได้เลย รองรับ SSH, RDP, VNC, Telnet, Kubernetes — เหมาะสำหรับองค์กรที่อยากให้ user เข้าถึงเครื่องต่าง ๆ จากเบราว์เซอร์โดยไม่ต้องติดตั้ง client
# วิธีง่ายที่สุด: ใช้ Docker
docker run -d --name guacd guacamole/guacd
docker run -d --name guacamole \
--link guacd:guacd \
-e POSTGRES_HOSTNAME=postgres \
-e POSTGRES_DATABASE=guacamole_db \
-e POSTGRES_USER=guacamole \
-e POSTGRES_PASSWORD=secret \
-p 8080:8080 \
guacamole/guacamole
# เข้าผ่าน http://server:8080/guacamole/
# default user/pass: guacadmin/guacadmin (เปลี่ยนทันที!)
ตารางเปรียบเทียบเครื่องมือ Remote Access:
| เครื่องมือ | Type | License | NAT Traversal | Self-hosted | UX |
|---|---|---|---|---|---|
| SSH | Shell | BSD | ❌ (ใช้ tunnel) | ✅ | CLI |
| Cockpit | Web Mgmt | LGPL | ❌ | ✅ | Web |
| VNC | Desktop | GPL | ❌ | ✅ | GUI |
| xrdp | Desktop | Apache | ❌ | ✅ | GUI |
| NoMachine | Desktop | Proprietary | ✅ | บางส่วน | GUI |
| Guacamole | Gateway | Apache | ❌ | ✅ | Web |
| MeshCentral | RMM | Apache | ✅ | ✅ | Web |
| RustDesk | Desktop | AGPL | ✅ | ✅ | GUI |
| TeamViewer | Desktop | Proprietary | ✅ | ❌ | GUI |
| AnyDesk | Desktop | Proprietary | ✅ | ❌ | GUI |
ระบบ Remote Access เป็น attack surface อันดับต้น ๆ ของเซิร์ฟเวอร์ที่เปิดสู่อินเทอร์เน็ต — การ harden ให้แน่นหนาเป็นเรื่องจำเป็น
# === ufw (Ubuntu/Debian) — Easy interface ===
# Default policy
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH จาก subnet เดียว
sudo ufw allow from 10.0.0.0/24 to any port 2222 proto tcp
# Rate limit SSH (ป้องกัน brute force)
sudo ufw limit 2222/tcp
# Enable
sudo ufw enable
# ดูสถานะ
sudo ufw status numbered
# === firewalld (Fedora/RHEL) — Zone-based ===
# ดู zone
sudo firewall-cmd --get-active-zones
# Add SSH service ใน zone public
sudo firewall-cmd --permanent --zone=public --add-port=2222/tcp
# Rich rule: allow จาก subnet เดียว
sudo firewall-cmd --permanent --zone=public --add-rich-rule='
rule family="ipv4" source address="10.0.0.0/24"
port port="2222" protocol="tcp" accept'
# Reload
sudo firewall-cmd --reload
# === nftables (modern Linux) ===
sudo tee /etc/nftables.conf <<'EOF'
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif lo accept
# ICMP
ip protocol icmp accept
ip6 nexthdr ipv6-icmp accept
# SSH (rate limited)
tcp dport 2222 ip saddr 10.0.0.0/24 limit rate 5/minute accept
# Cockpit
tcp dport 9090 ip saddr 10.0.0.0/24 accept
}
chain forward { type filter hook forward priority 0; policy drop; }
chain output { type filter hook output priority 0; policy accept; }
}
EOF
sudo systemctl enable --now nftables
Port Knocking ซ่อนพอร์ต SSH โดยให้ผู้ใช้ "เคาะ" พอร์ตชุดหนึ่งก่อน firewall จึงเปิด SSH port ให้ — ไม่ใช่ security ที่แข็งแรงในตัวเอง แต่ลด noise จาก scan ได้ดี
# ติดตั้ง knockd
sudo apt install -y knockd
# /etc/knockd.conf
sudo tee /etc/knockd.conf <<'EOF'
[options]
UseSyslog
[openSSH]
sequence = 7000,8000,9000
seq_timeout = 5
command = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
tcpflags = syn
[closeSSH]
sequence = 9000,8000,7000
seq_timeout = 5
command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
tcpflags = syn
EOF
sudo systemctl enable --now knockd
# ฝั่ง Client: เคาะ port แล้ว SSH
knock server.example.com 7000 8000 9000
ssh -p 2222 moo@server.example.com
Multi-Factor Authentication เพิ่มชั้นที่สอง — แม้ key/password รั่ว ผู้โจมตีก็ยังต้องมี factor ที่สอง (TOTP, hardware key)
# === Google Authenticator (TOTP) ===
sudo apt install -y libpam-google-authenticator
# รันในนาม user (ไม่ใช่ sudo!) เพื่อสร้าง secret
google-authenticator -t -d -f -r 3 -R 30 -W
# จะได้ QR code → scan ด้วย Google Authenticator app บนมือถือ
# จด emergency scratch codes ไว้ในที่ปลอดภัย
# แก้ /etc/pam.d/sshd — เพิ่มบรรทัดบนสุด
sudo sed -i '1i auth required pam_google_authenticator.so' /etc/pam.d/sshd
# แก้ /etc/ssh/sshd_config
sudo tee -a /etc/ssh/sshd_config <<EOF
ChallengeResponseAuthentication yes
KbdInteractiveAuthentication yes
UsePAM yes
AuthenticationMethods publickey,keyboard-interactive
EOF
sudo systemctl restart sshd
💡 YubiKey ใช้งานคล้ายกัน — Setup ผ่าน
pam_yubicoหรือใช้ FIDO2 ผ่าน OpenSSH 8.2+ (ssh-keygen -t ed25519-sk)
# ติดตั้ง auditd
sudo apt install -y auditd
# ดู rule ปัจจุบัน
sudo auditctl -l
# Watch SSH config
sudo auditctl -w /etc/ssh/sshd_config -p wa -k sshd_config
# Watch authorized_keys ของทุก user
sudo auditctl -w /root/.ssh/ -p wa -k root_ssh
sudo auditctl -w /home/ -p wa -k home_ssh
# ทำให้ rule persistent
sudo tee -a /etc/audit/rules.d/audit.rules <<EOF
-w /etc/ssh/sshd_config -p wa -k sshd_config
-w /root/.ssh/ -p wa -k root_ssh
EOF
sudo systemctl restart auditd
# ค้นหา event
sudo ausearch -k sshd_config -i | tail -20
sudo ausearch -m USER_LOGIN -i | tail -20
หลักการสำคัญที่สุดคือ ให้สิทธิ์เฉพาะที่จำเป็น:
sudo ตามต้องการ/etc/sudoers.d/ กำหนดเฉพาะคำสั่งที่อนุญาต ไม่ใช้ ALL=(ALL) NOPASSWD: ALL พร่ำเพรื่อgit-shell หรือ rssh สำหรับ user ที่ต้องการแค่ deployssh-keygen -s) ที่หมดอายุได้authorized_keys, sudoers, login log ทุก 1-3 เดือนตัวอย่าง sudoers granular:
# /etc/sudoers.d/deploy
# ให้ user deploy รัน restart service ได้เฉพาะที่ระบุ — ไม่ต้อง password
deploy ALL=(root) NOPASSWD: /bin/systemctl restart myapp
deploy ALL=(root) NOPASSWD: /bin/systemctl status myapp
deploy ALL=(root) NOPASSWD: /bin/journalctl -u myapp *
ก่อนนำ SSH server ออก production ควรเช็คทุกข้อต่อไปนี้:
PermitRootLogin noPasswordAuthentication no (key-only)AllowUsers หรือ AllowGroupsMaxAuthTries 3sshd_configunattended-upgrades)ssh-audit หรือ nmap --script ssh-*# ทดสอบ SSH config ของตัวเองด้วย ssh-audit
pip install ssh-audit
ssh-audit server.example.com
# หรือใช้ nmap script
nmap -p 2222 --script ssh2-enum-algos,ssh-auth-methods server.example.com
บทความนี้ได้นำเสนอครอบคลุม 3 เครื่องมือหลัก สำหรับการ Remote Access บนระบบ Linux:
scp, sftp, rsync, sshfs), tunneling (-L, -R, -D, -J), และ automationหลักการสำคัญที่ผู้ดูแลระบบทุกคนต้องจำ:
🔐 "Encryption + Authentication + Least Privilege + Audit" — สี่เสาหลักของระบบ Remote Access ที่ปลอดภัย ไม่ว่าจะเลือกเครื่องมือใด หลักการนี้ใช้ได้เสมอ