วิธีติดตั้ง Headscale ทำ Tailscale Control Server เอง
เรียนรู้วิธีติดตั้ง Headscale บน VPS เพื่อรัน Tailscale Control Server ของคุณเอง ตั้งค่าผ่านไฟล์ .deb กำหนดค่า server_url และเชื่อมต่อโหนดแรกด้วยแฟล็ก --login-server
Headscale คืออะไร
Headscale คือการนำ Tailscale control server มาติดตั้งใช้งานด้วยตนเอง (self-hosted) ซึ่งหมายความว่าเครื่องที่ทำหน้าที่ประสานงานเครือข่ายส่วนตัวของคุณคือ VPS ที่คุณเป็นเจ้าของเอง โครงการนี้เป็นโครงการของชุมชนและไม่ได้ดำเนินการโดย Tailscale Inc. โดยทุกเครื่องยังคงใช้งานไคลเอนต์ tailscale อย่างเป็นทางการ เพียงแค่ชี้ไปยังเซิร์ฟเวอร์ของคุณด้วยแฟล็ก --login-server
Control server คือส่วนที่ทำหน้าที่ตรวจสอบว่าเครื่องใดบ้างที่อยู่ในเครือข่าย โดยจะแจกจ่ายที่อยู่จาก 100.64.0.0/10 ให้กับแต่ละโหนด กระจาย public key และแจ้งให้โหนดทราบว่าต้องเชื่อมต่อหากันอย่างไร การสร้างอุโมงค์ข้อมูลยังคงใช้ WireGuard ซึ่งเป็นการเชื่อมต่อแบบโหนดต่อโหนดโดยตรง ทราฟฟิกข้อมูลระหว่างเครื่องของคุณสองเครื่องจะไม่ผ่านเครื่องที่รัน headscale เว้นแต่ว่าจะไม่สามารถสร้างเส้นทางตรงได้และโหนดต้องเปลี่ยนไปใช้ relay แทน การทำหน้าที่ประสานงานด้วยตนเองเป็นการเปลี่ยนผู้ถือครองสิทธิ์ในการควบคุม ไม่ใช่การเปลี่ยนขีดความสามารถของระบบ ดังนั้นจึงควรทำความเข้าใจ สิ่งที่ control server สามารถเข้าถึงได้และไม่สามารถเข้าถึงได้ในโมเดลนี้ ก่อนที่จะตัดสินใจย้ายระบบโดยมองว่าเป็นมาตรการเพิ่มความปลอดภัยเพียงอย่างเดียว
Headscale ให้บริการ 1 tailnet (1 เครือข่าย Tailscale) ต่อ 1 instance โดยโครงการระบุว่าเหมาะสำหรับการใช้งานส่วนบุคคลหรือองค์กรขนาดเล็ก หากมีเครื่องเพียง 3 หรือ 4 เครื่อง การใช้ WireGuard VPN แบบพื้นฐานบน VPS ที่คุณเป็นเจ้าของ จะมีซอฟต์แวร์ที่ต้องดูแลน้อยกว่า และมีจุดที่อาจขัดข้องน้อยกว่า Headscale จะคุ้มค่าเมื่อคุณไม่ต้องการเขียนบล็อก [Peer] ด้วยตนเองสำหรับ laptop เครื่องใหม่ทุกเครื่อง ค่าใช้จ่ายมักเป็นเหตุผลแรกที่ทำให้ผู้คนเริ่มค้นหาทางเลือกต่าง ๆ ดังนั้นควรอ่าน free plan แบบ hosted ครอบคลุมอะไรบ้าง ก่อนจัดหาเซิร์ฟเวอร์ เพราะเครื่องส่วนตัวจำนวนไม่มากมักอยู่ภายในขอบเขตของแผนดังกล่าว หากคุณใช้งานเกินขีดจำกัดนั้นแล้ว ให้คำนวณเปรียบเทียบกับ ค่าใช้จ่ายของ paid plan ต่อ user และ resource ที่ติด tag เพราะอุปกรณ์ของ user ไม่จำกัดจำนวน ขณะที่ resource ที่ติด tag มีโควตาและค่าใช้จ่ายแยกต่างหาก หากคุณต้องการ control plane แบบ self-hosted แต่ต้องการ client ของตนเองและ web interface สำหรับจัดการ peer มากกว่าการใช้สิ่งที่มาแทน Tailscale โดยตรง NetBird บน VPS เพียง 1 เครื่อง ก็เป็นทางเลือกที่ควรนำมาพิจารณา ทั้งสองแบบส่ง traffic ผ่าน WireGuard ดังนั้นการเลือกจึงขึ้นอยู่กับวิธีเขียน access rule และพฤติกรรมของ relay และควร เปรียบเทียบ NetBird กับ Tailscale ในประเด็นเหล่านี้ ก่อนติดตั้งอย่างใดอย่างหนึ่ง สำหรับการเปรียบเทียบโมเดลทั้งสองในภาพรวม โปรดดู ความแตกต่างระหว่าง WireGuard กับ Tailscale
สิ่งที่ต้องเตรียมก่อนการติดตั้ง
- VPS ที่รัน Ubuntu 24.04 พร้อม IPv4 สาธารณะและสิทธิ์ sudo หากเป็นเซิร์ฟเวอร์ใหม่ ให้ดำเนินการตาม สิบนาทีแรกบน VPS ใหม่ ให้เรียบร้อยก่อน
- DNS A record ที่ชี้ไปยังที่อยู่ IP นั้น คู่มือนี้ใช้
headscale.example.com - โดเมนหรือโดเมนย่อยที่สองสำหรับ MagicDNS คู่มือนี้ใช้
tailnet.example.netโดยต้องไม่เป็นโดเมนเดียวกับที่ใช้ในserver_url - เครื่องลูกข่ายหนึ่งเครื่องสำหรับเชื่อมต่อ โดยรันบน Linux, macOS, Windows, Android หรือ iOS ทั้งนี้ Headscale 0.29.6 ต้องการ Tailscale client เวอร์ชัน 1.80.0 หรือใหม่กว่า
ติดตั้ง headscale จากไฟล์ .deb อย่างเป็นทางการ
โครงการนี้เผยแพร่แพ็กเกจ .deb ไว้บนหน้า GitHub releases ของโครงการ ณ วันที่ 27 ตุลาคม 2026 รุ่นเสถียรล่าสุดคือ 0.29.6 ซึ่งปล่อยออกมาเมื่อวันที่ 24 ตุลาคม 2026 ให้ตรวจสอบสถาปัตยกรรมของระบบคุณก่อน เนื่องจากชื่อไฟล์จะระบุสถาปัตยกรรมไว้ด้วย
sudo apt update
sudo apt install -y wget
dpkg --print-architectureคำสั่งดังกล่าวจะแสดงผล amd64 บน VPS x86 ทั่วไป และแสดง arm64 บนแผนบริการประเภท Ampere หรือ Graviton ให้ใส่คำตอบลงในตัวแปรด้านล่างนี้
HEADSCALE_VERSION="0.29.6"
HEADSCALE_ARCH="amd64"
wget --output-document=headscale.deb \\
"https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_${HEADSCALE_ARCH}.deb"
sudo apt install -y ./headscale.deb
headscale versionจำเป็นต้องใส่ ./ ไว้หน้าชื่อไฟล์ หากไม่มี apt จะค้นหาแพ็กเกจที่ชื่อ headscale.deb ในที่เก็บแพ็กเกจของคุณและส่งผลให้เกิดข้อผิดพลาด
แพ็กเกจนี้จะสร้างผู้ใช้ระบบ headscale, เขียนไฟล์ /etc/headscale/config.yaml เริ่มต้น, ติดตั้ง systemd unit และเริ่มการทำงานของบริการ ดังนั้น headscale จึงทำงานอยู่แล้วเมื่อ apt ทำงานเสร็จสิ้น และ sudo systemctl is-active headscale จะแสดงผล active อย่างไรก็ตาม ในการติดตั้งใหม่แพ็กเกจจะไม่ได้เปิดใช้งาน unit ไว้ ดังนั้นบริการจะไม่กลับมาทำงานหลังจากการรีบูตจนกว่าคุณจะเปิดใช้งาน ซึ่งจะดำเนินการในขั้นตอนการรีสตาร์ทด้านล่าง นอกจากนี้บริการยังทำงานอยู่บนการตั้งค่าเริ่มต้น ซึ่งชี้ server_url ไปที่ http://127.0.0.1:8080 ซึ่งไม่ใช่ที่อยู่ที่ไคลเอนต์ของคุณจะเข้าถึงได้ ขณะนี้ยังไม่มีการเปิดเผยบริการใดๆ ออกไปเนื่องจาก listen_addr ที่มาพร้อมกับค่าเริ่มต้นนั้นทำงานบน loopback เช่นกัน แต่ห้ามเชื่อมต่อไคลเอนต์ใดๆ จนกว่าคุณจะตั้งค่า server_url และรีสตาร์ทบริการ ซึ่งจะดำเนินการในสองขั้นตอนถัดไป
กำหนดค่า server_url ก่อนที่ไคลเอนต์ใดๆ จะเข้าร่วม
แก้ไข /etc/headscale/config.yaml ด้วย sudo nano /etc/headscale/config.yaml หรือใช้การเปลี่ยนแปลงสามรายการเดียวกันด้วย sed ให้เก็บสำเนาของไฟล์ต้นฉบับไว้ เนื่องจากไฟล์มีความยาวและมีคำอธิบายประกอบไว้มาก ซึ่งถือเป็นข้อมูลอ้างอิงที่ดีที่สุดสำหรับการตั้งค่าส่วนที่เหลือ
sudo cp /etc/headscale/config.yaml /etc/headscale/config.yaml.orig
sudo sed -i 's|^server_url:.*|server_url: https://headscale.example.com|' /etc/headscale/config.yaml
sudo sed -i 's|^listen_addr:.*|listen_addr: 127.0.0.1:8080|' /etc/headscale/config.yaml
sudo sed -i 's|^ base_domain:.*| base_domain: tailnet.example.net|' /etc/headscale/config.yaml
sudo grep -E '^(server_url|listen_addr):|^ base_domain:' /etc/headscale/config.yamlserver_url คือที่อยู่สาธารณะที่ headscale ใช้เมื่อสร้าง URL สำหรับการลงทะเบียน ไคลเอนต์จะใช้ที่อยู่ควบคุมที่กำหนดค่าไว้ด้วย --login-server ดังนั้นให้ใช้ชื่อสาธารณะเดียวกันกับ https:// ในทั้งสองตำแหน่ง ห้ามใช้ 127.0.0.1 เอกสารของ headscale ระบุว่าสามารถใช้ HTTP แบบธรรมดาหรือ HTTPS บนพอร์ตอื่นที่ไม่ใช่ 443 ได้ แต่แนะนำอย่างยิ่งให้ใช้ HTTPS บนพอร์ต 443 สำหรับการใช้งานจริง และคู่มือส่วนที่เหลือนี้จะอ้างอิงตามการตั้งค่าดังกล่าว
listen_addr คือตำแหน่งที่ process ผูกอยู่ ให้คงค่าไว้ที่ loopback เนื่องจาก reverse proxy บนเครื่องเดียวกันจะทำหน้าที่จัดการ TLS (transport layer security) และส่งต่อคำขอไปยังบริการนี้ ดังนั้นจึงไม่มีความจำเป็นที่บริการภายนอกเซิร์ฟเวอร์จะต้องเข้าถึงพอร์ต 8080 ได้โดยตรง
base_domain คือ suffix ของ MagicDNS ซึ่งเป็นโดเมนที่โหนดของคุณจะได้รับชื่อภายใต้โดเมนนั้น โดยจะต้องเป็นชื่อโดเมนแบบสมบูรณ์ (fully qualified domain name) ที่ไม่มีจุดปิดท้าย และต้องเป็นโดเมนที่แตกต่างจากโดเมนใน server_url มิฉะนั้นพื้นที่ชื่อทั้งสองจะเกิดการทับซ้อนกัน
ไม่ต้องแก้ไขส่วนของฐานข้อมูล ค่าเริ่มต้นคือ SQLite ที่ /var/lib/headscale/db.sqlite ในไดเรกทอรีที่แพ็กเกจสร้างขึ้นและเป็นเจ้าของ ซึ่ง SQLite เพียงพอสำหรับขนาดของ tailnet นี้แล้ว
รีสตาร์ท headscale และตรวจสอบว่ากำลังทำงานอยู่
sudo systemctl restart headscale
sudo systemctl enable headscale
sudo systemctl is-active headscale
sudo systemctl is-enabled headscale
curl -sS -o /dev/null -w '%{http_code}\\n' http://127.0.0.1:8080/healthis-active จะแสดง active, is-enabled จะแสดง enabled และ curl จะแสดง 200 การรีสตาร์ทเป็นขั้นตอนที่โหลดการแก้ไขของคุณ headscale จะอ่านไฟล์คอนฟิกูเรชันเพียงครั้งเดียวในขณะเริ่มทำงาน ดังนั้นโพรเซสที่แพ็กเกจเริ่มไว้จะยังคงใช้ server_url ที่มาพร้อมกับซอฟต์แวร์จนกว่าคุณจะรีสตาร์ท sudo systemctl enable --now headscale ไม่สามารถทำงานนี้ได้ เนื่องจากเซอร์วิสกำลังทำงานอยู่แล้ว การสั่ง --now start จึงไม่มีผลใดๆ และค่าเดิมจะยังคงถูกใช้งานอยู่ การเริ่มทำงานตอนบูตเครื่องเป็นหน้าที่ของ enable ซึ่งไม่ได้ถูกตั้งค่าไว้ให้โดยอัตโนมัติ ในการติดตั้งใหม่แพ็กเกจจะเริ่มเซอร์วิสโดยไม่ได้เปิดใช้งานการเริ่มอัตโนมัติ ดังนั้นให้รัน sudo systemctl enable headscale หนึ่งครั้งตามที่ระบุไว้ข้างต้น
หาก is-active แสดงผลเป็นอย่างอื่นที่ไม่ใช่ active หรือ curl ไม่แสดงผลเป็น 200 ให้ตรวจสอบ log ด้วย sudo journalctl -u headscale -n 50 --no-pager ความล้มเหลวในขั้นตอนนี้มักเกิดจากไฟล์คอนฟิกูเรชันเสมอ เนื่องจาก headscale จะอ่านไฟล์ทั้งหมดก่อนที่จะเปิด socket ดังนั้นการย่อหน้าผิดหรือการตั้งค่าที่จำเป็นไม่ถูกต้องจะทำให้โพรเซสหยุดทำงานก่อนที่จะมีการเปิดพอร์ตใดๆ ให้แก้ไขไฟล์แล้วจึงรัน sudo systemctl restart headscale การเปลี่ยนแปลงคอนฟิกูเรชันในภายหลังทั้งหมดจำเป็นต้องรีสตาร์ทด้วยวิธีเดียวกันนี้ ไคลเอนต์จะเชื่อมต่อใหม่ด้วยตัวเองหลังจากนั้น หากคุณยังไม่คุ้นเคยกับ systemd สามารถดูข้อมูลเพิ่มเติมได้ที่ การรันเซอร์วิสและตัวตั้งเวลาของคุณเองด้วย systemd ซึ่งครอบคลุมคำสั่งที่ใช้ในที่นี้
ตรวจสอบไฟล์สถานะในขณะที่คุณอยู่ใน shell:
stat -c '%U %n' /var/lib/headscale/db.sqlite /var/lib/headscale/noise_private.keyทั้งสองบรรทัดจะขึ้นต้นด้วย headscale ซึ่งเป็นผู้ใช้ที่ไม่มีสิทธิ์ (unprivileged user) ที่แพ็กเกจสร้างขึ้น noise_private.key คือตัวตนของเซิร์ฟเวอร์ที่ใช้สื่อสารกับไคลเอนต์ ให้เก็บไฟล์นี้ไว้ หากคุณลบไฟล์นี้ headscale จะสร้างไฟล์ใหม่ขึ้นมา และไคลเอนต์จะต้องดึงคีย์เซิร์ฟเวอร์ใหม่ก่อนที่จะเชื่อมต่อใหม่ได้ ส่วนการลงทะเบียนที่มีอยู่เดิมจะยังคงอยู่ในฐานข้อมูล
การวาง TLS ไว้หน้า headscale
ไคลเอนต์ควรเข้าถึง server_url ผ่าน HTTPS การใช้ Caddy เป็นเส้นทางที่สั้นที่สุด เนื่องจาก Caddy จะร้องขอและต่ออายุใบรับรองด้วยตนเอง
sudo apt install -y caddyแทนที่ /etc/caddy/Caddyfile ด้วยบล็อกการตั้งค่าจากเอกสารประกอบของ headscale:
headscale.example.com {
reverse_proxy 127.0.0.1:8080 {
header_up True-Client-IP {remote_host}
header_up X-Real-IP {remote_host}
}
}sudo caddy validate --adapter caddyfile --config /etc/caddy/Caddyfile
sudo systemctl restart caddy
sudo systemctl is-active caddyvalidate จะแสดงผล adapted config to JSON เมื่อไฟล์ถูกประมวลผลสำเร็จ คำเตือนเรื่องรูปแบบไฟล์ที่ไม่ถูกต้องเป็นเพียงเรื่องความสวยงามเท่านั้น จากแล็ปท็อปของคุณ คำสั่ง curl -sS -o /dev/null -w '%{http_code}\\n' https://headscale.example.com/health ควรแสดงผล 200 เช่นกัน การตรวจสอบเพียงขั้นตอนเดียวนี้เป็นการพิสูจน์ว่า DNS, ไฟร์วอลล์, ใบรับรอง และพร็อกซีทำงานร่วมกันได้อย่างถูกต้อง
นี่คือรายละเอียดของพร็อกซีที่มักทำให้ผู้ใช้งานเสียเวลาไปทั้งเย็น การเชื่อมต่อควบคุมของ Tailscale เป็นการทำ HTTP upgrade ซึ่งเริ่มต้นด้วย POST แทนที่จะเป็น GET และค่าของเฮดเดอร์ Upgrade คือ tailscale-control-protocol โดย Caddy จะส่งผ่านค่าเหล่านี้ไปโดยไม่ต้องตั้งค่าเพิ่มเติม แต่ nginx ไม่ได้ทำเช่นนั้น ดังนั้นหากใช้ nginx เป็นหน้าด่าน คุณต้องส่งต่อเฮดเดอร์ Upgrade และ Connection อย่างชัดเจน เอกสารประกอบของ headscale ได้ระบุวิธีทำไว้ด้วยการใช้ upgrade map:
map $http_upgrade $connection_upgrade {
default keep-alive;
'' close;
}
server {
listen 443 ssl;
server_name headscale.example.com;
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
proxy_pass http://127.0.0.1:8080;
}
}หากละเว้นบรรทัดเหล่านั้น คำขอทั่วไปจะยังคงสำเร็จ ซึ่งเป็นเหตุผลว่าทำไม /health ถึงตอบกลับสถานะ 200 และทุกอย่างดูเหมือนปกติ ในขณะที่การเชื่อมต่อควบคุมไม่เคยเกิดขึ้นจริง ส่งผลให้โหนดใหม่ไม่สามารถลงทะเบียนได้ และโหนดเดิมไม่สามารถเชื่อมต่อใหม่หรือรายงานสถานะออนไลน์ได้ หากคุณเลือกใช้ nginx คุณสามารถดู Certbot บน Ubuntu 24.04 ร่วมกับ nginx เพื่อดูขั้นตอนการจัดการใบรับรองได้
พอร์ตที่ต้องเปิดใน UFW
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verboseพอร์ต 443 ใช้สำหรับเชื่อมต่อการควบคุมของไคลเอนต์ทุกรายไปยัง headscale หากคุณเปิดใช้งาน DERP relay ในตัวในภายหลัง พอร์ตนี้ยังจำเป็นต้องใช้ 3478/udp ซึ่งจะกล่าวถึงด้านล่าง พอร์ต 80 มีไว้สำหรับการทำ ACME (automatic certificate management environment) HTTP challenge และการเปลี่ยนเส้นทางไปยัง HTTPS แม้ว่า Caddy จะสามารถขอใบรับรองได้โดยปิดพอร์ต 80 ไว้ เนื่องจากสามารถใช้ TLS-ALPN challenge บนพอร์ต 443 ได้ แต่การเปิดพอร์ต 80 จะช่วยให้ใช้งานได้ทั้งสองวิธีและช่วยให้คำขอ http:// แบบปกติถูกเปลี่ยนเส้นทางได้
พอร์ต 8080 ควรปิดไว้ listen_addr คือ 127.0.0.1:8080 ดังนั้น proxy จะเข้าถึง headscale ผ่าน loopback interface โดยไม่เกี่ยวข้องกับกฎของ firewall การเปิดพอร์ต 8080 ใน UFW จะไม่ทำให้เกิดการเปิดเผยบริการใดๆ เนื่องจากไม่มีบริการใดรับฟังคำขอจากภายนอกผ่านพอร์ตนี้ และไม่ได้ประโยชน์ใดๆ เพิ่มเติม โปรดจำไว้ว่าผู้ให้บริการส่วนใหญ่มี firewall อีกชั้นในแผงควบคุม ซึ่งแยกต่างหากจาก UFW ดังนั้นพอร์ตอาจเปิดอยู่บนเซิร์ฟเวอร์แต่ยังคงถูกปิดอยู่ที่ระดับเครือข่ายภายนอก พื้นฐานการใช้งาน UFW firewall บน VPS อธิบายไวยากรณ์ของกฎเหล่านี้ไว้โดยละเอียด
การสร้างผู้ใช้และคีย์ preauth
sudo headscale users create alice
sudo headscale users listคำสั่ง headscale ทำหน้าที่เป็นไคลเอนต์ โดยจะสื่อสารกับ daemon ที่กำลังทำงานอยู่ผ่าน unix socket ที่ /var/run/headscale/headscale.sock ซึ่งถูกกำหนดโหมดไว้ที่ 0770 และมีกลุ่ม headscale เป็นเจ้าของ ส่งผลให้เกิดสองประเด็นคือ คำสั่งจะทำงานล้มเหลวหาก service ยังไม่เริ่มทำงาน ซึ่งเป็นเหตุผลว่าทำไมลำดับขั้นตอนในคู่มือนี้จึงมีความสำคัญ และจำเป็นต้องใช้ sudo เว้นแต่คุณจะเพิ่มบัญชีผู้ใช้ของคุณเข้าไปในกลุ่ม headscale
users list จะแสดง ID กำกับไว้ข้างชื่อ คุณจำเป็นต้องใช้ตัวเลขดังกล่าวเนื่องจากคำสั่งจัดการคีย์ต้องการระบุเป็นตัวเลข ID ของผู้ใช้ ไม่ใช่ชื่อ
sudo headscale preauthkeys create --user 1 --expiration 24hคีย์จะถูกแสดงออกมาเพียงครั้งเดียว ให้คัดลอกไว้ในตอนนี้ คีย์ preauth สามารถใช้งานได้เพียงครั้งเดียวและมีอายุการใช้งาน 1 ชั่วโมงเว้นแต่คุณจะกำหนดเป็นอย่างอื่น ดังนั้นการตั้งค่า --expiration 24h จึงมีประโยชน์ในระหว่างที่คุณยังอยู่ในขั้นตอนการทดสอบ ให้เพิ่ม --reusable สำหรับคีย์ที่ใช้ลงทะเบียนเครื่องหลายเครื่อง และให้ดูแลคีย์นั้นเสมือนรหัสผ่าน เนื่องจากใครก็ตามที่ถือคีย์นี้สามารถเข้าร่วมเครือข่ายของคุณได้
เชื่อมต่อไคลเอนต์เครื่องแรกของคุณด้วย --login-server
บนเครื่องที่คุณต้องการเข้าร่วมเครือข่าย:
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --login-server https://headscale.example.com --auth-key 'hskey-auth-PASTE-YOUR-KEY-HERE'
tailscale status
tailscale ip -4tailscale ip -4 จะแสดงที่อยู่ที่ headscale กำหนดให้ ซึ่งจะมีลักษณะคล้ายกับ 100.64.0.1 กลับไปที่ฝั่งเซิร์ฟเวอร์ คำสั่ง sudo headscale nodes list จะแสดงโหนดดังกล่าวพร้อมกับ ID, ผู้ใช้งาน และสถานะออนไลน์
ให้ใช้ที่อยู่ HTTPS สาธารณะจาก server_url สำหรับ --login-server ตัวไคลเอนต์จะตัดเครื่องหมายทับ (trailing slash) ท้าย URL ออกโดยอัตโนมัติ ดังนั้นคุณไม่จำเป็นต้องกังวลเรื่องการระบุสตริงให้ตรงเป๊ะทุกตัวอักษร
หากเครื่องดังกล่าวเคยล็อกอินเข้าใช้บริการของ Tailscale มาก่อน เครื่องนั้นจะยังคงสถานะการล็อกอินเดิมไว้ ให้รันคำสั่ง sudo tailscale logout ก่อน จากนั้นจึงรัน tailscale up พร้อมกับ --login-server
หากคุณละเว้น --auth-key ไว้ ไคลเอนต์จะแสดง URL ออกมาแทน ให้เปิด URL นั้นขึ้นมา หน้าเว็บจะแสดงตัวระบุสำหรับการลงทะเบียนครั้งนั้น ซึ่งคุณต้องทำการอนุมัติบนเซิร์ฟเวอร์:
sudo headscale auth register --user alice --auth-id PASTE-THE-ID-FROM-THE-PAGEรูปแบบนี้สะดวกกว่าสำหรับแล็ปท็อปส่วนตัว ส่วน Preauth keys จะเหมาะสมกว่าสำหรับงานที่ทำผ่านสคริปต์เนื่องจากไม่ต้องมีคนคอยเฝ้า เมื่อ VPS กลายเป็นโหนดหนึ่งในเครือข่ายแล้ว มันสามารถทำหน้าที่ส่งต่อทราฟฟิกอินเทอร์เน็ตให้กับเครื่องอื่นๆ ของคุณได้ ซึ่งเรียกว่า การตั้งค่า exit node โดยมีข้อแตกต่างเพียงอย่างเดียวคือคุณต้องอนุมัติเส้นทางที่ประกาศไว้บนเซิร์ฟเวอร์ด้วยคำสั่ง headscale แทนที่จะทำผ่านหน้าคอนโซลผู้ดูแลระบบแบบ hosted หากสิ่งที่คุณต้องการคือการเข้าถึงเครือข่ายส่วนตัวที่อยู่หลัง VPS นั้น แทนที่จะเป็นการออกสู่อินเทอร์เน็ต ขั้นตอนการอนุมัติแบบเดียวกันนี้จะครอบคลุมถึง การประกาศ subnet นั้นให้กับ tailnet ของคุณ การเผยแพร่แอปพลิเคชันจากโหนดเดียว แทนที่จะเป็นการกำหนดเส้นทางผ่านเครือข่ายทั้งหมด ถือเป็นงานอีกประเภทหนึ่ง ซึ่ง serve และ funnel คือสองวิธีในการดำเนินการดังกล่าว แม้ว่าทั้งสองวิธีจะพึ่งพากลไกใบรับรองและ ingress ของ Tailscale โดยตรง ดังนั้นให้ศึกษาข้อมูลในฐานะฟีเจอร์ของ tailnet แบบ hosted ไม่ใช่สิ่งที่ headscale จัดเตรียมไว้ให้โดยตรง
DERP และสิ่งที่ทำหน้าที่ส่งต่อทราฟฟิกเมื่อเส้นทางตรงล้มเหลว
DERP (designated encrypted relay for packets) คือเส้นทางสำรอง เมื่อโหนดสองโหนดไม่สามารถเปิดการเชื่อมต่อ WireGuard แบบตรงได้ ซึ่งมักเกิดจากทั้งสองโหนดอยู่หลัง NAT (network address translation) ที่เข้มงวด ทั้งคู่จะส่งแพ็กเก็ตผ่าน relay แทน ตัว relay ไม่มีกุญแจเข้ารหัส จึงไม่สามารถอ่านทราฟฟิกของคุณได้ แต่จะเห็นว่าโหนดใดกำลังสื่อสารกันและมีการรับส่งข้อมูลมากน้อยเพียงใด
ต้องมีความชัดเจนว่าการตั้งค่าเริ่มต้นทำงานอย่างไร Headscale ถูกจัดส่งมาโดยชี้ไปที่ https://controlplane.tailscale.com/derpmap/default พร้อมด้วย auto_update_enabled: true และ update_frequency: 3h ดังนั้น control plane จึงเป็นของคุณในขณะที่ relay เป็นของ Tailscale สำหรับคนส่วนใหญ่ถือเป็นการแลกเปลี่ยนที่ยอมรับได้ หากคุณไม่ต้องการเช่นนั้น ให้รัน relay ของคุณเอง
ในการรัน relay ของคุณเอง ให้ตั้งค่า enabled: true ภายใต้ derp.server ใน config.yaml โดยแทนที่ ipv4: 198.51.100.1 และ ipv6: 2001:db8::1 ที่มากับค่าเริ่มต้นด้วยที่อยู่สาธารณะของ relay คุณ หรือลบการตั้งค่าทั้งสองออกเพื่อใช้ DNS จากนั้นรีสตาร์ท headscale และเปิดพอร์ต STUN (session traversal utilities for NAT) ด้วย sudo ufw allow 3478/udp การตั้งค่าที่อยู่เหล่านี้จะแทนที่ DNS ดังนั้นการปล่อยค่า placeholder ไว้จะทำให้ไคลเอนต์ส่งข้อมูลไปยังที่อยู่ที่เข้าถึงไม่ได้ ไฟล์คอนฟิกูเรชันระบุข้อกำหนดไว้อย่างชัดเจนว่า server_url ต้องใช้ https เนื่องจาก DERP ต้องการ TLS การล้างรายการ derp.urls จะเป็นการลบ relay ของ Tailscale ออกจากแผนผัง โปรดตรวจสอบให้แน่ใจว่ามี relay แบบฝังตัวหรือแบบกำหนดเองที่ใช้งานได้ก่อนดำเนินการดังกล่าว Headscale จะปฏิเสธการเริ่มทำงานหากแผนผัง relay ว่างเปล่า และแผนผังที่มีเพียง relay ที่เข้าถึงไม่ได้จะทำให้โหนดที่ไม่สามารถเชื่อมต่อโดยตรงไม่สามารถสื่อสารกันได้
จากฝั่งไคลเอนต์ tailscale netcheck จะแสดงค่า latency ไปยังแต่ละภูมิภาคของ relay ที่รู้จัก และ tailscale status จะแสดง direct พร้อมที่อยู่ หรือ relay พร้อมรหัสภูมิภาคสำหรับการเชื่อมต่อระหว่าง peer ที่กำลังทำงานอยู่ หาก peer ค้างอยู่ที่ relay จำเป็นต้องตรวจสอบเส้นทางตรง รวมถึงกฎของ NAT และไฟร์วอลล์ หาก peer มีสถานะเป็น direct แต่ยังคงทำงานช้า นั่นเป็นอีกประเด็นหนึ่ง และ คำตอบทั่วไปสำหรับกรณีนี้คือปัญหาเรื่อง MTU ไม่ใช่ตัว tunnel เอง
เหตุใดโหนดจึงแสดงสถานะเป็นออฟไลน์?
Proxy ปฏิเสธการอัปเกรด สัญญาณบ่งชี้คือ /health ส่งค่าสถานะ 200 กลับมา แต่การลงทะเบียนใหม่ล้มเหลวหรือโหนดเดิมไม่สามารถเชื่อมต่อใหม่ได้ การเชื่อมต่อควบคุมคือ POST ที่ส่ง Upgrade: tailscale-control-protocol ไปด้วย หาก proxy ไม่ส่งต่อข้อมูลนี้ จะทำให้ทั้งการลงทะเบียนและช่องทางรายงานสถานะโหนดถูกบล็อก ให้ตรวจสอบการตั้งค่า nginx ของคุณเทียบกับบรรทัดการอัปเกรดด้านบน หรือเปลี่ยนไปใช้ Caddy เพื่อตัดปัญหาเรื่อง proxy ออกไป
ที่อยู่ควบคุมสาธารณะเปลี่ยนไป โหนดจะยังคงพยายามเชื่อมต่อไปยังที่อยู่ที่กำหนดไว้ด้วย --login-server การเปลี่ยน server_url จะไม่ไปอัปเดตการตั้งค่าในฝั่งไคลเอนต์ หากที่อยู่เดิมไม่สามารถเข้าถึงเซิร์ฟเวอร์ของคุณได้ ให้รัน sudo tailscale up --login-server https://headscale.example.com --force-reauth บนโหนดแต่ละตัว
ไคลเอนต์ไม่ได้ทำงานอยู่ บนโหนด ให้ตรวจสอบ sudo systemctl is-active tailscaled และ sudo journalctl -u tailscaled -n 50 --no-pager ไคลเอนต์ที่ไม่สามารถ resolve หรือเข้าถึงโดเมนของคุณได้จะบันทึกการพยายามเชื่อมต่อซ้ำไว้ที่นั่น
คีย์หมดอายุ รายละเอียดอยู่ในหัวข้อถัดไป
หากต้องการตรวจสอบฝั่งเซิร์ฟเวอร์ในระหว่างการทดสอบ ให้รัน sudo journalctl -u headscale -f บน VPS และรีสตาร์ท tailscaled บนไคลเอนต์ ตรวจสอบ journal ของไคลเอนต์ด้วยเช่นกัน เนื่องจากคำขอที่ส่งไปถึง headscale อาจไม่แสดงบรรทัด log ในระดับปกติเสมอไป ดังนั้นการที่ log ฝั่งเซิร์ฟเวอร์เงียบไม่ได้เป็นเครื่องยืนยันว่าไม่มีปัญหาเรื่อง DNS, firewall หรือ proxy
การหมดอายุของคีย์ และโหนดที่หยุดทำงานในอีกหลายสัปดาห์ต่อมา
มีการหมดอายุอยู่ 2 ประเภทที่แยกจากกัน การสับสนระหว่างสองสิ่งนี้ทำให้เสียเวลาโดยเปล่าประโยชน์
Preauth keys ถูกออกแบบมาให้หมดอายุอย่างรวดเร็ว ค่าเริ่มต้นคือหนึ่งชั่วโมงและใช้งานได้ครั้งเดียว หาก tailscale up ปฏิเสธคีย์ดังกล่าว ให้สร้างคีย์ใหม่บนเซิร์ฟเวอร์แทนการแก้ไขค่าใดๆ บนฝั่งไคลเอนต์
Node keys คือส่วนที่มีอายุการใช้งานยาวนาน ส่วน node ของ config.yaml จะกำหนดค่า expiry: 0 และการตั้งค่า 0 หมายความว่าจะไม่มีการหมดอายุโดยค่าเริ่มต้น อย่างไรก็ตาม การหมดอายุที่ระบุโดยไคลเอนต์หรือการหมดอายุผ่าน OIDC ยังคงมีผลบังคับใช้ได้ โหนดที่ถูกติดแท็ก (Tagged nodes) จะได้รับการยกเว้นจากการหมดอายุอัตโนมัติ แม้ว่าผู้ดูแลระบบจะสามารถสั่งให้หมดอายุด้วยตนเองได้ก็ตาม ให้ตั้งค่า expiry: 180d หากคุณต้องการกำหนดค่าเริ่มต้น 180 วันสำหรับการลงทะเบียนที่ไม่ได้ติดแท็ก แต่การหมดอายุที่ไคลเอนต์ร้องขอจะมีลำดับความสำคัญสูงกว่า และ OIDC สามารถแทนที่ค่าเริ่มต้นได้ การเปลี่ยนการตั้งค่านี้ไม่มีผลย้อนหลังกับการลงทะเบียนที่มีอยู่เดิม โหนดที่มีวันหมดอายุจำเป็นต้องทำการยืนยันตัวตนใหม่ก่อนถึงกำหนดเวลาดังกล่าวโดยใช้ sudo tailscale up --login-server https://headscale.example.com --force-reauth และเซิร์ฟเวอร์แบบ headless ที่ไม่มีใครยืนยันตัวตนใหม่จะหลุดออกจากเครือข่ายเมื่อการลงทะเบียนหมดอายุ
ให้ดำเนินการด้วยตนเองเมื่อมีคนทำแล็ปท็อปหาย sudo headscale nodes list จะแสดง ID ให้คุณทราบ จากนั้น sudo headscale nodes expire -i 3 จะทำการล็อกเอาต์โหนดนั้น และ sudo headscale nodes delete -i 3 จะลบโหนดนั้นออกจากเครือข่ายโดยสมบูรณ์
การสำรองข้อมูลและการอัปเกรด
/var/lib/headscale และ /etc/headscale รวมกันถือเป็นข้อมูลทั้งหมดของเซิร์ฟเวอร์ ให้หยุดการทำงานของ service ก่อนคัดลอกไฟล์เหล่านี้ เนื่องจาก SQLite อาจมีการเขียนข้อมูลค้างอยู่ และฐานข้อมูลที่ถูกคัดลอกในขณะที่มีการใช้งานอาจเกิดความไม่สอดคล้องกันของข้อมูล
sudo systemctl stop headscale
sudo tar czf /root/headscale-state.tgz -C /var/lib headscale
sudo tar czf /root/headscale-config.tgz -C /etc headscale
sudo systemctl start headscale
sudo chmod 600 /root/headscale-*.tgzให้ย้ายไฟล์ทั้งสองออกจากเซิร์ฟเวอร์ เนื่องจากไฟล์เหล่านี้ประกอบด้วย private key และข้อมูลการลงทะเบียนทั้งหมด จึงควรได้รับการดูแลเช่นเดียวกับตัวเซิร์ฟเวอร์เอง เนื้อหาใน การสำรองข้อมูลด้วย restic จาก VPS ครอบคลุมวิธีการทำสิ่งนี้ตามกำหนดเวลาและมีการเข้ารหัส
การอัปเกรดให้ทำซ้ำขั้นตอนการติดตั้ง โดยดาวน์โหลด .deb และ sudo apt install ./headscale.deb เวอร์ชันใหม่ จากนั้นให้ restart และรันการตรวจสอบด้วย is-active และ /health อีกครั้ง ตั้งแต่เวอร์ชัน 0.29 เป็นต้นมา เส้นทางการอัปเกรดมีความเข้มงวด โดยระบบจะบล็อกการข้ามเวอร์ชันย่อย (minor version) รวมถึงการดาวน์เกรดไปยังเวอร์ชันย่อยที่เก่ากว่า ให้ดำเนินการอัปเกรดทีละเวอร์ชันย่อย ทำการสำรองข้อมูลก่อนเริ่มแต่ละขั้นตอน และอ่านบันทึกประจำรุ่น (release notes) ของเวอร์ชันนั้นๆ ก่อนเสมอ เนื่องจากรุ่นเดียวกันอาจมีการเปลี่ยนแปลงพฤติกรรมของนโยบาย ACL และมีการย้าย configuration key หลายรายการ นอกจากนี้ เวอร์ชัน 0.29.6 ยังมีการอัปเดตความปลอดภัยสำหรับการตรวจสอบ certificate และ dependency ดังนั้นการติดตั้งเวอร์ชัน 0.29 ที่มีอยู่ควรทำการอัปเดตเป็น patch release นี้ด้วย
FAQ
headscale ทำงานทันทีหลังจากติดตั้งไฟล์ .deb หรือไม่?
ใช่ แพ็กเกจจะเริ่มบริการทันทีที่ติดตั้งโดยใช้ /etc/headscale/config.yaml ที่ตั้งค่าไว้ ซึ่งจะฟังเฉพาะบน loopback เท่านั้นและกำหนดค่า server_url เป็น http://127.0.0.1:8080 ในการติดตั้งใหม่ บริการจะยังไม่ถูกเปิดใช้งาน (enable) ดังนั้นมันจะไม่เริ่มทำงานตอนบูตเครื่องจนกว่าคุณจะรัน sudo systemctl enable headscale ที่อยู่เริ่มต้นนั้นไม่สามารถใช้งานกับไคลเอนต์ได้ ดังนั้นให้แก้ไขค่า server_url, listen_addr และ base_domain ในไฟล์คอนฟิก จากนั้นรัน sudo systemctl restart headscale และยืนยันสถานะด้วย sudo systemctl is-active headscale การรีสตาร์ทเป็นสิ่งจำเป็นเพราะ restart headscale จะอ่านไฟล์คอนฟิกเฉพาะตอนเริ่มทำงานเท่านั้น และการรัน enable --now บนบริการที่กำลังทำงานอยู่จะเป็นเพียงการเปิดใช้งานให้เริ่มตอนบูต แต่ไม่ได้รีสตาร์ทบริการ ทำให้ค่าเดิมยังคงมีผลอยู่ หากบริการล้มเหลวหลังการรีสตาร์ท sudo journalctl -u headscale -n 50 --no-pager จะระบุปัญหาที่เกิดขึ้น ซึ่งในขั้นตอนนี้มักเป็นข้อผิดพลาดในไฟล์ YAML เสมอ เนื่องจาก headscale จะตรวจสอบไฟล์ทั้งหมดก่อนที่จะผูกพอร์ต
ฉันยังต้องติดตั้งไคลเอนต์ Tailscale ปกติบนเครื่องของฉันหรือไม่?
ใช่ headscale ทำหน้าที่แทนเฉพาะส่วน control server เท่านั้น โหนดทุกโหนดจะรันไคลเอนต์อย่างเป็นทางการจาก Tailscale และคุณเพียงแค่ชี้ไคลเอนต์ไปยังเซิร์ฟเวอร์ของคุณด้วย sudo tailscale up --login-server https://headscale.example.com แฟล็กดังกล่าวมีอยู่ในไคลเอนต์มาตรฐานอยู่แล้ว จึงไม่จำเป็นต้องแก้ไขหรือคอมไพล์โปรแกรมใหม่
ทราฟฟิกของฉันจะวิ่งผ่านเซิร์ฟเวอร์ headscale หรือไม่?
โดยปกติแล้วจะไม่ใช่ headscale ทำหน้าที่ประสานงานเครือข่ายและแจกจ่ายคีย์รวมถึงที่อยู่ IP ในขณะที่เส้นทางข้อมูลจะเป็น WireGuard ที่เชื่อมต่อระหว่างโหนดของคุณโดยตรง ทราฟฟิกจะอ้อมผ่านเซิร์ฟเวอร์ก็ต่อเมื่อโหนดสองโหนดไม่สามารถติดต่อกันโดยตรงได้และต้องใช้ DERP relay ซึ่งในการตั้งค่าเริ่มต้น relay เหล่านี้จะเป็นเซิร์ฟเวอร์สาธารณะของ Tailscale ให้รัน tailscale status บนโหนดเพื่อตรวจสอบว่า peer ที่กำหนดนั้นเชื่อมต่อแบบ direct หรือผ่าน relay
ทำไมโหนดของฉันถึงยังคงออฟไลน์หลังจากลงทะเบียนแล้ว?
โหนดที่ปรากฏใน headscale nodes list แต่ไม่เคยออนไลน์จำเป็นต้องได้รับการตรวจสอบการเชื่อมต่อควบคุม (control connection) รวมถึง reverse proxy ด้วย การเชื่อมต่อดังกล่าวคือ HTTP upgrade ที่ส่งผ่าน POST พร้อมเฮดเดอร์ Upgrade: tailscale-control-protocol ซึ่ง nginx จะตัดการเชื่อมต่อนี้ทิ้งหากคุณไม่ส่งต่อเฮดเดอร์ Upgrade และ Connection อย่างชัดเจนด้วย proxy_set_header ซึ่งการตั้งค่าด้านบนได้ดำเนินการผ่านบล็อก map $http_upgrade $connection_upgrade แล้ว การไม่ส่งต่อเฮดเดอร์เหล่านี้ยังทำให้โหนดใหม่ไม่สามารถลงทะเบียนได้ ในขณะที่ Caddy จะส่งต่อเฮดเดอร์เหล่านี้โดยไม่ต้องตั้งค่าเพิ่มเติม ซึ่งทำให้เป็นวิธีที่รวดเร็วในการทดสอบว่าปัญหาเกิดจาก proxy หรือไม่
ฉันจำเป็นต้องมีชื่อโดเมนและ TLS สำหรับ headscale หรือไม่?
สำหรับการตั้งค่านี้ จำเป็นต้องใช้ชื่อโดเมนและ HTTPS ใบรับรองสามารถใช้กับ IP address โดยตรงได้ แต่ไฟล์คอนฟิกได้ระบุว่า DERP จำเป็นต้องใช้ TLS การใช้โดเมนร่วมกับ Caddy ใช้เวลาเพียงประมาณ 5 นาทีและทำให้คุณได้ HTTPS endpoint ที่ต่ออายุใบรับรองให้อัตโนมัติ แม้ว่าการใช้ HTTP ธรรมดาจะเป็นไปได้ แต่เอกสารของ headscale แนะนำอย่างยิ่งให้ใช้ HTTPS บนพอร์ต 443 สำหรับการใช้งานจริง และระบุว่าไคลเอนต์ Tailscale จะอนุมานว่ามีการใช้ HTTPS บนพอร์ต 443 ในบางสถานการณ์