From 73bd6934f9ad5b4420a04e1576a8108f4861fd41 Mon Sep 17 00:00:00 2001 From: MengMengCode <227010654+MengMengCode@users.noreply.github.com> Date: Wed, 10 Jun 2026 11:12:27 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=A2=84=E8=AE=BE=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E4=BB=A5=E5=8F=8AKEY=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/kvm/kvm.go | 29 +++++++++++++++++++---------- backend/internal/lxc/lxc.go | 25 ++++++++++++++++--------- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/backend/internal/kvm/kvm.go b/backend/internal/kvm/kvm.go index 6ad8635..7eabacf 100644 --- a/backend/internal/kvm/kvm.go +++ b/backend/internal/kvm/kvm.go @@ -407,6 +407,7 @@ func (m *Manager) defineContainer(id int, vmName string, cfg lxc.ContainerConfig mac := randomMAC() sshPassword := generateRandomString(16) sshPublicKey := "" + sshAuthMode := "" if !IsWindowsImage(image.ID) { sshAccess, err := lxc.ResolveCreateSSHAccess(cfg) if err != nil { @@ -414,6 +415,7 @@ func (m *Manager) defineContainer(id int, vmName string, cfg lxc.ContainerConfig } sshPassword = sshAccess.Password sshPublicKey = sshAccess.PublicKey + sshAuthMode = sshAccess.Mode } publicIPv4s, err := lxc.AllocatePublicIPv4Assignments(id, cfg.PublicIPv4s, cfg.IPv4Count, cfg.AssignIPv4) if err != nil { @@ -465,7 +467,7 @@ func (m *Manager) defineContainer(id int, vmName string, cfg lxc.ContainerConfig if err := createOverlayDisk(ImagePath(image.ID), diskPath, cfg.DiskGB); err != nil { return nil, err } - if err := createSeedISO(seedPath, vmName, cfg.Name, sshPassword, sshPublicKey, mac, ipv6List, ipv4List, *image); err != nil { + if err := createSeedISO(seedPath, vmName, cfg.Name, sshPassword, sshPublicKey, mac, ipv6List, ipv4List, *image, sshAuthMode); err != nil { return nil, err } xml = domainXML(vmName, int(cfg.VCPU), cfg.RAMMB, diskPath, seedPath, mac, cfg.IOSpeedMBps, cfg.NetworkBWMbps, image.Desktop != "") @@ -1853,8 +1855,9 @@ func shellQuoteWindows(value string) string { return `"` + strings.ReplaceAll(value, `"`, `\"`) + `"` } -func createSeedISO(seedPath, instanceID, hostname, password, publicKey, mac string, ipv6s []string, ipv4s []string, image Image) error { - guestSetup := kvmSSHSetupScript(password, publicKey) +func createSeedISO(seedPath, instanceID, hostname, password, publicKey, mac string, ipv6s []string, ipv4s []string, image Image, sshAuthMode string) error { + disablePubkey := sshAuthMode == "password" || sshAuthMode == "auto_password" + guestSetup := kvmSSHSetupScript(password, disablePubkey, publicKey) if desktopSetup := kvmDesktopSetupScript(image); desktopSetup != "" { guestSetup += "\n" + desktopSetup } @@ -2432,11 +2435,11 @@ func runKVMGuestAgentSSHSetup(name string, password string) error { if err := qemuGuestPing(name); err != nil { return err } - return qemuGuestExec(name, kvmSSHSetupScript(password), 180*time.Second) + return qemuGuestExec(name, kvmSSHSetupScript(password, false), 180*time.Second) } func runKVMSSHSetup(client *ssh.Client, password string) error { - return runKVMSSHScript(client, kvmSSHSetupScript(password), "KVM SSH", 150*time.Second) + return runKVMSSHScript(client, kvmSSHSetupScript(password, false), "KVM SSH", 150*time.Second) } func runKVMSSHScript(client *ssh.Client, script string, description string, timeout time.Duration) error { @@ -2465,12 +2468,16 @@ func runKVMSSHScript(client *ssh.Client, script string, description string, time } } -func kvmSSHSetupScript(password string, publicKeys ...string) string { +func kvmSSHSetupScript(password string, disablePubkeyAuth bool, publicKeys ...string) string { publicKey := "" if len(publicKeys) > 0 { publicKey = strings.TrimSpace(publicKeys[0]) } - return `set -u + pubkeyValue := "yes" + if disablePubkeyAuth { + pubkeyValue = "no" + } + script := `set -u ROOT_PASSWORD=` + shellQuote(password) + ` SSH_PUBLIC_KEY=` + shellQuote(publicKey) + ` export DEBIAN_FRONTEND=noninteractive @@ -2499,7 +2506,7 @@ fi mkdir -p /etc/ssh/sshd_config.d cat > /etc/ssh/sshd_config.d/99-clicd-root.conf <<'EOF' PermitRootLogin yes -PubkeyAuthentication yes +PubkeyAuthentication __CLICD_PUBKEY_AUTH__ PasswordAuthentication yes KbdInteractiveAuthentication yes ChallengeResponseAuthentication yes @@ -2507,8 +2514,8 @@ EOF if [ -f /etc/ssh/sshd_config ]; then grep -q '^PermitRootLogin ' /etc/ssh/sshd_config && sed -i 's/^PermitRootLogin .*/PermitRootLogin yes/' /etc/ssh/sshd_config || printf '\nPermitRootLogin yes\n' >> /etc/ssh/sshd_config grep -q '^#PermitRootLogin ' /etc/ssh/sshd_config && sed -i 's/^#PermitRootLogin .*/PermitRootLogin yes/' /etc/ssh/sshd_config || true - grep -q '^PubkeyAuthentication ' /etc/ssh/sshd_config && sed -i 's/^PubkeyAuthentication .*/PubkeyAuthentication yes/' /etc/ssh/sshd_config || printf '\nPubkeyAuthentication yes\n' >> /etc/ssh/sshd_config - grep -q '^#PubkeyAuthentication ' /etc/ssh/sshd_config && sed -i 's/^#PubkeyAuthentication .*/PubkeyAuthentication yes/' /etc/ssh/sshd_config || true + grep -q '^PubkeyAuthentication ' /etc/ssh/sshd_config && sed -i 's/^PubkeyAuthentication .*/PubkeyAuthentication __CLICD_PUBKEY_AUTH__/' /etc/ssh/sshd_config || printf '\nPubkeyAuthentication __CLICD_PUBKEY_AUTH__\n' >> /etc/ssh/sshd_config + grep -q '^#PubkeyAuthentication ' /etc/ssh/sshd_config && sed -i 's/^#PubkeyAuthentication .*/PubkeyAuthentication __CLICD_PUBKEY_AUTH__/' /etc/ssh/sshd_config || true grep -q '^PasswordAuthentication ' /etc/ssh/sshd_config && sed -i 's/^PasswordAuthentication .*/PasswordAuthentication yes/' /etc/ssh/sshd_config || printf '\nPasswordAuthentication yes\n' >> /etc/ssh/sshd_config grep -q '^#PasswordAuthentication ' /etc/ssh/sshd_config && sed -i 's/^#PasswordAuthentication .*/PasswordAuthentication yes/' /etc/ssh/sshd_config || true grep -q '^KbdInteractiveAuthentication ' /etc/ssh/sshd_config && sed -i 's/^KbdInteractiveAuthentication .*/KbdInteractiveAuthentication yes/' /etc/ssh/sshd_config || printf '\nKbdInteractiveAuthentication yes\n' >> /etc/ssh/sshd_config @@ -2549,6 +2556,8 @@ if [ -w /dev/tty1 ]; then printf '\nCLICD VNC console is ready. Press Enter for login prompt.\n' >/dev/tty1 || true fi ` + script = strings.ReplaceAll(script, "__CLICD_PUBKEY_AUTH__", pubkeyValue) + return script } func kvmDesktopSetupScript(image Image) string { diff --git a/backend/internal/lxc/lxc.go b/backend/internal/lxc/lxc.go index a9eeb98..810f64f 100644 --- a/backend/internal/lxc/lxc.go +++ b/backend/internal/lxc/lxc.go @@ -424,7 +424,7 @@ func (m *Manager) CreateContainer(cfg ContainerConfig) error { fmt.Printf("Warning: failed to install IPv6 init in %s: %v\n", lxcName, err) } } - if err := m.preconfigureSSH(rootfsPath, cfg.TemplateID); err != nil { + if err := m.preconfigureSSH(rootfsPath, cfg.TemplateID, sshAccess.Mode); err != nil { fmt.Printf("Warning: failed to pre-configure SSH in %s: %v\n", lxcName, err) } if sshAccess.PublicKey != "" { @@ -512,11 +512,13 @@ IPv6AcceptRA=no } // preconfigureSSH installs and configures SSH directly in the rootfs before first boot. -func (m *Manager) preconfigureSSH(rootfsPath, templateID string) error { +func (m *Manager) preconfigureSSH(rootfsPath, templateID string, sshAuthMode string) error { _ = templateID + // Disable pubkey auth when user chose password-only mode (password or auto_password) + disablePubkey := sshAuthMode == SSHAuthPassword || sshAuthMode == SSHAuthAutoPassword ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second) defer cancel() - cmd, err := m.rootfsCommand(rootfsPath, "sh", "-c", sshSetupScript(false)) + cmd, err := m.rootfsCommand(rootfsPath, "sh", "-c", sshSetupScript(false, disablePubkey)) if err != nil { return err } @@ -1758,7 +1760,7 @@ func (m *Manager) EnsureSSH(id int) error { config.SaveConfig() } - script := sshSetupScript(true) + script := sshSetupScript(true, false) // keep pubkey enabled for runtime ensure ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second) defer cancel() @@ -1826,7 +1828,11 @@ func (m *Manager) containerPortListening(lxcName string, port int) bool { return exec.CommandContext(ctx, "lxc-attach", "-n", lxcName, "--", "sh", "-c", check).Run() == nil } -func sshSetupScript(startService bool) string { +func sshSetupScript(startService bool, disablePubkeyAuth bool) string { + pubkeyValue := "yes" + if disablePubkeyAuth { + pubkeyValue = "no" + } script := `set -u # DNS setup: handle both traditional /etc/resolv.conf and systemd-resolved (Ubuntu 24.04). @@ -1945,7 +1951,7 @@ ssh-keygen -A >/dev/null 2>&1 || true cat >/etc/ssh/sshd_config.d/99-clicd.conf <<'EOF' PermitRootLogin yes -PubkeyAuthentication yes +PubkeyAuthentication __CLICD_PUBKEY_AUTH__ PasswordAuthentication yes KbdInteractiveAuthentication no ChallengeResponseAuthentication no @@ -1953,7 +1959,7 @@ UsePAM no EOF set_sshd_option PermitRootLogin yes -set_sshd_option PubkeyAuthentication yes +set_sshd_option PubkeyAuthentication __CLICD_PUBKEY_AUTH__ set_sshd_option PasswordAuthentication yes set_sshd_option KbdInteractiveAuthentication no set_sshd_option ChallengeResponseAuthentication no @@ -1981,6 +1987,7 @@ ensure_sshd_runtime_dir exit 32 } ` + script = strings.ReplaceAll(script, "__CLICD_PUBKEY_AUTH__", pubkeyValue) if !startService { return script } @@ -2054,7 +2061,7 @@ func (m *Manager) ResetSSHPassword(id int, password string) (string, error) { return "", err } rootfsPath := filepath.Join(m.LxcPath, lxcName, "rootfs") - if err := m.preconfigureSSH(rootfsPath, c.Template); err != nil { + if err := m.preconfigureSSH(rootfsPath, c.Template, ""); err != nil { return "", fmt.Errorf("failed to configure SSH: %v", err) } if err := m.setRootfsPassword(rootfsPath, newPassword); err != nil { @@ -2626,7 +2633,7 @@ func (m *Manager) ReinstallContainer(id int, templateID string, authConfig ...Co } } c.SSHPassword = sshAccess.Password - if err := m.preconfigureSSH(rootfsPath, templateID); err != nil { + if err := m.preconfigureSSH(rootfsPath, templateID, sshAccess.Mode); err != nil { fmt.Printf("Warning: failed to pre-configure SSH in %s after reinstall: %v\n", lxcName, err) } if sshAccess.PublicKey != "" {