แล็ปท็อปของคุณแสดงอุโมงค์สีเขียว บันทึกของไคลเอนต์รายงานว่าเส้นทางถูกพุช และที่อยู่เดียวที่ตอบกลับคือ IP อุโมงค์ของเซิร์ฟเวอร์ VPN เอง ping ที่เลยออกไปจะตายไม่ว่าคุณจะเล็งอะไร ไม่ว่าจะเป็น ClusterIP ที่อยู่ของ pod อื่น หรือชื่อโฮสต์สาธารณะ การรวมกันนี้ไม่ใช่ความคลุมเครือแต่เป็นสัญญาณวินิจฉัย: การถอดห่อทำงานได้ แต่ฮ็อปที่สองไม่ทำงาน

ผมเจอเรื่องนี้บนโหนด Debian ที่รันอิมเมจ OpenVPN ซึ่งติดตั้งผ่าน chart และอุโมงค์เป็นส่วนที่ง่าย ไคลเอนต์ได้ลีส 10.8.0.x, 10.8.0.1 ตอบกลับ และ 10.96.0.10 ซึ่งเป็นบริการ DNS ของคลัสเตอร์หมดเวลา ไม่มีอะไรในบันทึกของเซิร์ฟเวอร์ดูผิดปกติ และนั่นคือเหตุผลที่ผู้คนเริ่มเขียนกฎ iptables ใหม่ซึ่งไม่เคยจำเป็นต้องแตะเลย

pod ของ VPN คือเราเตอร์ และควรพูดให้ชัดเจน แพ็กเก็ตที่เข้ารหัสมาถึง ถูกแกะใน namespace เครือข่ายของ pod เอง แล้วจึงต้องออกผ่านอินเทอร์เฟซอื่น Linux ปฏิเสธขั้นที่สองนั้นเว้นแต่จะเปิดการส่งต่อภายใน namespace นั้นเอง

ไคลเอนต์เข้าถึงอะไรได้และอะไรไม่ได้

ที่อยู่ที่ตอบกลับเป็นของปลายทางอุโมงค์ และโปรเซส VPN ถือครองไว้ในเครื่อง จึงไม่เกี่ยวกับการส่งต่อเลย ทุกอย่างอื่นรวมถึง pod อื่น VIP ของบริการ หรืออินเทอร์เน็ต ต้องให้เคอร์เนลย้ายแพ็กเก็ตจาก tun0 ไปยัง eth0 เมื่อการย้ายนั้นถูกปฏิเสธ อาการคือความเงียบสม่ำเสมอไม่ใช่การปฏิเสธ เพราะไม่มีอะไรสร้างข้อผิดพลาด ICMP กลับลงไปในอุโมงค์ คุณจะได้ timeouts ไม่ใช่ข้อความแจ้งว่าเข้าถึงไม่ได้ และนั่นคือเบาะแสว่าไม่มีใครกำหนดเส้นทางเลย

การจำแนกสองนาทีทำให้ชั้นแคบลง ถ้าชื่อ DNS ล้มเหลวในขณะที่ ping ดิบไปยัง IP ของบริการสำเร็จ ปัญหาอยู่ที่ท่อ DNS ไม่ใช่การส่งต่อ ถ้าทุกปลายทางที่เลยที่อยู่ peer หมดเวลา รวมถึง IP เปล่า ให้เริ่มที่ namespace

อ่านการตั้งค่าภายใน namespace

ทุก pod ได้ namespace เครือข่ายของตัวเอง และ net.ipv4.ip_forward เป็น sysctl ต่อ namespace การเปลี่ยนบนโหนดไม่ได้บอกอะไรเกี่ยวกับคอนเทนเนอร์ และเป็นวิธีที่เชื่อถือได้ในการใช้เวลาช่วงบ่ายบนเครื่องผิด

kubectl exec -it deploy/openvpn -- sysctl net.ipv4.ip_forward
# net.ipv4.ip_forward = 0

ศูนย์ยืนยันการวินิจฉัย ค่ายังไม่ได้รับการสืบทอดจากการกำหนดค่าขณะรันของโหนด: มันเริ่มจากค่าเริ่มต้นของอิมเมจทุกครั้งที่มีการสร้างคอนเทนเนอร์ การแก้ไขที่ใช้ด้วยมือด้วย kubectl exec ... sysctl -w จึงหายไปในการรีสตาร์ตครั้งถัดไปและกลับมาเป็นข้อบกพร่องที่ดูเหมือนเป็นครั้งคราว

เปิดใช้งานเพื่อให้ manifest เก็บไว้

ตัวเลือกที่ยั่งยืนคือ init container ที่รันใน pod sandbox เดียวกันและเขียนค่าก่อนที่คอนเทนเนอร์ VPN จะเริ่ม คอนเทนเนอร์ใน pod ใช้ namespace เครือข่ายร่วมกัน การตั้งค่าจึงส่งต่อไปยังคอนเทนเนอร์หลัก

initContainers:
- name: enable-forwarding
  image: busybox:1.36
  command: ["sysctl", "-w", "net.ipv4.ip_forward=1"]
  securityContext:
    privileged: true

บรรทัด privileged: true นั่นแหละที่ผู้ตรวจทานคัดค้าน และพวกเขาก็ถูก การเขียน sysctl ต้องใช้สิทธิ์พิเศษหรือความสามารถที่จำกัดขอบเขต และคลัสเตอร์ที่รัน Pod Security Standard แบบจำกัดอาจปฏิเสธ init container ที่มีสิทธิ์พิเศษโดยตรง ถ้า admission ปฏิเสธ pod คุณจะรู้ตอนสร้าง ซึ่งอย่างน้อยก็ซื่อสัตย์และอ่านง่าย

ทางเลือกที่สะอาดกว่าคือรายการ securityContext.sysctls ระดับ pod ซึ่งให้ kubelet ใช้ sysctl ที่มี namespace โดยไม่ให้สิทธิ์พิเศษแก่คอนเทนเนอร์ใด ๆ มันครอบคลุมเฉพาะชื่อในรายการที่อนุญาตของคลัสเตอร์ การที่ net.ipv4.ip_forward อยู่ในรายการนั้นขึ้นอยู่กับการกำหนดค่า kubelet และ pod ที่ขอ sysctl นอกรายการจะถูกปฏิเสธที่ admission หรือติดอยู่ใน CreateContainerConfigError ขณะที่ sandbox ถูกลองใหม่ ตรวจสอบรายการที่อนุญาตก่อนสันนิษฐานว่าฟิลด์เสีย

ก่อนสร้างวิธีใดวิธีหนึ่งด้วยมือ ดูว่า chart เปิดสวิตช์ไว้แล้วหรือไม่ ซึ่งมักเป็นอะไรทำนอง ipForwardInitContainer: true การใช้ค่าจาก chart ทำให้ไฟล์ override ของคุณเล็กและอยู่รอดการอัปเกรดครั้งถัดไป ซึ่งการแก้ไข Deployment ด้วยมือไม่ทำ

ครึ่งหลัง: เส้นทางขากลับและนโยบาย

การเปิดส่งต่อเป็นสิ่งจำเป็นไม่ใช่พอเพียง ถ้าไคลเอนต์ใช้พูลส่วนตัวที่เครือข่ายคลัสเตอร์ไม่มีเส้นทางไป คำตอบก็หาทางกลับเข้าอุโมงค์ไม่ได้ และ pod ต้องมาสเคอเรดทราฟฟิกนั้นไปยังปลายทาง

นโยบายวางอยู่บนนั้น วัตถุ NetworkPolicy และกฎไฟร์วอลล์ของคลาวด์ถูกประเมินกับที่อยู่ต้นทางที่แพ็กเก็ตพกหลังการแปล ดังนั้นกฎที่เขียนสำหรับพูลไคลเอนต์อาจหยุดตรงกันอย่างเงียบ ๆ เมื่อเปิดการมาสเคอเรด

เมื่อไคลเอนต์ต้องการเข้าถึงเฉพาะบริการภายในคลัสเตอร์ การให้ VPN สิ้นสุดที่ VIP ของบริการเป็นการออกแบบที่เล็กกว่าการกำหนดเส้นทางเข้าสู่เครือข่าย pod และยังเก็บพูลไคลเอนต์ออกจากตารางเส้นทางของ CNI ทั้งหมด

แยกฮ็อปที่ล้มเหลวออกจากไคลเอนต์

ทำงานจากข้างในออกไปและหยุดที่ขั้นเงียบแรก เริ่มจาก peer ของ VPN จากนั้นลองที่อยู่ของ pod สักตัว จากนั้น VIP ของบริการ และหลังจากนั้นจึงเป็นปลายทางสาธารณะ ทุกความสำเร็จเลื่อนขอบเขตไปหนึ่งฮ็อป และทุกความล้มเหลวบอกชื่อชั้นที่ควรตรวจสอบ หลังการดีพลอยใด ๆ ให้ exec เข้าไปและอ่าน sysctl อีกครั้ง เพราะ pod ที่ถูกแทนที่จะเริ่มจากค่าเริ่มต้นของอิมเมจ และการแก้ไขที่ไม่เคยอยู่ใน manifest ก็หายไปแล้ว

เอกสารของ Kubernetes เรื่อง การใช้ sysctl ในคลัสเตอร์ ระบุชื่อที่ตั้งได้ผ่านบริบทความปลอดภัยของ pod และเป็นที่แรกที่ควรดูกเมื่อ admission ปฏิเสธการเปลี่ยนแปลง

บทความที่เกี่ยวข้อง