!8 feat: 新增mysql ssh代理连接方式

Merge pull request !8 from das/N/A
This commit is contained in:
Coder慌
2022-07-20 03:13:29 +00:00
committed by Gitee
8 changed files with 150 additions and 2 deletions

View File

@@ -67,6 +67,28 @@
/>
<el-button v-else class="ml5 mt5" size="small" @click="showInputDb"> + 添加数据库 </el-button>
</el-form-item>
<el-form-item prop="enable_ssh" label="SSH:" v-if="form.type === 'mysql'">
<el-checkbox v-model="form.enable_ssh" :true-label=1 :false-label=0></el-checkbox>
</el-form-item>
<el-form-item prop="ssh_host" label="SSH Host:" v-if="form.enable_ssh === 1 && form.type === 'mysql'">
<el-input v-model.trim="form.ssh_host" placeholder="请输入主机ip" auto-complete="off"></el-input>
</el-form-item>
<el-form-item prop="ssh_user" label="SSH User:" v-if="form.enable_ssh === 1 && form.type === 'mysql'">
<el-input v-model.trim="form.ssh_user" placeholder="请输入用户名"></el-input>
</el-form-item>
<el-form-item prop="ssh_pass" label="SSH Pass:" v-if="form.enable_ssh === 1 && form.type === 'mysql'">
<el-input
type="password"
show-password
v-model.trim="form.ssh_pass"
placeholder="请输入密码,修改操作可不填"
autocomplete="new-password"
></el-input>
</el-form-item>
<el-form-item prop="ssh_port" label="SSH Port:" v-if="form.enable_ssh === 1 && form.type === 'mysql'">
<el-input type="number" v-model.number="form.ssh_port" placeholder="请输入端口"></el-input>
</el-form-item>
</el-form>
<template #footer>
@@ -127,6 +149,11 @@ export default defineComponent({
projectId: null,
envId: null,
env: null,
enable_ssh: null,
ssh_host: null,
ssh_user: null,
ssh_pass: null,
ssh_port: 22,
},
btnLoading: false,
rules: {
@@ -264,6 +291,7 @@ export default defineComponent({
if (valid) {
const reqForm = { ...state.form };
reqForm.password = await RsaEncrypt(reqForm.password);
reqForm.ssh_pass = await RsaEncrypt(reqForm.ssh_pass);
dbApi.saveDb.request(reqForm).then(() => {
ElMessage.success('保存成功');
emit('val-change', state.form);

View File

@@ -55,6 +55,15 @@ func (d *Db) Save(rc *ctx.ReqCtx) {
// 密码脱敏记录日志
form.Password = "****"
if form.Type == "mysql" && form.EnableSSH == 1 {
originSSHPwd, err := utils.DefaultRsaDecrypt(form.SSHPass, true)
biz.ErrIsNilAppendErr(err, "解密密码错误: %s")
db.SSHPass = originSSHPwd
// 密码脱敏记录日志
form.SSHPass = "****"
}
rc.ReqParam = form
db.SetBaseInfo(rc.LoginAccount)

View File

@@ -14,6 +14,12 @@ type DbForm struct {
Project string `json:"project"`
Env string `json:"env"`
EnvId uint64 `binding:"required" json:"envId"`
EnableSSH int `json:"enable_ssh"`
SSHHost string `json:"ssh_host"`
SSHPort int `json:"ssh_port"`
SSHUser string `json:"ssh_user"`
SSHPass string `json:"ssh_pass"`
}
type DbSqlSaveForm struct {

View File

@@ -19,4 +19,9 @@ type SelectDataDbVO struct {
CreateTime *time.Time `json:"createTime"`
Creator *string `json:"creator"`
CreatorId *int64 `json:"creatorId"`
EnableSSH *int `json:"enable_ssh"`
SSHHost *string `json:"ssh_host"`
SSHPort *int `json:"ssh_port"`
SSHUser *string `json:"ssh_user"`
}

View File

@@ -12,12 +12,14 @@ import (
"mayfly-go/pkg/global"
"mayfly-go/pkg/model"
"mayfly-go/pkg/utils"
"net"
"reflect"
"strconv"
"strings"
"sync"
"time"
"github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
)
@@ -75,7 +77,12 @@ func (d *dbAppImpl) GetById(id uint64, cols ...string) *entity.Db {
func (d *dbAppImpl) Save(dbEntity *entity.Db) {
// 默认tcp连接
dbEntity.Network = "tcp"
if dbEntity.Type == "mysql" && dbEntity.EnableSSH == 1 {
dbEntity.Network = "mysql+ssh"
} else {
dbEntity.Network = "tcp"
}
// 测试连接
if dbEntity.Password != "" {
TestConnection(*dbEntity)
@@ -155,6 +162,18 @@ func (da *dbAppImpl) GetDbInstance(id uint64, db string) *DbInstance {
biz.IsTrue(strings.Contains(d.Database, db), "未配置该库的操作权限")
global.Log.Infof("连接db: %s:%d/%s", d.Host, d.Port, db)
//SSH Conect
if d.Type == "mysql" && d.EnableSSH == 1 {
sshClient, err := utils.SSHConnect(d.SSHUser, d.SSHPass, d.SSHHost, "", d.SSHPort)
if err != nil {
global.Log.Errorf("ssh连接失败: %s@%s:%d", d.SSHUser, d.SSHHost, d.SSHPort)
panic(biz.NewBizErr(fmt.Sprintf("ssh连接失败: %s", err.Error())))
}
mysql.RegisterDial("mysql+ssh", func(addr string) (net.Conn, error) {
return sshClient.Dial("tcp", addr)
})
}
// 将数据库替换为要访问的数据库,原本数据库为空格拼接的所有库
d.Database = db
DB, err := sql.Open(d.Type, getDsn(d))
@@ -202,6 +221,18 @@ func GetDbInstanceByCache(id string) *DbInstance {
}
func TestConnection(d entity.Db) {
//SSH Conect
if d.Type == "mysql" && d.EnableSSH == 1 {
sshClient, err := utils.SSHConnect(d.SSHUser, d.SSHPass, d.SSHHost, "", d.SSHPort)
if err != nil {
global.Log.Errorf("ssh连接失败: %s@%s:%d", d.SSHUser, d.SSHHost, d.SSHPort)
panic(biz.NewBizErr(fmt.Sprintf("ssh连接失败: %s", err.Error())))
}
mysql.RegisterDial("mysql+ssh", func(addr string) (net.Conn, error) {
return sshClient.Dial("tcp", addr)
})
}
// 验证第一个库是否可以连接即可
d.Database = strings.Split(d.Database, " ")[0]
DB, err := sql.Open(d.Type, getDsn(&d))

View File

@@ -20,4 +20,10 @@ type Db struct {
Project string
EnvId uint64
Env string
EnableSSH int `orm:"column(enable_ssh)" json:"enable_ssh"`
SSHHost string `orm:"column(ssh_host)" json:"ssh_host"`
SSHPort int `orm:"column(ssh_port)" json:"ssh_port"`
SSHUser string `orm:"column(ssh_user)" json:"ssh_user"`
SSHPass string `orm:"column(ssh_pass)" json:"-"`
}

View File

@@ -29,7 +29,7 @@ CREATE TABLE `t_db` (
`type` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '数据库实例类型(mysql...)',
`database` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '数据库,空格分割多个数据库',
`params` varchar(125) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '其他连接参数',
`network` varchar(8) COLLATE utf8mb4_bin DEFAULT NULL,
`network` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
`project_id` bigint(20) DEFAULT NULL,
`project` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL,
`env_id` bigint(20) DEFAULT NULL COMMENT '环境id',
@@ -41,6 +41,11 @@ CREATE TABLE `t_db` (
`update_time` datetime DEFAULT NULL,
`modifier_id` bigint(20) DEFAULT NULL,
`modifier` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL,
`enable_ssh` tinyint(1) unsigned NOT NULL DEFAULT '0',
`ssh_host` varchar(50) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
`ssh_port` int(8) NOT NULL,
`ssh_user` varchar(255) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
`ssh_pass` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='数据库资源信息表';

View File

@@ -0,0 +1,58 @@
package utils
import (
"fmt"
"io/ioutil"
"net"
"time"
"golang.org/x/crypto/ssh"
)
func SSHConnect(user, password, host, key string, port int) (*ssh.Client, error) {
var (
auth []ssh.AuthMethod
addr string
clientConfig *ssh.ClientConfig
client *ssh.Client
config ssh.Config
//session *ssh.Session
err error
)
// get auth method
auth = make([]ssh.AuthMethod, 0)
if key == "" {
auth = append(auth, ssh.Password(password))
} else {
pemBytes, err := ioutil.ReadFile(key)
if err != nil {
return nil, err
}
var signer ssh.Signer
if password == "" {
signer, err = ssh.ParsePrivateKey(pemBytes)
} else {
signer, err = ssh.ParsePrivateKeyWithPassphrase(pemBytes, []byte(password))
}
if err != nil {
return nil, err
}
auth = append(auth, ssh.PublicKeys(signer))
}
clientConfig = &ssh.ClientConfig{
User: user,
Auth: auth,
Timeout: 30 * time.Second,
Config: config,
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
addr = fmt.Sprintf("%s:%d", host, port)
client, err = ssh.Dial("tcp", addr, clientConfig)
return client, err
}