การเลือก OpenVPN แบบสุ่มใน systemd: ทำไม ExecStart จึงล้มเหลว
systemd ไม่ใช่เชลล์ ดังนั้นสำเนาที่สลับกันภายใน ExecStart จึงตายไป ย้ายการเลือกไปที่ ExecStartPre หรือปล่อยให้ OpenVPN เลือกเซิร์ฟเวอร์เอง
ไฟล์ .ovpn สี่สิบไฟล์สำหรับประเทศหนึ่งมาถึงจากผู้ให้บริการ และแผนดูเหมือนชัดเจน: คัดลอกไฟล์สุ่มหนึ่งไฟล์ไปยังตำแหน่งของมัน แล้วจึงเริ่มอุโมงค์ เมื่อเขียนเป็นไปป์ไลน์ภายใน ExecStart แผนนั้นก็ตายตอนบูตด้วยรหัสสถานะที่ไม่ธิบายอะไรเลย ไฟล์ยูนิตไม่ใช่สคริปต์ และการแก้ไขคือการนำเชลล์เข้ามาอย่างชัดเจนหรือเลิกเลือกไฟล์ไปเลย
อาการ: ยูนิตที่เริ่มแล้วตายทันที
สถานะแสดงว่าบริการล้มเหลวภายในเสี้ยววินาที และบรรทัดในเจอร์นัลระบุคำสั่งที่ไม่มีเชลล์ใดจะได้รับ ไม่มีอะไรเกี่ยวกับอุโมงค์ปรากฏขึ้น เพราะอุโมงค์ไม่เคยเริ่มต้น
การอ่านบรรทัดคำสั่งที่แน่นอนจากเจอร์นัลเป็นวิธีที่เร็วที่สุดในการยืนยันเรื่องนี้ systemd จะสะท้อนสิ่งที่มันเรียกใช้ และสตริงจะแสดงอักขระไปป์เป็นอาร์กิวเมนต์ตามตัวอักษร ไม่ใช่เป็นตัวดำเนินการ
ทำไม ExecStart จึงรันไปป์ไลน์ไม่ได้
systemd แยกบรรทัดคำสั่งเอง ใช้การใส่เครื่องหมายคำพูดและการแทนที่ตัวแปรของตัวเอง แล้วจึงเรียกใช้ผลลัพธ์โดยตรง ท่อ การเปลี่ยนเส้นทาง การขยาย glob โดยเชลล์ และการแทนที่คำสั่งไม่ใช่ส่วนหนึ่งของไวยากรณ์นั้นเลย นี่เป็นการออกแบบ ไม่ใช่การละเลย: ยูนิตบอกแบบประกาศว่าอะไรควรถูกเรียกใช้ และตรรกะของเชลล์เป็นของสคริปต์ที่ยูนิตเรียก การแก้ไขทั้งสองทางจึงตามมาจากข้อนี้ และไม่ต้องให้สิทธิ์ยูนิตเพิ่มขึ้น
นี่อธิบายรหัสออกที่ไม่น่าเชื่อถือด้วย อาร์กิวเมนต์แรกถูกถือว่าเป็นไบนารี ดังนั้นบรรทัดที่ผิดรูปแบบจะล้มเหลวก่อนที่ตรรกะของคุณจะทำงาน
ทำการเลือกใน ExecStartPre
การแก้ไขโดยตรงคือขั้นเตรียมการที่เรียกเชลล์อย่างชัดเจน และเก็บการเลือกกับการเริ่มต้นไว้ในยูนิตเดียวกัน เพื่อให้ความล้มเหลวในอย่างหนึ่งไม่ปล่อยให้อีกอย่างทำงานอยู่คนเดียว
[Service]
Type=notify
ExecStartPre=/bin/bash -c "ls /etc/openvpn/profiles/au* | shuf -n 1 | xargs -r -I{} cp -f {} /etc/openvpn/%i.conf"
ExecStart=/usr/sbin/openvpn --config /etc/openvpn/%i.conf
รายละเอียดสามอย่างตัดสินว่าสิ่งนี้จะทนต่อการพบกับเครื่องจริงหรือไม่ การตั้งชื่อเป็นกับดักแรก: systemd แทนที่ %i ขณะแยกวิเคราะห์ยูนิต นานก่อนที่ bash จะทำงาน ตัวระบุจึงต้องอยู่นอกเครื่องหมายคำพูดเดี่ยว xargs -r ป้องกันไม่ให้ cp ทำงานโดยไม่มีอาร์กิวเมนต์เมื่อ glob ไม่ตรงกับสิ่งใด และขั้นเตรียมการที่ล้มเหลวจะหยุดยูนิต และนั่นคือผลลัพธ์ที่ถูกต้อง: การเริ่มด้วยการกำหนดค่าของเมื่อวานนั้นแย่กว่าการไม่เริ่ม การเลือกแบบสุ่มที่เลือกไฟล์เดิมเสมอโดยปกติไม่ใช่ความโชคร้าย ตรวจสอบว่าไฟล์ที่คัดลอกถูกเขียนทับโดยการอัปเดตแพ็กเกจ หรือว่า glob ตรงกับรายการเก่าเพียงรายการเดียว
หรือปล่อยให้ OpenVPN เลือกปลายทาง
OpenVPN สามารถเก็บปลายทางหลายรายการไว้ในโปรไฟล์เดียวและเลือกแบบสุ่มได้แล้ว ซึ่งขจัดเชลล์ การคัดลอก และสิทธิ์การเขียนออกจากเส้นทางการบูตทั้งหมด
client
remote-random
<connection>
remote au1.example.net 1194 udp
</connection>
<connection>
remote au2.example.net 1194 udp
</connection>
รูปแบบนี้เก็บการเลือกเซิร์ฟเวอร์ไว้ในไฟล์ที่ผู้ให้บริการเขียนขึ้น การหมุนเวียนที่เปลี่ยนชื่อไฟล์จึงไม่ทำให้ยูนิตเสียหาย มันยังทำให้รายการผู้สมัครตรวจสอบได้ ซึ่ง glob บนไดเรกทอรีไม่เคยทำได้ การสลับยังคงมีที่ของมันเมื่อโปรไฟล์ต่างกันมากกว่าปลายทาง: ใบรับรองต่างกัน คำสั่งเส้นทางต่างกัน นโยบายรหัสต่างกัน ในกรณีนั้นการเลือกคือระหว่างการกำหนดค่าทั้งหมด และขั้นตอนเชลล์คือวิธีที่ซื่อตรงในการแสดงออก
อ่านย้อนกลับอินสแตนซ์และไฟล์ที่มันใช้
ยืนยันว่ายูนิตใดทำงานจริงก่อนที่จะดีบักเนื้อหาของมัน บริการเมตาที่แพ็กเกจและเทมเพลตเป็นโครงสร้างพื้นฐาน ยูนิตที่ตรงกับไฟล์ของคุณคืออินสแตนซ์ที่ตั้งชื่อตามไฟล์เหล่านั้น
systemctl list-units 'openvpn*'
systemctl cat [email protected]
journalctl -u openvpn@au -n 30 --no-pager
อินสแตนซ์ที่ไม่มีใครจำได้ว่าเปิดใช้งานมักย้อนกลับไปยังไฟล์กำหนดค่าที่หลงทางในไดเรกทอรีแพ็กเกจ ปิดอินสแตนซ์ที่ตั้งชื่อนั้นแทนเทมเพลต มิฉะนั้นการอัปเกรดแพ็กเกจครั้งถัดไปจะนำมันกลับมา หลังการเปลี่ยนแปลง ให้เริ่มยูนิตและอ่านบรรทัดคำสั่งที่เจอร์นัลรายงาน แล้วยืนยันเส้นทางการกำหนดค่าที่มันเปิดจริง การตรวจสอบเพียงครั้งเดียวนั้นแยกการเลือกแบบสุ่มที่ทำงานได้ออกจากบริการที่นำไฟล์ของเดือนที่แล้วกลับมาใช้อย่างเงียบๆ