1 คะแนน โดย GN⁺ 2024-05-13 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • Wag เป็นโปรเจกต์ที่เพิ่มการยืนยันตัวตนหลายปัจจัย การจำกัดเส้นทาง และการลงทะเบียนอุปกรณ์ให้กับ WireGuard โดยสามารถแยกเส้นทางที่ต้องใช้ MFA ออกจากเส้นทางสาธารณะที่เข้าถึงได้ตลอดเวลา
  • มี API สำหรับลงทะเบียนไคลเอนต์ใหม่, ความพร้อมใช้งานสูง, การอัปเดตและการแจ้งเตือนผู้ใช้แบบเรียลไทม์ รวมถึงการผสานรวม MFA หลายแบบ เช่น Security Key, SSO, PAM และ TOTP
  • การรันเซิร์ฟเวอร์ต้องเปิดใช้งาน IP forwarding และหากรันแบบแมนนวลต้องติดตั้ง iptables กับ libpam รวมถึงต้อง รันด้วย root เพื่อจัดการ iptables และอุปกรณ์ WireGuard
  • จัดการได้ผ่านเว็บ UI และ CLI โดย CLI มีคำสั่งย่อย start, registration, devices, users, webadmin สำหรับจัดการโทเค็นลงทะเบียน การล็อกอุปกรณ์ การรีเซ็ต MFA และบัญชีผู้ดูแลเว็บ
  • ข้อจำกัดคือรองรับ AllowedIP เพียงรายการเดียวต่อไคลเอนต์ และโดยหลักรองรับเฉพาะ Linux ส่วน Windows อาจใช้งานได้หลังทำขั้นตอนบางอย่าง

ฟีเจอร์ของ WireGuard ที่ Wag เพิ่มเข้ามา

  • Wag เพิ่ม MFA, การจำกัดเส้นทาง และการลงทะเบียนอุปกรณ์ให้กับ WireGuard
  • สามารถกำหนดเส้นทางโดยแบ่งเป็นเส้นทางที่ต้องผ่านการยืนยัน MFA และ เส้นทางสาธารณะ ที่เข้าถึงได้ตลอดเวลา
  • มี API ที่ใช้งานง่ายสำหรับลงทะเบียนไคลเอนต์ใหม่
  • รองรับความพร้อมใช้งานสูง การอัปเดตผู้ใช้และการแจ้งเตือนแบบเรียลไทม์
  • การผสานรวม MFA รวมวิธีต่อไปนี้
    • Security Key
    • SSO
    • PAM
    • TOTP
  • เอกสารอยู่ที่ Documentation

เงื่อนไขการติดตั้งและการรัน

  • ต้องเปิดใช้งาน forwarding บนเซิร์ฟเวอร์
    • IPv4 ใช้การตั้งค่า net.ipv4.ip_forward=1
    • IPv6 ใช้การตั้งค่า sysctl ที่เกี่ยวข้อง เช่น net.ipv6.conf.all.forwarding=1
  • ตัวอย่างการรัน Docker Compose ใช้อิมเมจ wagvpn/wag:latest
    • ตัวอย่างพอร์ตหน้าจัดการคือ 4433/tcp
    • ตัวอย่างพอร์ตหน้าลงทะเบียนสาธารณะคือ 8081/tcp
    • ตัวอย่างพอร์ต WireGuard คือ 53230/udp
    • เชื่อมต่ออุปกรณ์ /dev/net/tun เข้ากับคอนเทนเนอร์
  • การติดตั้งแบบแมนนวลต้องใช้ iptables และ libpam
  • Wag ต้องรันด้วย root เพื่อจัดการ iptables และ อุปกรณ์ WireGuard
  • ไบนารีรีลีสต้องใช้ glibc 2.31+
  • การ build จากซอร์สต้องใช้ go1.23.1 และ npm

วิธีจัดการ

  • หลังเปิดใช้งาน UI สำหรับจัดการและตั้งค่า Wag แล้ว ระบบจะสร้างผู้ดูแลคนแรกและพิมพ์รหัสผ่านออกทาง STDOUT
  • จากนั้นสามารถล็อกอินเข้าเว็บ UI เพื่อจัดการผู้ใช้ได้
  • ผู้ใช้ root สามารถจัดการเซิร์ฟเวอร์ Wag ผ่าน CLI ได้
  • รูปแบบ CLI คือ wag subcommand [-options]
  • คำสั่งย่อยที่รองรับมีดังนี้
    • start: เริ่มเซิร์ฟเวอร์ Wag โดยไม่ทำให้เป็น daemon
    • registration: จัดการการสร้าง ลบ และดูรายการโทเค็นลงทะเบียน
    • devices: จัดการการดูรายการ ลบ ล็อก ปลดล็อกอุปกรณ์ WireGuard และดูเซสชัน MFA ที่ใช้งานอยู่
    • users: จัดการ MFA ของผู้ใช้ ลบผู้ใช้ ล็อกบัญชี และรีเซ็ต MFA
    • webadmin: เพิ่ม ลบ ดูรายการ ล็อกและปลดล็อกบัญชีผู้ดูแลเว็บ UI
    • version, firewall ก็รวมอยู่ในคำสั่งที่รองรับด้วย

โทเค็นลงทะเบียนและโฟลว์ MFA

  • การลงทะเบียนอุปกรณ์ใหม่เริ่มจากสร้าง โทเค็นลงทะเบียน ด้วยคำสั่งอย่าง wag registration -add -username tester
  • เมื่อนำโทเค็นที่สร้างขึ้นส่งไปยัง endpoint ลงทะเบียนสาธารณะ จะได้รับการตอบกลับเป็นการตั้งค่า WireGuard
  • การตั้งค่าที่ส่งกลับมามีรายการอย่าง Interface, PrivateKey, Address, Peer, Endpoint, PublicKey, AllowedIPs, PersistentKeepAlive
  • ผู้ใช้เชื่อมต่อไปยังที่อยู่ VPN ของเซิร์ฟเวอร์และกรอก โค้ด 2FA
  • ระยะเวลาที่เซสชันคงอยู่ก่อนหมดอายุกำหนดในไฟล์ตั้งค่า

คอนโซลจัดการผ่านเว็บ

  • หากต้องการล็อกอินเข้าคอนโซลจัดการ ต้องตั้งค่า Webserver.Management.Enabled เป็น true
  • เพิ่มบัญชีผู้ดูแลเว็บในคอนโซลด้วย sudo ./wag webadmin -add -username <your_username> -password <your-password-here>
  • จากนั้นเข้าที่อยู่ listen สำหรับการจัดการและกรอกข้อมูลรับรอง
  • ตัวเว็บอินเทอร์เฟซเอง ไม่สามารถเพิ่มผู้ใช้ผู้ดูแล ได้
  • แนะนำว่าไม่ควรเปิดเผยพอร์ทัลจัดการออกสู่ภายนอก และควรตั้ง ListenAddress เป็น 127.0.0.1 หรือ localhost แล้วเปิดให้เข้าถึงผ่าน SSH forwarding

รายการตั้งค่าหลัก

  • NumberProxies ระบุจำนวน reverse proxy ที่เชื่อถือได้ซึ่งอยู่หน้าไคลเอนต์ และทำให้ Wag parse IP ไคลเอนต์โดยอ้างอิง X-Forward-For
  • Socket คือ socket ควบคุมของ Wag และหากเปลี่ยนค่านี้จะสามารถรัน Wag หลายอินสแตนซ์บนเครื่องเดียวกันได้
  • NAT เปิดหรือปิด masquerading และเมื่อเปิดใช้งาน ทราฟฟิกทั้งหมดจะดูเหมือนเริ่มต้นจากเซิร์ฟเวอร์ VPN
  • NATExcludeRanges ระบุช่วง CIDR ที่จะยกเว้นจาก NAT เมื่อ NAT=true
  • ExposePorts เปิดเผยพอร์ตของเซิร์ฟเวอร์ VPN ให้ไคลเอนต์และเพิ่มกฎ iptables
  • CheckUpdates ค่าเริ่มต้นปิดอยู่ และเมื่อเปิดใช้งาน UI จัดการจะแสดงการแจ้งเตือนเวอร์ชันใหม่ของ Wag และเข้าถึง api.github.com
  • Acls กำหนดกลุ่มและนโยบาย แต่จะมีผล เฉพาะตอนรันครั้งแรกเท่านั้น และระหว่าง runtime ให้แก้ไขผ่านเว็บ UI
  • Webserver รวมการตั้งค่า endpoint ลงทะเบียนสาธารณะ พอร์ทัล MFA ของ tunnel และพอร์ทัลจัดการ
  • Wireguard ตั้งค่าชื่ออุปกรณ์ พอร์ต listen, private key, subnet ที่ VPN รับผิดชอบ, MTU และเซิร์ฟเวอร์ DNS
  • Clustering รวมการตั้งค่าชื่อคลัสเตอร์ สถานะคลัสเตอร์ etcd ระดับ log, witness node, ตำแหน่งฐานข้อมูล และใบรับรองคลัสเตอร์

การทำงานของนโยบาย ACL

  • Policies กำหนดเส้นทางที่ VPN จะ capture รวมถึงพอร์ตและโปรโตคอลที่จะผ่าน Wag
  • การใช้กฎอิงตาม ความยาว prefix ของ subnet โดย match ที่เฉพาะเจาะจงที่สุดจะเป็นตัวกำหนดระดับการเข้าถึงเส้นทาง
  • ตัวอย่างเช่น หากกำหนด /16 เป็น MFA และกำหนด /32 เฉพาะภายในนั้นเป็น Allow ค่า /32 ที่เฉพาะเจาะจงกว่าจะมีลำดับความสำคัญ จึงเข้าถึงได้โดยไม่ต้องใช้ MFA
  • พฤติกรรมนี้เปลี่ยนใน v6.0.0 ก่อนหน้านั้นเส้นทาง MFA จะมีลำดับความสำคัญเสมอ
  • หากมีหลายนโยบายกำหนดบนเส้นทางเดียวกัน นโยบายจะถูกผสานกัน และกฎ MFA จะมีลำดับความสำคัญ
  • ตั้งแต่เวอร์ชันที่ยังไม่รีลีส จะสามารถใช้กฎ Deny เพื่อบล็อกการเข้าถึงเส้นทางได้
  • เนื่องจากกฎที่เฉพาะเจาะจงที่สุดจะสร้าง “bucket” กฎใหม่ หากใน bucket /32 มีเพียง deny การเข้าถึงพอร์ตอื่นของ /32 เดียวกันก็อาจไม่ได้รับอนุญาตด้วย

กฎพอร์ตและโปรโตคอล

  • การเข้าถึงบริการสามารถกำหนดด้วยกฎพอร์ตและโปรโตคอลได้
  • ประเภทกฎที่รองรับมี 3 แบบ
    • Any: หากไม่มีกฎแยกต่างหากหรือใช้คีย์เวิร์ด any จะอนุญาตทุกการจับคู่บริการและพอร์ต
    • Single Service: อนุญาตพอร์ต TCP·UDP เฉพาะของโฮสต์ เช่น 192.168.1.1 22/tcp 53/udp
    • Ranges: ระบุช่วงพอร์ต เช่น 192.168.1.1 22-1024/tcp 23-53/any
  • ช่วงพอร์ตต้องเขียนพอร์ตต่ำกว่าก่อน
  • ICMP ไม่มีพอร์ต จึงระบุได้โดยไม่ต้องใส่พอร์ต เช่น 1.1.1.1 icmp

ข้อจำกัดและการพัฒนา

  • Wag รองรับ AllowedIP เพียงหนึ่งรายการต่อไคลเอนต์
  • ข้อจำกัดนี้เหมาะกับโครงสร้างที่เชื่อมจากไคลเอนต์ไปยังเซิร์ฟเวอร์
  • โดยหลักเป็น เฉพาะ Linux และ Windows อาจใช้งานได้หลังทำขั้นตอนบางอย่าง
  • ในโหมดพัฒนา สามารถใช้ environment variable เพื่อตั้งค่า IP ของ request ที่เข้ามาทาง tunnel ให้เป็น IP ของไคลเอนต์ได้
  • ตัวอย่างการทดสอบคือรัน sudo go test -v . ใน internal/router
  • สำหรับการมีส่วนร่วมจากภายนอก แนะนำให้เขียน test เมื่อเป็นไปได้หากเพิ่มฟีเจอร์หรือแก้บั๊ก แล้วเปิด Pull Request

1 ความคิดเห็น

 
GN⁺ 2024-05-13
ความเห็นบน Hacker News
  • ดูเผิน ๆ ก็น่าสนใจ แต่มีบางจุดที่ติดใจ
    จากตัวอย่าง curl [http://public.server.address:8080/register_device?key=e83253...](<http://public.server.address/register_device/…;) และคำอธิบายว่า “บริการจะคืน response ที่ทำเป็น template ไว้ทั้งหมด” ดูเหมือนว่าใน ขั้นตอนลงทะเบียน เซิร์ฟเวอร์จะเป็นฝ่ายสร้าง private key แล้วส่งให้ไคลเอนต์ แทนที่ไคลเอนต์จะสร้าง private key เองแล้วส่ง public key ไปให้เซิร์ฟเวอร์
    อีกอย่าง ตัวอย่างเป็น HTTP ด้วย อย่างน้อยควรเปลี่ยนส่วนนั้นเพื่อไม่ให้คนคิดว่า HTTP ก็เป็นตัวเลือกที่โอเค
    สงสัยด้วยว่าเมื่อเซสชันหมดอายุ ไคลเอนต์มีวิธีรู้เรื่องนี้หรือไม่ หรือว่าอย่างเซสชัน SSH จะหยุดค้างไปเฉย ๆ?
    ผมเคยมองหาไคลเอนต์ WireGuard ที่ทำงานคล้ายการตรวจจับ captive portal ของ Wi‑Fi เป็นครั้งคราว โดยในอุดมคติคือเพิ่มบรรทัดเดียวอย่าง persistentkeepalive ในไฟล์ตั้งค่า เพื่อดึง URL แล้วตรวจเป็นระยะ ๆ ถ้าได้ OK ก็ปกติ ถ้าไม่มี response ก็เป็นปัญหาเครือข่าย ถ้าได้ header Location ก็เปิดเบราว์เซอร์ไปยังตำแหน่งนั้นเพื่อยืนยันเซสชันใหม่ เป็นต้น
    ยังหาไคลเอนต์แบบนั้นไม่เจอ

    • URL ลงทะเบียนสามารถรับพารามิเตอร์ pubkey ได้แบบเลือกใช้ ดังนั้นจึงไม่จำเป็นต้องพึ่งวิธีที่เซิร์ฟเวอร์สร้าง private key เอง เอกสารยังไม่พอเลยอาจทำให้สับสนได้
      ตอบคำถามสุดท้าย eBPF XDP ที่ผมใช้ทำได้แค่ PASS, DROP, REDIRECT ดังนั้นจึงจัดการด้วยผลลัพธ์ที่ง่ายที่สุดคือ PASS/DROP และการเชื่อมต่อก็จะค้างไปเฉย ๆ
      อย่างไรก็ตาม ถ้าเพิ่มหน้าตรวจจับ captive portal เข้าไปในรายการ MFA ของ wag ก็สามารถตั้งค่าการตรวจจับเองได้ และหลังจากนั้นเบราว์เซอร์จะจัดการต่อให้
      ผมไม่มีแผนจะทำฟีเจอร์ใน wag ที่ทำงานแบบ intercept หรือ proxy ถ้าทำแบบนั้น การจัดการตอน authentication หมดอายุหรือ logout จะง่ายขึ้นบ้าง แต่นั่นไม่ใช่ทิศทางของโครงการ
    • ฟีเจอร์แบบนั้นน่าจะเจ๋งมาก และหวังว่าผู้เขียนโปรเจกต์นี้จะพิจารณา
    • ผมเคยทำเซิร์ฟเวอร์ที่คล้ายกัน ต้องใช้ client certificate ต่ออุปกรณ์ แล้วใช้มันเชื่อมต่อหน้า login ผ่าน mTLS จากนั้นยืนยันตัวตนผู้ใช้ด้วย OIDC และเปิดใช้งาน tunnel แต่ส่วนที่ยากคือไคลเอนต์
      ผมเขียนไคลเอนต์ Go สำหรับ Mac ตัวหนึ่ง และใช้คำสั่ง wg ของ Brew เพื่อจัดการการสร้าง key ด้วย แต่มันยังหยาบและต้องใช้ sudo
      ถ้ามีแอป native ดี ๆ ที่ใช้สิทธิ์ด้านเครือข่ายก็คงดี แต่มันเกินความสามารถของผม
  • สงสัยว่าคุณได้จัดการหรือมีแผนจะจัดการปัญหา การจัดการเซสชัน แล้วหรือยัง
    โดยแก่นแล้ว key ของ WireGuard ก็เหมือน session key ถาวร
    ถ้าซอฟต์แวร์ที่ทำ transport layer ของ WireGuard เป็นโซลูชัน VPN server ที่เหมาะสม ก็ควร implement การจัดการเซสชันด้วย กล่าวคือผ่านช่องทางที่สองกับเซิร์ฟเวอร์เพื่อหมุนเวียน session key เป็นระยะ ๆ, ยุติเซสชัน, เปลี่ยน IP address, ตั้งค่า route ใหม่ และทำ authentication ซ้ำเมื่อจำเป็น

    • ถ้าใช้เพื่อกรณีนั้น ผมคงใช้ Firezone มีตัวเลือกบังคับให้ผู้ใช้ login เข้าแพลตฟอร์มเป็นระยะ ๆ และเมื่อจับคู่กับ identity provider ภายนอกผ่าน OIDC ก็จะเป็นวิธีแก้การจัดการเซสชันที่แข็งแรงและเรียบง่ายมาก
    • ในบริบทของ wag ผมไม่แน่ใจว่า “session key ถาวร” หมายถึงอะไรกันแน่
      key ของ WireGuard ทำให้สื่อสารกับเซิร์ฟเวอร์ wag ได้ แต่เซสชันจริงถูกเก็บเป็น eBPF map ที่มีข้อมูลว่าผู้ใช้ผ่านการยืนยันตัวตนแล้วหรือไม่
      ดังนั้นแม้มีคนขโมยข้อมูล private key ไป ก็ยังเข้าถึงเส้นทางที่ถูกจำกัดด้วย MFA ไม่ได้
    • ถ้าจะทำไคลเอนต์ VPN แบบ GlobalProtect ด้วย WireGuard ผมคงมี key ยืนยันตัวตนถาวรต่อไคลเอนต์ แล้วใช้มันสร้าง tunnel เริ่มต้นไปยัง VPN controller จากนั้นทำ authentication ภายใน tunnel นั้นและรับ session key แยกต่างหาก โครงสร้างคือ tunnel แรกจะถูกตัดทันทีเมื่อยืนยันตัวตนเสร็จและได้รับ session key จริงแล้ว
    • ถ้าช่องทางที่สองมีไว้สำหรับหมุนเวียน session key เป็นระยะ ๆ, ยุติเซสชัน, เปลี่ยน IP address, ตั้งค่า route ใหม่ และยืนยันตัวตนซ้ำ นั่นก็แทบจะเป็น โปรโตคอล IKE ของ IPsec ไม่ใช่หรือ? ใช้ IPsec ไปเลยไม่ได้หรือ?
  • สงสัยว่ามีการป้องกัน การ brute force โค้ด TOTP หรือไม่ เช่น rate limit หรือจำกัดจำนวนครั้งที่ลองใหม่
    ผมไล่อ่านโค้ดเร็ว ๆ แต่ไม่เจอการจัดการแบบนั้น
    สถานการณ์ที่นึกไว้คือมีคนเปิด UI สำหรับกรอก TOTP ในเบราว์เซอร์ เปิด developer tools แล้วลองยิงโค้ด TOTP ที่เป็นไปได้ทั้งหมดซ้ำ ๆ

    • มีการป้องกันการ brute force โค้ด TOTP อยู่ การยืนยันตัวตนแต่ละครั้งมี การจำกัดจำนวนครั้ง ที่ผู้ใช้ลองได้ และถ้าเกินนั้นบัญชีจะถูกล็อก ต้องให้ผู้ดูแลระบบปลดล็อก
      โดยเฉพาะยังมีเจตนาให้ผู้ใช้ฉุกคิดด้วยว่าทำไมอุปกรณ์ถึงพยายามยืนยันตัวตนแบบฝืน ๆ เพราะสถานการณ์แบบนั้นอาจบ่งชี้ว่า endpoint ถูกเจาะ
    • น่าจะตรงนี้: https://github.com/NHAS/wag/blob/cdbdbec3393fa86bf6c823117c8...
    • ผมไม่รู้รายละเอียดของ implementation นี้ แต่โดยทั่วไป ถ้ามีข้อมูล login ที่ไปถึงขั้น TOTP ได้แล้ว คือมี username และ password อยู่แล้ว ผู้ใช้นั้นก็ถูกเจาะไปแล้ว
  • ฟังดูคล้าย Headscale หรือ Tailscale มาก ดีที่เห็นทางเลือกสำหรับจัดการเครือข่าย WireGuard
    สงสัยว่ามี เอกสารเปรียบเทียบ ที่ช่วยให้เข้าใจไหมว่าฟีเจอร์ทับซ้อนกันแค่ไหน, มีอะไรเพิ่มเข้ามา, ต่างกันอย่างไร และอะไรที่จะไม่ implement ต่อไปในอนาคต

    • ในแง่ที่ใช้ WireGuard ก็คล้ายกันแน่นอน
      ผมไม่ได้ใส่การเปรียบเทียบโดยตรงไว้ในเอกสาร และตอนนี้ไม่ใช่ทิศทางที่ผมจะไป โปรเจกต์นี้ตรงกับความต้องการของผมและสนุกพอสมควร
      Wag เหมาะกับ โครงสร้างแบบ hub-and-spoke ที่ต้องการขอบเขตชัดเจน มากกว่า mesh แบบ Tailscale ที่ทุกอย่างถึงกันและกฎเป็นตัวกำหนด overlay
      ทั้ง wag และ Tailscale เพิ่มการผสาน SSO และ 2FA โดยพฤตินัยเพื่อปกป้องผู้ใช้
      ทั้งคู่มีวิธีลงทะเบียนและ web UI สำหรับบริหารจัดการ แต่ผมเป็นนักพัฒนาคนเดียวที่ไม่ชอบงาน web development ดังนั้น Tailscale น่าจะขัดเกลากว่ามาก
      สิ่งที่จะไม่ implement แน่นอนคือฝั่ง intercept หรือ TLS proxy เพื่อ redirect ผู้ใช้หลัง session logout เหตุผลหลักคือ ณ ตอนนี้การทำสิ่งนั้นด้วย eBPF สำหรับผมยังหนักไปหน่อย และถ้าจะทำให้ใช้งานได้จริงก็คงต้องเขียนส่วนประกอบ DNAT/SNAT ซึ่งผมไม่อยากใช้
  • เป็น IPv4 เท่านั้น เหรอ ถ้าเป็นไซต์ที่เลือก WireGuard ก็น่าจะมีการตั้งค่าที่ทันสมัยกว่า และอาจใช้ ULA แบบ service ของตัวเองกันเยอะ

    • ผมมีแผนจะเพิ่มการรองรับ IPv6 เร็ว ๆ นี้ และกำลังคิดถึงการ map ที่อยู่ IPv4 ของผู้ใช้ไปยัง พื้นที่ IPv6 ส่วนตัว เพื่อลดความเสี่ยงที่จะชนกับเครือข่าย local จริงของผู้ใช้
      สงสัยว่าตอนพูดถึง ULA มีจุดไหนที่คิดไว้เป็นพิเศษหรือเปล่า
    • สงสัยว่า ข้อดีของ ULA ที่คิดไว้ตรงนี้คืออะไร