feat: support ssh key passphrase

This commit is contained in:
Fu Diwei 2024-10-16 12:30:15 +08:00
parent 79dce124a7
commit 71f43c5bd4
8 changed files with 70 additions and 349 deletions

View File

@ -18,11 +18,12 @@ type ssh struct {
}
type sshAccess struct {
Host string `json:"host"`
Username string `json:"username"`
Password string `json:"password"`
Key string `json:"key"`
Port string `json:"port"`
Host string `json:"host"`
Port string `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
Key string `json:"key"`
KeyPassphrase string `json:"keyPassphrase"`
}
func NewSSH(option *DeployerOption) (Deployer, error) {
@ -45,6 +46,7 @@ func (s *ssh) Deploy(ctx context.Context) error {
if err := json.Unmarshal([]byte(s.option.Access), access); err != nil {
return err
}
// 连接
client, err := s.getClient(access)
if err != nil {
@ -57,7 +59,7 @@ func (s *ssh) Deploy(ctx context.Context) error {
// 执行前置命令
preCommand := getDeployString(s.option.DeployConfig, "preCommand")
if preCommand != "" {
err, stdout, stderr := s.sshExecCommand(client, preCommand)
stdout, stderr, err := s.sshExecCommand(client, preCommand)
if err != nil {
return fmt.Errorf("failed to run pre-command: %w, stdout: %s, stderr: %s", err, stdout, stderr)
}
@ -78,7 +80,7 @@ func (s *ssh) Deploy(ctx context.Context) error {
s.infos = append(s.infos, toStr("ssh上传私钥成功", nil))
// 执行命令
err, stdout, stderr := s.sshExecCommand(client, getDeployString(s.option.DeployConfig, "command"))
stdout, stderr, err := s.sshExecCommand(client, getDeployString(s.option.DeployConfig, "command"))
if err != nil {
return fmt.Errorf("failed to run command: %w, stdout: %s, stderr: %s", err, stdout, stderr)
}
@ -88,18 +90,19 @@ func (s *ssh) Deploy(ctx context.Context) error {
return nil
}
func (s *ssh) sshExecCommand(client *sshPkg.Client, command string) (error, string, string) {
func (s *ssh) sshExecCommand(client *sshPkg.Client, command string) (string, string, error) {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create ssh session: %w", err), "", ""
return "", "", fmt.Errorf("failed to create ssh session: %w", err)
}
defer session.Close()
var stdoutBuf bytes.Buffer
session.Stdout = &stdoutBuf
var stderrBuf bytes.Buffer
session.Stderr = &stderrBuf
err = session.Run(command)
return err, stdoutBuf.String(), stderrBuf.String()
return stdoutBuf.String(), stderrBuf.String(), err
}
func (s *ssh) upload(client *sshPkg.Client, content, path string) error {
@ -131,7 +134,15 @@ func (s *ssh) getClient(access *sshAccess) (*sshPkg.Client, error) {
var authMethod sshPkg.AuthMethod
if access.Key != "" {
signer, err := sshPkg.ParsePrivateKey([]byte(access.Key))
var signer sshPkg.Signer
var err error
if access.KeyPassphrase != "" {
signer, err = sshPkg.ParsePrivateKeyWithPassphrase([]byte(access.Key), []byte(access.KeyPassphrase))
} else {
signer, err = sshPkg.ParsePrivateKey([]byte(access.Key))
}
if err != nil {
return nil, err
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

6
ui/dist/index.html vendored
View File

@ -5,10 +5,10 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Certimate - Your Trusted SSL Automation Partner</title>
<script type="module" crossorigin src="/assets/index-DipHpsma.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CV_7sKTK.css">
<script type="module" crossorigin src="/assets/index-DIhd7QG6.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CV_7sKTK.css">
</head>
<body class="bg-background">
<div id="root"></div>
<div id="root"></div>
</body>
</html>

View File

@ -66,17 +66,21 @@ const AccessSSHForm = ({ data, op, onAfterReq }: AccessSSHFormProps) => {
.max(5, t("common.errmsg.string_max", { max: 5 })),
username: z
.string()
.min(1, "username.not.empty")
.min(1, "access.authorization.form.ssh_username.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
password: z
.string()
.min(0, "password.not.empty")
.min(0, "access.authorization.form.ssh_password.placeholder")
.max(64, t("common.errmsg.string_max", { max: 64 })),
key: z
.string()
.min(0, "access.authorization.form.ssh_key.placeholder")
.max(20480, t("common.errmsg.string_max", { max: 20480 })),
keyFile: z.any().optional(),
keyPassphrase: z
.string()
.min(0, "access.authorization.form.ssh_key_passphrase.placeholder")
.max(2048, t("common.errmsg.string_max", { max: 2048 })),
});
let config: SSHConfig = {
@ -86,6 +90,7 @@ const AccessSSHForm = ({ data, op, onAfterReq }: AccessSSHFormProps) => {
password: "",
key: "",
keyFile: "",
keyPassphrase: "",
};
if (data) config = data.config as SSHConfig;
@ -102,6 +107,7 @@ const AccessSSHForm = ({ data, op, onAfterReq }: AccessSSHFormProps) => {
password: config.password,
key: config.key,
keyFile: config.keyFile,
keyPassphrase: config.keyPassphrase,
},
});
@ -121,6 +127,7 @@ const AccessSSHForm = ({ data, op, onAfterReq }: AccessSSHFormProps) => {
username: data.username,
password: data.password,
key: data.key,
keyPassphrase: data.keyPassphrase,
},
};
@ -322,9 +329,9 @@ const AccessSSHForm = ({ data, op, onAfterReq }: AccessSSHFormProps) => {
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>{t("access.authorization.form.username.label")}</FormLabel>
<FormLabel>{t("access.authorization.form.ssh_username.label")}</FormLabel>
<FormControl>
<Input placeholder={t("access.authorization.form.username.placeholder")} {...field} />
<Input placeholder={t("access.authorization.form.ssh_username.placeholder")} {...field} />
</FormControl>
<FormMessage />
@ -337,9 +344,9 @@ const AccessSSHForm = ({ data, op, onAfterReq }: AccessSSHFormProps) => {
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>{t("access.authorization.form.password.label")}</FormLabel>
<FormLabel>{t("access.authorization.form.ssh_password.label")}</FormLabel>
<FormControl>
<Input placeholder={t("access.authorization.form.password.placeholder")} {...field} type="password" />
<Input placeholder={t("access.authorization.form.ssh_password.placeholder")} {...field} type="password" />
</FormControl>
<FormMessage />
@ -390,6 +397,21 @@ const AccessSSHForm = ({ data, op, onAfterReq }: AccessSSHFormProps) => {
)}
/>
<FormField
control={form.control}
name="keyPassphrase"
render={({ field }) => (
<FormItem>
<FormLabel>{t("access.authorization.form.ssh_key_passphrase.label")}</FormLabel>
<FormControl>
<Input placeholder={t("access.authorization.form.ssh_key_passphrase.placeholder")} {...field} type="password" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormMessage />
<div className="flex justify-end">

View File

@ -100,6 +100,7 @@ export type SSHConfig = {
password?: string;
key?: string;
keyFile?: string;
keyPassphrase?: string;
};
export type WebhookConfig = {

View File

@ -46,9 +46,15 @@
"access.authorization.form.ssh_host.placeholder": "Please enter Host",
"access.authorization.form.ssh_port.label": "SSH Port",
"access.authorization.form.ssh_port.placeholder": "Please enter Port",
"access.authorization.form.ssh_key.label": "Key (Log in using private key)",
"access.authorization.form.ssh_username.label": "Username",
"access.authorization.form.ssh_username.placeholder": "Please enter username",
"access.authorization.form.ssh_password.label": "Password (Log-in using password)",
"access.authorization.form.ssh_password.placeholder": "Please enter password",
"access.authorization.form.ssh_key.label": "Key (Log-in using private key)",
"access.authorization.form.ssh_key.placeholder": "Please enter Key",
"access.authorization.form.ssh_key_file.placeholder": "Please select file",
"access.authorization.form.ssh_key_passphrase.label": "Key Passphrase (Log-in using private key)",
"access.authorization.form.ssh_key_passphrase.placeholder": "Please enter Key Passphrase",
"access.authorization.form.ssh_key_path.label": "Private Key Save Path",
"access.authorization.form.ssh_key_path.placeholder": "Please enter private key save path",
"access.authorization.form.ssh_cert_path.label": "Certificate Save Path",

View File

@ -46,10 +46,15 @@
"access.authorization.form.ssh_host.placeholder": "请输入 Host",
"access.authorization.form.ssh_port.label": "SSH 端口",
"access.authorization.form.ssh_port.placeholder": "请输入 Port",
"access.authorization.form.ssh_username.label": "用户名",
"access.authorization.form.ssh_username.placeholder": "请输入用户名",
"access.authorization.form.ssh_password.label": "密码(使用密码登录)",
"access.authorization.form.ssh_password.placeholder": "请输入密码",
"access.authorization.form.ssh_key.label": "Key使用私钥登录",
"access.authorization.form.ssh_key.placeholder": "请输入 Key",
"access.authorization.form.ssh_key_file.placeholder": "请选择文件",
"access.authorization.form.ssh_key.label.passphrase": "私钥密码",
"access.authorization.form.ssh_key_passphrase.label": "Key 口令(使用私钥登录)",
"access.authorization.form.ssh_key_passphrase.placeholder": "请输入 Key 口令",
"access.authorization.form.ssh_key_path.label": "私钥保存路径",
"access.authorization.form.ssh_key_path.placeholder": "请输入私钥保存路径",
"access.authorization.form.ssh_cert_path.label": "证书保存路径",