1 คะแนน โดย GN⁺ 2 시간 전 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • หากเก็บค่าลับไว้เป็นข้อความล้วนในคอนฟิก Nix, รีโพซิทอรี Git แบบส่วนตัว หรือ git-crypt บน NixOS จะมีความเสี่ยงที่ Nix store ซึ่งทุกคนอ่านได้ทำให้ผู้ที่เข้าถึงเครื่องสามารถอ่านค่าลับได้
  • sops-nix เข้ารหัสไฟล์ค่าลับด้วยกฎใน .sops.yaml และเวิร์กโฟลว์การแก้ไขของ sops จากนั้นตอน activate จะถอดรหัสด้วยคีย์ SSH ของโฮสต์และวางข้อความล้วนไว้บน tmpfs ที่ /run/secrets/<name>
  • agenix ระบุ public key ผู้รับของค่าลับแต่ละรายการใน secrets.nix และถอดรหัสไฟล์ .age ลงบน tmpfs ที่ /run/agenix/<name> โดยต้อง rekey เมื่อมีโฮสต์ใหม่หรือมีการเปลี่ยนคีย์
  • วิธีวางค่าลับไว้ในไฟล์ระบบโดยตรงใช้ได้กับเซิร์ฟเวอร์เดี่ยวหรือแล็ปท็อป แต่ถ้าอ่านตอน evaluation เช่น builtins.readFile "/var/lib/myservice/token" ค่านั้นจะเข้าไปอยู่ใน Nix store จึงควรหลีกเลี่ยง
  • ถ้าเพิ่งเริ่มต้นและมีโทเคนอิสระเพียงไม่กี่ตัว agenix จะมีขั้นตอนน้อยกว่า แต่ถ้าเป็นโฮสต์อย่างเมลเซิร์ฟเวอร์ที่มีค่าลับเกี่ยวข้องกันจำนวนมาก sops-nix ที่รวมหลายค่าไว้ในไฟล์เดียวจะเหมาะกว่า

ความเสี่ยงพื้นฐานเมื่อจัดการค่าลับบน NixOS

  • การจัดการค่าลับบน NixOS แบ่งได้เป็น sops-nix, agenix/ragenix, การใช้ไฟล์ระบบ, รีโพซิทอรี Git แบบส่วนตัว, git-crypt และการเขียนลงในคอนฟิก Nix โดยตรง
  • ไม่ควรใช้รีโพซิทอรี Git แบบส่วนตัว, git-crypt หรือการเขียนลงในคอนฟิก Nix โดยตรง หากมีการใช้เครื่องร่วมกันหรือมีแผนจะเปิดเผยคอนฟิก
    • Nix store เปิดให้อ่านได้โดยทุกคน ดังนั้นผู้ที่มีสิทธิ์เข้าถึงเครื่องจะอ่านค่าลับได้
    • ความเสี่ยงนี้สำคัญเป็นพิเศษในช่วงที่มีช่องโหว่อย่าง CVE-2026-31431(copyfail) และ CVE-2026-43284 และ CVE-2026-43500(dirtyfrag)
  • เคยมีกรณีที่ค่าลับรั่วไหลจากคอนฟิกที่เผยแพร่แล้วอย่างน้อยสองครั้ง โดยยังมีตัวอย่างอยู่ที่ 1, 2

sops-nix

  • sops-nix อาจรู้สึกว่าตั้งค่ายากในครั้งแรกที่ใช้งาน แต่เอกสารถูกปรับปรุงขึ้นมาก และการที่ sops รองรับการเข้ารหัสและถอดรหัสค่าลับด้วยคีย์ SSH โดยตรงก็เป็นพัฒนาการสำคัญ
  • อย่างไรก็ตาม sops-nix ยังตามไม่ทันการรองรับคีย์ SSH นี้ โดยมีงานที่เกี่ยวข้องอยู่ใน sops-nix#779, sops-nix#922
  • เวิร์กโฟลว์การใช้งานคือสร้างกฎเข้ารหัสและถอดรหัสใน .sops.yaml แล้วใช้คำสั่ง sops เพื่อแก้ไขไฟล์ค่าลับ
    • ตัวอย่างเช่น กำหนด SSH public key เป็นผู้รับ age สำหรับพาธ secrets/*.yaml
    • เมื่อรัน sops secrets/shush.yaml โปรแกรมจะแก้ไขไฟล์ด้วย editor ที่เลือกไว้ และสามารถใส่ค่า YAML อย่าง hello: sops ได้
    • เมื่อออกจาก editor ค่าเหล่านั้นจะถูกเข้ารหัสเป็นรูปแบบ ENC[AES256_GCM,...] และกลับมาแก้ไขซ้ำได้ด้วยคำสั่งเดิม
  • ในคอนฟิก NixOS โมดูล sops-nix จะจัดการงานส่วนใหญ่ให้
    • ใช้ defaultSopsFile = ./secrets/shush.yaml; เพื่อกำหนดไฟล์ค่าลับเริ่มต้น
    • ใช้ age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; เพื่อระบุคีย์ SSH ของโฮสต์
    • กำหนดสิทธิ์ไฟล์ใน secrets."hello" ด้วย owner, group, mode
  • ตอน activate sops-nix จะถอดรหัสไฟล์ด้วยคีย์ SSH ของโฮสต์และวางข้อความล้วนไว้ที่ /run/secrets/<name>
    • พาธนี้อยู่บน tmpfs จึงทำให้ค่าลับไม่แตะดิสก์
    • บริการที่ต้องใช้ค่านั้นเพียงอ่านจากพาธดังกล่าว
  • ฟีเจอร์ template มีประโยชน์กับคอนฟิกร่วมกันหรือคอนฟิกที่ผู้ใช้อื่นต้องอ้างอิง
    • หากไฟล์คอนฟิกของบริการต้องการทั้งข้อความล้วนและค่าลับบางส่วน ก็ไม่จำเป็นต้องเข้ารหัสทั้งไฟล์
    • ตัวอย่างเช่น เก็บ SMTP_USER=isabel ไว้เป็นข้อความล้วน และใส่ placeholder ของค่าลับเป็น SMTP_PASSWORD=${config.sops.placeholder."mailserver/smtp_password"}

agenix

  • agenix ต่างจาก sops-nix ตรงที่ตั้งค่าค่าลับและคีย์ที่เข้าถึงได้ใน secrets.nix จึงให้ความรู้สึกที่ใกล้กับ Nix มากกว่า
  • ใน secrets.nix จะ bind SSH public key ของผู้ใช้และโฮสต์ แล้วกำหนดว่าไฟล์ .age แต่ละไฟล์ให้ public key ใดเข้าถึงได้บ้าง
    • เช่น "secret1.age".publicKeys = [ isabel host1 ];, "secret2.age".publicKeys = [ isabel host2 ]; ทำให้กำหนดรายชื่อผู้รับของแต่ละค่าลับต่างกันได้
  • ต้องสร้างไฟล์ค่าลับด้วย CLI ของ agenix
    • ใช้คำสั่ง agenix -e secret1.age เพื่อสร้างหรือกลับมาแก้ไข secret1.age ภายหลัง
  • วิธีเชื่อมเข้ากับคอนฟิกของโฮสต์คล้ายกับ sops-nix แต่เนื่องจากค่าลับแต่ละรายการเป็นไฟล์แยกกัน จึงมีพื้นผิวที่เล็กกว่า
    • ใช้ age.secrets.secret1.file = ./secrets/secret1.age; เพื่อระบุไฟล์
    • ใช้ owner, group, mode เพื่อกำหนดเจ้าของและสิทธิ์
  • ตอนบูต ระบบจะถอดรหัสไฟล์ .age แต่ละไฟล์ด้วยคีย์ SSH ของโฮสต์แล้ววางไว้ที่ /run/agenix/<name>
    • พาธนี้ก็อยู่บน tmpfs เช่นกัน
  • จุดที่มักสะดุดบ่อยที่สุดคือ rekeying
    • เมื่อเพิ่มโฮสต์ใหม่หรือเปลี่ยนคีย์ ต้องเข้ารหัสค่าลับใหม่ทุกตัวที่มีรายการ publicKeys เปลี่ยนไปใน secrets.nix
    • agenix --rekey ช่วยจัดการเรื่องนี้ แต่ต้องมี private key ของผู้รับปัจจุบันอย่างน้อยหนึ่งรายเพื่ออ่าน ciphertext เดิม
    • ในการใช้งานจริง การ rekey มักเกิดบนเครื่องที่เชื่อถือได้มากที่สุด ไม่ใช่โฮสต์ใหม่ที่กำลังจะนำขึ้นระบบ

วิธีใช้ไฟล์ระบบอย่างเดียว

  • การวางค่าลับไว้ในไฟล์ระบบโดยตรงมีต้นทุนคือคอนฟิกจะไม่สามารถอธิบายเครื่องได้ครบถ้วนอีกต่อไป
  • เมื่อติดตั้งใหม่ ต้องนำไฟล์ทั้งหมดกลับไปวางในตำแหน่งและสิทธิ์ที่ถูกต้องอีกครั้ง
    • ในงานกู้ระบบ เช่น การกู้เซิร์ฟเวอร์ตอนตีสอง เรื่องนี้อาจกลายเป็นหายนะครั้งใหญ่
  • แพตเทิร์นที่ควรหลีกเลี่ยงคือรูปแบบอย่าง builtins.readFile "/var/lib/myservice/token"
    • วิธีนี้อ่านไฟล์ตั้งแต่ตอน evaluation และนำเนื้อหาเข้าไปไว้ใน Nix store
    • เนื่องจาก Nix store เปิดให้อ่านได้โดยทุกคน จึงกลายเป็นปัญหาแบบเดียวกับที่เตือนไว้ตั้งแต่ต้นทุกประการ
  • สิ่งที่ควรทำแทนคือส่งพาธให้บริการ แทนการส่งค่าจริง
  • สำหรับเซิร์ฟเวอร์เดี่ยวหรือแล็ปท็อปอาจพอรับได้ แต่ถ้าเป็น fleet หรือเป็นสภาพแวดล้อมที่ต้องการสร้างใหม่ทั้งหมดจากคอนฟิกเพียงอย่างเดียว sops-nix หรือ agenix จะเหมาะกว่า
  • ถ้าแต่ละเครื่องมีค่าเพียงหนึ่งหรือสองตัวที่ไม่ควรอยู่ในรีโพซิทอรีจริง ๆ ก็อาจใช้ได้ แต่ภาระในการนำกลับมาใส่อีกครั้งในอนาคตจะตกกับตัวเองในวันหน้า

เปรียบเทียบ sops-nix กับ agenix

  • เหตุผลหลักในการเลือก sops-nix คือสามารถรวมข้อมูลให้มากที่สุดเท่าที่ทำได้ไว้ในไฟล์เดียว
    • หากเป็นกรณีอย่างเมลเซิร์ฟเวอร์ที่มีค่าลับเกี่ยวข้องกันจำนวนมาก ก็สามารถเก็บค่าลับได้มากกว่าในไฟล์เดียว แทนที่จะแยกเป็นราว 5 ไฟล์แบบ agenix
    • เป็นเครื่องมือที่ทรงพลังและเหมาะกับการใช้งานต่อเนื่อง โดยเฉพาะโฮสต์อย่างเมลเซิร์ฟเวอร์ที่มีค่าลับจำนวนมากซึ่งควรพิจารณาเป็นตัวเลือกแรก
  • agenix เด่นในเรื่อง ความเรียบง่าย
    • ไม่มี YAML schema ที่ต้องเรียนรู้
    • ไม่มี .sops.yaml ที่ต้องซิงก์
    • secrets.nix เป็น Nix ล้วน จึงใช้การ bind แบบ let ... in เดียวกับที่ใช้กับโฮสต์และผู้ใช้กับคีย์ได้ตรง ๆ
    • แบบจำลองความคิดคือ “ค่าลับหนึ่งรายการ, หนึ่งไฟล์, หนึ่งรายการผู้รับ” ซึ่งเข้ากันได้ดีกับรูปแบบการควบคุมการเข้าถึง
    • เพราะมีชิ้นส่วนที่ต้องขยับน้อยที่สุดแต่ยังให้การควบคุมการเข้าถึงคีย์ตามโฮสต์ จึงเป็นตัวเลือกที่แนะนำได้ง่ายสำหรับคนที่เพิ่งถามเรื่องการจัดการค่าลับบนเครื่อง NixOS
  • ทั้งสองเครื่องมือแก้ปัญหาเดียวกันได้ และความต่างในตอนนี้ส่วนใหญ่เป็นเรื่อง การใช้งาน
    • หากเพิ่งเริ่มต้นและมีหลายบริการที่ต้องใช้ชุดค่าลับที่เกี่ยวข้องกัน sops-nix จะขยายต่อได้ดีกว่า
    • หากเพิ่งเริ่มต้นและส่วนใหญ่มีแค่โทเคนอิสระไม่กี่ตัว agenix จะพาไปถึงเป้าหมายได้ด้วยขั้นตอนที่น้อยกว่า
    • หากกำลังจะเลือกเครื่องมือจัดการค่าลับตัวแรก การเริ่มจาก agenix เพื่อให้คุ้นกับเวิร์กโฟลว์ก่อน แล้วค่อยย้ายไป sops-nix เมื่อเริ่มรู้สึกถึงความไม่สะดวกของแนวทาง “ค่าลับหนึ่งรายการต่อหนึ่งไฟล์” จะเป็นทางเลือกที่ดีกว่า
  • มีการแก้ไขข้อมูลเกี่ยวกับการทนทานต่อควอนตัมแล้ว
    • age และ sops รองรับคีย์เข้ารหัสที่ปลอดภัยต่อควอนตัม
    • age#578 ถูกปิดแล้ว และ v1.3.0 ได้ออกแล้ว
    • ตอนสร้างคีย์ age ให้ใส่ -pq เช่น age-keygen -pq -o key.txt

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

 
GN⁺ 2 시간 전
ความเห็นจาก Lobste.rs
  • เคยเห็นคำอธิบายว่า sops-nix และ agenix ไม่ได้เก็บซีเคร็ตที่ถอดรหัสแล้วไว้บนดิสก์ แต่ก็สงสัยว่าในทางปฏิบัติมันมีข้อดีอะไร
    ซีเคร็ตที่เข้ารหัสกับคีย์ของมันไม่ได้อยู่บนดิสก์ทั้งคู่อยู่แล้วหรือ? หรือว่าอย่างใดอย่างหนึ่งถูกเก็บไว้ในที่อย่าง TPM?
    เพิ่งเริ่มใช้ Nix ได้ไม่นาน และในการดีพลอยแบบ self-hosting ขนาดเล็กก็ใช้วิธีง่าย ๆ คือใส่ไฟล์ลง filesystem ด้วย scp
    ลองค้นเพิ่มนิดหน่อยก็เหมือนจะสามารถป้องกัน SSH private key ด้วย TPM ได้ และก็สงสัยว่าทำใน VM ได้ไหม แต่พอเห็นว่าอาจมีการรองรับ vTPM ก็เหมือนตอบคำถามตัวเองได้แล้ว
    • บนเซิร์ฟเวอร์ของฉันใช้ sops-nix ซึ่งสำหรับ threat model ของฉันก็ถือว่าเพียงพอและทำงานได้ดี ตอนนี้เลยเริ่มสนใจวิธีเก็บ SSH private key ไว้ใน TPM
      ฝั่งเซิร์ฟเวอร์ยังมีทางเข้าถึงแบบ NixOps ด้วย ดูวิธีที่ Colmena ทำได้ที่นี่: https://colmena.cli.rs/0.4/features/keys.html
      แก่นสำคัญคือให้เครื่องที่เชื่อถือได้และมีซีเคร็ตเป็นคน push ไปยังเซิร์ฟเวอร์ระยะไกล คล้ายกับที่ตอนนี้คุณทำด้วย scp แต่ทำให้กระบวนการนั้นเป็นสไตล์ Nix มากขึ้น
    • ถึงเวลาหยิบวลีโปรดออกมาใช้แล้ว: “แล้วแต่สถานการณ์!” การจัดการซีเคร็ตที่นี่กำลังแก้สองปัญหาพร้อมกัน: ปกป้องไฟล์ซีเคร็ตบนดิสก์ และปกป้องซีเคร็ตในคอนฟิกที่ใช้สร้างระบบ เพราะสุดท้ายแล้วค่าพวกนั้นก็ ต้องมีอยู่ที่ไหนสักแห่ง
      ช่วงไม่กี่คืนที่ผ่านมาเพิ่งใช้เวลาตั้งค่าแนว agenix บน system flake ของตัวเองใหม่ เลยพูดได้เฉพาะเรื่อง agenix สำหรับคนที่สนใจ ฉันเลือก agenix-rekey เพราะไม่ต้องมีไฟล์ .yml ที่เก็บซีเคร็ต และสามารถตั้งค่าซีเคร็ตทั้งหมดไว้ใน Nix ได้เหมือนที่ทำไว้อยู่แล้ว
      ซีเคร็ตที่เข้ารหัสจะอยู่ใน Nix store และเหมือนกับอย่างอื่นใน Nix store คืออ่านได้ทั่วระบบ ส่วน SSH private key ที่ใช้ถอดรหัสซีเคร็ตนั้น โดยทั่วไปก็คือ private key ของ SSH server จริง ๆ และจะเลือก ไม่ใช้แบบนั้น ก็ได้ ซีเคร็ตจะถูกถอดรหัสตอน activation ซึ่งก็ประมาณช่วงบูต แล้วนำไปวางไว้ที่ /run/agenix/<user>
      เครื่องมือชื่อ secrix ไปไกลกว่านั้นอีก โดยผูกซีเคร็ตเข้ากับบริการ systemd แล้วผูกบริการนั้นกลับเข้ากับบริการที่ต้องใช้ซีเคร็ต ดังนั้นซีเคร็ตจะถูกถอดรหัสเฉพาะตอนที่บริการนั้นกำลังรันอยู่เท่านั้น แต่ในทางปฏิบัติ ผู้ใช้ NixOS ไม่ค่อยเปิดปิดบริการบ่อย ๆ เลย ทำให้ส่วนใหญ่ลงเอยที่บริการรันตลอดอยู่ดี อาจเหมาะกับซีเคร็ตที่ใช้ตอน activation ของระบบ เช่น การสร้างผู้ใช้
      agenix-rekey ทำให้การ rekey น่ารำคาญน้อยลง และแทนที่ secrets.nix ด้วย flake output มันใช้ YubiKey split identity เป็นครึ่งหนึ่งของคีย์ การ rekey คือยืนยันตัวตนด้วยคีย์นั้นและอีกครึ่งหนึ่งเพื่อถอดรหัสซีเคร็ต แล้วจึงเข้ารหัสคีย์ใหม่ด้วย SSH public key ของเซิร์ฟเวอร์ Public key ถูกสร้างขึ้นตอนติดตั้งระบบ และฉันใช้ nixos-anywhere พร้อม --copy-host-keys เพื่อดึงคีย์ที่สร้างใน installation closure มา ซีเคร็ตที่เข้ารหัสจะเก็บไว้ในรีโป แต่เก็บไว้ในซับโมดูลแยกที่เข้าถึงได้เฉพาะ trusted builder
      อีกอย่าง vTPM โดยมากไม่ได้อิงฮาร์ดแวร์ และผู้ให้บริการหลายเจ้าถึงจะมี TPM ให้ก็มักเป็นแค่ TPM v1.2 ไม่ใช่ TPM v2 ซึ่งผู้ให้บริการของฉัน BinaryLane ก็เป็นแบบนั้น มันเพิ่มชั้นความปลอดภัยขึ้นมาอีกชั้นก็จริง แต่ไม่ใช่ HSM วิเศษแบบ TPM จริง และไม่สามารถป้องกันการถูกเจาะจากผู้ให้บริการหรือโฮสต์โนดได้ ถ้าผู้ให้บริการจะรองรับ vTPM ที่อิง HSM จริง ต้นทุนก็น่าจะสูงมาก และ AWS ก็ดูเหมือนจะคิดราคาแบบ AWS
    • ฉันใช้ agenix + agenix-rekey + age-plugin-1p
      master key อยู่ใน 1Password ดังนั้นฉันจึงแก้ไข ดู หรือ rekey รหัสผ่านของเซิร์ฟเวอร์ไหนก็ได้โดยไม่ต้องเก็บ credential ใด ๆ ไว้บนดิสก์โน้ตบุ๊ก
      คุณสามารถให้สิทธิ์เข้าถึงกับเซิร์ฟเวอร์ที่ต้องการเข้าถึงคีย์ตอนรันได้ บอก agenix-rekey ว่าเซิร์ฟเวอร์นั้นเห็นคีย์อะไรได้บ้าง แล้วรัน agenix rekey จากนั้นคีย์เวอร์ชันที่เข้ารหัสและเซิร์ฟเวอร์นั้นถอดรหัสได้จะถูกบันทึกลงใน Nix store ส่วน SSH private key ของเซิร์ฟเวอร์นั้นอยู่แค่บนเซิร์ฟเวอร์นั้นและไม่ออกไปข้างนอกเลย agenix-rekey ต้องใช้แค่ public key ในการ rekey
      ดังนั้นกรณีที่ซีเคร็ตรั่วก็คือบัญชี 1Password ของฉันถูกแฮ็ก หรือไม่ก็เซิร์ฟเวอร์ที่ใช้ซีเคร็ตนั้นถูกแฮ็ก
    • ใน Agenix ค่าปริยายคือจะถอดรหัสซีเคร็ตที่เข้ารหัสด้วย /etc/ssh/ssh_host_ed25519_key แล้วใส่ลงใน ramfs ที่ mount ไว้ที่ /run/agenix.d
      ดังนั้นก็ใช่เลย ทั้งข้อมูลที่เข้ารหัส คีย์เข้ารหัส และข้อมูลที่ถอดรหัสแล้ว ล้วนเข้าถึงได้จาก filesystem
    • แบบนี้ยังทำให้สามารถแชร์ คอนฟิก NixOS ทั้งชุดเป็นสาธารณะได้ด้วย แม้คนที่ทำแบบนั้นจะไม่ได้มีมาก แต่ฉันรู้สึกว่าครึ่งหนึ่งของคำสัญญาของ Nix คือคนอื่นสามารถมาช่วยดีบักปัญหาระบบของฉันได้ด้วย
  • กำลังคิดอยู่ว่าอยากลองใช้ agenix กับ agenix-rekey ดู เหมือนมันจะลด pain point ที่หลายคนพูดถึงไปได้เยอะ แต่ยังไม่ได้ลองจริง
    https://github.com/oddlama/agenix-rekey
  • ฉันเคยจัดการ credential ด้วย agenix แต่ย้ายกลับไปใช้วิธีเก็บไว้ใน filesystem เฉย ๆ แล้ว เพราะง่ายกว่า และสำหรับฉันการติดตั้งใหม่ก็ไม่ได้เกิดขึ้นบ่อย
    อีกอย่าง credential ที่อยู่ยาว ๆ ก็มีน้อยลงเรื่อย ๆ ด้วย ตอนนี้กำลังค่อย ๆ เลิก การคัดลอก credential ระยะยาว แล้วหันไปใช้ credential แบบอิงฮาร์ดแวร์ ซึ่งอาจใช้โดยตรงหรือเอาไปแลกเป็น credential อายุสั้นแทน