188 lines
4.6 KiB
Go
188 lines
4.6 KiB
Go
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()
|
|
}
|