package system import ( "fmt" "os" "os/exec" "strings" "sunhpc/internal/config" ) // ConfigureSSH 配置 SSH 服务 // 参数: cfg - config.SSHConfig 结构体 // 返回: error - 配置错误 func ConfigureSSH(cfg config.SSHConfig) error { const sshdConfig = "/etc/ssh/sshd_config" // 读取现有配置 content, err := os.ReadFile(sshdConfig) if err != nil { return fmt.Errorf("读取 sshd_config 失败: %v", err) } // 备份原始配置 backupPath := sshdConfig + ".sunhpc.bak" if _, err := os.Stat(backupPath); os.IsNotExist(err) { if err := os.WriteFile(backupPath, content, 0644); err != nil { fmt.Printf("警告: 无法创建备份文件 %s: %v\n", backupPath, err) } } // 解析和修改配置 lines := strings.Split(string(content), "\n") newLines := make([]string, 0, len(lines)) configMap := make(map[string]bool) // 处理每一行 for _, line := range lines { trimmed := strings.TrimSpace(line) if trimmed == "" || strings.HasPrefix(trimmed, "#") { newLines = append(newLines, line) continue } parts := strings.Fields(trimmed) if len(parts) < 2 { newLines = append(newLines, line) continue } key := parts[0] configMap[key] = true // 根据配置更新 switch key { case "PermitRootLogin": if cfg.PermitRootLogin != "" { newLines = append(newLines, fmt.Sprintf("PermitRootLogin %s", cfg.PermitRootLogin)) } else { newLines = append(newLines, line) } case "PasswordAuthentication": if cfg.PasswordAuth != "" { newLines = append(newLines, fmt.Sprintf("PasswordAuthentication %s", cfg.PasswordAuth)) } else { newLines = append(newLines, line) } default: newLines = append(newLines, line) } } // 添加缺失的配置项 if cfg.PermitRootLogin != "" && !configMap["PermitRootLogin"] { newLines = append(newLines, fmt.Sprintf("PermitRootLogin %s", cfg.PermitRootLogin)) } if cfg.PasswordAuth != "" && !configMap["PasswordAuthentication"] { newLines = append(newLines, fmt.Sprintf("PasswordAuthentication %s", cfg.PasswordAuth)) } // 写入新配置 newContent := strings.Join(newLines, "\n") if err := os.WriteFile(sshdConfig, []byte(newContent), 0644); err != nil { return fmt.Errorf("写入 sshd_config 失败: %v", err) } // 测试配置语法 if err := testSSHDConfig(); err != nil { // 恢复备份 if backup, err := os.ReadFile(backupPath); err == nil { os.WriteFile(sshdConfig, backup, 0644) } return fmt.Errorf("SSH 配置语法错误: %v", err) } // 重启 SSH 服务 return restartSSHD() } // testSSHDConfig 测试 sshd 配置语法 func testSSHDConfig() error { cmd := exec.Command("sshd", "-t") cmd.Stderr = os.Stderr return cmd.Run() } // restartSSHD 重启 SSH 服务 func restartSSHD() error { // 尝试不同的服务管理器 serviceMgrs := []struct { name string args []string }{ {"systemctl", []string{"restart", "sshd"}}, {"systemctl", []string{"restart", "ssh"}}, {"service", []string{"sshd", "restart"}}, {"service", []string{"ssh", "restart"}}, } for _, mgr := range serviceMgrs { if _, err := exec.LookPath(mgr.name); err == nil { cmd := exec.Command(mgr.name, mgr.args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err == nil { return nil } } } return fmt.Errorf("无法重启 SSH 服务,请手动重启") } // AddSSHKey 添加 SSH 公钥到指定用户 func AddSSHKey(username, pubkey string) error { // 获取用户主目录 homeDir, err := getUserHomeDir(username) if err != nil { return err } sshDir := homeDir + "/.ssh" authKeys := sshDir + "/authorized_keys" // 创建 .ssh 目录 if err := os.MkdirAll(sshDir, 0700); err != nil { return err } // 追加公钥 f, err := os.OpenFile(authKeys, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) if err != nil { return err } defer f.Close() _, err = f.WriteString(pubkey + "\n") if err != nil { return err } // 修改所有权 return chownRecursive(sshDir, username) } // getUserHomeDir 获取用户主目录 func getUserHomeDir(username string) (string, error) { cmd := exec.Command("getent", "passwd", username) output, err := cmd.Output() if err != nil { return "", fmt.Errorf("用户 %s 不存在", username) } parts := strings.Split(strings.TrimSpace(string(output)), ":") if len(parts) >= 6 { return parts[5], nil } return "", fmt.Errorf("无法获取用户 %s 的主目录", username) } // chownRecursive 递归修改文件所有者 func chownRecursive(path, username string) error { cmd := exec.Command("chown", "-R", username+":"+username, path) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() }