feat: 主机增加记住密码,支持带密码私钥连接 (#570)

This commit is contained in:
ssongliu 2023-04-10 21:50:24 +08:00 committed by GitHub
parent a5fd55e90e
commit 0ddbdfeac9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 116 additions and 50 deletions

View File

@ -245,11 +245,13 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
upMap["port"] = req.Port
upMap["user"] = req.User
upMap["auth_mode"] = req.AuthMode
upMap["remember_password"] = req.RememberPassword
if len(req.Password) != 0 {
upMap["password"] = req.Password
}
if len(req.PrivateKey) != 0 {
upMap["private_key"] = req.PrivateKey
upMap["pass_phrase"] = req.PassPhrase
}
upMap["description"] = req.Description
if err := hostService.Update(req.ID, upMap); err != nil {

View File

@ -44,6 +44,9 @@ func (b *BaseApi) WsSsh(c *gin.Context) {
var connInfo ssh.ConnInfo
_ = copier.Copy(&connInfo, &host)
connInfo.PrivateKey = []byte(host.PrivateKey)
if len(host.PassPhrase) != 0 {
connInfo.PassPhrase = []byte(host.PassPhrase)
}
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {

View File

@ -5,15 +5,17 @@ import (
)
type HostOperate struct {
ID uint `json:"id"`
GroupID uint `json:"groupID"`
Name string `json:"name"`
Addr string `json:"addr" validate:"required"`
Port uint `json:"port" validate:"required,number,max=65535,min=1"`
User string `json:"user" validate:"required"`
AuthMode string `json:"authMode" validate:"oneof=password key"`
PrivateKey string `json:"privateKey"`
Password string `json:"password"`
ID uint `json:"id"`
GroupID uint `json:"groupID"`
Name string `json:"name"`
Addr string `json:"addr" validate:"required"`
Port uint `json:"port" validate:"required,number,max=65535,min=1"`
User string `json:"user" validate:"required"`
AuthMode string `json:"authMode" validate:"oneof=password key"`
Password string `json:"password"`
PrivateKey string `json:"privateKey"`
PassPhrase string `json:"passPhrase"`
RememberPassword bool `json:"rememberPassword"`
Description string `json:"description"`
}
@ -23,8 +25,9 @@ type HostConnTest struct {
Port uint `json:"port" validate:"required,number,max=65535,min=1"`
User string `json:"user" validate:"required"`
AuthMode string `json:"authMode" validate:"oneof=password key"`
PrivateKey string `json:"privateKey"`
Password string `json:"password"`
PrivateKey string `json:"privateKey"`
PassPhrase string `json:"passPhrase"`
}
type SearchHostWithPage struct {
@ -43,15 +46,19 @@ type ChangeHostGroup struct {
}
type HostInfo struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
GroupID uint `json:"groupID"`
GroupBelong string `json:"groupBelong"`
Name string `json:"name"`
Addr string `json:"addr"`
Port uint `json:"port"`
User string `json:"user"`
AuthMode string `json:"authMode"`
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
GroupID uint `json:"groupID"`
GroupBelong string `json:"groupBelong"`
Name string `json:"name"`
Addr string `json:"addr"`
Port uint `json:"port"`
User string `json:"user"`
AuthMode string `json:"authMode"`
Password string `json:"password"`
PrivateKey string `json:"privateKey"`
PassPhrase string `json:"passPhrase"`
RememberPassword bool `json:"rememberPassword"`
Description string `json:"description"`
}

View File

@ -2,14 +2,17 @@ package model
type Host struct {
BaseModel
GroupID uint `gorm:"type:decimal;not null" json:"group_id"`
Name string `gorm:"type:varchar(64);not null" json:"name"`
Addr string `gorm:"type:varchar(16);not null" json:"addr"`
Port int `gorm:"type:decimal;not null" json:"port"`
User string `gorm:"type:varchar(64);not null" json:"user"`
AuthMode string `gorm:"type:varchar(16);not null" json:"authMode"`
Password string `gorm:"type:varchar(64)" json:"password"`
PrivateKey string `gorm:"type:varchar(256)" json:"privateKey"`
GroupID uint `gorm:"type:decimal;not null" json:"group_id"`
Name string `gorm:"type:varchar(64);not null" json:"name"`
Addr string `gorm:"type:varchar(16);not null" json:"addr"`
Port int `gorm:"type:decimal;not null" json:"port"`
User string `gorm:"type:varchar(64);not null" json:"user"`
AuthMode string `gorm:"type:varchar(16);not null" json:"authMode"`
Password string `gorm:"type:varchar(64)" json:"password"`
PrivateKey string `gorm:"type:varchar(256)" json:"privateKey"`
PassPhrase string `gorm:"type:varchar(256)" json:"passPhrase"`
RememberPassword bool `json:"rememberPassword"`
Description string `gorm:"type:varchar(256)" json:"description"`
}

View File

@ -52,11 +52,15 @@ func (u *HostService) TestByInfo(req dto.HostConnTest) bool {
req.Password = host.Password
req.AuthMode = host.AuthMode
req.PrivateKey = host.PrivateKey
req.PassPhrase = host.PassPhrase
}
var connInfo ssh.ConnInfo
_ = copier.Copy(&connInfo, &req)
connInfo.PrivateKey = []byte(req.PrivateKey)
if len(req.PassPhrase) != 0 {
connInfo.PassPhrase = []byte(req.PassPhrase)
}
client, err := connInfo.NewClient()
if err != nil {
return false
@ -85,6 +89,10 @@ func (u *HostService) TestLocalConn(id uint) bool {
if err := copier.Copy(&connInfo, &host); err != nil {
return false
}
connInfo.PrivateKey = []byte(host.PrivateKey)
if len(host.PassPhrase) != 0 {
connInfo.PassPhrase = []byte(host.PassPhrase)
}
client, err := connInfo.NewClient()
if err != nil {
return false
@ -115,6 +123,11 @@ func (u *HostService) SearchWithPage(search dto.SearchHostWithPage) (int64, inte
}
group, _ := groupRepo.Get(commonRepo.WithByID(host.GroupID))
item.GroupBelong = group.Name
if !item.RememberPassword {
item.Password = ""
item.PrivateKey = ""
item.PassPhrase = ""
}
dtoHosts = append(dtoHosts, item)
}
return total, dtoHosts, err
@ -182,6 +195,8 @@ func (u *HostService) Create(req dto.HostOperate) (*dto.HostInfo, error) {
upMap["auth_mode"] = req.AuthMode
upMap["password"] = req.Password
upMap["private_key"] = req.PrivateKey
upMap["pass_phrase"] = req.PassPhrase
upMap["remember_password"] = req.RememberPassword
upMap["description"] = req.Description
if err := hostRepo.Update(sameHostID, upMap); err != nil {
return nil, err

View File

@ -23,6 +23,7 @@ func Init() {
migrations.AddDefaultGroup,
migrations.AddTableRuntime,
migrations.UpdateTableApp,
migrations.UpdateTableHost,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)

View File

@ -264,3 +264,13 @@ var UpdateTableApp = &gormigrate.Migration{
return nil
},
}
var UpdateTableHost = &gormigrate.Migration{
ID: "20230410-update-table-host",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.Host{}); err != nil {
return err
}
return nil
},
}

View File

@ -127,20 +127,8 @@ func (w *wsBufferWriter) Write(p []byte) (int, error) {
}
func makePrivateKeySigner(privateKey []byte, passPhrase []byte) (gossh.Signer, error) {
var signer gossh.Signer
if passPhrase != nil {
s, err := gossh.ParsePrivateKeyWithPassphrase(privateKey, passPhrase)
if err != nil {
return nil, fmt.Errorf("error parsing SSH key: '%v'", err)
}
signer = s
} else {
s, err := gossh.ParsePrivateKey(privateKey)
if err != nil {
return nil, fmt.Errorf("error parsing SSH key: '%v'", err)
}
signer = s
if len(passPhrase) != 0 {
return gossh.ParsePrivateKeyWithPassphrase(privateKey, passPhrase)
}
return signer, nil
return gossh.ParsePrivateKey(privateKey)
}

View File

@ -18,6 +18,10 @@ export namespace Host {
port: number;
user: string;
authMode: string;
password: string;
privateKey: string;
passPhrase: string;
rememberPassword: boolean;
description: string;
}
export interface HostOperate {
@ -28,8 +32,10 @@ export namespace Host {
port: number;
user: string;
authMode: string;
privateKey: string;
password: string;
privateKey: string;
passPhrase: string;
rememberPassword: boolean;
description: string;
}

View File

@ -674,10 +674,12 @@ const message = {
port: 'Port',
user: 'Username',
authMode: 'Auth Mode',
passwordMode: 'password',
passwordMode: 'Password',
rememberPassword: 'Remember password',
keyMode: 'PrivateKey',
password: 'Password',
key: 'Private Key',
key: 'Private key',
keyPassword: 'Private key password',
emptyTerminal: 'No terminal is currently connected',
},
logs: {

View File

@ -673,10 +673,12 @@ const message = {
port: '端口',
user: '用户名',
authMode: '认证方式',
passwordMode: '密码输入',
keyMode: '密钥输入',
passwordMode: '密码认证',
rememberPassword: '记住密码',
keyMode: '私钥认证',
password: '密码',
key: '密钥',
key: '私钥',
keyPassword: '私钥密码',
emptyTerminal: '暂无终端连接',
},
logs: {

View File

@ -36,7 +36,22 @@
>
<el-input clearable type="textarea" v-model="dialogData.rowData!.privateKey" />
</el-form-item>
<el-form-item :label="$t('terminal.port')" prop="port">
<el-form-item
:label="$t('terminal.keyPassword')"
v-if="dialogData.rowData!.authMode === 'key'"
prop="passPhrase"
>
<el-input
type="password"
show-password
clearable
v-model="dialogData.rowData!.passPhrase"
/>
</el-form-item>
<el-checkbox clearable v-model.number="dialogData.rowData!.rememberPassword">
{{ $t('terminal.rememberPassword') }}
</el-checkbox>
<el-form-item style="margin-top: 10px" :label="$t('terminal.port')" prop="port">
<el-input clearable v-model.number="dialogData.rowData!.port" />
</el-form-item>
<el-form-item :label="$t('commons.table.group')" prop="groupID">

View File

@ -40,7 +40,17 @@
<el-form-item :label="$t('terminal.key')" v-if="hostInfo.authMode === 'key'" prop="privateKey">
<el-input clearable type="textarea" v-model="hostInfo.privateKey" />
</el-form-item>
<el-form-item :label="$t('terminal.port')" prop="port">
<el-form-item
:label="$t('terminal.keyPassword')"
v-if="hostInfo.authMode === 'key'"
prop="passPhrase"
>
<el-input type="password" show-password clearable v-model="hostInfo.passPhrase" />
</el-form-item>
<el-checkbox clearable v-model.number="hostInfo.rememberPassword">
{{ $t('terminal.rememberPassword') }}
</el-checkbox>
<el-form-item style="margin-top: 10px" :label="$t('terminal.port')" prop="port">
<el-input clearable v-model.number="hostInfo.port" />
</el-form-item>
<el-form-item :label="$t('commons.table.title')" prop="name">
@ -91,6 +101,8 @@ let hostInfo = reactive<Host.HostOperate>({
authMode: 'password',
password: '',
privateKey: '',
passPhrase: '',
rememberPassword: false,
description: '',
});