ลองเล่น Network กับ DN42

ซื้อ router Mikrotik มาใช้ที่บ้านได้สักพัก ผมเห็นฟีเจอร์ BGP ผมก็สงสัยมาสักพักว่าจะไปเล่นกับใครดี เลยลองหาข้อมูลดูพบว่ามี network DN42 อยู่ ก็เลยลองขอเข้าไปร่วมดู

DN42

DN42 คือการจำลองการทำงานของ internet ของจริง ซ้อนลงไปบน internet ของจริงอีกทีโดยใช้ VPN

ไอเดียคือแบบนี้ครับ

  • ในโลกจริงถ้าผมจะเชื่อมต่อกับหน่วยงานอื่น ผมต้องลาก Fiber เข้าไปหาหน่วยงานนั้นๆ เรียกว่า Peering
  • บางทีหน่วยงานนั้นจะ forward ผมหาหน่วยงานอื่นๆ ได้อีก แบบนี้เรียกว่า Transit
  • พอมี Transit แล้วเราก็สามารถติดต่อหาหน่วยงานอื่นๆ ได้ทั่วโลกโดยส่งต่อกันไปเป็นทอดๆ นี่คือการทำงานของ internet
  • การจะเชื่อมต่อได้นั้นนอกจากจะต้องลากสายหากันแล้ว เรายังจะต้องจดทะเบียนเพื่อให้ทราบข้อมูลของเรา และขอ IP Address ของเราด้วย

ทีนี้ในฝั่งของ DN42 จะเป็นแบบนี้

  • เราจะเชื่อมต่อหาใครเราก็ไปขอ Peering กับเค้า แต่เราไม่ต้องลากสาย แค่ใช้ VPN
  • โดยทั่วไปแล้วทุกคนใน DN42 จะเป็น Transit ด้วย
  • เช่นเดียวกัน DN42 จะมีฐานข้อมูลผู้ใช้งานและการออก IP ด้วย โดยแทนที่จะใช้ IP ทั้งหมด ก็จะใช้ IP ในกลุ่ม 172.20.0.0/14 ที่เป็น private IP เท่านั้น

ซึ่งการจะเข้าร่วม DN42 นั้นทำได้ง่ายๆ ตามเอกสาร Getting started

กรอกเอกสาร

ขั้นแรกสุดคือการกรอกเอกสารก่อนที่เว็บ NixNodes IO โดยกด Create object และสร้างของต่างๆ ดังนี้

mntner

mntner เป็นข้อมูลผู้ถือครองวัตถุอื่นๆ ตรงนี้ให้กรอกข้อมูลดังนี้

  • mntner: กรอกชื่อแล้วตามด้วย -MNT เช่น WHS-MNT
  • sha512-pw: กรอกรหัสผ่านเป็น plaintext
  • admin-c และ tech-c: ชื่อผู้ติดต่อ ตรงนี้เราติดไว้ก่อนโดยใส่ DUMMY-DN42
  • mnt-by: กรอกให้เหมือนกับ mntner (และอย่างอื่นๆ ที่เราจะสร้างต่อไปจะมีให้กรอก mnt-by ทั้งหมด ต้องกรอกให้เหมือน mntner เช่นเดียวกัน)

พอเซฟเสร็จแล้วทางขวามือจะเห็นว่ามีช่องให้กรอกรหัสผ่าน ให้กรอกรหัสลงไปแล้วกดปุ่ม + เพื่อบันทึกรหัส จะได้แก้ไขข้อมูลได้

person

person คือข้อมูลบุคคล

  • nic-hdl: กรอกชื่อแล้วตามด้วย -DN42 เช่น WHS-DN42
  • person: กรอกอะไรก็ได้ อาจจะเป็นชื่อจริงหรือ username
  • email: กรอกอีเมลลงไป บางคนอาจจะจด email พิเศษที่ไว้ติดต่อเรื่อง DN42 โดยเฉพาะ
  • mnt-by: อย่าลืมเซตให้เหมือน mntner
  • กดปุ่ม + แล้วสามารถเพิ่ม field อื่นๆ ได้อีก เช่น pgp-id, www, contact

พอเซฟแล้วให้กลับไปแก้ mntner เมื่อกี้ (ใช้ช่อง search ขวาบน) โดยระบุ admin-c, tech-c ให้ตรงกับ nic-hdl

autnum

autnum จะกำหนดเลข AS (Autonomous system) ของเรา ซึ่งเท่าที่เห็นใช้กัน บางเครือข่ายจะใช้ AS เลขเดียวทั่วโลกเลย แต่บางเครือข่ายก็จะมีหลาย AS ตามจุดที่ใช้งาน สำหรับของผมจะใช้ AS เดียวทั้งหมด

  • autnum: กรอกว่า AS แล้วตามด้วยเลขอะไรก็ได้ในช่วง 4242420000-4242423999 ที่ไม่ซ้ำกับคนอื่น (ดูได้ที่เว็บนี้) สำหรับผมใช้ AS4242421842 ตาม IP เครื่องนี้คือ 103.246.18.42 จะได้ไม่ต้องจำหลายเลข
  • as-name: กรอกอะไรก็ได้
  • admin-c, tech-c: กรอกตาม nic-hdl ของ person
  • mnt-by: เซตให้เหมือน mntner

inetnum

ถัดมาหลังจากมีเลข AS แล้วเราก็จะขอ IP address ใช้กัน โดยกรอก inetnum

  • inetnum: กรอก IP block ในช่วง 172.20.0.0/14 โดยดูได้จากเว็บนี้ว่าบล็อคไหนใช้ไปแล้ว (ไม่ควรใช้เกิน /27) โดยให้กรอกเป็นช่วง IP แรกถึงสุดท้ายแบบในภาพ
  • netname: กรอกอะไรก็ได้
  • nserver: ระบุ nameserver สำหรับ reverse DNS ถ้ายังไม่มีใส่ชื่อ DNS มั่วๆ ไปก่อน
  • admin-c, tech-c, mnt-by: เหมือนเดิม
  • status: ระบุ ASSIGNED
  • bgp-status: ในภาพจะเห็นว่ามีอยู่ แต่อันนี้ไม่ต้องกรอกเองเพราะระบบจะอัพเดตเอง

route

และเมื่อขอ IP แล้วก็ต้องขอสิทธิ์ในการประกาศ route ด้วย

  • route: กรอก IP block เดิมไป แต่เป็นแบบ CIDR
  • origin: กรอก AS เราลงไป (ที่ขอใน autnum)
  • mnt-by: กรอกเหมือน mntner

Peering

ขั้นตอนต่อมาเริ่มสนุกแล้วครับนั่นคือการ Peering กับคนอื่น อันนี้เราต้องไปตามหาคนที่เราจะ peer ด้วยก่อน จะ peer กับผมก็ได้ หรือใช้เว็บ PeerFinder ก็ได้ โดยในเว็บจะให้เรากรอก public IP ของเรา (ไม่ใช่ IP ใน DN42) แล้ว server ของคนที่เข้าร่วมโครงการนี้จะ ping หา IP เราดูว่าของใครใกล้สุด

เสร็จแล้วเราก็ต้องไปตามหาคนนั้นๆ ครับ จะไปตามหาใน IRC ก็ได้ หรือจะเมลไปก็ได้ ซึ่งอย่างผมตอนนี้จะ peer อยู่ 2 ที่ คือ

  • icez hosting provider ผมเอง อันนี้ผมหลังไมค์ทักเค้าไปบอกว่าจะ peer ซึ่งเค้าขอ OpenVPN แต่ผมไม่สะดวกใช้ (เพราะมี IPsec กลับบ้านอยู่แล้วไม่อยากเซตเพิ่ม) ก็เลยว่าไหนๆ เราอยู่ใน switch ตัวเดียวกันละ เปิด BGP แลก route กันตรงๆ เลยน่าจะได้ ไม่ต้อง VPN ก็ได้คงไม่มีใครแอบดัก
  • Tech9Computers อันนี้ผมเมลไปหาเค้าบอกว่าขอ peer ด้วย แล้วก็แนบ IPSec PSK ไป (PGP Encrypt แล้ว) เค้าก็ตอบมาเลยว่ายินดี peer ด้วย แต่ตอน peer จริงๆ ผมเซต IPSec/GRE ไม่ถูก เลยเข้า IRC ไปถามเค้า

ขั้นตอนการ peering ก็ดูได้จากในเว็บครับ หลักๆ จะมี 2 ส่วนคือ

  1. Setup VPN ตรงนี้แล้วแต่จะตกลงกันว่าจะใช้ VPN โปรแกรมไหน ใช้ได้ทุกโปรแกรมเลยไม่ว่าจะเป็น OpenVPN, Tinc, Wireguard, IPSec/L2TP, IPSec/GRE
  2. พอ VPN ติดแล้วเราจะแลกเปลี่ยน route กันด้วย BGP

Setup BGP

สำหรับการ setup BGP นั้นมี 2 โปรแกรมที่นิยมคือ Bird กับ Quagga ซึ่งผมใช้ Bird อยู่ ขั้นตอนติดตั้งคร่าวๆ ก็ดูได้จากเว็บ DN42 เช่นกัน และ config ต่างๆ ก็ใช้จากในเว็บได้เลย

และที่แนะนำคือควรจะติด community tag ไว้ด้วยครับ เพื่อให้เครื่องอื่นๆ สามารถวางแผน routing ได้ ดูจากหน้าเว็บ DN42 อีกเช่นเคย ตัวอย่างเช่นในเคสผม

  • Peering ผมกับ Tech9Computers ping ประมาณ 30 และ bandwidth น่าจะสัก 1Mbps (กะประมาณเอา), ใช้ IPSec/GRE ฉะนั้นน่าจะ encrypt เนียวดี ก็จะเลือกใส่ community string
    protocol bgp Tech9 from dnpeers {
    neighbor 172.23.220.64 as 4242421588;
    import where dn42_import_filter(4,22,34);
    export where dn42_export_filter(4,22,34);
    };
    

    โดย 4 ระบุ ping, 22 ระบุ bandwidth และ 34 ระบุ encryption

  • Peering ผมกับ Icez ping ไม่ถึง 1ms, bandwidth น่าจะ 100Mbps (เพราะอยู่ใน switch เดียวกัน เผลอๆ จะเป็น Gigabit), ไม่ได้ encrypt ก็จะใช้ dn42_import_filter(1,24,31)

ตรงนี้พอ Bird ทำงานแล้วเราสั่ง ip route ดูก็จะเห็นว่าได้ route ของ DN42 เต็มไปหมด ซึ่งปัญหาที่ผมเจอคือ Bird เข้าใจผิดเรื่อง route เพราะ DN42 ใช้ช่วงทับกับ Docker ตรงนี้ก็แก้ไขได้โดย restart เครื่อง เพื่อให้ Bird เห็น interface ของ DN42 VPN ก่อน Docker จะติด

ที่แนะนำให้ลองเล่นคือ ถ้ามี peering หลายอันแนะนำให้ลองปิด BGP ไปข้างนึงดูครับ (birdc disable "Tech9" ก็ได้) จะเห็นว่า routing table จะเปลี่ยนไปใช้อีก peering นึงอัตโนมัติเลย ตรงนี้คือเหตุผลที่ทำไมอินเทอร์เน็ตเป็นระบบ self-healing

Setup DNS

หลังจากเรา peering กับ DN42 ได้แล้วเราอาจจะทดสอบโดยขอ ping IP ฝั่งคนที่เรา peer ด้วย หรือ DNS Server 172.23.0.53 ถ้าใช้ได้ก็ยินดีต้อนรับสู่ DN42 ครับ

แต่ยังไม่จบ เพราะเราเข้าเว็บใน DN42 ไม่ได้เนื่องจากยังไม่ได้ setup DNS ซึ่งวิธีง่ายสุดก็คือเปลี่ยน DNS ในเครื่องเราไปใช้ 172.23.0.53 เลย แต่จะไม่ค่อยปลอดภัยเท่าไร ควรจะใช้เฉพาะกับเว็บใน DN42 เท่านั้น ซึ่งจะใช้ domain name .dn42 วิธีการตรงนี้ขึ้นอยู่กับ DNS Resolver ที่ใช้อยู่ สามารถดูได้จาก เว็บ DN42

ตัวอย่างเช่นในบ้านผมใช้ Mikrotik ซึ่งมันปรับอะไรไม่ได้ แต่มีอีกเครื่องหนึ่งคือ UniFi ที่รัน OpenWRT ผมก็ติดตั้ง dnsmasq เข้าไป แล้วใช้ config ตามเค้าคือ

config dnsmasq
        option boguspriv '0'
        option rebind_protection '1'
        list rebind_domain 'dn42'
        list server '/dn42/172.23.0.53'
        list server '/20.172.in-addr.arpa/172.23.0.53'
        list server '/22.172.in-addr.arpa/172.23.0.53'
        list server '/23.172.in-addr.arpa/172.23.0.53'

ก็จะเป็นการระบุว่า โดเมน dn42 ให้ตอบเป็น IP local ได้ (rebind_domain) และโดเมน dn42 ให้ใช้ DNS ที่ 172.23.0.53 รวมถึง 20-23.172.in-addr.arpa ด้วย ซึ่งเป็น reverse DNS ของ DN42

เสร็จแล้วทดสอบโดยลองเข้าเว็บ whatismyip.dn42 ถ้าเข้าได้เป็นอันเสร็จสิ้น

ต่อเน็ตที่บ้านให้ใช้ DN42

ทีนี้ที่บ้านผม VPN เข้ากับ server อยู่แล้ว ก็เลยจะ config ให้ใช้ DN42 ได้ด้วย ซึ่งก็ไม่ยากเท่าไร เพราะเราใช้ BGP แลกเปลี่ยน route ในบ้านอยู่แล้ว พอต่อเข้า DN42 ปุ๊บ router ที่บ้านก็จะได้ route DN42 ไปพร้อมๆ กันเลย ก็จะเหลือแค่เซต iptables ให้ forward route ให้

ก่อนอื่นก็คือจะต้องตัดแบ่ง subnet DN42 ของเราให้ไปที่บ้านก่อน ซึ่งผมก็จะใช้ Visual subnet calculator ช่วยตัด (เพิ่งมาเจอ tool นี้หลังเรียน datacom ไปแล้ว เสียดาย) และทำ Google Docs จดไว้ว่าแจก IP อะไรไปแล้วบ้าง

ทีนี้ IP block ที่มีมันไม่ได้พอแจกทุกเครื่องในบ้าน (และเราก็ไม่อยากให้ทุกเครื่องมี IP จริง) เราเลยต้อง NAT ครับ โดยผมจะ NAT ที่ Mikrotik ซึ่งมี IP DN42 เช่นกัน

/ip firewall nat add action=src-nat chain=srcnat dst-address=!10.50.50.1 out-interface=\
    l2tp-out1 src-address=!172.20.18.128/27 to-addresses=172.20.18.137

ก็แปลว่าถ้าเครื่องที่ส่งไม่อยู่ใน subnet DN42 ของผมและจะออกไปทาง VPN ให้ NAT เป็น IP ของ router ให้หมด

ทีนี้เครื่องส่วนตัวผมจะ allocate DN42 IP ไว้ให้เลย และให้ connect เข้ามาได้ด้วย ทีแรกผมใช้วิธีให้มันมี 2 IP คือ DHCP + DN42 Static IP ซึ่งพังง่ายมาก เพราะระบบไม่ได้ออกแบบมาให้มี DHCP + Static พร้อมกัน ตอนหลังเลยเปลี่ยนแผนใหม่ว่าใช้ NAT ดีกว่า

/ip firewall nat
add add action=src-nat chain=srcnat dst-address=!10.50.50.1 out-interface=\
    l2tp-out1 src-address=192.168.2.32 to-addresses=\
    172.20.18.138
add action=dst-nat chain=dstnat dst-address=172.20.18.138 in-interface=\
    l2tp-out1 to-addresses=192.168.2.32

ก็แปลว่าถ้า source เป็น 192.168.2.32 ให้ rewrite ต้นทางเป็น 172.20.18.138 และถ้าเข้ามาหา 172.20.18.138 ก็ให้เปลี่ยนปลายทางเป็น 192.168.2.32 ตรงนี้ถ้าเซต Firewall rule ไว้ ก็ต้องแก้ด้วยเพราะมันจะใช้ IP ปลายทางที่แก้แล้วมาเช็คเงื่อนไขของ Firewall

อื่นๆ ที่ควรลอง

พอเราเชื่อมต่อกับ DN42 แล้วก็ลองอย่างอื่นๆ ได้อีกครับ เช่น

  • เพิ่ม peering point ใน AS เรา
  • ทำ DNS Server และจดโดเมน DN42 (เว็บผมเข้าได้ที่ whs.dn42 แต่ตอนนี้ยังไม่มีอะไร)
  • เซต reverse DNS ตรงนี้ผมไม่เคยเซต DNS Server เอง (domain ทั้งหมดที่ถือใช้ hosted DNS หมด อย่าง whs.in.th อยู่บน Google Cloud) พอใช้ DN42 มันไม่มีทางเลี่ยงแล้วก็เลยต้องเซตเอง แต่สำหรับเว็บจริงคงยังไม่ใช้เพราะติดเรื่อง DNSSEC key อีกด้วย
  • ลอง Anycast (ไว้มีหลายๆ node จะลอง)
  • ติดตั้ง Looking glass (เช่นของ weiti หรือผมชอบใช้ของ peer ผม tech9computers)
  • ให้บริการ service ต่างๆ ใน DN42 (สามารถดูบริการต่างๆ ได้ที่หน้า Internal services ซึ่งหน้านี้เข้าจากด้านนอก DN42 ไม่ได้
    • แอบบอกว่ามี chan.dn42 ด้วยล่ะ

ปิดปั้มที่บ้านแบบ IoT Part 5: แจ้งเตือน และตั้งเวลา

จากตอนที่แล้วเราได้เซตให้ปิดปั้มเองเรียบร้อยแล้ว แต่ปัญหายังเหลืออยู่ว่าควรจะบอกด้วยว่าปิดปั้มแล้วเพื่อความชัวร์ (และใช้ debug เวลา VPN หลุดด้วย) และจะต้องต่อเวลาได้ในกรณีที่ซักผ้าอยู่ ในตอนสุดท้ายเราจะ ก็จะเพิ่มระบบ Notification เข้ามาครับ

โปรแกรม Home Assistant จะรองรับ Progressive Web App push ซึ่งสามารถ push ไปหา Chrome บน PC/Android ได้ และสำหรับ iOS จะต้องติดตั้งแอพ Home Assistant

สำหรับ PWA Push ทำดังนี้ครับ

  1. เข้าไปสมัคร Firebase Push Notification ที่ https://console.firebase.google.com
  2. เข้าไปที่หน้า project กดตรงประแจที่ Overview เลือก Cloud Messaging แล้วจด Server key กับ Sender ID ไว้

แล้วเพิ่ม config ว่า

ios:
notify:
  - platform: html5
    gcm_api_key: '...'
    gcm_sender_id: '...'

โดยระบุ gcm_api_key เป็น Server key ที่จดไว้ และ gcm_sender_id เป็น Sender ID ที่จดไว้

จากนั้นให้เอาอุปกรณ์เข้ามาที่หน้าเว็บของ Home assistant และ Add to home screen สำหรับ Android จากนั้นใน sidebar ให้เปิด Push Notification ซึ่งบน server จะเห็นมีไฟล์ html5_push_registrations.conf ปรากฏขึ้นมา ตรงนี้แนะนำให้ restart ทีนึง

สำหรับ iOS เมื่อเอาแอพเชื่อมต่อมาแล้วให้ restart Home Assistant ทีนึงก็ใช้ได้เลยครับ

จากนั้นเราจะเพิ่มกฎว่าถ้าไม่มีคนอยู่บ้านให้ push ทำได้ดังนี้ครับ

automation:
  - alias: Notify me when nobody's home
    trigger:
      platform: state
      entity_id: group.family
      from: home
      to: not_home
    action:
      - service: notify.html5_yui
        data:
          message: Nobody home!
      - service: notify.ios_my_love
        data:
          message: Nobody home!

โดยตรงชื่ออุปกรณ์ให้หาจากหน้า service ครับ (/dev-service)

ทดสอบแจ้งเตือนได้โดยกดที่ automation ที่เราสร้างมาใหม่แล้วกด Trigger ครับ จะได้แบบนี้

ตั้งเวลา

โจทย์ข้อสุดท้ายคือ สามารถตั้งเวลาปิดได้สำหรับเวลาซักผ้า ซึ่งเป็นปัญหาที่ผมนึกวิธีอยู่นานพอสมควร

วิธีที่ใช้คือ

  1. มี input slider component ตัวหนึ่งให้ลากตั้งเวลาได้
  2. ทุกๆ 1 นาที slider จะลดค่าลง 1
  3. เมื่อ slider เหลือ 0 นาที ให้ปิดปั้ม
  4. เมื่อ slider คือ -1 แสดงว่าไม่ใช้ เพื่อป้องกัน slider เหลือ 0 ค้าง หรือเมื่อต้องการยกเลิก timer
  5. ให้ trigger “Turn off pump when nobody’s home” ไม่ทำงานถ้า slider นี้ทำงานอยู่

config ก็ตามนี้ครับ

input_slider:
  pump_off_timer:
    name: Pump off timer (min)
    initial: -1
    min: -1
    max: 180
    step: 10
automation:
  - alias: Pump timer tick
    hide_entity: true
    trigger:
      platform: time
      seconds: 00
    condition:
      condition: and
      conditions:
        - condition: numeric_state
          entity_id: input_slider.pump_off_timer
          above: 0
    action:
      service: input_slider.select_value
      data_template:
        entity_id: input_slider.pump_off_timer
        value: '{{states("input_slider.pump_off_timer")|int - 1}}'
  - alias: Pump timer trigger
    hide_entity: true
    trigger:
      platform: numeric_state
      entity_id: input_slider.pump_off_timer
      above: 0
      below: 0
    condition:
      condition: and
      conditions:
        - condition: state
          entity_id: group.family
          state: not_home
    action:
      service: switch.turn_off
      data:
        entity_id: switch.pump

Trigger ชุดใหม่นี้จะไม่ขึ้นในเว็บครับ เนื่องจาก hide_entity: true แต่สามารถดูได้ในหน้า dev-state (ปุ่ม <> ใน sidebar ของ Home Assistant)

และให้แก้ trigger เดิมดังนี้

automation:
  - alias: Turn off pump when nobody's home
    initial_state: 'off'
    trigger:
      platform: state
      entity_id: group.family
      from: home
      to: not_home
    condition:
      condition: and
      conditions:
        - condition: numeric_state
          entity_id: input_slider.pump_off_timer
          below: -1
    action:
      service: switch.turn_off
      data:
        entity_id: switch.pump

โดยเพิ่ม condition เข้ามาว่า pump_off_timer จะต้องเป็น -1 จึงจะปิดปั้มให้อัตโนมัติ ถ้า timer ยังทำงานอยู่ก็จะไม่ปิด

ในหน้าเว็บจะปรากฏ slider ขึ้นมา ก็สามารถลากเอาได้เลยว่าจะปิดปั้มในกี่นาที

สรุป

ในบทความนี้เราก็ทำให้ปั้มในบ้านเรากลายเป็นอุปกรณ์ IoT สามารถเปิดปิดจากอินเทอร์เน็ตได้โดยคำนึงถึง security ด้วย และใช้ระบบอัตโนมัติทำให้เปิดปิดได้อัตโนมัติเพื่อประหยัดน้ำ โดยอุปกรณ์ทั้งหมดที่ใช้ต้นทุนก็ไม่ถึง 1 พันบาท (ไม่นับ server ซึ่งผมก็ใช้เปิดบล็อคที่ผู้อ่านกำลังเข้าอยู่แล้ว)

สำหรับใครที่จะลอง implement ตามก็แนะนำว่าสามารถข้ามพาร์ทที่ใช้ router ไปได้ครับ โดยใช้ Raspberry Pi ลง Home Assistant ไว้ในบ้านก็จะสามารถเซตตามได้เลยโดยไม่ต้องเซต VPN แต่ถ้าไม่มี IP จริงจะทำให้เข้าจากนอกบ้านได้ด้วยก็จะลำบากหน่อย

ก็ขอบคุณมากๆ ที่ติดตามกันมาถึงตอนสุดท้ายครับ Feedback ออกมาค่อนข้างดีมากๆ ถ้ามีคำแนะนำอะไรหรือสงสัยก็พูดคุยกันได้ในโพสต์ Facebook หรือใน Twitter ที่แชร์บล็อคนี้ครับ