Percubaan saya dengan libvirt pada mulanya tidaklah serius. Hanya ingin mengenali asas pengurusan mesin maya menerusi virt-install dan virsh, sambil-sambil membaca dokumentasi yang ditemui di Internet. Namun begitu, semakin lama saya mencuba, semakin tertarik saya kepada aspek keselamatan dan pengesahan sambungan klien dalam libvirt, sesuatu yang sebelum ini saya abaikan kerana berasakan tidak perlu.

Rujukan seperti ArchWiki hanya memperkenalkan topik autentikasi ini secara sepintas lalu, dengan pautan kepada dokumentasi rasmi: libvirt: Connection authentication. Dari sinilah saya mula menyelami secara lebih serius kaedah-kaedah autentikasi yang disokong oleh libvirt.

Memandangkan sistem saya sudahpun dikonfigurasikan dengan Kerberos sebagai kaedah pengesahan untuk sambungan SSH dalam rangkaian tempatan, saya tertarik untuk mengintegrasikan libvirt dengan Kerberos melalui GSSAPI. Dalam teori, ia kelihatan mudah, dan saya dengan penuh yakin menganggap ia boleh disiapkan dalam masa singkat.

Namun realitinya tidak begitu. Apa yang kelihatan mudah di permukaan, rupanya penuh dengan kekangan konfigurasi, modul yang tidak didokumentasikan sepenuhnya, dan cabaran pemahaman konsep SASL, TLS, serta hak akses pengguna. Beberapa kali saya hampir berputus asa, benar-benar terasa ingin berhenti.

Alhamdulillah, selepas beberapa pusingan cubaan, penyahpepijatan, dan bacaan lintang-pukang, saya akhirnya berjaya menyiapkan satu konfigurasi libvirt yang selamat, lengkap dengan sokongan TLS serta autentikasi Kerberos menggunakan SASL.

Catatan ini saya tulis bukan sahaja untuk rujukan pengguna lain yang ingin mencapai perkara sama, tetapi juga sebagai ingatan kepada diri sendiri bahawa jalan yang sukar selalunya menyimpan pelajaran yang tidak ternilai.

Skop dan Objektif Artikel

Artikel ini menggunakan pendekatan modular daemons, bukan lagi libvirtd.service. Jika anda masih menggunakan monolithic daemon, sesetengah bahagian perlu disesuaikan.

Penulisan ini memfokuskan kepada konfigurasi libvirt untuk menyokong sambungan selamat melalui protokol TLS serta pengesahan berasaskan Kerberos menggunakan modul SASL. Ia mengandaikan persekitaran rangkaian tempatan (LAN) yang telah sedia mempunyai perkhidmatan Kerberos dan akses kepada pengguna sah melalui GSSAPI.

Tujuan utama adalah untuk:

  • Menyediakan konfigurasi TLS yang betul antara hos dan klien libvirt ;
  • Mengaktifkan dan menyelaras modul SASL dengan kaedah GSSAPI untuk autentikasi pengguna berasaskan Kerberos ;
  • Menghurai isu-isu umum yang mungkin timbul semasa integrasi, termasuk konfigurasi hak akses, ralat modul, serta tingkah laku sambungan;
  • Memberikan penyelesaian berasaskan senario sebenar, bukan sekadar bergantung pada dokumentasi rasmi.

Sasaran Pembaca

Artikel ini ditujukan kepada pengguna yang telah terbiasa dengan asas libvirt, serta memahami konsep asas Kerberos, SASL, dan konfigurasi perkhidmatan dalam sistem berasaskan Linux, khususnya Arch Linux dan yang setaraf dengannya.

Keperluan Sistem

Sesetengah sistem operasi tidak memasang plugin SASL Kerberos secara lalai. Anda boleh menyemak keberadaannya dengan arahan berikut:

bash
pluginviewer | grep gssapi

Sekiranya tiada output terpapar, anda perlu memasang pakej cyrus-sasl-gssapi. Setelah dipasang, semakan semula menggunakan pluginviewer akan memaparkan mekanisme seperti berikut:

...
Plugin "gssapiv2" [loaded],     API version: 4
        SASL mechanism: GSSAPI, best SSF: 256, supports setpass: no
        security flags: NO_ANONYMOUS|NO_PLAINTEXT|NO_ACTIVE|PASS_CREDENTIALS|MUTUAL_AUTH
        features: WANT_CLIENT_FIRST|PROXY_AUTHENTICATION|DONTUSE_USERPASSWD
...
  • Senarai pemasangan pakej (distribusi Arch Linux) :
    *virt-install / virt-manager
    *virt-viewer (untuk akses GUI VM melalui protokol SPICE menggunakan remote-viewer)
    *qemu-base
    *qemu-hw-display-qxl
    *qemu-ui-spice-app
    *dnsmasq (diperlukan untuk virtual NAT bridge)
    *dmidecode
    *openssl (untuk penjanaan sijil TLS)
    edk2-ovmf
    swtpm

Senarai Pemilikan dan Mod Fail Sijil / Kunci TLS serta Keytab Kerberos

Saya kongsikan dahulu struktur pemilikan (pengguna & kumpulan) serta mod kebenaran direktori dan fail untuk sijil + kunci TLS serta keytab Kerberos.

  • Mod direktori : 0750
  • Mod fail sijil TLS : 0644
  • Mod fail kunci TLS : 0600
  • Mod fail keytab : 0600
  • Tambah pengguna ke dalam kumpulan libvirt-qemu:
    bash
    sudo usermod -aG libvirt-qemu $USER

Penghasilan Sijil TLS x509

Saya gunakan OpenSSL untuk menghasilkan kesemua sijil dan kunci di direktori $HOME saya sendiri, yang kemudian disalin dan ditampal ke direktori sebenar.

Sijil serta kunci CA akar dicipta hanya sekali dan disalin ke direktori-direktori projek yang memerlukan.

Sila rujuk libvirt | Public Key Infrastructure set up untuk mengetahui struktur direktori dan lokasi fail sijil yang betul.

Certificate Authority (CA)

cd ke dalam direktori ~/pki/CA.

  1. Rujuk nota tepi saya tentang penjanaan sijil dan kunci TLS untuk CA akar dengan OpenSSL di sini: Jana Sijil HTTPS untuk Domain yang Berasaskan ’localhost’.

  2. Salin cacert.pem ke senarai direktori dan namakan semula sijilnya mengikut keperluan.

    bash
    cp cacert.pem ~/.pki
    sudo cp cacert.pem /etc/pki/CA
    sudo cp cacert.pem /etc/pki/libvirt-spice/ca-cert.pem

Libvirt (TLS x509)

Menjana Sijil Pelayan

cd ke dalam direktori ~/pki/libvirt/server.

  1. Hasilkan permintaan sijil (CSR).
    Nota penting: CN di sini mesti dinyatakan dalam bentuk FQDN (nama hos penuh).

    bash
    openssl req -out server.csr -new -newkey rsa:2048 -sha256 \
      -noenc -keyout serverkey.pem \
      -subj "/C=MY/ST=Negeri/L=Bandar/O=Libvirt-Project/CN=<pc.example.com>"
  2. Cipta fail konfigurasi dengan maklumat berikut. Edit DNS dan alamat IP yang merujuk ke PC sendiri.

    ~/pki/libvirt/server/config
    basicConstraints=CA:FALSE
    keyUsage = digitalSignature, keyEncipherment
    extendedKeyUsage = serverAuth
    subjectAltName = @alt_names
    
    [alt_names]
    DNS.1 = pc.example.com
    IP.1  = 192.168.x.x
  3. Tanda tangani sijil menggunakan CA akar yang dijana sebelum ini.

    bash
    openssl x509 -in server.csr \
      -req -sha256 -out servercert.pem \
      -days 365 -extfile config \
      -CA ~/pki/CA/cacert.pem \
      -CAkey ~/pki/CA/cakey.pem \
      -CAcreateserial

    Gunakan opsyen -CAcreateserial jika ini adalah kali pertama anda menandatangani sijil. Jika CA pernah menandatangani sijil lain, gantikan sahaja opsyen tersebut dengan -CAserial ~/pki/CA/cacert.srl.

  4. Salin sijil dan kunci pelayan yang dihasilkan ke direktori berikut.

    bash
    sudo cp servercert.pem /etc/pki/libvirt
    sudo cp serverkey.pem /etc/pki/libvirt/private

Menjana Sijil Klien

cd ke dalam direktori ~/pki/libvirt/client.

  1. Hasilkan permintaan sijil (CSR).

    bash
    openssl req -out client.csr -new -newkey rsa:2048 -sha256 \
      -noenc -keyout clientkey.pem \
      -subj "/C=MY/ST=Negeri/L=Bandar/O=Libvirt-Project/CN=<nama-klien>"
  2. Cipta fail konfigurasi dengan maklumat berikut.

    ~/pki/libvirt/client/config
    basicConstraints=CA:FALSE
    keyUsage = digitalSignature, keyEncipherment
    extendedKeyUsage = clientAuth
  3. Tanda tangani sijil menggunakan sijil CA akar yang dijana sebelum ini.

    bash
    openssl x509 -in client.csr \
      -req -sha256 -out clientcert.pem \
      ... (salin baris opsyen selebihnya)
  4. Salin sijil dan kunci pelayan yang dihasilkan ke direktori berikut.

    bash
    cp clientcert.pem ~/.pki/libvirt
    cp clientkey.pem ~/.pki/libvirt
    sudo cp clientcert.pem /etc/pki/libvirt
    sudo cp clientkey.pem /etc/pki/libvirt/private

SPICE (TLS x509)

cd ke dalam direktori ~/pki/libvirt-spice.

  1. Hasilkan permintaan sijil (CSR).

    bash
    openssl req -out server.csr -new -newkey rsa:2048 -sha256 \
      -noenc -keyout server-key.pem \
      -subj "/C=MY/ST=Negeri/L=Bandar/O=Libvirt-Spice/CN=<pc.example.com>"
  2. Cipta fail konfigurasi dengan maklumat berikut.

    ~/pki/libvirt-spice/config
    basicConstraints=CA:FALSE
    keyUsage = digitalSignature
    subjectAltName = @alt_names
    ...
    (salin senarai nama alternatif yang terdapat pada fail konfig pelayan libvirt)
  3. Tanda tangani sijil menggunakan CA akar yang dijana sebelum ini.

  4. Salin sijil dan kunci pelayan yang dihasilkan ke direktori berikut.

    bash
    sudo cp server-cert.pem /etc/pki/libvirt-spice
    sudo cp server-key.pem /etc/pki/libvirt-spice

Konfigurasi GSSAPI/Kerberos

Libvirt (GSSAPI)

Menurut dokumentasi rasmi libvirt di pautan GSSAPI/Kerberos auth, hos perlu memiliki satu prinsipal iaitu libvirt/full.hostname@KERBEROS.REALM. Prinsipal ini perlu dieksport ke dalam keytab dan diletakkan di lokasi khusus, iaitu /etc/libvirt/krb5.tab.

Namun begitu, berdasarkan pengalaman saya, hanya meletakkan satu prinsipal ini mengakibatkan kegagalan proses autentikasi. Berikut ialah mesej ralat yang saya hadapi:

virtproxyd: authentication failed: Failed to start SASL negotiation: -13 (SASL(-13): authentication failure: GSSAPI Failure: gss_accept_sec>
virtproxyd: authentication failed: authentication failed
virtproxyd: Cannot recv data: Input/output error

Saya mendapati bahawa autentikasi hanya berjaya setelah saya menambah dua prinsipal berikut ke dalam fail krb5.tab.

  1. host/pc.example.com@EXAMPLE.COM
  2. libvirt/pc.example.com@EXAMPLE.COM
  • Tambah kunci untuk servis libvirt.

    bash
    sudo kadmin.local
    kadmin.local:  addprinc -randkey libvirt/pc.example.com
    No policy specified for libvirt/pc.example.com@EXAMPLE.COM; defaulting to no policy
    Principal "libvirt/pc.example.com@EXAMPLE.COM" created.
    kadmin.local:  ktadd -k /etc/libvirt/krb5.tab libvirt/pc.example.com
    Entry for principal libvirt/pc.example.com with kvno 2, encryption type aes256-cts-hmac-sha384-192 added to keytab WRFILE:/etc/libvirt/krb5.tab.
    Entry for principal libvirt/pc.example.com with kvno 2, encryption type aes128-cts-hmac-sha256-128 added to keytab WRFILE:/etc/libvirt/krb5.tab.
    
  • Semak kandungan keytab :

    bash
    sudo klist -kt /etc/libvirt/krb5.tab
    Keytab name: FILE:/etc/libvirt/krb5.tab
    KVNO Timestamp           Principal
    ---- ------------------- ------------------------------------------------------
       3 08/01/2025 11:46:03 host/pc.example.com@EXAMPLE.COM
       3 08/01/2025 11:46:03 host/pc.example.com@EXAMPLE.COM
       2 08/01/2025 11:57:17 libvirt/pc.example.com@EXAMPLE.COM
       2 08/01/2025 11:57:17 libvirt/pc.example.com@EXAMPLE.COM
    

SPICE (GSSAPI)

Autentikasi SASL melalui GSSAPI juga disokong oleh SPICE, akan tetapi proses konfigurasi ini lebih rumit berbanding libvirt. Dokumentasi yang tersedia banyak merujuk kepada penggunaan prinsipal qemu/full.hostname@KERBEROS.REALM, yang ternyata tidak berfungsi dalam kes saya.

Hanya selepas beberapa kali percubaan, barulah muncul ralat yang mendedahkan prinsipal sebenar yang diperlukan:

(remote-viewer): GSpice-CRITICAL : Failed to start SASL negotiation: -1 (SASL(-1): generic failure: GSSAPI Error: Unspecified GSS failure.
Minor code may provide more information (Server spice/pc.example.com@EXAMPLE.COM not found in Kerberos database))

Rupa-rupanya, SPICE menjangkakan prinsipal spice/FQDN@REALM, bukannya qemu.

Berikut ialah langkah-langkah yang saya ambil untuk menyelesaikan isu ini:

  1. Tambah prinsipal spice/pc.example.com dengan opsyen -randkey menggunakan kadmin.local.

  2. Eksport kunci prinsipal ini ke dalam fail /etc/qemu/krb5.tab.

Fail Konfigurasi TLS + SASL (GSSAPI) Libvirt

/etc/libvirt/virtproxyd.conf
listen_tls = 1
auth_tls = "sasl"
/etc/sasl2/libvirt.conf
mech_list: gssapi
keytab: /etc/libvirt/krb5.tab

Fail Konfigurasi TLS + SASL (GSSAPI) SPICE

/etc/libvirt/qemu.conf
spice_listen = "0.0.0.0"
spice_tls = 1
spice_sasl = 1
/etc/sasl2/qemu.conf
mech_list: gssapi
keytab: /etc/qemu/krb5.tab

Uji Sambungan

  1. Sebelum saya mengaktifkan sambungan supaya servis dan soket yang berkaitan bermula secara automatik semasa boot, saya hanya mulakan sahaja dahulu segalanya sebagai langkah menguji. Rujuk dokumentasi libvirt di Modular driver daemons: Switching to modular daemons untuk bacaan lanjut.

  2. Mulakan servis dan soket untuk set daemon berikut:

    bash
    for drv in qemu network nodedev storage
      do
        sudo systemctl start virt${drv}d{,-ro,-admin}.socket
      done
    sudo systemctl start virtproxyd{,-ro,-admin}.socket
    sudo systemctl start virtproxyd-tls.socket

    Dalam perintah baris mula-mula di atas, situasi saya cuma memerlukan pemacu hypervisor virtqemud, serta pemacu-pemacu kedua iaitu virtnetworkd, virtnodedevd dan virtstoraged. Oleh itu, saya hanya mengaktifkan daemon dan soket yang berkaitan dengan perkhidmatan berkenaan.

  3. kinit dan semak tiket:

    bash
    kinit <username>
    klist
    Ticket cache: KEYRING:persistent:1000:1000
    Default principal: <username>@EXAMPLE.COM
    Valid starting Expires Service principal 08/02/2025 22:36:06 08/03/2025 08:36:06 krbtgt/EXAMPLE.COM@EXAMPLE.COM renew until 08/09/2025 22:36:06
  4. Kemas kini fail konfig shell (i.e. .bashrc atau .zshrc) dengan menambah environment URI lalai untuk sambungan.

    ~/.zshrc
    export LIBVIRT_DEFAULT_URI="qemu://pc.example.com/system"
  5. source fail konfig shell.

    ~/.zshrc
    source ~/.zshrc
  6. Uji sambungan dengan arahan virsh dan semak URI sambungan dengan menaip uri. Arahan tersebut akan mengembalikan alamat yang ditetapkan dalam fail ~/.zshrc tadi:

    bash
    virsh
    Welcome to virsh, the virtualization interactive terminal.
    Type: 'help' for help with commands
    'quit' to quit
    virsh # uri qemu://pc.example.com/system

    Jika berjaya, semakan tiket seterusnya akan turut memaparkan prinsipal servis untuk libvirt :

    ...
    Valid starting       Expires              Service principal
    08/02/2025 22:45:12  08/03/2025 08:36:06  libvirt/pc.example.com@EXAMPLE.COM
            renew until 08/09/2025 22:36:06
    ...
    
  7. Semak status perkhidmatan jika timbul sebarang isu untuk melihat mesej ralat:

    bash
    sudo systemctl status <service-name>

Cipta VM

  • Demi ujian pantas, gunakan aplikasi GUI virt-manager untuk mencipta VM.

  • Opsyen untuk paparan spice dengan TLS sekiranya menggunakan virt-install :

    --graphics spice,listen=0.0.0.0,defaultMode=secure 
    

  • Jika mengubah terus XML domain:
    <graphics type='spice' autoport='yes' defaultMode='secure'>
      <listen type='address' address='0.0.0.0'/>
    </graphics>
    

  • Paparan grafik SPICE untuk mengakses GUI VM boleh didapatkan dengan arahan berikut:

    bash
    remote-viewer --spice-ca-file=/etc/pki/libvirt-spice/ca-cert.pem "spice://pc.example.com?tls-port=5900"
  • Setelah memastikan semuanya berjalan lancar, barulah saya aktifkan systemd untuk memulakan modular daemon bagi pemacu terlibat semasa sistem boot.

    bash
    for drv in qemu network nodedev storage
      do
        sudo systemctl unmask virt${drv}d.service
        sudo systemctl unmask virt${drv}d{,-ro,-admin}.socket
        sudo systemctl enable virt${drv}d.service
        sudo systemctl enable virt${drv}d{,-ro,-admin}.socket
      done
    
    sudo systemctl unmask virtproxyd.service
    sudo systemctl unmask virtproxyd{,-ro,-admin}.socket
    sudo systemctl enable virtproxyd.service
    sudo systemctl enable virtproxyd{,-ro,-admin}.socket
    
    sudo systemctl unmask virtproxyd-tls.socket
    sudo systemctl enable virtproxyd-tls.socket

Penyelesaian Masalah

  • Jika libvirt NAT gagal mendapatkan alamat DHCP, pastikan firewall tidak menyekat rangkaian virbr0 : Gunakan peraturan umum berikut untuk membenarkan trafik NAT dan DHCP:

    bash
    sudo ufw allow in on virbr0
    sudo ufw allow out on virbr0
    sudo ufw reload

    virbr0 ialah antara muka rangkaian maya (virtual bridge) yang digunakan oleh libvirt secara lalai untuk NAT.

  • Isu kebenaran ditolak melibatkan hak akses ke direktori sistem:
    Pastikan direktori /var/lib/libvirt/images dimiliki oleh pengguna dan kumpulan libvirt-qemu.

  • Jika berlaku ralat dalaman (’internal error’) semasa memulakan pemasangan VM yang menolak paparan grafik SPICE, cuba tukarkan paparan grafik kepada VNC terlebih dahulu. Yang penting, pemasangan berjalan dan VM-nya wujud. Kemudian, bolehlah sunting perincian VM tersebut untuk mendapatkan paparan grafik SPICE dengan autentikasi TLS + GSSAPI (SASL), baik melalui virt-manager, mahupun virsh.

Kesimpulan

Pendekatan modular daemons dalam konfigurasi libvirt yang digabungkan dengan sokongan TLS dan GSSAPI (Kerberos) menawarkan kelebihan dari aspek keselamatan dan pengasingan perkhidmatan.

Walau bagaimanapun, pelaksanaannya bukanlah sesuatu yang mudah memandangkan dokumentasi rasmi agak terpecah dan memerlukan pemahaman mendalam terhadap Kerberos, SASL, dan pengurusan TLS.

Semoga catatan ini dapat membantu pengguna lain yang sedang mencuba kaedah autentikasi selamat untuk libvirt.


Kali terakhir dikemaskini:

Tentang Blog & Penulis

Topik perbincangan dalam blog ini merangkumi Linux dan perisian sumber terbuka, Virtual Machine, serta Typesetting system.
Fokus semasa: Full Stack Development
Minat sampingan: Analisis Data
Bakat tersembunyi hamba (yang patutlah dipendam buat selama-lamanya): Menyanyi dan melukis.

Sumber dari Wallpaper Cave.