添加新建虚拟机/重装系统 预设SSH密码以及KEY Auth功能

This commit is contained in:
MengMengCode
2026-06-09 23:25:19 +08:00
parent 9a826add87
commit 0c9f420474
12 changed files with 712 additions and 91 deletions
+71 -5
View File
@@ -241,6 +241,9 @@ type ContainerConfig struct {
AssignIPv6 bool `json:"assign_ipv6"`
IPv6Count int `json:"ipv6_count,omitempty"`
IPv6Addresses []string `json:"ipv6_addresses,omitempty"`
SSHAuthMode string `json:"ssh_auth_mode,omitempty"`
SSHPassword string `json:"ssh_password,omitempty"`
SSHPublicKey string `json:"ssh_public_key,omitempty"`
ExpiresAt string `json:"expires_at"`
}
@@ -270,6 +273,10 @@ func (m *Manager) CreateContainer(cfg ContainerConfig) error {
if config.FindContainerByName(cfg.Name) != nil {
return fmt.Errorf("container name already exists: %s", cfg.Name)
}
sshAccess, err := ResolveCreateSSHAccess(cfg)
if err != nil {
return err
}
// Allocate ID and build LXC name
id := config.AllocateContainerID()
@@ -329,7 +336,7 @@ func (m *Manager) CreateContainer(cfg ContainerConfig) error {
}
}
sshPassword := generateRandomString(16)
sshPassword := sshAccess.Password
sshPort := 0
portMappings := []config.PortMapping{}
@@ -420,6 +427,13 @@ func (m *Manager) CreateContainer(cfg ContainerConfig) error {
if err := m.preconfigureSSH(rootfsPath, cfg.TemplateID); err != nil {
fmt.Printf("Warning: failed to pre-configure SSH in %s: %v\n", lxcName, err)
}
if sshAccess.PublicKey != "" {
if err := m.installRootAuthorizedKey(rootfsPath, sshAccess.PublicKey); err != nil {
_ = m.cleanupContainerStorage(lxcName)
config.RemoveContainer(id)
return fmt.Errorf("failed to install SSH public key: %v", err)
}
}
if err := m.shiftRootfsForUnprivileged(lxcName); err != nil {
_ = m.cleanupContainerStorage(lxcName)
@@ -1931,6 +1945,7 @@ ssh-keygen -A >/dev/null 2>&1 || true
cat >/etc/ssh/sshd_config.d/99-clicd.conf <<'EOF'
PermitRootLogin yes
PubkeyAuthentication yes
PasswordAuthentication yes
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
@@ -1938,6 +1953,7 @@ UsePAM no
EOF
set_sshd_option PermitRootLogin yes
set_sshd_option PubkeyAuthentication yes
set_sshd_option PasswordAuthentication yes
set_sshd_option KbdInteractiveAuthentication no
set_sshd_option ChallengeResponseAuthentication no
@@ -2102,6 +2118,45 @@ func (m *Manager) setRootfsPassword(rootfsPath, password string) error {
return nil
}
func (m *Manager) installRootAuthorizedKey(rootfsPath, publicKey string) error {
key, err := NormalizeSSHPublicKey(publicKey)
if err != nil {
return err
}
if key == "" {
return nil
}
sshDir := filepath.Join(rootfsPath, "root", ".ssh")
if err := os.MkdirAll(sshDir, 0700); err != nil {
return err
}
authPath := filepath.Join(sshDir, "authorized_keys")
existing, _ := os.ReadFile(authPath)
lines := strings.Split(string(existing), "\n")
for _, line := range lines {
if strings.TrimSpace(line) == key {
_ = os.Chmod(sshDir, 0700)
_ = os.Chmod(authPath, 0600)
_ = os.Chown(sshDir, 0, 0)
_ = os.Chown(authPath, 0, 0)
return nil
}
}
content := strings.TrimRight(string(existing), "\r\n")
if content != "" {
content += "\n"
}
content += key + "\n"
if err := os.WriteFile(authPath, []byte(content), 0600); err != nil {
return err
}
_ = os.Chmod(sshDir, 0700)
_ = os.Chmod(authPath, 0600)
_ = os.Chown(sshDir, 0, 0)
_ = os.Chown(authPath, 0, 0)
return nil
}
func safeRootfsCommandArgs(args []string) ([]string, error) {
if len(args) == 0 {
return nil, fmt.Errorf("empty rootfs command")
@@ -2499,7 +2554,7 @@ func copyRootfsContents(src, dst string) error {
}
// ReinstallContainer reinstalls the container OS
func (m *Manager) ReinstallContainer(id int, templateID string) error {
func (m *Manager) ReinstallContainer(id int, templateID string, authConfig ...ContainerConfig) error {
c := config.FindContainer(id)
if c == nil {
return fmt.Errorf("container not found: %d", id)
@@ -2509,6 +2564,14 @@ func (m *Manager) ReinstallContainer(id int, templateID string) error {
if tmpl == nil {
return fmt.Errorf("template not found: %s", templateID)
}
authCfg := ContainerConfig{SSHAuthMode: SSHAuthKeep}
if len(authConfig) > 0 {
authCfg = authConfig[0]
}
sshAccess, err := ResolveReinstallSSHAccess(c.SSHPassword, authCfg)
if err != nil {
return err
}
lxcName := c.LxcName()
@@ -2562,12 +2625,15 @@ func (m *Manager) ReinstallContainer(id int, templateID string) error {
fmt.Printf("Warning: failed to install IPv6 init in %s after reinstall: %v\n", lxcName, err)
}
}
if c.SSHPassword == "" {
c.SSHPassword = generateRandomString(16)
}
c.SSHPassword = sshAccess.Password
if err := m.preconfigureSSH(rootfsPath, templateID); err != nil {
fmt.Printf("Warning: failed to pre-configure SSH in %s after reinstall: %v\n", lxcName, err)
}
if sshAccess.PublicKey != "" {
if err := m.installRootAuthorizedKey(rootfsPath, sshAccess.PublicKey); err != nil {
return fmt.Errorf("failed to install SSH public key: %v", err)
}
}
if err := m.shiftRootfsForUnprivileged(lxcName); err != nil {
return err
}