Scert/backend/internal/cli/ssh_key.go

119 lines
3.3 KiB
Go

package cli
import (
"errors"
"fmt"
"io"
"os"
"strings"
"github.com/spf13/cobra"
"git.pcux.cn/xuans/Scert/backend/internal/sshkey"
)
func newSSHKeyCommand() *cobra.Command {
command := &cobra.Command{Use: "ssh-key", Short: "管理 SSH 部署私钥"}
command.AddCommand(newSSHKeyListCommand(), newSSHKeyAddCommand(), newSSHKeyDeleteCommand())
return command
}
func newSSHKeyListCommand() *cobra.Command {
return &cobra.Command{Use: "list", Short: "查看 SSH 私钥元数据", RunE: func(cmd *cobra.Command, args []string) error {
service, err := openApplication(cmd)
if err != nil {
return err
}
defer service.Close()
items, err := service.SSHKeys.List(cmd.Context())
if err != nil {
return err
}
return printJSON(cmd, items)
}}
}
func newSSHKeyAddCommand() *cobra.Command {
var name, filePath, passphraseFile string
command := &cobra.Command{Use: "add", Short: "从文件安全录入 SSH 私钥", RunE: func(cmd *cobra.Command, args []string) error {
privateKey, err := readLimitedFile(filePath, sshkey.MaxPrivateKeyBytes)
if err != nil {
return fmt.Errorf("读取 SSH 私钥文件失败:%w", err)
}
passphrase := ""
if strings.TrimSpace(passphraseFile) != "" {
content, err := readLimitedFile(passphraseFile, 4096)
if err != nil {
return fmt.Errorf("读取 SSH 私钥口令文件失败:%w", err)
}
passphrase = strings.TrimRight(content, "\r\n")
}
service, err := openApplication(cmd)
if err != nil {
return err
}
defer service.Close()
item, err := service.SSHKeys.Create(cmd.Context(), name, privateKey, passphrase)
if err != nil {
return err
}
return printJSON(cmd, item)
}}
command.Flags().StringVar(&name, "name", "", "SSH 私钥名称")
command.Flags().StringVar(&filePath, "file", "", "SSH 私钥文件路径")
command.Flags().StringVar(&passphraseFile, "passphrase-file", "", "可选的私钥口令文件路径")
_ = command.MarkFlagRequired("name")
_ = command.MarkFlagRequired("file")
return command
}
func newSSHKeyDeleteCommand() *cobra.Command {
var id string
command := &cobra.Command{Use: "delete", Short: "删除未被服务器使用的 SSH 私钥", RunE: func(cmd *cobra.Command, args []string) error {
service, err := openApplication(cmd)
if err != nil {
return err
}
defer service.Close()
reference, err := service.SSHKeys.Reference(id)
if err != nil {
return err
}
used, err := service.Servers.IsPrivateKeyRefUsed(cmd.Context(), reference)
if err != nil {
return err
}
if used {
return errors.New("SSH 私钥仍被部署服务器使用,不能删除")
}
if err := service.SSHKeys.Delete(cmd.Context(), id); err != nil {
return err
}
fmt.Fprintf(cmd.OutOrStdout(), "SSH 私钥已删除:%s\n", id)
return nil
}}
command.Flags().StringVar(&id, "id", "", "SSH 私钥 ID")
_ = command.MarkFlagRequired("id")
return command
}
func readLimitedFile(path string, limit int) (string, error) {
path = strings.TrimSpace(path)
if path == "" {
return "", errors.New("文件路径不能为空")
}
file, err := os.Open(path)
if err != nil {
return "", err
}
defer file.Close()
content, err := io.ReadAll(io.LimitReader(file, int64(limit)+1))
if err != nil {
return "", err
}
if len(content) > limit {
return "", fmt.Errorf("文件不能超过 %d 字节", limit)
}
return string(content), nil
}