From fbb996843cecbc6a22aaeb4c82a06d5450bbe21f Mon Sep 17 00:00:00 2001 From: dushixiang Date: Thu, 8 Apr 2021 18:57:51 +0800 Subject: [PATCH 1/9] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8E=9F=E7=94=9Fssh?= =?UTF-8?q?=E6=8E=A5=E5=85=A5=E6=97=B6=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E8=A2=AB=E6=8C=A1=E4=BD=8F=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/components/access/Term.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/web/src/components/access/Term.js b/web/src/components/access/Term.js index 786cc9d..68f098d 100644 --- a/web/src/components/access/Term.js +++ b/web/src/components/access/Term.js @@ -20,7 +20,8 @@ class Term extends Component { term: undefined, webSocket: undefined, fitAddon: undefined, - sessionId: undefined + sessionId: undefined, + enterBtnIndex: 1001 }; componentDidMount = async () => { @@ -215,17 +216,17 @@ class Term extends Component { }}/> - + + + + + + + + + + + + + + + + + + `总计 ${total} 条` + }} + loading={this.state.loading} + /> + + + ); + } +} + +export default ChooseAsset; diff --git a/web/src/components/command/DynamicCommand.js b/web/src/components/command/DynamicCommand.js index 08eea4d..c7161bc 100644 --- a/web/src/components/command/DynamicCommand.js +++ b/web/src/components/command/DynamicCommand.js @@ -32,10 +32,10 @@ import { SyncOutlined, UndoOutlined } from '@ant-design/icons'; -import {compare} from "../../utils/utils"; import {hasPermission, isAdmin} from "../../service/permission"; import dayjs from "dayjs"; +import ChooseAsset from "./ChooseAsset"; const confirm = Modal.confirm; const {Content} = Layout; @@ -191,6 +191,12 @@ class DynamicCommand extends Component { }); }; + setCheckedAssets = (checkedAssets) => { + this.setState({ + checkedAssets: checkedAssets + }) + } + executeCommand = e => { let checkedAssets = this.state.checkedAssets; if (checkedAssets.length === 0) { @@ -198,18 +204,10 @@ class DynamicCommand extends Component { return; } - let assets = this.state.assets; let cAssets = checkedAssets.map(item => { - let name = ''; - for (let i = 0; i < assets.length; i++) { - if (assets[i]['id'] === item) { - name = assets[i]['name']; - break; - } - } return { - id: item, - name: name + id: item['id'], + name: item['name'] } }); @@ -474,17 +472,6 @@ class DynamicCommand extends Component { assetsVisible: true, commandId: record['id'] }); - - let result = await request.get('/assets?protocol=ssh'); - if (result.code === 1) { - let assets = result.data; - assets.sort(compare('name')); - this.setState({ - assets: assets - }); - } else { - message.error(result.message); - } }}>执行 @@ -640,7 +627,8 @@ class DynamicCommand extends Component { { this.setState({ @@ -648,19 +636,11 @@ class DynamicCommand extends Component { }); }} > - - 全选 - - + - { - return { - label: item.name, - value: item.id, - key: item.id, - } - })} value={this.state.checkedAssets} onChange={this.onChange}/> + From ef9efb7fbaa8b587243f841352323b1a42a5eb34 Mon Sep 17 00:00:00 2001 From: dushixiang Date: Thu, 8 Apr 2021 21:48:35 +0800 Subject: [PATCH 4/9] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- docs/faq.md | 2 +- docs/install-naive.md | 35 ++++++++++++++++++++++++++++------- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 155b27e..d0cc84b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Next Terminal基于 [Apache Guacamole](https://guacamole.apache.org/) 开发, - 批量执行命令 - 在线会话管理(监控、强制断开) - 离线会话管理(查看录屏) -- 双因素认证 感谢 [naiba](https://github.com/naiba) 贡献 +- 双因素认证 - 资产标签 - 资产授权 - 多用户&用户分组 diff --git a/docs/faq.md b/docs/faq.md index fda1bc1..10ca15d 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -39,7 +39,7 @@ docker pull dushixiang/next-terminal:latest ```shell docker rm -f ``` -再重新执行一次 [docker方式安装命令](install-naive.md) +再重新执行一次 [docker方式安装命令](install-docker.md) diff --git a/docs/install-naive.md b/docs/install-naive.md index 6baa096..35c321e 100644 --- a/docs/install-naive.md +++ b/docs/install-naive.md @@ -119,10 +119,7 @@ mkfontdir fc-cache ``` ### 安装 Next Terminal -建立next-terminal目录 -```shell -mkdir ~/next-terminal && cd ~/next-terminal -``` +> 示例步骤安装在 `/usr/local/next-terminal`,你可以自由选择安装目录。 下载 ```shell @@ -131,11 +128,10 @@ wget https://github.com/dushixiang/next-terminal/releases/latest/download/next-t 解压 ```shell -tar -xvf next-terminal.tgz -cd next-terminal +tar -zxvf next-terminal.tgz -C /usr/local/ ``` -在当前目录下创建或修改配置文件`config.yml` +在`/usr/local/next-terminal`或`/etc/next-terminal`下创建或修改配置文件`config.yml` ```shell db: sqlite # 当db为sqlite时mysql的配置无效 @@ -160,3 +156,28 @@ server: ```shell ./next-terminal ``` + +使用systemd方式启动 + +在 `/etc/systemd/system/` 目录创建文件并写入以下内容 +```shell +[Unit] +Description=next-terminal service +After=network.target + +[Service] +User=root +WorkingDirectory=/usr/local/next-terminal +ExecStart=/usr/local/next-terminal/next-terminal +Restart=on-failure + +[Install] +WantedBy=multi-user.target +``` + +重载系统服务&&设置开机启动&&启动服务 +```shell +systemctl daemon-reload +systemctl enable next-terminal +systemctl start next-terminal +``` \ No newline at end of file From 4f73cd36735c86e4f0d2ef308d768d690b254a2b Mon Sep 17 00:00:00 2001 From: dushixiang Date: Mon, 12 Apr 2021 22:53:21 +0800 Subject: [PATCH 5/9] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=8E=9F=E7=94=9F?= =?UTF-8?q?=E5=AE=89=E8=A3=85=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/install-naive.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/install-naive.md b/docs/install-naive.md index 35c321e..146f7b7 100644 --- a/docs/install-naive.md +++ b/docs/install-naive.md @@ -157,9 +157,9 @@ server: ./next-terminal ``` -使用systemd方式启动 +使用系统服务方式启动 -在 `/etc/systemd/system/` 目录创建文件并写入以下内容 +在 `/etc/systemd/system/` 目录创建 `next-terminal.service` 文件并写入以下内容 ```shell [Unit] Description=next-terminal service @@ -175,9 +175,10 @@ Restart=on-failure WantedBy=multi-user.target ``` -重载系统服务&&设置开机启动&&启动服务 +重载系统服务&&设置开机启动&&启动服务&&查看状态 ```shell systemctl daemon-reload systemctl enable next-terminal systemctl start next-terminal +systemctl staus next-terminal ``` \ No newline at end of file From 5cee2dd50e49bf45802baec0250e5a514fe1dd40 Mon Sep 17 00:00:00 2001 From: dushixiang Date: Tue, 13 Apr 2021 23:56:53 +0800 Subject: [PATCH 6/9] =?UTF-8?q?=E5=A2=9E=E5=8A=A0AES=20CBC=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E5=B7=A5=E5=85=B7=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/install-naive.md | 2 +- server/utils/util_test.go | 18 +++++++++++++++++ server/utils/utils.go | 42 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/docs/install-naive.md b/docs/install-naive.md index 146f7b7..9dfc927 100644 --- a/docs/install-naive.md +++ b/docs/install-naive.md @@ -180,5 +180,5 @@ WantedBy=multi-user.target systemctl daemon-reload systemctl enable next-terminal systemctl start next-terminal -systemctl staus next-terminal +systemctl status next-terminal ``` \ No newline at end of file diff --git a/server/utils/util_test.go b/server/utils/util_test.go index 2f9d0f3..70c6330 100644 --- a/server/utils/util_test.go +++ b/server/utils/util_test.go @@ -1,6 +1,7 @@ package utils_test import ( + "encoding/base64" "net" "testing" @@ -33,3 +34,20 @@ func TestTcping(t *testing.T) { _ = conn.Close() }() } + +func TestAesEncryptCBC(t *testing.T) { + origData := []byte("Hello Next Terminal") // 待加密的数据 + key := []byte("qwertyuiopasdfgh") // 加密的密钥 + encryptedCBC, err := utils.AesEncryptCBC(origData, key) + assert.NoError(t, err) + assert.Equal(t, "s2xvMRPfZjmttpt+x0MzG9dsWcf1X+h9nt7waLvXpNM=", base64.StdEncoding.EncodeToString(encryptedCBC)) +} + +func TestAesDecryptCBC(t *testing.T) { + origData, err := base64.StdEncoding.DecodeString("s2xvMRPfZjmttpt+x0MzG9dsWcf1X+h9nt7waLvXpNM=") // 待解密的数据 + assert.NoError(t, err) + key := []byte("qwertyuiopasdfgh") // 解密的密钥 + decryptCBC, err := utils.AesDecryptCBC(origData, key) + assert.NoError(t, err) + assert.Equal(t, "Hello Next Terminal", string(decryptCBC)) +} diff --git a/server/utils/utils.go b/server/utils/utils.go index d586471..0f94e50 100644 --- a/server/utils/utils.go +++ b/server/utils/utils.go @@ -2,6 +2,8 @@ package utils import ( "bytes" + "crypto/aes" + "crypto/cipher" "crypto/md5" "database/sql/driver" "encoding/base64" @@ -224,3 +226,43 @@ func Check(f func() error) { logrus.Error("Received error:", err) } } + +func PKCS5Padding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padText := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padText...) +} + +func PKCS5UnPadding(origData []byte) []byte { + length := len(origData) + unPadding := int(origData[length-1]) + return origData[:(length - unPadding)] +} + +func AesEncryptCBC(origData, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + blockSize := block.BlockSize() + origData = PKCS5Padding(origData, blockSize) + blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) + encrypted := make([]byte, len(origData)) + blockMode.CryptBlocks(encrypted, origData) + return encrypted, nil +} + +func AesDecryptCBC(encrypted, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + blockSize := block.BlockSize() + blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) + origData := make([]byte, len(encrypted)) + blockMode.CryptBlocks(origData, encrypted) + origData = PKCS5UnPadding(origData) + return origData, nil +} From 11f2d8a1f44c08bf746fe9517b6adeda0d5070d6 Mon Sep 17 00:00:00 2001 From: dushixiang Date: Wed, 14 Apr 2021 22:27:12 +0800 Subject: [PATCH 7/9] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=87=8D=E7=BD=AEtotp?= =?UTF-8?q?=E7=9A=84=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/faq.md | 31 ++++++++++++++++++++++++++++++- main.go | 5 ++++- pkg/config/config.go | 2 ++ server/api/routes.go | 20 ++++++++++++++++++-- 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 10ca15d..9402d86 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -18,7 +18,8 @@ location / {
访问realvnc提示验证失败? -把加密类型修改为 Prefer On +1. 把密码类型修改为VNC +2. 把加密类型修改为 Prefer On
@@ -88,3 +89,31 @@ Mar 5 20:00:16.923 [DEBU] 用户「admin」密码初始化为: next-terminal +
+ TOTP客户端丢了怎么办? +首先需要进入程序所在目录,使用docker安装的程序目录为:/usr/local/next-terminal + +执行命令 + +```shell +./next-terminal --reset-totp admin +``` + +其中 admin 为用户登录账号,成功之后会输出 + +``` shell + + _______ __ ___________ .__ .__ + \ \ ____ ___ ____/ |_ \__ ___/__________ _____ |__| ____ _____ | | + / | \_/ __ \\ \/ /\ __\ | |_/ __ \_ __ \/ \| |/ \\__ \ | | +/ | \ ___/ > < | | | |\ ___/| | \/ Y Y \ | | \/ __ \| |__ +\____|__ /\___ >__/\_ \ |__| |____| \___ >__| |__|_| /__|___| (____ /____/ + \/ \/ \/ \/ \/ \/ \/ v0.3.0 + +当前数据库模式为:mysql +Mar 5 20:00:16.923 [DEBU] 用户「admin」已重置TOTP + +``` + +
+ diff --git a/main.go b/main.go index 597d7d6..5a9b938 100644 --- a/main.go +++ b/main.go @@ -39,7 +39,10 @@ func Run() error { e := api.SetupRoutes(db) if global.Config.ResetPassword != "" { - return api.ResetPassword() + return api.ResetPassword(global.Config.ResetPassword) + } + if global.Config.ResetTotp != "" { + return api.ResetTotp(global.Config.ResetTotp) } sessionRepo := repository.NewSessionRepository(db) propertyRepo := repository.NewPropertyRepository(db) diff --git a/pkg/config/config.go b/pkg/config/config.go index 5fe0868..03e79e5 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -18,6 +18,7 @@ type Config struct { Mysql *Mysql Sqlite *Sqlite ResetPassword string + ResetTotp string } type Mysql struct { @@ -83,6 +84,7 @@ func SetupConfig() *Config { Key: viper.GetString("server.key"), }, ResetPassword: viper.GetString("reset-password"), + ResetTotp: viper.GetString("reset-totp"), Debug: viper.GetBool("debug"), Demo: viper.GetBool("demo"), } diff --git a/server/api/routes.go b/server/api/routes.go index 0a67e1b..ad545da 100644 --- a/server/api/routes.go +++ b/server/api/routes.go @@ -275,8 +275,8 @@ func InitDBData() (err error) { return nil } -func ResetPassword() error { - user, err := userRepository.FindByUsername(global.Config.ResetPassword) +func ResetPassword(username string) error { + user, err := userRepository.FindByUsername(username) if err != nil { return err } @@ -296,6 +296,22 @@ func ResetPassword() error { return nil } +func ResetTotp(username string) error { + user, err := userRepository.FindByUsername(username) + if err != nil { + return err + } + u := &model.User{ + TOTPSecret: "-", + ID: user.ID, + } + if err := userRepository.Update(u); err != nil { + return err + } + log.Debugf("用户「%v」已重置TOTP", user.Username) + return nil +} + func SetupCache() *cache.Cache { // 配置缓存器 mCache := cache.New(5*time.Minute, 10*time.Minute) From bceda9a95c9e8b515efc5be0565eb3cd56b045b4 Mon Sep 17 00:00:00 2001 From: dushixiang Date: Sat, 17 Apr 2021 17:34:48 +0800 Subject: [PATCH 8/9] =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E6=95=8F=E6=84=9F=E4=BF=A1=E6=81=AF=E7=9A=84=E5=8A=A0?= =?UTF-8?q?=E5=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/faq.md | 31 ++++++++++- docs/install-docker.md | 1 + docs/install-naive.md | 5 +- main.go | 14 ++++- pkg/config/config.go | 29 +++++----- pkg/service/asset.go | 58 ++++++++++++++++++++ pkg/service/credential.go | 58 ++++++++++++++++++++ pkg/service/job.go | 4 +- pkg/service/session.go | 4 ++ pkg/service/user.go | 2 +- server/api/asset.go | 13 +++-- server/api/credential.go | 47 ++++++++++++---- server/api/routes.go | 54 ++++++++++++++++++- server/api/session.go | 5 +- server/api/ssh.go | 2 +- server/api/tunnel.go | 2 +- server/model/asset.go | 1 + server/model/credential.go | 1 + server/repository/asset.go | 83 +++++++++++++++++++++++++++- server/repository/credential.go | 90 +++++++++++++++++++++++++++++++ server/repository/session.go | 53 ++++++++++++++++++ server/utils/util_test.go | 26 +++++++++ server/utils/utils.go | 17 ++++++ web/package.json | 2 +- web/src/components/asset/Asset.js | 4 +- 25 files changed, 566 insertions(+), 40 deletions(-) create mode 100644 pkg/service/asset.go create mode 100644 pkg/service/credential.go diff --git a/docs/faq.md b/docs/faq.md index 9402d86..934d932 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -108,7 +108,7 @@ Mar 5 20:00:16.923 [DEBU] 用户「admin」密码初始化为: next-terminal / | \_/ __ \\ \/ /\ __\ | |_/ __ \_ __ \/ \| |/ \\__ \ | | / | \ ___/ > < | | | |\ ___/| | \/ Y Y \ | | \/ __ \| |__ \____|__ /\___ >__/\_ \ |__| |____| \___ >__| |__|_| /__|___| (____ /____/ - \/ \/ \/ \/ \/ \/ \/ v0.3.0 + \/ \/ \/ \/ \/ \/ \/ v0.4.0 当前数据库模式为:mysql Mar 5 20:00:16.923 [DEBU] 用户「admin」已重置TOTP @@ -117,3 +117,32 @@ Mar 5 20:00:16.923 [DEBU] 用户「admin」已重置TOTP +
+ 想要修改数据库敏感信息加密的key怎么办? +首先需要进入程序所在目录,使用docker安装的程序目录为:/usr/local/next-terminal + +执行命令 + +```shell +./next-terminal --encryption-key 旧的加密key new-encryption-key 新的的加密key +``` + +成功之后会输出 + +``` shell + + _______ __ ___________ .__ .__ + \ \ ____ ___ ____/ |_ \__ ___/__________ _____ |__| ____ _____ | | + / | \_/ __ \\ \/ /\ __\ | |_/ __ \_ __ \/ \| |/ \\__ \ | | +/ | \ ___/ > < | | | |\ ___/| | \/ Y Y \ | | \/ __ \| |__ +\____|__ /\___ >__/\_ \ |__| |____| \___ >__| |__|_| /__|___| (____ /____/ + \/ \/ \/ \/ \/ \/ \/ v0.4.0 + +当前数据库模式为:mysql +Mar 5 20:00:16.923 [DEBU] encryption key has being changed. + +``` + +最后重新启动程序,并且把加密key修改为新的。 +
+ diff --git a/docs/install-docker.md b/docs/install-docker.md index 89ad9a9..77eb4c5 100644 --- a/docs/install-docker.md +++ b/docs/install-docker.md @@ -104,6 +104,7 @@ docker run -d \ | MYSQL_PASSWORD | `mysql`数据库密码 | | MYSQL_DATABASE | `mysql`数据库名称 | | SERVER_ADDR | 服务器监听地址,默认`0.0.0.0:8088` | +| ENCRYPTION_KEY | 授权凭证和资产的密码,密钥等敏感信息加密的key,默认`next-terminal` | ## 其他 diff --git a/docs/install-naive.md b/docs/install-naive.md index 9dfc927..43605ad 100644 --- a/docs/install-naive.md +++ b/docs/install-naive.md @@ -147,9 +147,12 @@ sqlite: file: 'next-terminal.db' server: addr: 0.0.0.0:8088 -# 当设置下面两个参数时会自动开启https模式 +# 当设置下面两个参数时会自动开启https模式(前提是证书文件存在) # cert: /root/next-terminal/cert.pem # key: /root/next-terminal/key.pem + +# 授权凭证和资产的密码,密钥等敏感信息加密的key,默认`next-terminal` +#encryption-key: next-terminal ``` 启动 diff --git a/main.go b/main.go index 5a9b938..d5e953b 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "crypto/md5" "fmt" "next-terminal/pkg/config" @@ -12,7 +13,7 @@ import ( "github.com/labstack/gommon/log" ) -const Version = "v0.3.4" +const Version = "v0.4.0" func main() { err := Run() @@ -34,6 +35,12 @@ func Run() error { // 为了兼容之前调用global包的代码 后期预期会改为调用pgk/config global.Config = config.GlobalCfg + if global.Config.EncryptionKey == "" { + global.Config.EncryptionKey = "next-terminal" + } + md5Sum := fmt.Sprintf("%x", md5.Sum([]byte(global.Config.EncryptionKey))) + global.Config.EncryptionPassword = []byte(md5Sum) + global.Cache = api.SetupCache() db := api.SetupDB() e := api.SetupRoutes(db) @@ -44,6 +51,11 @@ func Run() error { if global.Config.ResetTotp != "" { return api.ResetTotp(global.Config.ResetTotp) } + + if global.Config.NewEncryptionKey != "" { + return api.ChangeEncryptionKey(global.Config.EncryptionKey, global.Config.NewEncryptionKey) + } + sessionRepo := repository.NewSessionRepository(db) propertyRepo := repository.NewPropertyRepository(db) ticker := task.NewTicker(sessionRepo, propertyRepo) diff --git a/pkg/config/config.go b/pkg/config/config.go index 03e79e5..f7b31a5 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -11,14 +11,17 @@ import ( var GlobalCfg *Config type Config struct { - Debug bool - Demo bool - DB string - Server *Server - Mysql *Mysql - Sqlite *Sqlite - ResetPassword string - ResetTotp string + Debug bool + Demo bool + DB string + Server *Server + Mysql *Mysql + Sqlite *Sqlite + ResetPassword string + ResetTotp string + EncryptionKey string + EncryptionPassword []byte + NewEncryptionKey string } type Mysql struct { @@ -83,10 +86,12 @@ func SetupConfig() *Config { Cert: viper.GetString("server.cert"), Key: viper.GetString("server.key"), }, - ResetPassword: viper.GetString("reset-password"), - ResetTotp: viper.GetString("reset-totp"), - Debug: viper.GetBool("debug"), - Demo: viper.GetBool("demo"), + ResetPassword: viper.GetString("reset-password"), + ResetTotp: viper.GetString("reset-totp"), + Debug: viper.GetBool("debug"), + Demo: viper.GetBool("demo"), + EncryptionKey: viper.GetString("encryption-key"), + NewEncryptionKey: viper.GetString("new-encryption-key"), } GlobalCfg = config return config diff --git a/pkg/service/asset.go b/pkg/service/asset.go new file mode 100644 index 0000000..934e127 --- /dev/null +++ b/pkg/service/asset.go @@ -0,0 +1,58 @@ +package service + +import ( + "encoding/base64" + + "next-terminal/pkg/global" + "next-terminal/server/repository" + "next-terminal/server/utils" +) + +type AssetService struct { + assetRepository *repository.AssetRepository +} + +func NewAssetService(assetRepository *repository.AssetRepository) *AssetService { + return &AssetService{assetRepository: assetRepository} +} + +func (r AssetService) Encrypt() error { + items, err := r.assetRepository.FindAll() + if err != nil { + return err + } + for i := range items { + item := items[i] + if item.Encrypted { + continue + } + if item.Password != "" && item.Password != "-" { + encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Password), global.Config.EncryptionPassword) + if err != nil { + return err + } + item.Password = base64.StdEncoding.EncodeToString(encryptedCBC) + } + + if item.PrivateKey != "" && item.PrivateKey != "-" { + encryptedCBC, err := utils.AesEncryptCBC([]byte(item.PrivateKey), global.Config.EncryptionPassword) + if err != nil { + return err + } + item.PrivateKey = base64.StdEncoding.EncodeToString(encryptedCBC) + } + + if item.Passphrase != "" && item.Passphrase != "-" { + encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Passphrase), global.Config.EncryptionPassword) + if err != nil { + return err + } + item.Passphrase = base64.StdEncoding.EncodeToString(encryptedCBC) + } + err = r.assetRepository.EncryptedById(true, item.Password, item.PrivateKey, item.Passphrase, item.ID) + if err != nil { + return err + } + } + return nil +} diff --git a/pkg/service/credential.go b/pkg/service/credential.go new file mode 100644 index 0000000..89d0371 --- /dev/null +++ b/pkg/service/credential.go @@ -0,0 +1,58 @@ +package service + +import ( + "encoding/base64" + + "next-terminal/pkg/global" + "next-terminal/server/repository" + "next-terminal/server/utils" +) + +type CredentialService struct { + credentialRepository *repository.CredentialRepository +} + +func NewCredentialService(credentialRepository *repository.CredentialRepository) *CredentialService { + return &CredentialService{credentialRepository: credentialRepository} +} + +func (r CredentialService) Encrypt() error { + items, err := r.credentialRepository.FindAll() + if err != nil { + return err + } + for i := range items { + item := items[i] + if item.Encrypted { + continue + } + if item.Password != "" && item.Password != "-" { + encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Password), global.Config.EncryptionPassword) + if err != nil { + return err + } + item.Password = base64.StdEncoding.EncodeToString(encryptedCBC) + } + + if item.PrivateKey != "" && item.PrivateKey != "-" { + encryptedCBC, err := utils.AesEncryptCBC([]byte(item.PrivateKey), global.Config.EncryptionPassword) + if err != nil { + return err + } + item.PrivateKey = base64.StdEncoding.EncodeToString(encryptedCBC) + } + + if item.Passphrase != "" && item.Passphrase != "-" { + encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Passphrase), global.Config.EncryptionPassword) + if err != nil { + return err + } + item.Passphrase = base64.StdEncoding.EncodeToString(encryptedCBC) + } + err = r.credentialRepository.EncryptedById(true, item.Password, item.PrivateKey, item.Passphrase, item.ID) + if err != nil { + return err + } + } + return nil +} diff --git a/pkg/service/job.go b/pkg/service/job.go index d923ebc..9c592f6 100644 --- a/pkg/service/job.go +++ b/pkg/service/job.go @@ -160,7 +160,7 @@ func (r ShellJob) Run() { msgChan := make(chan string) for i := range assets { - asset, err := r.jobService.assetRepository.FindById(assets[i].ID) + asset, err := r.jobService.assetRepository.FindByIdAndDecrypt(assets[i].ID) if err != nil { msgChan <- fmt.Sprintf("资产「%v」Shell执行失败,查询数据异常「%v」", assets[i].Name, err.Error()) return @@ -176,7 +176,7 @@ func (r ShellJob) Run() { ) if asset.AccountType == "credential" { - credential, err := r.jobService.credentialRepository.FindById(asset.CredentialId) + credential, err := r.jobService.credentialRepository.FindByIdAndDecrypt(asset.CredentialId) if err != nil { msgChan <- fmt.Sprintf("资产「%v」Shell执行失败,查询授权凭证数据异常「%v」", assets[i].Name, err.Error()) return diff --git a/pkg/service/session.go b/pkg/service/session.go index ae6b267..6a8be9f 100644 --- a/pkg/service/session.go +++ b/pkg/service/session.go @@ -33,3 +33,7 @@ func (r SessionService) FixSessionState() error { } return nil } + +func (r SessionService) EmptyPassword() error { + return r.sessionRepository.EmptyPassword() +} diff --git a/pkg/service/user.go b/pkg/service/user.go index b57f67c..9f0bcd5 100644 --- a/pkg/service/user.go +++ b/pkg/service/user.go @@ -58,7 +58,7 @@ func (r UserService) InitUser() (err error) { return nil } -func (r UserService) FixedUserOnlineState() error { +func (r UserService) FixUserOnlineState() error { // 修正用户登录状态 onlineUsers, err := r.userRepository.FindOnlineUsers() if err != nil { diff --git a/server/api/asset.go b/server/api/asset.go index 2e4e470..3a22fed 100644 --- a/server/api/asset.go +++ b/server/api/asset.go @@ -9,6 +9,7 @@ import ( "strings" "next-terminal/pkg/constant" + "next-terminal/pkg/global" "next-terminal/server/model" "next-terminal/server/utils" @@ -199,6 +200,9 @@ func AssetUpdateEndpoint(c echo.Context) error { item.Description = "-" } + if err := assetRepository.Encrypt(&item, global.Config.EncryptionPassword); err != nil { + return err + } if err := assetRepository.UpdateById(&item, id); err != nil { return err } @@ -264,7 +268,7 @@ func AssetGetEndpoint(c echo.Context) (err error) { } var item model.Asset - if item, err = assetRepository.FindById(id); err != nil { + if item, err = assetRepository.FindByIdAndDecrypt(id); err != nil { return err } attributeMap, err := assetRepository.FindAssetAttrMapByAssetId(id) @@ -289,9 +293,12 @@ func AssetTcpingEndpoint(c echo.Context) (err error) { active := utils.Tcping(item.IP, item.Port) - if err := assetRepository.UpdateActiveById(active, item.ID); err != nil { - return err + if item.Active != active { + if err := assetRepository.UpdateActiveById(active, item.ID); err != nil { + return err + } } + return Success(c, active) } diff --git a/server/api/credential.go b/server/api/credential.go index 25caf1a..49a4643 100644 --- a/server/api/credential.go +++ b/server/api/credential.go @@ -1,11 +1,13 @@ package api import ( + "encoding/base64" "errors" "strconv" "strings" "next-terminal/pkg/constant" + "next-terminal/pkg/global" "next-terminal/server/model" "next-terminal/server/utils" @@ -32,27 +34,28 @@ func CredentialCreateEndpoint(c echo.Context) error { case constant.Custom: item.PrivateKey = "-" item.Passphrase = "-" - if len(item.Username) == 0 { + if item.Username == "" { item.Username = "-" } - if len(item.Password) == 0 { + if item.Password == "" { item.Password = "-" } case constant.PrivateKey: item.Password = "-" - if len(item.Username) == 0 { + if item.Username == "" { item.Username = "-" } - if len(item.PrivateKey) == 0 { + if item.PrivateKey == "" { item.PrivateKey = "-" } - if len(item.Passphrase) == 0 { + if item.Passphrase == "" { item.Passphrase = "-" } default: return Fail(c, -1, "类型错误") } + item.Encrypted = true if err := credentialRepository.Create(&item); err != nil { return err } @@ -96,26 +99,48 @@ func CredentialUpdateEndpoint(c echo.Context) error { case constant.Custom: item.PrivateKey = "-" item.Passphrase = "-" - if len(item.Username) == 0 { + if item.Username == "" { item.Username = "-" } - if len(item.Password) == 0 { + if item.Password == "" { item.Password = "-" } + if item.Password != "-" { + encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Password), global.Config.EncryptionPassword) + if err != nil { + return err + } + item.Password = base64.StdEncoding.EncodeToString(encryptedCBC) + } case constant.PrivateKey: item.Password = "-" - if len(item.Username) == 0 { + if item.Username == "" { item.Username = "-" } - if len(item.PrivateKey) == 0 { + if item.PrivateKey == "" { item.PrivateKey = "-" } - if len(item.Passphrase) == 0 { + if item.PrivateKey != "-" { + encryptedCBC, err := utils.AesEncryptCBC([]byte(item.PrivateKey), global.Config.EncryptionPassword) + if err != nil { + return err + } + item.PrivateKey = base64.StdEncoding.EncodeToString(encryptedCBC) + } + if item.Passphrase == "" { item.Passphrase = "-" } + if item.Passphrase != "-" { + encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Passphrase), global.Config.EncryptionPassword) + if err != nil { + return err + } + item.Passphrase = base64.StdEncoding.EncodeToString(encryptedCBC) + } default: return Fail(c, -1, "类型错误") } + item.Encrypted = true if err := credentialRepository.UpdateById(&item, id); err != nil { return err @@ -149,7 +174,7 @@ func CredentialGetEndpoint(c echo.Context) error { return err } - item, err := credentialRepository.FindById(id) + item, err := credentialRepository.FindByIdAndDecrypt(id) if err != nil { return err } diff --git a/server/api/routes.go b/server/api/routes.go index ad545da..4785536 100644 --- a/server/api/routes.go +++ b/server/api/routes.go @@ -1,8 +1,10 @@ package api import ( + "crypto/md5" "fmt" "net/http" + "os" "strings" "time" @@ -45,6 +47,7 @@ var ( sessionService *service.SessionService mailService *service.MailService numService *service.NumService + assetService *service.AssetService ) func SetupRoutes(db *gorm.DB) *echo.Echo { @@ -54,6 +57,7 @@ func SetupRoutes(db *gorm.DB) *echo.Echo { if err := InitDBData(); err != nil { log.WithError(err).Error("初始化数据异常") + os.Exit(0) } if err := ReloadData(); err != nil { @@ -251,6 +255,7 @@ func InitService() { sessionService = service.NewSessionService(sessionRepository) mailService = service.NewMailService(propertyRepository) numService = service.NewNumService(numRepository) + assetService = service.NewAssetService(assetRepository) } func InitDBData() (err error) { @@ -266,12 +271,18 @@ func InitDBData() (err error) { if err := jobService.InitJob(); err != nil { return err } - if err := userService.FixedUserOnlineState(); err != nil { + if err := userService.FixUserOnlineState(); err != nil { return err } if err := sessionService.FixSessionState(); err != nil { return err } + if err := sessionService.EmptyPassword(); err != nil { + return err + } + if err := assetService.Encrypt(); err != nil { + return err + } return nil } @@ -312,6 +323,47 @@ func ResetTotp(username string) error { return nil } +func ChangeEncryptionKey(oldEncryptionKey, newEncryptionKey string) error { + + oldPassword := []byte(fmt.Sprintf("%x", md5.Sum([]byte(oldEncryptionKey)))) + newPassword := []byte(fmt.Sprintf("%x", md5.Sum([]byte(newEncryptionKey)))) + + credentials, err := credentialRepository.FindAll() + if err != nil { + return err + } + for i := range credentials { + credential := credentials[i] + if err := credentialRepository.Decrypt(&credential, oldPassword); err != nil { + return err + } + if err := credentialRepository.Encrypt(&credential, newPassword); err != nil { + return err + } + if err := credentialRepository.UpdateById(&credential, credential.ID); err != nil { + return err + } + } + assets, err := assetRepository.FindAll() + if err != nil { + return err + } + for i := range assets { + asset := assets[i] + if err := assetRepository.Decrypt(&asset, oldPassword); err != nil { + return err + } + if err := assetRepository.Encrypt(&asset, newPassword); err != nil { + return err + } + if err := assetRepository.UpdateById(&asset, asset.ID); err != nil { + return err + } + } + log.Infof("encryption key has being changed.") + return nil +} + func SetupCache() *cache.Cache { // 配置缓存器 mCache := cache.New(5*time.Minute, 10*time.Minute) diff --git a/server/api/session.go b/server/api/session.go index 9a91d83..00e3a9f 100644 --- a/server/api/session.go +++ b/server/api/session.go @@ -137,6 +137,9 @@ func CloseSessionById(sessionId string, code int, reason string) { session.DisconnectedTime = utils.NowJsonTime() session.Code = code session.Message = reason + session.Password = "-" + session.PrivateKey = "-" + session.Passphrase = "-" _ = sessionRepository.UpdateById(&session, sessionId) } @@ -359,7 +362,7 @@ type File struct { func SessionLsEndpoint(c echo.Context) error { sessionId := c.Param("id") - session, err := sessionRepository.FindById(sessionId) + session, err := sessionRepository.FindByIdAndDecrypt(sessionId) if err != nil { return err } diff --git a/server/api/ssh.go b/server/api/ssh.go index a76be4b..1827369 100644 --- a/server/api/ssh.go +++ b/server/api/ssh.go @@ -54,7 +54,7 @@ func SSHEndpoint(c echo.Context) (err error) { cols, _ := strconv.Atoi(c.QueryParam("cols")) rows, _ := strconv.Atoi(c.QueryParam("rows")) - session, err := sessionRepository.FindById(sessionId) + session, err := sessionRepository.FindByIdAndDecrypt(sessionId) if err != nil { msg := Message{ Type: Closed, diff --git a/server/api/tunnel.go b/server/api/tunnel.go index a1330cc..e8a3bc7 100644 --- a/server/api/tunnel.go +++ b/server/api/tunnel.go @@ -65,7 +65,7 @@ func TunEndpoint(c echo.Context) error { configuration.SetParameter("width", width) configuration.SetParameter("height", height) configuration.SetParameter("dpi", dpi) - session, err = sessionRepository.FindById(sessionId) + session, err = sessionRepository.FindByIdAndDecrypt(sessionId) if err != nil { CloseSessionById(sessionId, NotFoundSession, "会话不存在") return err diff --git a/server/model/asset.go b/server/model/asset.go index 0a69c2f..ed6f344 100644 --- a/server/model/asset.go +++ b/server/model/asset.go @@ -21,6 +21,7 @@ type Asset struct { Created utils.JsonTime `json:"created"` Tags string `json:"tags"` Owner string `gorm:"index" json:"owner"` + Encrypted bool `json:"encrypted"` } type AssetForPage struct { diff --git a/server/model/credential.go b/server/model/credential.go index a2fb006..cbf7f32 100644 --- a/server/model/credential.go +++ b/server/model/credential.go @@ -14,6 +14,7 @@ type Credential struct { Passphrase string `json:"passphrase"` Created utils.JsonTime `json:"created"` Owner string `gorm:"index" json:"owner"` + Encrypted bool `json:"encrypted"` } func (r *Credential) TableName() string { diff --git a/server/repository/asset.go b/server/repository/asset.go index 8c9b684..10f9bce 100644 --- a/server/repository/asset.go +++ b/server/repository/asset.go @@ -1,6 +1,7 @@ package repository import ( + "encoding/base64" "fmt" "strings" @@ -145,7 +146,36 @@ func (r AssetRepository) Find(pageIndex, pageSize int, name, protocol, tags stri return } +func (r AssetRepository) Encrypt(item *model.Asset, password []byte) error { + if item.Password != "" && item.Password != "-" { + encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Password), password) + if err != nil { + return err + } + item.Password = base64.StdEncoding.EncodeToString(encryptedCBC) + } + if item.PrivateKey != "" && item.PrivateKey != "-" { + encryptedCBC, err := utils.AesEncryptCBC([]byte(item.PrivateKey), password) + if err != nil { + return err + } + item.PrivateKey = base64.StdEncoding.EncodeToString(encryptedCBC) + } + if item.Passphrase != "" && item.Passphrase != "-" { + encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Passphrase), password) + if err != nil { + return err + } + item.Passphrase = base64.StdEncoding.EncodeToString(encryptedCBC) + } + item.Encrypted = true + return nil +} + func (r AssetRepository) Create(o *model.Asset) (err error) { + if err := r.Encrypt(o, global.Config.EncryptionPassword); err != nil { + return err + } if err = r.DB.Create(o).Error; err != nil { return err } @@ -157,8 +187,54 @@ func (r AssetRepository) FindById(id string) (o model.Asset, err error) { return } +func (r AssetRepository) Decrypt(item *model.Asset, password []byte) error { + if item.Encrypted { + if item.Password != "" && item.Password != "-" { + origData, err := base64.StdEncoding.DecodeString(item.Password) + if err != nil { + return err + } + decryptedCBC, err := utils.AesDecryptCBC(origData, password) + if err != nil { + return err + } + item.Password = string(decryptedCBC) + } + if item.PrivateKey != "" && item.PrivateKey != "-" { + origData, err := base64.StdEncoding.DecodeString(item.PrivateKey) + if err != nil { + return err + } + decryptedCBC, err := utils.AesDecryptCBC(origData, password) + if err != nil { + return err + } + item.PrivateKey = string(decryptedCBC) + } + if item.Passphrase != "" && item.Passphrase != "-" { + origData, err := base64.StdEncoding.DecodeString(item.Passphrase) + if err != nil { + return err + } + decryptedCBC, err := utils.AesDecryptCBC(origData, password) + if err != nil { + return err + } + item.Passphrase = string(decryptedCBC) + } + } + return nil +} + +func (r AssetRepository) FindByIdAndDecrypt(id string) (o model.Asset, err error) { + err = r.DB.Where("id = ?", id).First(&o).Error + if err == nil { + err = r.Decrypt(&o, global.Config.EncryptionPassword) + } + return +} + func (r AssetRepository) UpdateById(o *model.Asset, id string) error { - o.ID = id return r.DB.Updates(o).Error } @@ -167,6 +243,11 @@ func (r AssetRepository) UpdateActiveById(active bool, id string) error { return r.DB.Exec(sql, active, id).Error } +func (r AssetRepository) EncryptedById(encrypted bool, password, privateKey, passphrase, id string) error { + sql := "update assets set encrypted = ?, password = ?,private_key = ?, passphrase = ? where id = ?" + return r.DB.Exec(sql, encrypted, password, privateKey, passphrase, id).Error +} + func (r AssetRepository) DeleteById(id string) error { return r.DB.Where("id = ?", id).Delete(&model.Asset{}).Error } diff --git a/server/repository/credential.go b/server/repository/credential.go index 0a29b2d..120ac82 100644 --- a/server/repository/credential.go +++ b/server/repository/credential.go @@ -1,8 +1,12 @@ package repository import ( + "encoding/base64" + "next-terminal/pkg/constant" + "next-terminal/pkg/global" "next-terminal/server/model" + "next-terminal/server/utils" "gorm.io/gorm" ) @@ -65,6 +69,9 @@ func (r CredentialRepository) Find(pageIndex, pageSize int, name, order, field s } func (r CredentialRepository) Create(o *model.Credential) (err error) { + if err := r.Encrypt(o, global.Config.EncryptionPassword); err != nil { + return err + } if err = r.DB.Create(o).Error; err != nil { return err } @@ -76,6 +83,79 @@ func (r CredentialRepository) FindById(id string) (o model.Credential, err error return } +func (r CredentialRepository) Encrypt(item *model.Credential, password []byte) error { + if item.Password != "-" { + encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Password), password) + if err != nil { + return err + } + item.Password = base64.StdEncoding.EncodeToString(encryptedCBC) + } + if item.PrivateKey != "-" { + encryptedCBC, err := utils.AesEncryptCBC([]byte(item.PrivateKey), password) + if err != nil { + return err + } + item.PrivateKey = base64.StdEncoding.EncodeToString(encryptedCBC) + } + if item.Passphrase != "-" { + encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Passphrase), password) + if err != nil { + return err + } + item.Passphrase = base64.StdEncoding.EncodeToString(encryptedCBC) + } + item.Encrypted = true + return nil +} + +func (r CredentialRepository) Decrypt(item *model.Credential, password []byte) error { + if item.Encrypted { + if item.Password != "" && item.Password != "-" { + origData, err := base64.StdEncoding.DecodeString(item.Password) + if err != nil { + return err + } + decryptedCBC, err := utils.AesDecryptCBC(origData, password) + if err != nil { + return err + } + item.Password = string(decryptedCBC) + } + if item.PrivateKey != "" && item.PrivateKey != "-" { + origData, err := base64.StdEncoding.DecodeString(item.PrivateKey) + if err != nil { + return err + } + decryptedCBC, err := utils.AesDecryptCBC(origData, password) + if err != nil { + return err + } + item.PrivateKey = string(decryptedCBC) + } + if item.Passphrase != "" && item.Passphrase != "-" { + origData, err := base64.StdEncoding.DecodeString(item.Passphrase) + if err != nil { + return err + } + decryptedCBC, err := utils.AesDecryptCBC(origData, password) + if err != nil { + return err + } + item.Passphrase = string(decryptedCBC) + } + } + return nil +} + +func (r CredentialRepository) FindByIdAndDecrypt(id string) (o model.Credential, err error) { + err = r.DB.Where("id = ?", id).First(&o).Error + if err == nil { + err = r.Decrypt(&o, global.Config.EncryptionPassword) + } + return +} + func (r CredentialRepository) UpdateById(o *model.Credential, id string) error { o.ID = id return r.DB.Updates(o).Error @@ -107,3 +187,13 @@ func (r CredentialRepository) CountByUserId(userId string) (total int64, err err err = db.Find(&model.Credential{}).Count(&total).Error return } + +func (r CredentialRepository) FindAll() (o []model.Credential, err error) { + err = r.DB.Find(&o).Error + return +} + +func (r CredentialRepository) EncryptedById(encrypted bool, password, privateKey, passphrase, id string) error { + sql := "update assets set encrypted = ?, password = ?,private_key = ?, passphrase = ? where id = ?" + return r.DB.Exec(sql, encrypted, password, privateKey, passphrase, id).Error +} diff --git a/server/repository/session.go b/server/repository/session.go index 66f082f..284dfac 100644 --- a/server/repository/session.go +++ b/server/repository/session.go @@ -1,12 +1,15 @@ package repository import ( + "encoding/base64" "os" "path" "time" "next-terminal/pkg/constant" + "next-terminal/pkg/global" "next-terminal/server/model" + "next-terminal/server/utils" "gorm.io/gorm" ) @@ -93,6 +96,51 @@ func (r SessionRepository) FindById(id string) (o model.Session, err error) { return } +func (r SessionRepository) FindByIdAndDecrypt(id string) (o model.Session, err error) { + err = r.DB.Where("id = ?", id).First(&o).Error + if err == nil { + err = r.Decrypt(&o) + } + return +} + +func (r SessionRepository) Decrypt(item *model.Session) error { + if item.Password != "" && item.Password != "-" { + origData, err := base64.StdEncoding.DecodeString(item.Password) + if err != nil { + return err + } + decryptedCBC, err := utils.AesDecryptCBC(origData, global.Config.EncryptionPassword) + if err != nil { + return err + } + item.Password = string(decryptedCBC) + } + if item.PrivateKey != "" && item.PrivateKey != "-" { + origData, err := base64.StdEncoding.DecodeString(item.PrivateKey) + if err != nil { + return err + } + decryptedCBC, err := utils.AesDecryptCBC(origData, global.Config.EncryptionPassword) + if err != nil { + return err + } + item.PrivateKey = string(decryptedCBC) + } + if item.Passphrase != "" && item.Passphrase != "-" { + origData, err := base64.StdEncoding.DecodeString(item.Passphrase) + if err != nil { + return err + } + decryptedCBC, err := utils.AesDecryptCBC(origData, global.Config.EncryptionPassword) + if err != nil { + return err + } + item.Passphrase = string(decryptedCBC) + } + return nil +} + func (r SessionRepository) FindByConnectionId(connectionId string) (o model.Session, err error) { err = r.DB.Where("connection_id = ?", connectionId).First(&o).Error return @@ -167,3 +215,8 @@ func (r SessionRepository) CountSessionByDay(day int) (results []D, err error) { return } + +func (r SessionRepository) EmptyPassword() error { + sql := "update sessions set password = '-',private_key = '-', passphrase = '-' where 1=1" + return r.DB.Exec(sql).Error +} diff --git a/server/utils/util_test.go b/server/utils/util_test.go index 70c6330..b8d4fba 100644 --- a/server/utils/util_test.go +++ b/server/utils/util_test.go @@ -1,7 +1,10 @@ package utils_test import ( + "crypto/md5" "encoding/base64" + "encoding/hex" + "fmt" "net" "testing" @@ -51,3 +54,26 @@ func TestAesDecryptCBC(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "Hello Next Terminal", string(decryptCBC)) } + +func TestPbkdf2(t *testing.T) { + pbkdf2, err := utils.Pbkdf2("1234") + assert.NoError(t, err) + println(hex.EncodeToString(pbkdf2)) +} + +func TestAesEncryptCBCWithAnyKey(t *testing.T) { + origData := []byte("admin") // 待加密的数据 + key := []byte(fmt.Sprintf("%x", md5.Sum([]byte("next-terminal")))) // 加密的密钥 + encryptedCBC, err := utils.AesEncryptCBC(origData, key) + assert.NoError(t, err) + assert.Equal(t, "3qwawlPxghyiLS5hdr/p0g==", base64.StdEncoding.EncodeToString(encryptedCBC)) +} + +func TestAesDecryptCBCWithAnyKey(t *testing.T) { + origData, err := base64.StdEncoding.DecodeString("3qwawlPxghyiLS5hdr/p0g==") // 待解密的数据 + assert.NoError(t, err) + key := []byte(fmt.Sprintf("%x", md5.Sum([]byte("next-terminal")))) // 加密的密钥 + decryptCBC, err := utils.AesDecryptCBC(origData, key) + assert.NoError(t, err) + assert.Equal(t, "admin", string(decryptCBC)) +} diff --git a/server/utils/utils.go b/server/utils/utils.go index 0f94e50..0d5333c 100644 --- a/server/utils/utils.go +++ b/server/utils/utils.go @@ -5,6 +5,8 @@ import ( "crypto/aes" "crypto/cipher" "crypto/md5" + "crypto/rand" + "crypto/sha256" "database/sql/driver" "encoding/base64" "fmt" @@ -19,6 +21,8 @@ import ( "strings" "time" + "golang.org/x/crypto/pbkdf2" + "github.com/gofrs/uuid" "github.com/sirupsen/logrus" "golang.org/x/crypto/bcrypt" @@ -239,6 +243,7 @@ func PKCS5UnPadding(origData []byte) []byte { return origData[:(length - unPadding)] } +// AesEncryptCBC /* func AesEncryptCBC(origData, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { @@ -266,3 +271,15 @@ func AesDecryptCBC(encrypted, key []byte) ([]byte, error) { origData = PKCS5UnPadding(origData) return origData, nil } + +func Pbkdf2(password string) ([]byte, error) { + //生成随机盐 + salt := make([]byte, 32) + _, err := rand.Read(salt) + if err != nil { + return nil, err + } + //生成密文 + dk := pbkdf2.Key([]byte(password), salt, 1, 32, sha256.New) + return dk, nil +} diff --git a/web/package.json b/web/package.json index ae3f55c..5390912 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "next-terminal", - "version": "0.3.4", + "version": "0.4.0", "private": true, "dependencies": { "@ant-design/icons": "^4.3.0", diff --git a/web/src/components/asset/Asset.js b/web/src/components/asset/Asset.js index 0c39582..5d50347 100644 --- a/web/src/components/asset/Asset.js +++ b/web/src/components/asset/Asset.js @@ -355,10 +355,10 @@ class Asset extends Component { window.open(`#/access?assetId=${id}&assetName=${name}&protocol=${protocol}`); } } else { - message.warn('您访问的资产未在线,请确认网络状态。', 10); + message.warn({content: '您访问的资产未在线,请确认网络状态。', key: id, duration: 10}); } } else { - message.error('操作失败 :( ' + result.message, 10); + message.error({content: result.message, key: id, duration: 10}); } } From 8f22ecfb76721ea83ee2e3afe54f94646facba88 Mon Sep 17 00:00:00 2001 From: dushixiang Date: Sat, 17 Apr 2021 20:00:02 +0800 Subject: [PATCH 9/9] =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E6=95=8F=E6=84=9F=E4=BF=A1=E6=81=AF=E7=9A=84=E5=8A=A0?= =?UTF-8?q?=E5=AF=86=20close=20#127=20close=20#46?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/service/asset.go | 30 +++--------------------------- pkg/service/credential.go | 30 +++--------------------------- server/api/routes.go | 19 ++++++++++++------- server/repository/asset.go | 5 ----- server/repository/credential.go | 5 ----- 5 files changed, 18 insertions(+), 71 deletions(-) diff --git a/pkg/service/asset.go b/pkg/service/asset.go index 934e127..d431436 100644 --- a/pkg/service/asset.go +++ b/pkg/service/asset.go @@ -1,11 +1,8 @@ package service import ( - "encoding/base64" - "next-terminal/pkg/global" "next-terminal/server/repository" - "next-terminal/server/utils" ) type AssetService struct { @@ -26,31 +23,10 @@ func (r AssetService) Encrypt() error { if item.Encrypted { continue } - if item.Password != "" && item.Password != "-" { - encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Password), global.Config.EncryptionPassword) - if err != nil { - return err - } - item.Password = base64.StdEncoding.EncodeToString(encryptedCBC) + if err := r.assetRepository.Encrypt(&item, global.Config.EncryptionPassword); err != nil { + return err } - - if item.PrivateKey != "" && item.PrivateKey != "-" { - encryptedCBC, err := utils.AesEncryptCBC([]byte(item.PrivateKey), global.Config.EncryptionPassword) - if err != nil { - return err - } - item.PrivateKey = base64.StdEncoding.EncodeToString(encryptedCBC) - } - - if item.Passphrase != "" && item.Passphrase != "-" { - encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Passphrase), global.Config.EncryptionPassword) - if err != nil { - return err - } - item.Passphrase = base64.StdEncoding.EncodeToString(encryptedCBC) - } - err = r.assetRepository.EncryptedById(true, item.Password, item.PrivateKey, item.Passphrase, item.ID) - if err != nil { + if err := r.assetRepository.UpdateById(&item, item.ID); err != nil { return err } } diff --git a/pkg/service/credential.go b/pkg/service/credential.go index 89d0371..b860f95 100644 --- a/pkg/service/credential.go +++ b/pkg/service/credential.go @@ -1,11 +1,8 @@ package service import ( - "encoding/base64" - "next-terminal/pkg/global" "next-terminal/server/repository" - "next-terminal/server/utils" ) type CredentialService struct { @@ -26,31 +23,10 @@ func (r CredentialService) Encrypt() error { if item.Encrypted { continue } - if item.Password != "" && item.Password != "-" { - encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Password), global.Config.EncryptionPassword) - if err != nil { - return err - } - item.Password = base64.StdEncoding.EncodeToString(encryptedCBC) + if err := r.credentialRepository.Encrypt(&item, global.Config.EncryptionPassword); err != nil { + return err } - - if item.PrivateKey != "" && item.PrivateKey != "-" { - encryptedCBC, err := utils.AesEncryptCBC([]byte(item.PrivateKey), global.Config.EncryptionPassword) - if err != nil { - return err - } - item.PrivateKey = base64.StdEncoding.EncodeToString(encryptedCBC) - } - - if item.Passphrase != "" && item.Passphrase != "-" { - encryptedCBC, err := utils.AesEncryptCBC([]byte(item.Passphrase), global.Config.EncryptionPassword) - if err != nil { - return err - } - item.Passphrase = base64.StdEncoding.EncodeToString(encryptedCBC) - } - err = r.credentialRepository.EncryptedById(true, item.Password, item.PrivateKey, item.Passphrase, item.ID) - if err != nil { + if err := r.credentialRepository.UpdateById(&item, item.ID); err != nil { return err } } diff --git a/server/api/routes.go b/server/api/routes.go index 4785536..efe77e7 100644 --- a/server/api/routes.go +++ b/server/api/routes.go @@ -41,13 +41,14 @@ var ( jobLogRepository *repository.JobLogRepository loginLogRepository *repository.LoginLogRepository - jobService *service.JobService - propertyService *service.PropertyService - userService *service.UserService - sessionService *service.SessionService - mailService *service.MailService - numService *service.NumService - assetService *service.AssetService + jobService *service.JobService + propertyService *service.PropertyService + userService *service.UserService + sessionService *service.SessionService + mailService *service.MailService + numService *service.NumService + assetService *service.AssetService + credentialService *service.CredentialService ) func SetupRoutes(db *gorm.DB) *echo.Echo { @@ -256,6 +257,7 @@ func InitService() { mailService = service.NewMailService(propertyRepository) numService = service.NewNumService(numRepository) assetService = service.NewAssetService(assetRepository) + credentialService = service.NewCredentialService(credentialRepository) } func InitDBData() (err error) { @@ -280,6 +282,9 @@ func InitDBData() (err error) { if err := sessionService.EmptyPassword(); err != nil { return err } + if err := credentialService.Encrypt(); err != nil { + return err + } if err := assetService.Encrypt(); err != nil { return err } diff --git a/server/repository/asset.go b/server/repository/asset.go index 10f9bce..dfa73e1 100644 --- a/server/repository/asset.go +++ b/server/repository/asset.go @@ -243,11 +243,6 @@ func (r AssetRepository) UpdateActiveById(active bool, id string) error { return r.DB.Exec(sql, active, id).Error } -func (r AssetRepository) EncryptedById(encrypted bool, password, privateKey, passphrase, id string) error { - sql := "update assets set encrypted = ?, password = ?,private_key = ?, passphrase = ? where id = ?" - return r.DB.Exec(sql, encrypted, password, privateKey, passphrase, id).Error -} - func (r AssetRepository) DeleteById(id string) error { return r.DB.Where("id = ?", id).Delete(&model.Asset{}).Error } diff --git a/server/repository/credential.go b/server/repository/credential.go index 120ac82..f0f9d27 100644 --- a/server/repository/credential.go +++ b/server/repository/credential.go @@ -192,8 +192,3 @@ func (r CredentialRepository) FindAll() (o []model.Credential, err error) { err = r.DB.Find(&o).Error return } - -func (r CredentialRepository) EncryptedById(encrypted bool, password, privateKey, passphrase, id string) error { - sql := "update assets set encrypted = ?, password = ?,private_key = ?, passphrase = ? where id = ?" - return r.DB.Exec(sql, encrypted, password, privateKey, passphrase, id).Error -}