mirror of
https://github.com/usual2970/certimate.git
synced 2025-06-07 21:19:51 +00:00
feat: support ssh challenge-response
This commit is contained in:
parent
7d55383cf7
commit
9ad0e6fb57
@ -997,6 +997,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
|
|||||||
jumpServers[i] = pSSH.JumpServerConfig{
|
jumpServers[i] = pSSH.JumpServerConfig{
|
||||||
SshHost: jumpServer.Host,
|
SshHost: jumpServer.Host,
|
||||||
SshPort: jumpServer.Port,
|
SshPort: jumpServer.Port,
|
||||||
|
SshAuthMethod: jumpServer.AuthMethod,
|
||||||
SshUsername: jumpServer.Username,
|
SshUsername: jumpServer.Username,
|
||||||
SshPassword: jumpServer.Password,
|
SshPassword: jumpServer.Password,
|
||||||
SshKey: jumpServer.Key,
|
SshKey: jumpServer.Key,
|
||||||
@ -1007,6 +1008,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
|
|||||||
deployer, err := pSSH.NewDeployer(&pSSH.DeployerConfig{
|
deployer, err := pSSH.NewDeployer(&pSSH.DeployerConfig{
|
||||||
SshHost: access.Host,
|
SshHost: access.Host,
|
||||||
SshPort: access.Port,
|
SshPort: access.Port,
|
||||||
|
SshAuthMethod: access.AuthMethod,
|
||||||
SshUsername: access.Username,
|
SshUsername: access.Username,
|
||||||
SshPassword: access.Password,
|
SshPassword: access.Password,
|
||||||
SshKey: access.Key,
|
SshKey: access.Key,
|
||||||
|
@ -315,14 +315,16 @@ type AccessConfigForSlackBot struct {
|
|||||||
type AccessConfigForSSH struct {
|
type AccessConfigForSSH struct {
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
Port int32 `json:"port"`
|
Port int32 `json:"port"`
|
||||||
Username string `json:"username"`
|
AuthMethod string `json:"authMethod,omitempty"`
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
Key string `json:"key,omitempty"`
|
Key string `json:"key,omitempty"`
|
||||||
KeyPassphrase string `json:"keyPassphrase,omitempty"`
|
KeyPassphrase string `json:"keyPassphrase,omitempty"`
|
||||||
JumpServers []struct {
|
JumpServers []struct {
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
Port int32 `json:"port"`
|
Port int32 `json:"port"`
|
||||||
Username string `json:"username"`
|
AuthMethod string `json:"authMethod,omitempty"`
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
Key string `json:"key,omitempty"`
|
Key string `json:"key,omitempty"`
|
||||||
KeyPassphrase string `json:"keyPassphrase,omitempty"`
|
KeyPassphrase string `json:"keyPassphrase,omitempty"`
|
||||||
|
@ -24,7 +24,12 @@ type JumpServerConfig struct {
|
|||||||
// SSH 端口。
|
// SSH 端口。
|
||||||
// 零值时默认值 22。
|
// 零值时默认值 22。
|
||||||
SshPort int32 `json:"sshPort,omitempty"`
|
SshPort int32 `json:"sshPort,omitempty"`
|
||||||
|
// SSH 认证方式。
|
||||||
|
// 可取值 "none"、"password"、"key"。
|
||||||
|
// 零值时根据有无密码或私钥字段决定。
|
||||||
|
SshAuthMethod string `json:"sshAuthMethod,omitempty"`
|
||||||
// SSH 登录用户名。
|
// SSH 登录用户名。
|
||||||
|
// 零值时默认值 "root"。
|
||||||
SshUsername string `json:"sshUsername,omitempty"`
|
SshUsername string `json:"sshUsername,omitempty"`
|
||||||
// SSH 登录密码。
|
// SSH 登录密码。
|
||||||
SshPassword string `json:"sshPassword,omitempty"`
|
SshPassword string `json:"sshPassword,omitempty"`
|
||||||
@ -41,7 +46,12 @@ type DeployerConfig struct {
|
|||||||
// SSH 端口。
|
// SSH 端口。
|
||||||
// 零值时默认值 22。
|
// 零值时默认值 22。
|
||||||
SshPort int32 `json:"sshPort,omitempty"`
|
SshPort int32 `json:"sshPort,omitempty"`
|
||||||
|
// SSH 认证方式。
|
||||||
|
// 可取值 "none"、"password" 或 "key"。
|
||||||
|
// 零值时根据有无密码或私钥字段决定。
|
||||||
|
SshAuthMethod string `json:"sshAuthMethod,omitempty"`
|
||||||
// SSH 登录用户名。
|
// SSH 登录用户名。
|
||||||
|
// 零值时默认值 "root"。
|
||||||
SshUsername string `json:"sshUsername,omitempty"`
|
SshUsername string `json:"sshUsername,omitempty"`
|
||||||
// SSH 登录密码。
|
// SSH 登录密码。
|
||||||
SshPassword string `json:"sshPassword,omitempty"`
|
SshPassword string `json:"sshPassword,omitempty"`
|
||||||
@ -141,6 +151,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
jumpConn,
|
jumpConn,
|
||||||
jumpServerConf.SshHost,
|
jumpServerConf.SshHost,
|
||||||
jumpServerConf.SshPort,
|
jumpServerConf.SshPort,
|
||||||
|
jumpServerConf.SshAuthMethod,
|
||||||
jumpServerConf.SshUsername,
|
jumpServerConf.SshUsername,
|
||||||
jumpServerConf.SshPassword,
|
jumpServerConf.SshPassword,
|
||||||
jumpServerConf.SshKey,
|
jumpServerConf.SshKey,
|
||||||
@ -174,6 +185,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
targetConn,
|
targetConn,
|
||||||
d.config.SshHost,
|
d.config.SshHost,
|
||||||
d.config.SshPort,
|
d.config.SshPort,
|
||||||
|
d.config.SshAuthMethod,
|
||||||
d.config.SshUsername,
|
d.config.SshUsername,
|
||||||
d.config.SshPassword,
|
d.config.SshPassword,
|
||||||
d.config.SshKey,
|
d.config.SshKey,
|
||||||
@ -262,7 +274,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
|||||||
return &deployer.DeployResult{}, nil
|
return &deployer.DeployResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSshClient(conn net.Conn, host string, port int32, username string, password string, key string, keyPassphrase string) (*ssh.Client, error) {
|
func createSshClient(conn net.Conn, host string, port int32, authMethod string, username, password, key, keyPassphrase string) (*ssh.Client, error) {
|
||||||
if host == "" {
|
if host == "" {
|
||||||
host = "localhost"
|
host = "localhost"
|
||||||
}
|
}
|
||||||
@ -271,28 +283,65 @@ func createSshClient(conn net.Conn, host string, port int32, username string, pa
|
|||||||
port = 22
|
port = 22
|
||||||
}
|
}
|
||||||
|
|
||||||
var authMethod ssh.AuthMethod
|
if username == "" {
|
||||||
if key != "" {
|
username = "root"
|
||||||
var signer ssh.Signer
|
}
|
||||||
var err error
|
|
||||||
|
|
||||||
if keyPassphrase != "" {
|
const AUTH_METHOD_NONE = "none"
|
||||||
signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(key), []byte(keyPassphrase))
|
const AUTH_METHOD_PASSWORD = "password"
|
||||||
|
const AUTH_METHOD_KEY = "key"
|
||||||
|
if authMethod == "" {
|
||||||
|
if key != "" {
|
||||||
|
authMethod = AUTH_METHOD_KEY
|
||||||
|
} else if password != "" {
|
||||||
|
authMethod = AUTH_METHOD_PASSWORD
|
||||||
} else {
|
} else {
|
||||||
signer, err = ssh.ParsePrivateKey([]byte(key))
|
authMethod = AUTH_METHOD_NONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
authentications := make([]ssh.AuthMethod, 0)
|
||||||
|
switch authMethod {
|
||||||
|
case AUTH_METHOD_NONE:
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
case AUTH_METHOD_PASSWORD:
|
||||||
return nil, err
|
{
|
||||||
|
authentications = append(authentications, ssh.Password(password))
|
||||||
|
authentications = append(authentications, ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) ([]string, error) {
|
||||||
|
if len(questions) == 1 {
|
||||||
|
return []string{password}, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unexpected keyboard interactive question: %s", questions[0])
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
authMethod = ssh.PublicKeys(signer)
|
|
||||||
} else {
|
case AUTH_METHOD_KEY:
|
||||||
authMethod = ssh.Password(password)
|
{
|
||||||
|
var signer ssh.Signer
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if keyPassphrase != "" {
|
||||||
|
signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(key), []byte(keyPassphrase))
|
||||||
|
} else {
|
||||||
|
signer, err = ssh.ParsePrivateKey([]byte(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
authentications = append(authentications, ssh.PublicKeys(signer))
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported auth method '%s'", authMethod)
|
||||||
}
|
}
|
||||||
|
|
||||||
sshConn, chans, reqs, err := ssh.NewClientConn(conn, fmt.Sprintf("%s:%d", host, port), &ssh.ClientConfig{
|
sshConn, chans, reqs, err := ssh.NewClientConn(conn, fmt.Sprintf("%s:%d", host, port), &ssh.ClientConfig{
|
||||||
User: username,
|
User: username,
|
||||||
Auth: []ssh.AuthMethod{authMethod},
|
Auth: authentications,
|
||||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
62
migrations/1748959200_upgrade.go
Normal file
62
migrations/1748959200_upgrade.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pocketbase/pocketbase/core"
|
||||||
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
m.Register(func(app core.App) error {
|
||||||
|
tracer := NewTracer("(v0.3)1748959200")
|
||||||
|
tracer.Printf("go ...")
|
||||||
|
|
||||||
|
// migrate data
|
||||||
|
{
|
||||||
|
collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
records, err := app.FindAllRecords(collection)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, record := range records {
|
||||||
|
changed := false
|
||||||
|
|
||||||
|
if record.GetString("provider") == "ssh" {
|
||||||
|
config := make(map[string]any)
|
||||||
|
if err := record.UnmarshalJSONField("config", &config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if config["authMethod"] == nil || config["authMethod"] == "" {
|
||||||
|
if config["key"] != nil && config["key"] != "" {
|
||||||
|
config["authMethod"] = "key"
|
||||||
|
} else if config["password"] != nil && config["password"] != "" {
|
||||||
|
config["authMethod"] = "password"
|
||||||
|
} else {
|
||||||
|
config["authMethod"] = "none"
|
||||||
|
}
|
||||||
|
record.Set("config", config)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if changed {
|
||||||
|
if err := app.Save(record); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tracer.Printf("record #%s in collection '%s' updated", record.Id, collection.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tracer.Printf("done")
|
||||||
|
return nil
|
||||||
|
}, func(app core.App) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { ArrowDownOutlined, ArrowUpOutlined, CloseOutlined, PlusOutlined } from "@ant-design/icons";
|
import { ArrowDownOutlined, ArrowUpOutlined, CloseOutlined, PlusOutlined } from "@ant-design/icons";
|
||||||
import { Button, Collapse, Form, type FormInstance, Input, InputNumber, Space } from "antd";
|
import { Button, Collapse, Form, type FormInstance, Input, InputNumber, Select, Space } from "antd";
|
||||||
import { createSchemaFieldRule } from "antd-zod";
|
import { createSchemaFieldRule } from "antd-zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import Show from "@/components/Show";
|
||||||
import TextFileInput from "@/components/TextFileInput";
|
import TextFileInput from "@/components/TextFileInput";
|
||||||
import { type AccessConfigForSSH } from "@/domain/access";
|
import { type AccessConfigForSSH } from "@/domain/access";
|
||||||
import { validDomainName, validIPv4Address, validIPv6Address, validPortNumber } from "@/utils/validators";
|
import { validDomainName, validIPv4Address, validIPv6Address, validPortNumber } from "@/utils/validators";
|
||||||
@ -18,10 +19,15 @@ export type AccessFormSSHConfigProps = {
|
|||||||
onValuesChange?: (values: AccessFormSSHConfigFieldValues) => void;
|
onValuesChange?: (values: AccessFormSSHConfigFieldValues) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AUTH_METHOD_NONE = "none" as const;
|
||||||
|
const AUTH_METHOD_PASSWORD = "password" as const;
|
||||||
|
const AUTH_METHOD_KEY = "key" as const;
|
||||||
|
|
||||||
const initFormModel = (): AccessFormSSHConfigFieldValues => {
|
const initFormModel = (): AccessFormSSHConfigFieldValues => {
|
||||||
return {
|
return {
|
||||||
host: "127.0.0.1",
|
host: "127.0.0.1",
|
||||||
port: 22,
|
port: 22,
|
||||||
|
authMethod: AUTH_METHOD_PASSWORD,
|
||||||
username: "root",
|
username: "root",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -38,6 +44,9 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
|||||||
.int(t("access.form.ssh_port.placeholder"))
|
.int(t("access.form.ssh_port.placeholder"))
|
||||||
.refine((v) => validPortNumber(v), t("common.errmsg.port_invalid"))
|
.refine((v) => validPortNumber(v), t("common.errmsg.port_invalid"))
|
||||||
),
|
),
|
||||||
|
authMethod: z.union([z.literal(AUTH_METHOD_NONE), z.literal(AUTH_METHOD_PASSWORD), z.literal(AUTH_METHOD_KEY)], {
|
||||||
|
message: t("access.form.ssh_auth_method.placeholder"),
|
||||||
|
}),
|
||||||
username: z
|
username: z
|
||||||
.string()
|
.string()
|
||||||
.min(1, t("access.form.ssh_username.placeholder"))
|
.min(1, t("access.form.ssh_username.placeholder"))
|
||||||
@ -45,11 +54,13 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
|||||||
password: z
|
password: z
|
||||||
.string()
|
.string()
|
||||||
.max(64, t("common.errmsg.string_max", { max: 64 }))
|
.max(64, t("common.errmsg.string_max", { max: 64 }))
|
||||||
.nullish(),
|
.nullish()
|
||||||
|
.refine((v) => fieldAuthMethod !== AUTH_METHOD_PASSWORD || !!v?.trim(), t("access.form.ssh_password.placeholder")),
|
||||||
key: z
|
key: z
|
||||||
.string()
|
.string()
|
||||||
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
||||||
.nullish(),
|
.nullish()
|
||||||
|
.refine((v) => fieldAuthMethod !== AUTH_METHOD_KEY || !!v?.trim(), t("access.form.ssh_key.placeholder")),
|
||||||
keyPassphrase: z
|
keyPassphrase: z
|
||||||
.string()
|
.string()
|
||||||
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
||||||
@ -57,47 +68,43 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
|||||||
.refine((v) => !v || formInst.getFieldValue("key"), t("access.form.ssh_key.placeholder")),
|
.refine((v) => !v || formInst.getFieldValue("key"), t("access.form.ssh_key.placeholder")),
|
||||||
jumpServers: z
|
jumpServers: z
|
||||||
.array(
|
.array(
|
||||||
z
|
z.object({
|
||||||
.object({
|
host: z.string().refine((v) => validDomainName(v) || validIPv4Address(v) || validIPv6Address(v), t("common.errmsg.host_invalid")),
|
||||||
host: z.string().refine((v) => validDomainName(v) || validIPv4Address(v) || validIPv6Address(v), t("common.errmsg.host_invalid")),
|
port: z.preprocess(
|
||||||
port: z.preprocess(
|
(v) => Number(v),
|
||||||
(v) => Number(v),
|
z
|
||||||
z
|
.number()
|
||||||
.number()
|
.int(t("access.form.ssh_port.placeholder"))
|
||||||
.int(t("access.form.ssh_port.placeholder"))
|
.refine((v) => validPortNumber(v), t("common.errmsg.port_invalid"))
|
||||||
.refine((v) => validPortNumber(v), t("common.errmsg.port_invalid"))
|
),
|
||||||
),
|
authMethod: z.union([z.literal(AUTH_METHOD_NONE), z.literal(AUTH_METHOD_PASSWORD), z.literal(AUTH_METHOD_KEY)], {
|
||||||
username: z
|
message: t("access.form.ssh_auth_method.placeholder"),
|
||||||
.string()
|
}),
|
||||||
.min(1, t("access.form.ssh_username.placeholder"))
|
username: z
|
||||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
.string()
|
||||||
password: z
|
.min(1, t("access.form.ssh_username.placeholder"))
|
||||||
.string()
|
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||||
.max(64, t("common.errmsg.string_max", { max: 64 }))
|
password: z
|
||||||
.nullish(),
|
.string()
|
||||||
key: z
|
.max(64, t("common.errmsg.string_max", { max: 64 }))
|
||||||
.string()
|
.nullish(),
|
||||||
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
key: z
|
||||||
.nullish(),
|
.string()
|
||||||
keyPassphrase: z
|
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
||||||
.string()
|
.nullish(),
|
||||||
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
keyPassphrase: z
|
||||||
.nullish(),
|
.string()
|
||||||
})
|
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
||||||
.superRefine((data, ctx) => {
|
.nullish(),
|
||||||
if (data.keyPassphrase && !data.key) {
|
}),
|
||||||
ctx.addIssue({
|
{ message: t("access.form.ssh_jump_servers.errmsg.invalid") }
|
||||||
path: ["keyPassphrase"],
|
|
||||||
code: z.ZodIssueCode.custom,
|
|
||||||
message: t("access.form.ssh_key.placeholder"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
.nullish(),
|
.nullish(),
|
||||||
});
|
});
|
||||||
const formRule = createSchemaFieldRule(formSchema);
|
const formRule = createSchemaFieldRule(formSchema);
|
||||||
|
|
||||||
|
const fieldAuthMethod = Form.useWatch("authMethod", formInst);
|
||||||
|
|
||||||
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||||
onValuesChange?.(values);
|
onValuesChange?.(values);
|
||||||
};
|
};
|
||||||
@ -125,36 +132,39 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Form.Item name="authMethod" label={t("access.form.ssh_auth_method.label")} rules={[formRule]}>
|
||||||
|
<Select placeholder={t("access.form.ssh_auth_method.placeholder")}>
|
||||||
|
<Select.Option key={AUTH_METHOD_NONE} value={AUTH_METHOD_NONE}>
|
||||||
|
{t("access.form.ssh_auth_method.option.none.label")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option key={AUTH_METHOD_PASSWORD} value={AUTH_METHOD_PASSWORD}>
|
||||||
|
{t("access.form.ssh_auth_method.option.password.label")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option key={AUTH_METHOD_KEY} value={AUTH_METHOD_KEY}>
|
||||||
|
{t("access.form.ssh_auth_method.option.key.label")}
|
||||||
|
</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item name="username" label={t("access.form.ssh_username.label")} rules={[formRule]}>
|
<Form.Item name="username" label={t("access.form.ssh_username.label")} rules={[formRule]}>
|
||||||
<Input autoComplete="new-password" placeholder={t("access.form.ssh_username.placeholder")} />
|
<Input autoComplete="new-password" placeholder={t("access.form.ssh_username.placeholder")} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Show when={fieldAuthMethod === AUTH_METHOD_PASSWORD}>
|
||||||
name="password"
|
<Form.Item name="password" label={t("access.form.ssh_password.label")} rules={[formRule]}>
|
||||||
label={t("access.form.ssh_password.label")}
|
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_password.placeholder")} />
|
||||||
rules={[formRule]}
|
</Form.Item>
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_password.tooltip") }}></span>}
|
</Show>
|
||||||
>
|
|
||||||
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_password.placeholder")} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
<Show when={fieldAuthMethod === AUTH_METHOD_KEY}>
|
||||||
name="key"
|
<Form.Item name="key" label={t("access.form.ssh_key.label")} rules={[formRule]}>
|
||||||
label={t("access.form.ssh_key.label")}
|
<TextFileInput allowClear autoSize={{ minRows: 1, maxRows: 5 }} placeholder={t("access.form.ssh_key.placeholder")} />
|
||||||
rules={[formRule]}
|
</Form.Item>
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_key.tooltip") }}></span>}
|
|
||||||
>
|
|
||||||
<TextFileInput allowClear autoSize={{ minRows: 1, maxRows: 5 }} placeholder={t("access.form.ssh_key.placeholder")} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item name="keyPassphrase" label={t("access.form.ssh_key_passphrase.label")} rules={[formRule]}>
|
||||||
name="keyPassphrase"
|
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_key_passphrase.placeholder")} />
|
||||||
label={t("access.form.ssh_key_passphrase.label")}
|
</Form.Item>
|
||||||
rules={[formRule]}
|
</Show>
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_key_passphrase.tooltip") }}></span>}
|
|
||||||
>
|
|
||||||
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_key_passphrase.placeholder")} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item name="jumpServers" label={t("access.form.ssh_jump_servers.label")} rules={[formRule]}>
|
<Form.Item name="jumpServers" label={t("access.form.ssh_jump_servers.label")} rules={[formRule]}>
|
||||||
<Form.List name="jumpServers">
|
<Form.List name="jumpServers">
|
||||||
@ -174,6 +184,60 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Fields = () => {
|
||||||
|
const authMethod = Form.useWatch(["jumpServers", field.name, "authMethod"], formInst);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<div className="w-2/3">
|
||||||
|
<Form.Item name={[field.name, "host"]} label={t("access.form.ssh_host.label")} rules={[formRule]}>
|
||||||
|
<Input placeholder={t("access.form.ssh_host.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
<div className="w-1/3">
|
||||||
|
<Form.Item name={[field.name, "port"]} label={t("access.form.ssh_port.label")} rules={[formRule]}>
|
||||||
|
<InputNumber className="w-full" placeholder={t("access.form.ssh_port.placeholder")} min={1} max={65535} />
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Form.Item name={[field.name, "authMethod"]} label={t("access.form.ssh_auth_method.label")} rules={[formRule]}>
|
||||||
|
<Select placeholder={t("access.form.ssh_auth_method.placeholder")}>
|
||||||
|
<Select.Option key={AUTH_METHOD_NONE} value={AUTH_METHOD_NONE}>
|
||||||
|
{t("access.form.ssh_auth_method.option.none.label")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option key={AUTH_METHOD_PASSWORD} value={AUTH_METHOD_PASSWORD}>
|
||||||
|
{t("access.form.ssh_auth_method.option.password.label")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option key={AUTH_METHOD_KEY} value={AUTH_METHOD_KEY}>
|
||||||
|
{t("access.form.ssh_auth_method.option.key.label")}
|
||||||
|
</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name={[field.name, "username"]} label={t("access.form.ssh_username.label")} rules={[formRule]}>
|
||||||
|
<Input autoComplete="new-password" placeholder={t("access.form.ssh_username.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Show when={authMethod === AUTH_METHOD_PASSWORD}>
|
||||||
|
<Form.Item name={[field.name, "password"]} label={t("access.form.ssh_password.label")} rules={[formRule]}>
|
||||||
|
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_password.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Show when={authMethod === AUTH_METHOD_KEY}>
|
||||||
|
<Form.Item name={[field.name, "key"]} label={t("access.form.ssh_key.label")} rules={[formRule]}>
|
||||||
|
<TextFileInput allowClear autoSize={{ minRows: 1, maxRows: 5 }} placeholder={t("access.form.ssh_key.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name={[field.name, "keyPassphrase"]} label={t("access.form.ssh_key_passphrase.label")} rules={[formRule]}>
|
||||||
|
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_key_passphrase.placeholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
</Show>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
key: field.key,
|
key: field.key,
|
||||||
label: <Label />,
|
label: <Label />,
|
||||||
@ -214,58 +278,12 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
|||||||
/>
|
/>
|
||||||
</Space.Compact>
|
</Space.Compact>
|
||||||
),
|
),
|
||||||
children: (
|
children: <Fields />,
|
||||||
<>
|
|
||||||
<div className="flex space-x-2">
|
|
||||||
<div className="w-2/3">
|
|
||||||
<Form.Item name={[field.name, "host"]} label={t("access.form.ssh_host.label")} rules={[formRule]}>
|
|
||||||
<Input placeholder={t("access.form.ssh_host.placeholder")} />
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
<div className="w-1/3">
|
|
||||||
<Form.Item name={[field.name, "port"]} label={t("access.form.ssh_port.label")} rules={[formRule]}>
|
|
||||||
<InputNumber className="w-full" placeholder={t("access.form.ssh_port.placeholder")} min={1} max={65535} />
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Form.Item name={[field.name, "username"]} label={t("access.form.ssh_username.label")} rules={[formRule]}>
|
|
||||||
<Input autoComplete="new-password" placeholder={t("access.form.ssh_username.placeholder")} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
name={[field.name, "password"]}
|
|
||||||
label={t("access.form.ssh_password.label")}
|
|
||||||
rules={[formRule]}
|
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_password.tooltip") }}></span>}
|
|
||||||
>
|
|
||||||
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_password.placeholder")} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
name={[field.name, "key"]}
|
|
||||||
label={t("access.form.ssh_key.label")}
|
|
||||||
rules={[formRule]}
|
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_key.tooltip") }}></span>}
|
|
||||||
>
|
|
||||||
<TextFileInput allowClear autoSize={{ minRows: 1, maxRows: 5 }} placeholder={t("access.form.ssh_key.placeholder")} />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
name={[field.name, "keyPassphrase"]}
|
|
||||||
label={t("access.form.ssh_key_passphrase.label")}
|
|
||||||
rules={[formRule]}
|
|
||||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.ssh_key_passphrase.tooltip") }}></span>}
|
|
||||||
>
|
|
||||||
<Input.Password allowClear autoComplete="new-password" placeholder={t("access.form.ssh_key_passphrase.placeholder")} />
|
|
||||||
</Form.Item>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<Button className="w-full" type="dashed" icon={<PlusOutlined />} onClick={() => add()}>
|
<Button className="w-full" type="dashed" icon={<PlusOutlined />} onClick={() => add(initFormModel())}>
|
||||||
{t("access.form.ssh_jump_servers.add")}
|
{t("access.form.ssh_jump_servers.add")}
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
|
@ -379,7 +379,8 @@ export type AccessConfigForSlackBot = {
|
|||||||
export type AccessConfigForSSH = {
|
export type AccessConfigForSSH = {
|
||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
username: string;
|
authMethod?: string;
|
||||||
|
username?: string;
|
||||||
password?: string;
|
password?: string;
|
||||||
key?: string;
|
key?: string;
|
||||||
keyPassphrase?: string;
|
keyPassphrase?: string;
|
||||||
|
@ -378,18 +378,21 @@
|
|||||||
"access.form.ssh_host.placeholder": "Please enter server host",
|
"access.form.ssh_host.placeholder": "Please enter server host",
|
||||||
"access.form.ssh_port.label": "Server port",
|
"access.form.ssh_port.label": "Server port",
|
||||||
"access.form.ssh_port.placeholder": "Please enter server port",
|
"access.form.ssh_port.placeholder": "Please enter server port",
|
||||||
|
"access.form.ssh_auth_method.label": "Authentication method",
|
||||||
|
"access.form.ssh_auth_method.placeholder": "Please select authentication method",
|
||||||
|
"access.form.ssh_auth_method.option.none.label": "None",
|
||||||
|
"access.form.ssh_auth_method.option.password.label": "Password",
|
||||||
|
"access.form.ssh_auth_method.option.key.label": "SSH key",
|
||||||
"access.form.ssh_username.label": "Username",
|
"access.form.ssh_username.label": "Username",
|
||||||
"access.form.ssh_username.placeholder": "Please enter username",
|
"access.form.ssh_username.placeholder": "Please enter username",
|
||||||
"access.form.ssh_password.label": "Password (Optional)",
|
"access.form.ssh_password.label": "Password",
|
||||||
"access.form.ssh_password.placeholder": "Please enter password",
|
"access.form.ssh_password.placeholder": "Please enter password",
|
||||||
"access.form.ssh_password.tooltip": "Required when using password to connect to SSH.",
|
"access.form.ssh_key.label": "SSH key",
|
||||||
"access.form.ssh_key.label": "SSH key (Optional)",
|
|
||||||
"access.form.ssh_key.placeholder": "Please enter SSH key",
|
"access.form.ssh_key.placeholder": "Please enter SSH key",
|
||||||
"access.form.ssh_key.tooltip": "Required when using key to connect to SSH.",
|
|
||||||
"access.form.ssh_key_passphrase.label": "SSH key passphrase (Optional)",
|
"access.form.ssh_key_passphrase.label": "SSH key passphrase (Optional)",
|
||||||
"access.form.ssh_key_passphrase.placeholder": "Please enter SSH key passphrase",
|
"access.form.ssh_key_passphrase.placeholder": "Please enter SSH key passphrase",
|
||||||
"access.form.ssh_key_passphrase.tooltip": "Optional when using key to connect to SSH.",
|
|
||||||
"access.form.ssh_jump_servers.label": "SSH jump server (Optional)",
|
"access.form.ssh_jump_servers.label": "SSH jump server (Optional)",
|
||||||
|
"access.form.ssh_jump_servers.errmsg.invalid": "Please configure a valid jump server",
|
||||||
"access.form.ssh_jump_servers.item.label": "Jump server",
|
"access.form.ssh_jump_servers.item.label": "Jump server",
|
||||||
"access.form.ssh_jump_servers.add": "Add jump server",
|
"access.form.ssh_jump_servers.add": "Add jump server",
|
||||||
"access.form.sslcom_eab_kid.label": "ACME EAB KID",
|
"access.form.sslcom_eab_kid.label": "ACME EAB KID",
|
||||||
|
@ -378,18 +378,21 @@
|
|||||||
"access.form.ssh_host.placeholder": "请输入服务器地址",
|
"access.form.ssh_host.placeholder": "请输入服务器地址",
|
||||||
"access.form.ssh_port.label": "服务器端口",
|
"access.form.ssh_port.label": "服务器端口",
|
||||||
"access.form.ssh_port.placeholder": "请输入服务器端口",
|
"access.form.ssh_port.placeholder": "请输入服务器端口",
|
||||||
|
"access.form.ssh_auth_method.label": "认证方式",
|
||||||
|
"access.form.ssh_auth_method.placeholder": "请选择认证方式",
|
||||||
|
"access.form.ssh_auth_method.option.none.label": "无",
|
||||||
|
"access.form.ssh_auth_method.option.password.label": "密码",
|
||||||
|
"access.form.ssh_auth_method.option.key.label": "密钥",
|
||||||
"access.form.ssh_username.label": "用户名",
|
"access.form.ssh_username.label": "用户名",
|
||||||
"access.form.ssh_username.placeholder": "请输入用户名",
|
"access.form.ssh_username.placeholder": "请输入用户名",
|
||||||
"access.form.ssh_password.label": "密码(可选)",
|
"access.form.ssh_password.label": "密码",
|
||||||
"access.form.ssh_password.placeholder": "请输入密码",
|
"access.form.ssh_password.placeholder": "请输入密码",
|
||||||
"access.form.ssh_password.tooltip": "使用密码连接到 SSH 时必填。<br>该字段与密钥文件字段二选一,如果同时填写优先使用 SSH 密钥登录。",
|
"access.form.ssh_key.label": "SSH 密钥",
|
||||||
"access.form.ssh_key.label": "SSH 密钥(可选)",
|
|
||||||
"access.form.ssh_key.placeholder": "请输入 SSH 密钥文件内容",
|
"access.form.ssh_key.placeholder": "请输入 SSH 密钥文件内容",
|
||||||
"access.form.ssh_key.tooltip": "使用 SSH 密钥连接到 SSH 时必填。<br>该字段与密码字段二选一,如果同时填写优先使用 SSH 密钥登录。",
|
|
||||||
"access.form.ssh_key_passphrase.label": "SSH 密钥口令(可选)",
|
"access.form.ssh_key_passphrase.label": "SSH 密钥口令(可选)",
|
||||||
"access.form.ssh_key_passphrase.placeholder": "请输入 SSH 密钥口令",
|
"access.form.ssh_key_passphrase.placeholder": "请输入 SSH 密钥口令",
|
||||||
"access.form.ssh_key_passphrase.tooltip": "使用 SSH 密钥连接到 SSH 时选填。",
|
|
||||||
"access.form.ssh_jump_servers.label": "SSH 跳板机(可选)",
|
"access.form.ssh_jump_servers.label": "SSH 跳板机(可选)",
|
||||||
|
"access.form.ssh_jump_servers.errmsg.invalid": "请配置有效的 SSH 跳板机",
|
||||||
"access.form.ssh_jump_servers.item.label": "跳板机",
|
"access.form.ssh_jump_servers.item.label": "跳板机",
|
||||||
"access.form.ssh_jump_servers.add": "添加跳板机",
|
"access.form.ssh_jump_servers.add": "添加跳板机",
|
||||||
"access.form.sslcom_eab_kid.label": "ACME EAB KID",
|
"access.form.sslcom_eab_kid.label": "ACME EAB KID",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user