1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 08:19:15 +08:00

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["port"] = req.Port
upMap["user"] = req.User upMap["user"] = req.User
upMap["auth_mode"] = req.AuthMode upMap["auth_mode"] = req.AuthMode
upMap["remember_password"] = req.RememberPassword
if len(req.Password) != 0 { if len(req.Password) != 0 {
upMap["password"] = req.Password upMap["password"] = req.Password
} }
if len(req.PrivateKey) != 0 { if len(req.PrivateKey) != 0 {
upMap["private_key"] = req.PrivateKey upMap["private_key"] = req.PrivateKey
upMap["pass_phrase"] = req.PassPhrase
} }
upMap["description"] = req.Description upMap["description"] = req.Description
if err := hostService.Update(req.ID, upMap); err != nil { 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 var connInfo ssh.ConnInfo
_ = copier.Copy(&connInfo, &host) _ = copier.Copy(&connInfo, &host)
connInfo.PrivateKey = []byte(host.PrivateKey) connInfo.PrivateKey = []byte(host.PrivateKey)
if len(host.PassPhrase) != 0 {
connInfo.PassPhrase = []byte(host.PassPhrase)
}
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil) wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if err != nil { if err != nil {

View File

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

View File

@ -2,14 +2,17 @@ package model
type Host struct { type Host struct {
BaseModel BaseModel
GroupID uint `gorm:"type:decimal;not null" json:"group_id"`
Name string `gorm:"type:varchar(64);not null" json:"name"` GroupID uint `gorm:"type:decimal;not null" json:"group_id"`
Addr string `gorm:"type:varchar(16);not null" json:"addr"` Name string `gorm:"type:varchar(64);not null" json:"name"`
Port int `gorm:"type:decimal;not null" json:"port"` Addr string `gorm:"type:varchar(16);not null" json:"addr"`
User string `gorm:"type:varchar(64);not null" json:"user"` Port int `gorm:"type:decimal;not null" json:"port"`
AuthMode string `gorm:"type:varchar(16);not null" json:"authMode"` User string `gorm:"type:varchar(64);not null" json:"user"`
Password string `gorm:"type:varchar(64)" json:"password"` AuthMode string `gorm:"type:varchar(16);not null" json:"authMode"`
PrivateKey string `gorm:"type:varchar(256)" json:"privateKey"` 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"` 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.Password = host.Password
req.AuthMode = host.AuthMode req.AuthMode = host.AuthMode
req.PrivateKey = host.PrivateKey req.PrivateKey = host.PrivateKey
req.PassPhrase = host.PassPhrase
} }
var connInfo ssh.ConnInfo var connInfo ssh.ConnInfo
_ = copier.Copy(&connInfo, &req) _ = copier.Copy(&connInfo, &req)
connInfo.PrivateKey = []byte(req.PrivateKey) connInfo.PrivateKey = []byte(req.PrivateKey)
if len(req.PassPhrase) != 0 {
connInfo.PassPhrase = []byte(req.PassPhrase)
}
client, err := connInfo.NewClient() client, err := connInfo.NewClient()
if err != nil { if err != nil {
return false return false
@ -85,6 +89,10 @@ func (u *HostService) TestLocalConn(id uint) bool {
if err := copier.Copy(&connInfo, &host); err != nil { if err := copier.Copy(&connInfo, &host); err != nil {
return false return false
} }
connInfo.PrivateKey = []byte(host.PrivateKey)
if len(host.PassPhrase) != 0 {
connInfo.PassPhrase = []byte(host.PassPhrase)
}
client, err := connInfo.NewClient() client, err := connInfo.NewClient()
if err != nil { if err != nil {
return false return false
@ -115,6 +123,11 @@ func (u *HostService) SearchWithPage(search dto.SearchHostWithPage) (int64, inte
} }
group, _ := groupRepo.Get(commonRepo.WithByID(host.GroupID)) group, _ := groupRepo.Get(commonRepo.WithByID(host.GroupID))
item.GroupBelong = group.Name item.GroupBelong = group.Name
if !item.RememberPassword {
item.Password = ""
item.PrivateKey = ""
item.PassPhrase = ""
}
dtoHosts = append(dtoHosts, item) dtoHosts = append(dtoHosts, item)
} }
return total, dtoHosts, err return total, dtoHosts, err
@ -182,6 +195,8 @@ func (u *HostService) Create(req dto.HostOperate) (*dto.HostInfo, error) {
upMap["auth_mode"] = req.AuthMode upMap["auth_mode"] = req.AuthMode
upMap["password"] = req.Password upMap["password"] = req.Password
upMap["private_key"] = req.PrivateKey upMap["private_key"] = req.PrivateKey
upMap["pass_phrase"] = req.PassPhrase
upMap["remember_password"] = req.RememberPassword
upMap["description"] = req.Description upMap["description"] = req.Description
if err := hostRepo.Update(sameHostID, upMap); err != nil { if err := hostRepo.Update(sameHostID, upMap); err != nil {
return nil, err return nil, err

View File

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

View File

@ -264,3 +264,13 @@ var UpdateTableApp = &gormigrate.Migration{
return nil 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) { func makePrivateKeySigner(privateKey []byte, passPhrase []byte) (gossh.Signer, error) {
var signer gossh.Signer if len(passPhrase) != 0 {
if passPhrase != nil { return gossh.ParsePrivateKeyWithPassphrase(privateKey, passPhrase)
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
} }
return gossh.ParsePrivateKey(privateKey)
return signer, nil
} }

View File

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

View File

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

View File

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

View File

@ -36,7 +36,22 @@
> >
<el-input clearable type="textarea" v-model="dialogData.rowData!.privateKey" /> <el-input clearable type="textarea" v-model="dialogData.rowData!.privateKey" />
</el-form-item> </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-input clearable v-model.number="dialogData.rowData!.port" />
</el-form-item> </el-form-item>
<el-form-item :label="$t('commons.table.group')" prop="groupID"> <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-form-item :label="$t('terminal.key')" v-if="hostInfo.authMode === 'key'" prop="privateKey">
<el-input clearable type="textarea" v-model="hostInfo.privateKey" /> <el-input clearable type="textarea" v-model="hostInfo.privateKey" />
</el-form-item> </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-input clearable v-model.number="hostInfo.port" />
</el-form-item> </el-form-item>
<el-form-item :label="$t('commons.table.title')" prop="name"> <el-form-item :label="$t('commons.table.title')" prop="name">
@ -91,6 +101,8 @@ let hostInfo = reactive<Host.HostOperate>({
authMode: 'password', authMode: 'password',
password: '', password: '',
privateKey: '', privateKey: '',
passPhrase: '',
rememberPassword: false,
description: '', description: '',
}); });