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{
|
||||
SshHost: jumpServer.Host,
|
||||
SshPort: jumpServer.Port,
|
||||
SshAuthMethod: jumpServer.AuthMethod,
|
||||
SshUsername: jumpServer.Username,
|
||||
SshPassword: jumpServer.Password,
|
||||
SshKey: jumpServer.Key,
|
||||
@ -1007,6 +1008,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
|
||||
deployer, err := pSSH.NewDeployer(&pSSH.DeployerConfig{
|
||||
SshHost: access.Host,
|
||||
SshPort: access.Port,
|
||||
SshAuthMethod: access.AuthMethod,
|
||||
SshUsername: access.Username,
|
||||
SshPassword: access.Password,
|
||||
SshKey: access.Key,
|
||||
|
@ -315,14 +315,16 @@ type AccessConfigForSlackBot struct {
|
||||
type AccessConfigForSSH struct {
|
||||
Host string `json:"host"`
|
||||
Port int32 `json:"port"`
|
||||
Username string `json:"username"`
|
||||
AuthMethod string `json:"authMethod,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Key string `json:"key,omitempty"`
|
||||
KeyPassphrase string `json:"keyPassphrase,omitempty"`
|
||||
JumpServers []struct {
|
||||
Host string `json:"host"`
|
||||
Port int32 `json:"port"`
|
||||
Username string `json:"username"`
|
||||
AuthMethod string `json:"authMethod,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Key string `json:"key,omitempty"`
|
||||
KeyPassphrase string `json:"keyPassphrase,omitempty"`
|
||||
|
@ -24,7 +24,12 @@ type JumpServerConfig struct {
|
||||
// SSH 端口。
|
||||
// 零值时默认值 22。
|
||||
SshPort int32 `json:"sshPort,omitempty"`
|
||||
// SSH 认证方式。
|
||||
// 可取值 "none"、"password"、"key"。
|
||||
// 零值时根据有无密码或私钥字段决定。
|
||||
SshAuthMethod string `json:"sshAuthMethod,omitempty"`
|
||||
// SSH 登录用户名。
|
||||
// 零值时默认值 "root"。
|
||||
SshUsername string `json:"sshUsername,omitempty"`
|
||||
// SSH 登录密码。
|
||||
SshPassword string `json:"sshPassword,omitempty"`
|
||||
@ -41,7 +46,12 @@ type DeployerConfig struct {
|
||||
// SSH 端口。
|
||||
// 零值时默认值 22。
|
||||
SshPort int32 `json:"sshPort,omitempty"`
|
||||
// SSH 认证方式。
|
||||
// 可取值 "none"、"password" 或 "key"。
|
||||
// 零值时根据有无密码或私钥字段决定。
|
||||
SshAuthMethod string `json:"sshAuthMethod,omitempty"`
|
||||
// SSH 登录用户名。
|
||||
// 零值时默认值 "root"。
|
||||
SshUsername string `json:"sshUsername,omitempty"`
|
||||
// SSH 登录密码。
|
||||
SshPassword string `json:"sshPassword,omitempty"`
|
||||
@ -141,6 +151,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
||||
jumpConn,
|
||||
jumpServerConf.SshHost,
|
||||
jumpServerConf.SshPort,
|
||||
jumpServerConf.SshAuthMethod,
|
||||
jumpServerConf.SshUsername,
|
||||
jumpServerConf.SshPassword,
|
||||
jumpServerConf.SshKey,
|
||||
@ -174,6 +185,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
||||
targetConn,
|
||||
d.config.SshHost,
|
||||
d.config.SshPort,
|
||||
d.config.SshAuthMethod,
|
||||
d.config.SshUsername,
|
||||
d.config.SshPassword,
|
||||
d.config.SshKey,
|
||||
@ -262,7 +274,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
|
||||
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 == "" {
|
||||
host = "localhost"
|
||||
}
|
||||
@ -271,28 +283,65 @@ func createSshClient(conn net.Conn, host string, port int32, username string, pa
|
||||
port = 22
|
||||
}
|
||||
|
||||
var authMethod ssh.AuthMethod
|
||||
if key != "" {
|
||||
var signer ssh.Signer
|
||||
var err error
|
||||
if username == "" {
|
||||
username = "root"
|
||||
}
|
||||
|
||||
if keyPassphrase != "" {
|
||||
signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(key), []byte(keyPassphrase))
|
||||
const AUTH_METHOD_NONE = "none"
|
||||
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 {
|
||||
signer, err = ssh.ParsePrivateKey([]byte(key))
|
||||
authMethod = AUTH_METHOD_NONE
|
||||
}
|
||||
}
|
||||
|
||||
authentications := make([]ssh.AuthMethod, 0)
|
||||
switch authMethod {
|
||||
case AUTH_METHOD_NONE:
|
||||
{
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
case AUTH_METHOD_PASSWORD:
|
||||
{
|
||||
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 {
|
||||
authMethod = ssh.Password(password)
|
||||
|
||||
case AUTH_METHOD_KEY:
|
||||
{
|
||||
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{
|
||||
User: username,
|
||||
Auth: []ssh.AuthMethod{authMethod},
|
||||
Auth: authentications,
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
})
|
||||
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 { 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 { z } from "zod";
|
||||
|
||||
import Show from "@/components/Show";
|
||||
import TextFileInput from "@/components/TextFileInput";
|
||||
import { type AccessConfigForSSH } from "@/domain/access";
|
||||
import { validDomainName, validIPv4Address, validIPv6Address, validPortNumber } from "@/utils/validators";
|
||||
@ -18,10 +19,15 @@ export type AccessFormSSHConfigProps = {
|
||||
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 => {
|
||||
return {
|
||||
host: "127.0.0.1",
|
||||
port: 22,
|
||||
authMethod: AUTH_METHOD_PASSWORD,
|
||||
username: "root",
|
||||
};
|
||||
};
|
||||
@ -38,6 +44,9 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
||||
.int(t("access.form.ssh_port.placeholder"))
|
||||
.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
|
||||
.string()
|
||||
.min(1, t("access.form.ssh_username.placeholder"))
|
||||
@ -45,11 +54,13 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
||||
password: z
|
||||
.string()
|
||||
.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
|
||||
.string()
|
||||
.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
|
||||
.string()
|
||||
.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")),
|
||||
jumpServers: z
|
||||
.array(
|
||||
z
|
||||
.object({
|
||||
host: z.string().refine((v) => validDomainName(v) || validIPv4Address(v) || validIPv6Address(v), t("common.errmsg.host_invalid")),
|
||||
port: z.preprocess(
|
||||
(v) => Number(v),
|
||||
z
|
||||
.number()
|
||||
.int(t("access.form.ssh_port.placeholder"))
|
||||
.refine((v) => validPortNumber(v), t("common.errmsg.port_invalid"))
|
||||
),
|
||||
username: z
|
||||
.string()
|
||||
.min(1, t("access.form.ssh_username.placeholder"))
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
password: z
|
||||
.string()
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 }))
|
||||
.nullish(),
|
||||
key: z
|
||||
.string()
|
||||
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
||||
.nullish(),
|
||||
keyPassphrase: z
|
||||
.string()
|
||||
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
||||
.nullish(),
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
if (data.keyPassphrase && !data.key) {
|
||||
ctx.addIssue({
|
||||
path: ["keyPassphrase"],
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: t("access.form.ssh_key.placeholder"),
|
||||
});
|
||||
}
|
||||
})
|
||||
z.object({
|
||||
host: z.string().refine((v) => validDomainName(v) || validIPv4Address(v) || validIPv6Address(v), t("common.errmsg.host_invalid")),
|
||||
port: z.preprocess(
|
||||
(v) => Number(v),
|
||||
z
|
||||
.number()
|
||||
.int(t("access.form.ssh_port.placeholder"))
|
||||
.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
|
||||
.string()
|
||||
.min(1, t("access.form.ssh_username.placeholder"))
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 })),
|
||||
password: z
|
||||
.string()
|
||||
.max(64, t("common.errmsg.string_max", { max: 64 }))
|
||||
.nullish(),
|
||||
key: z
|
||||
.string()
|
||||
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
||||
.nullish(),
|
||||
keyPassphrase: z
|
||||
.string()
|
||||
.max(20480, t("common.errmsg.string_max", { max: 20480 }))
|
||||
.nullish(),
|
||||
}),
|
||||
{ message: t("access.form.ssh_jump_servers.errmsg.invalid") }
|
||||
)
|
||||
.nullish(),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
|
||||
const fieldAuthMethod = Form.useWatch("authMethod", formInst);
|
||||
|
||||
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
|
||||
onValuesChange?.(values);
|
||||
};
|
||||
@ -125,36 +132,39 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
||||
</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]}>
|
||||
<Input autoComplete="new-password" placeholder={t("access.form.ssh_username.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
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>
|
||||
<Show when={fieldAuthMethod === AUTH_METHOD_PASSWORD}>
|
||||
<Form.Item 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>
|
||||
|
||||
<Form.Item
|
||||
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>
|
||||
<Show when={fieldAuthMethod === AUTH_METHOD_KEY}>
|
||||
<Form.Item 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="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>
|
||||
<Form.Item 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>
|
||||
|
||||
<Form.Item name="jumpServers" label={t("access.form.ssh_jump_servers.label")} rules={[formRule]}>
|
||||
<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 {
|
||||
key: field.key,
|
||||
label: <Label />,
|
||||
@ -214,58 +278,12 @@ const AccessFormSSHConfig = ({ form: formInst, formName, disabled, initialValues
|
||||
/>
|
||||
</Space.Compact>
|
||||
),
|
||||
children: (
|
||||
<>
|
||||
<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>
|
||||
</>
|
||||
),
|
||||
children: <Fields />,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
) : 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")}
|
||||
</Button>
|
||||
</Space>
|
||||
|
@ -379,7 +379,8 @@ export type AccessConfigForSlackBot = {
|
||||
export type AccessConfigForSSH = {
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
authMethod?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
key?: string;
|
||||
keyPassphrase?: string;
|
||||
|
@ -378,18 +378,21 @@
|
||||
"access.form.ssh_host.placeholder": "Please enter server host",
|
||||
"access.form.ssh_port.label": "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.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.tooltip": "Required when using password to connect to SSH.",
|
||||
"access.form.ssh_key.label": "SSH key (Optional)",
|
||||
"access.form.ssh_key.label": "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.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.errmsg.invalid": "Please configure a valid jump server",
|
||||
"access.form.ssh_jump_servers.item.label": "Jump server",
|
||||
"access.form.ssh_jump_servers.add": "Add jump server",
|
||||
"access.form.sslcom_eab_kid.label": "ACME EAB KID",
|
||||
|
@ -378,18 +378,21 @@
|
||||
"access.form.ssh_host.placeholder": "请输入服务器地址",
|
||||
"access.form.ssh_port.label": "服务器端口",
|
||||
"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.placeholder": "请输入用户名",
|
||||
"access.form.ssh_password.label": "密码(可选)",
|
||||
"access.form.ssh_password.label": "密码",
|
||||
"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.tooltip": "使用 SSH 密钥连接到 SSH 时必填。<br>该字段与密码字段二选一,如果同时填写优先使用 SSH 密钥登录。",
|
||||
"access.form.ssh_key_passphrase.label": "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.errmsg.invalid": "请配置有效的 SSH 跳板机",
|
||||
"access.form.ssh_jump_servers.item.label": "跳板机",
|
||||
"access.form.ssh_jump_servers.add": "添加跳板机",
|
||||
"access.form.sslcom_eab_kid.label": "ACME EAB KID",
|
||||
|
Loading…
x
Reference in New Issue
Block a user