Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
K
koko
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
koko
Commits
50194e95
Unverified
Commit
50194e95
authored
Oct 25, 2019
by
Eric_Lee
Committed by
GitHub
Oct 25, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #120 from jumpserver/dev
Dev
parents
3271c65b
981f283e
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
328 additions
and
185 deletions
+328
-185
koko.po
cmd/locale/en_US/LC_MESSAGES/koko.po
+59
-49
koko.mo
cmd/locale/zh_CN/LC_MESSAGES/koko.mo
+0
-0
koko.po
cmd/locale/zh_CN/LC_MESSAGES/koko.po
+73
-58
banner.go
pkg/handler/banner.go
+27
-0
pagination.go
pkg/handler/pagination.go
+11
-16
session.go
pkg/handler/session.go
+55
-27
proxy.go
pkg/proxy/proxy.go
+30
-6
assets.go
pkg/service/assets.go
+1
-1
connmanager.go
pkg/srvconn/connmanager.go
+25
-21
sftpconn.go
pkg/srvconn/sftpconn.go
+35
-3
sshconn.go
pkg/srvconn/sshconn.go
+12
-4
No files found.
cmd/locale/en_US/LC_MESSAGES/koko.po
View file @
50194e95
...
...
@@ -8,171 +8,181 @@ msgstr ""
"X-Generator: xgotext\n"
#. i18n.T
#: pkg/auth/server.go:17
msgid "Please enter 6 digits."
msgstr ""
#. i18n.T
#: pkg/auth/server.go:18
msgid "[MFA auth]: "
msgstr ""
#. i18n.T
#: pkg/handler/banner.go:32
#: pkg/handler/banner.go:31
msgid "\t%d) Enter {{.GreenBoldColor}}%s{{.ColorEnd}} to %s.%s"
msgstr ""
#. i18n.T
#: pkg/handler/banner.go:4
7
#: pkg/handler/banner.go:4
6
msgid "Welcome to use Jumpserver open source fortress system"
msgstr ""
#. i18n.T
#: pkg/handler/banner.go:4
9
#: pkg/handler/banner.go:4
8
msgid "directly login"
msgstr ""
#. i18n.T
#: pkg/handler/banner.go:
50
#: pkg/handler/banner.go:
49
msgid "part IP, Hostname, Comment"
msgstr ""
#. i18n.T
#: pkg/handler/banner.go:
50
#: pkg/handler/banner.go:
49
msgid "to search login if unique"
msgstr ""
#. i18n.T
#: pkg/handler/banner.go:5
1
#: pkg/handler/banner.go:5
0
msgid "/ + IP, Hostname, Comment"
msgstr ""
#. i18n.T
#: pkg/handler/banner.go:5
1
#: pkg/handler/banner.go:5
0
msgid "to search, such as: /192.168"
msgstr ""
#. i18n.T
#: pkg/handler/banner.go:5
2
#: pkg/handler/banner.go:5
1
msgid "display the host you have permission"
msgstr ""
#. i18n.T
#: pkg/handler/banner.go:5
3
#: pkg/handler/banner.go:5
2
msgid "display the node that you have permission"
msgstr ""
#. i18n.T
#: pkg/handler/banner.go:5
4
#: pkg/handler/banner.go:5
3
msgid "refresh your assets and nodes"
msgstr ""
#. i18n.T
#: pkg/handler/banner.go:5
5
#: pkg/handler/banner.go:5
4
msgid "print help"
msgstr ""
#. i18n.T
#: pkg/handler/banner.go:5
6
#: pkg/handler/banner.go:5
5
msgid "exit"
msgstr ""
#. i18n.T
#: pkg/handler/
pagination.go:133
#: pkg/handler/
banner.go:90
msgid "ID"
msgstr ""
#. i18n.T
#: pkg/handler/
pagination.go:133
#: pkg/handler/
banner.go:91
msgid "hostname"
msgstr ""
#. i18n.T
#: pkg/handler/
pagination.go:133
#: pkg/handler/
banner.go:92
msgid "IP"
msgstr ""
#. i18n.T
#: pkg/handler/
pagination.go:13
3
msgid "
systemUsers
"
#: pkg/handler/
banner.go:9
3
msgid "
comment
"
msgstr ""
#. i18n.T
#: pkg/handler/
pagination.go:133
msgid "
comment
"
#: pkg/handler/
banner.go:94
msgid "
Page: %d, Count: %d, Total Page: %d, Total Count: %d
"
msgstr ""
#. i18n.T
#: pkg/handler/
pagination.go:152
msgid "
Page: %d, Count: %d, Total Page: %d, Total Count: %d
"
#: pkg/handler/
banner.go:95
msgid "
No Assets
"
msgstr ""
#. i18n.T
#: pkg/handler/
pagination.go:179
#: pkg/handler/
banner.go:96
msgid ""
"\n"
"Tips: Enter the asset ID and
log directly into
the asset.\n"
"Tips: Enter the asset ID and
directly login
the asset.\n"
msgstr ""
#. i18n.T
#: pkg/handler/
pagination.go:180
#: pkg/handler/
banner.go:97
msgid ""
"\n"
"Page up: P/p\tPage down: Enter|N/n\tBACK: b.\n"
msgstr ""
#. i18n.T
#: pkg/handler/session.go:267
msgid "No Assets"
msgstr ""
#. i18n.T
#: pkg/handler/session.go:289
#: pkg/handler/banner.go:98
msgid "Node: [ ID.Name(Asset amount) ]"
msgstr ""
#. i18n.T
#: pkg/handler/
session.go:290
#: pkg/handler/
banner.go:99
msgid "Tips: Enter g+NodeID to display the host under the node, such as g1"
msgstr ""
#. i18n.T
#: pkg/handler/
session.go:304
#: pkg/handler/
banner.go:100
msgid "Refresh done"
msgstr ""
#. i18n.T
#: pkg/proxy/parser.go:124
#: pkg/handler/banner.go:101
msgid "Tips: Enter system user ID and directly login the asset [ %s(%s) ]"
msgstr ""
#. i18n.T
#: pkg/handler/banner.go:102
msgid "Back: B/b"
msgstr ""
#. i18n.T
#: pkg/handler/banner.go:103
msgid "Name"
msgstr ""
#. i18n.T
#: pkg/handler/banner.go:104
msgid "Username"
msgstr ""
#. i18n.T
#: pkg/proxy/parser.go:131
msgid "Command `%s` is forbidden"
msgstr ""
#. i18n.T
#: pkg/proxy/proxy.go:152
#: pkg/proxy/proxy.go:143
msgid "Reuse SSH connections (%s@%s) [Number of connections: %d]"
msgstr ""
#. i18n.T
#: pkg/proxy/proxy.go:161
msgid "Connecting to %s@%s %.1f"
msgstr ""
#. i18n.T
#: pkg/proxy/proxy.go:1
71
#: pkg/proxy/proxy.go:1
80
msgid "System user <%s> and asset <%s> protocol are inconsistent."
msgstr ""
#. i18n.T
#: pkg/proxy/proxy.go:1
77
#: pkg/proxy/proxy.go:1
86
msgid ""
"Terminal only support protocol ssh/telnet, please use web terminal to access"
msgstr ""
#. i18n.T
#: pkg/proxy/sessmanager.go:6
3
#: pkg/proxy/sessmanager.go:6
7
msgid "Connect with api server failed"
msgstr ""
#. i18n.T
#: pkg/proxy/switch.go:1
42
#: pkg/proxy/switch.go:1
59
msgid "Connect idle more than %d minutes, disconnect"
msgstr ""
#. i18n.T
#: pkg/proxy/switch.go:1
48
#: pkg/proxy/switch.go:1
66
msgid "Terminated by administrator"
msgstr ""
cmd/locale/zh_CN/LC_MESSAGES/koko.mo
View file @
50194e95
No preview for this file type
cmd/locale/zh_CN/LC_MESSAGES/koko.po
View file @
50194e95
...
...
@@ -8,175 +8,190 @@ msgstr ""
"X-Generator: xgotext\n"
#. i18n.T
#: pkg/auth/server.go:17
msgid "Please enter 6 digits."
msgstr "请输入六位数字"
#. i18n.T
#: pkg/auth/server.go:18
msgid "[MFA auth]: "
msgstr "[MFA认证]"
#. i18n.T
#: pkg/handler/banner.go:32
#: pkg/handler/banner.go:31
msgid "\t%d) Enter {{.GreenBoldColor}}%s{{.ColorEnd}} to %s.%s"
msgstr "\t%d) 输入 {{.GreenBoldColor}}%s{{.ColorEnd}} 进行%s.%s"
#. i18n.T
#: pkg/handler/banner.go:4
7
#: pkg/handler/banner.go:4
6
msgid "Welcome to use Jumpserver open source fortress system"
msgstr "欢迎使用Jumpserver开源堡垒机系统"
#. i18n.T
#: pkg/handler/banner.go:4
9
#: pkg/handler/banner.go:4
8
msgid "directly login"
msgstr "直接登
陆
"
msgstr "直接登
录
"
#. i18n.T
#: pkg/handler/banner.go:
50
#: pkg/handler/banner.go:
49
msgid "part IP, Hostname, Comment"
msgstr "部分IP、主机名、备注"
#. i18n.T
#: pkg/handler/banner.go:
50
#: pkg/handler/banner.go:
49
msgid "to search login if unique"
msgstr "进行搜索登录(如果唯一)"
#. i18n.T
#: pkg/handler/banner.go:5
1
#: pkg/handler/banner.go:5
0
msgid "/ + IP, Hostname, Comment"
msgstr "/ + IP
,
主机名 or 备注"
msgstr "/ + IP
,
主机名 or 备注"
#. i18n.T
#: pkg/handler/banner.go:5
1
#: pkg/handler/banner.go:5
0
msgid "to search, such as: /192.168"
msgstr "搜索, 如
:
/192.168"
msgstr "搜索, 如
:
/192.168"
#. i18n.T
#: pkg/handler/banner.go:5
2
#: pkg/handler/banner.go:5
1
msgid "display the host you have permission"
msgstr "显示您有权限的主机"
#. i18n.T
#: pkg/handler/banner.go:5
3
#: pkg/handler/banner.go:5
2
msgid "display the node that you have permission"
msgstr "显示您有权限的节点"
#. i18n.T
#: pkg/handler/banner.go:5
4
#: pkg/handler/banner.go:5
3
msgid "refresh your assets and nodes"
msgstr "刷新最新的机器和节点信息"
#. i18n.T
#: pkg/handler/banner.go:5
5
#: pkg/handler/banner.go:5
4
msgid "print help"
msgstr "显示帮助"
#. i18n.T
#: pkg/handler/banner.go:5
6
#: pkg/handler/banner.go:5
5
msgid "exit"
msgstr "退出"
#. i18n.T
#: pkg/handler/
pagination.go:133
#: pkg/handler/
banner.go:90
msgid "ID"
msgstr "ID"
#. i18n.T
#: pkg/handler/
pagination.go:133
#: pkg/handler/
banner.go:91
msgid "hostname"
msgstr "主机名"
#. i18n.T
#: pkg/handler/
pagination.go:133
#: pkg/handler/
banner.go:92
msgid "IP"
msgstr "IP"
#. i18n.T
#: pkg/handler/pagination.go:133
msgid "systemUsers"
msgstr "登录用户"
#. i18n.T
#: pkg/handler/pagination.go:133
#: pkg/handler/banner.go:93
msgid "comment"
msgstr "备注"
#. i18n.T
#: pkg/handler/
pagination.go:152
#: pkg/handler/
banner.go:94
msgid "Page: %d, Count: %d, Total Page: %d, Total Count: %d"
msgstr "页码: %d, 每页行数: %d, 总页数: %d, 总数量: %d"
msgstr "页码:%d,每页行数:%d,总页数:%d,总数量:%d"
#. i18n.T
#: pkg/handler/banner.go:95
msgid "No Assets"
msgstr "没有资产"
#. i18n.T
#: pkg/handler/pagination.go:179
#: pkg/handler/banner.go:96
#, fuzzy
msgid ""
"\n"
"Tips: Enter the asset ID and
log directly into
the asset.\n"
"Tips: Enter the asset ID and
directly login
the asset.\n"
msgstr ""
"\n"
"提示
: 输入资产ID,直接登录资产.
\n"
"提示
:输入资产ID,登录资产
\n"
#. i18n.T
#: pkg/handler/
pagination.go:180
#: pkg/handler/
banner.go:97
msgid ""
"\n"
"Page up: P/p\tPage down: Enter|N/n\tBACK: b.\n"
msgstr ""
"\n"
"上一页
: P/p 下一页: Enter|N/n 返回:
B/b\n"
"上一页
:P/p 下一页:Enter|N/n 返回:
B/b\n"
#. i18n.T
#: pkg/handler/session.go:267
msgid "No Assets"
msgstr "没有资产"
#. i18n.T
#: pkg/handler/session.go:289
#: pkg/handler/banner.go:98
msgid "Node: [ ID.Name(Asset amount) ]"
msgstr "节点
:
[ ID.名称(资产数量) ]"
msgstr "节点
:
[ ID.名称(资产数量) ]"
#. i18n.T
#: pkg/handler/
session.go:290
#: pkg/handler/
banner.go:99
msgid "Tips: Enter g+NodeID to display the host under the node, such as g1"
msgstr "提示
: 输入 g+节点ID 显示节点下主机.
如: g1"
msgstr "提示
:输入 g+节点ID 显示节点下主机,
如: g1"
#. i18n.T
#: pkg/handler/
session.go:304
#: pkg/handler/
banner.go:100
msgid "Refresh done"
msgstr "刷新完成"
#. i18n.T
#: pkg/proxy/parser.go:124
#: pkg/handler/banner.go:101
#, fuzzy
msgid "Tips: Enter system user ID and directly login the asset [ %s(%s) ]"
msgstr ""
"\n"
"提示:输入系统用户ID,登录资产[ %s(%s) ]\n"
#. i18n.T
#: pkg/handler/banner.go:102
msgid "Back: B/b"
msgstr "返回:B/b"
#. i18n.T
#: pkg/handler/banner.go:103
msgid "Name"
msgstr "名称"
#. i18n.T
#: pkg/handler/banner.go:104
#, fuzzy
msgid "Username"
msgstr "用户名"
#. i18n.T
#: pkg/proxy/parser.go:131
msgid "Command `%s` is forbidden"
msgstr "命令 `%s` 是被禁止的 ..."
#. i18n.T
#: pkg/proxy/proxy.go:152
#: pkg/proxy/proxy.go:143
msgid "Reuse SSH connections (%s@%s) [Number of connections: %d]"
msgstr "复用SSH连接(%s@%s)[连接数量: %d]"
#. i18n.T
#: pkg/proxy/proxy.go:161
msgid "Connecting to %s@%s %.1f"
msgstr "开始连接到 %s@%s %.1f"
#. i18n.T
#: pkg/proxy/proxy.go:1
71
#: pkg/proxy/proxy.go:1
80
msgid "System user <%s> and asset <%s> protocol are inconsistent."
msgstr "系统用户<%s>和资产<%s>协议不一致"
#. i18n.T
#: pkg/proxy/proxy.go:1
77
#: pkg/proxy/proxy.go:1
86
msgid ""
"Terminal only support protocol ssh/telnet, please use web terminal to access"
msgstr "终端仅支持ssh/telnet协议,请使用web终端登录"
#. i18n.T
#: pkg/proxy/sessmanager.go:6
3
#: pkg/proxy/sessmanager.go:6
7
msgid "Connect with api server failed"
msgstr "连接API服务失败"
#. i18n.T
#: pkg/proxy/switch.go:1
42
#: pkg/proxy/switch.go:1
59
msgid "Connect idle more than %d minutes, disconnect"
msgstr "空闲时间超过%d分钟,断开连接"
#. i18n.T
#: pkg/proxy/switch.go:1
48
#: pkg/proxy/switch.go:1
66
msgid "Terminated by administrator"
msgstr "管理员中断连接"
pkg/handler/banner.go
View file @
50194e95
...
...
@@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"io"
"sync"
"text/template"
"github.com/jumpserver/koko/pkg/config"
...
...
@@ -79,3 +80,29 @@ func displayBanner(sess io.ReadWriter, user string) {
utils
.
IgnoreErrWriteString
(
sess
,
v
.
Text
())
}
}
var
i18nMap
map
[
string
]
string
var
i18nOnce
sync
.
Once
func
getI18nFromMap
(
name
string
)
string
{
i18nOnce
.
Do
(
func
()
{
i18nMap
=
map
[
string
]
string
{
"ID"
:
i18n
.
T
(
"ID"
),
"Hostname"
:
i18n
.
T
(
"hostname"
),
"IP"
:
i18n
.
T
(
"IP"
),
"Comment"
:
i18n
.
T
(
"comment"
),
"AssetTableCaption"
:
i18n
.
T
(
"Page: %d, Count: %d, Total Page: %d, Total Count: %d"
),
"NoAssets"
:
i18n
.
T
(
"No Assets"
),
"LoginTip"
:
i18n
.
T
(
"
\n
Tips: Enter the asset ID and directly login the asset.
\n
"
),
"PageActionTip"
:
i18n
.
T
(
"
\n
Page up: P/p Page down: Enter|N/n BACK: b.
\n
"
),
"NodeHeaderTip"
:
i18n
.
T
(
"Node: [ ID.Name(Asset amount) ]"
),
"NodeEndTip"
:
i18n
.
T
(
"Tips: Enter g+NodeID to display the host under the node, such as g1"
),
"RefreshDone"
:
i18n
.
T
(
"Refresh done"
),
"SelectUserTip"
:
i18n
.
T
(
"Tips: Enter system user ID and directly login the asset [ %s(%s) ]"
),
"BackTip"
:
i18n
.
T
(
"Back: B/b"
),
"Name"
:
i18n
.
T
(
"Name"
),
"Username"
:
i18n
.
T
(
"Username"
),
}
})
return
i18nMap
[
name
]
}
pkg/handler/pagination.go
View file @
50194e95
...
...
@@ -8,7 +8,6 @@ import (
"github.com/jumpserver/koko/pkg/common"
"github.com/jumpserver/koko/pkg/config"
"github.com/jumpserver/koko/pkg/i18n"
"github.com/jumpserver/koko/pkg/model"
"github.com/jumpserver/koko/pkg/service"
"github.com/jumpserver/koko/pkg/utils"
...
...
@@ -132,7 +131,8 @@ func (p *AssetPagination) Start() []model.Asset {
}
func
(
p
*
AssetPagination
)
displayPageAssets
()
{
Labels
:=
[]
string
{
i18n
.
T
(
"ID"
),
i18n
.
T
(
"hostname"
),
i18n
.
T
(
"IP"
),
i18n
.
T
(
"comment"
)}
Labels
:=
[]
string
{
getI18nFromMap
(
"ID"
),
getI18nFromMap
(
"Hostname"
),
getI18nFromMap
(
"IP"
),
getI18nFromMap
(
"Comment"
)}
fields
:=
[]
string
{
"ID"
,
"hostname"
,
"IP"
,
"comment"
}
data
:=
make
([]
map
[
string
]
string
,
len
(
p
.
currentData
))
for
i
,
j
:=
range
p
.
currentData
{
...
...
@@ -152,7 +152,7 @@ func (p *AssetPagination) displayPageAssets() {
data
[
i
]
=
row
}
w
,
_
:=
p
.
term
.
GetSize
()
caption
:=
fmt
.
Sprintf
(
i18n
.
T
(
"Page: %d, Count: %d, Total Page: %d, Total Count: %d
"
),
caption
:=
fmt
.
Sprintf
(
getI18nFromMap
(
"AssetTableCaption
"
),
p
.
page
.
CurrentPage
(),
p
.
page
.
PageSize
(),
p
.
page
.
TotalPage
(),
p
.
page
.
TotalCount
(),
)
caption
=
utils
.
WrapperString
(
caption
,
utils
.
Green
)
...
...
@@ -269,12 +269,12 @@ func (p *UserAssetPagination) Start() []model.Asset {
func
(
p
*
UserAssetPagination
)
displayPageAssets
()
{
if
len
(
p
.
Data
.
Data
)
==
0
{
_
,
_
=
p
.
term
.
Write
([]
byte
(
i18n
.
T
(
"No Assets"
)))
_
,
_
=
p
.
term
.
Write
([]
byte
(
"
\n\r
"
))
_
,
_
=
p
.
term
.
Write
([]
byte
(
getI18nFromMap
(
"NoAssets"
)
+
"
\n\r
"
))
return
}
Labels
:=
[]
string
{
i18n
.
T
(
"ID"
),
i18n
.
T
(
"hostname"
),
i18n
.
T
(
"IP"
),
i18n
.
T
(
"comment"
)}
Labels
:=
[]
string
{
getI18nFromMap
(
"ID"
),
getI18nFromMap
(
"Hostname"
),
getI18nFromMap
(
"IP"
),
getI18nFromMap
(
"Comment"
)}
fields
:=
[]
string
{
"ID"
,
"hostname"
,
"IP"
,
"comment"
}
p
.
currentData
=
model
.
AssetList
(
p
.
Data
.
Data
)
.
SortBy
(
config
.
GetConf
()
.
AssetListSortBy
)
data
:=
make
([]
map
[
string
]
string
,
len
(
p
.
currentData
))
...
...
@@ -324,9 +324,9 @@ func (p *UserAssetPagination) displayPageAssets() {
currentPage
=
(
currentOffset
/
pageSize
)
+
1
}
}
caption
:=
fmt
.
Sprintf
(
i18n
.
T
(
"Page: %d, Count: %d, Total Page: %d, Total Count: %d
"
),
currentPage
,
pageSize
,
totalPage
,
totalCount
,
)
caption
:=
fmt
.
Sprintf
(
getI18nFromMap
(
"AssetTableCaption
"
),
currentPage
,
pageSize
,
totalPage
,
totalCount
)
caption
=
utils
.
WrapperString
(
caption
,
utils
.
Green
)
table
:=
common
.
WrapperTable
{
Fields
:
fields
,
...
...
@@ -387,11 +387,6 @@ func getPageSize(term *utils.Terminal) int {
}
func
displayAssetPaginationTipsInfo
(
w
io
.
Writer
)
{
tips
:=
[]
string
{
i18n
.
T
(
"
\n
Tips: Enter the asset ID and log directly into the asset.
\n
"
),
i18n
.
T
(
"
\n
Page up: P/p Page down: Enter|N/n BACK: b.
\n
"
),
}
for
_
,
tip
:=
range
tips
{
_
,
_
=
w
.
Write
([]
byte
(
tip
))
}
utils
.
IgnoreErrWriteString
(
w
,
getI18nFromMap
(
"LoginTip"
))
utils
.
IgnoreErrWriteString
(
w
,
getI18nFromMap
(
"PageActionTip"
))
}
pkg/handler/session.go
View file @
50194e95
...
...
@@ -8,12 +8,11 @@ import (
"strings"
"github.com/gliderlabs/ssh"
"github.com/olekukonko/tablewriter"
"github.com/xlab/treeprint"
"github.com/jumpserver/koko/pkg/cctx"
"github.com/jumpserver/koko/pkg/common"
"github.com/jumpserver/koko/pkg/config"
"github.com/jumpserver/koko/pkg/i18n"
"github.com/jumpserver/koko/pkg/logger"
"github.com/jumpserver/koko/pkg/model"
"github.com/jumpserver/koko/pkg/proxy"
...
...
@@ -196,57 +195,86 @@ func (h *interactiveHandler) displayAllAssets() {
}
}
func
(
h
*
interactiveHandler
)
chooseSystemUser
(
systemUsers
[]
model
.
SystemUser
)
model
.
SystemUser
{
func
(
h
*
interactiveHandler
)
chooseSystemUser
(
asset
model
.
Asset
,
systemUsers
[]
model
.
SystemUser
)
(
systemUser
model
.
SystemUser
,
ok
bool
)
{
length
:=
len
(
systemUsers
)
switch
length
{
case
0
:
return
model
.
SystemUser
{}
return
model
.
SystemUser
{}
,
false
case
1
:
return
systemUsers
[
0
]
return
systemUsers
[
0
]
,
true
default
:
}
displaySystemUsers
:=
selectHighestPrioritySystemUsers
(
systemUsers
)
if
len
(
displaySystemUsers
)
==
1
{
return
displaySystemUsers
[
0
]
return
displaySystemUsers
[
0
]
,
true
}
table
:=
tablewriter
.
NewWriter
(
h
.
term
)
table
.
SetHeader
([]
string
{
"ID"
,
"Name"
})
for
i
:=
0
;
i
<
len
(
displaySystemUsers
);
i
++
{
table
.
Append
([]
string
{
strconv
.
Itoa
(
i
+
1
),
displaySystemUsers
[
i
]
.
Name
})
Labels
:=
[]
string
{
getI18nFromMap
(
"ID"
),
getI18nFromMap
(
"Name"
),
getI18nFromMap
(
"Username"
)}
fields
:=
[]
string
{
"ID"
,
"Name"
,
"Username"
}
data
:=
make
([]
map
[
string
]
string
,
len
(
displaySystemUsers
))
for
i
,
j
:=
range
displaySystemUsers
{
row
:=
make
(
map
[
string
]
string
)
row
[
"ID"
]
=
strconv
.
Itoa
(
i
+
1
)
row
[
"Name"
]
=
j
.
Name
row
[
"Username"
]
=
j
.
Username
data
[
i
]
=
row
}
table
.
SetBorder
(
false
)
table
.
SetHeaderAlignment
(
tablewriter
.
ALIGN_LEFT
)
table
.
SetAlignment
(
tablewriter
.
ALIGN_LEFT
)
w
,
_
:=
h
.
term
.
GetSize
()
table
:=
common
.
WrapperTable
{
Fields
:
fields
,
Labels
:
Labels
,
FieldsSize
:
map
[
string
][
3
]
int
{
"ID"
:
{
0
,
0
,
5
},
"Name"
:
{
0
,
8
,
0
},
"Username"
:
{
0
,
10
,
0
},
},
Data
:
data
,
TotalSize
:
w
,
TruncPolicy
:
common
.
TruncMiddle
,
}
table
.
Initial
()
h
.
term
.
SetPrompt
(
"ID> "
)
defer
h
.
term
.
SetPrompt
(
"Opt> "
)
for
count
:=
0
;
count
<
3
;
count
++
{
table
.
Render
()
selectUserTip
:=
fmt
.
Sprintf
(
getI18nFromMap
(
"SelectUserTip"
),
asset
.
Hostname
,
asset
.
IP
)
for
{
utils
.
IgnoreErrWriteString
(
h
.
term
,
table
.
Display
())
utils
.
IgnoreErrWriteString
(
h
.
term
,
selectUserTip
)
utils
.
IgnoreErrWriteString
(
h
.
term
,
getI18nFromMap
(
"BackTip"
))
utils
.
IgnoreErrWriteString
(
h
.
term
,
"
\r\n
"
)
line
,
err
:=
h
.
term
.
ReadLine
()
if
err
!=
nil
{
break
return
}
line
=
strings
.
TrimSpace
(
line
)
switch
strings
.
ToLower
(
line
)
{
case
"q"
,
"b"
,
"quit"
,
"exit"
,
"back"
:
return
}
if
num
,
err
:=
strconv
.
Atoi
(
line
);
err
==
nil
{
if
num
>
0
&&
num
<=
len
(
displaySystemUsers
)
{
return
displaySystemUsers
[
num
-
1
]
return
displaySystemUsers
[
num
-
1
]
,
true
}
}
}
return
displaySystemUsers
[
0
]
}
func
(
h
*
interactiveHandler
)
displayAssets
(
assets
model
.
AssetList
)
{
if
len
(
assets
)
==
0
{
_
,
_
=
io
.
WriteString
(
h
.
term
,
i18n
.
T
(
"No
Assets"
)
+
"
\n\r
"
)
_
,
_
=
io
.
WriteString
(
h
.
term
,
getI18nFromMap
(
"No
Assets"
)
+
"
\n\r
"
)
}
else
{
sortedAssets
:=
assets
.
SortBy
(
config
.
GetConf
()
.
AssetListSortBy
)
pag
:=
NewAssetPagination
(
h
.
term
,
sortedAssets
)
selectOneAssets
:=
pag
.
Start
()
if
len
(
selectOneAssets
)
==
1
{
systemUsers
:=
service
.
GetUserAssetSystemUsers
(
h
.
user
.
ID
,
selectOneAssets
[
0
]
.
ID
)
systemUser
:=
h
.
chooseSystemUser
(
systemUsers
)
systemUser
,
ok
:=
h
.
chooseSystemUser
(
selectOneAssets
[
0
],
systemUsers
)
if
!
ok
{
return
}
h
.
assetSelect
=
&
selectOneAssets
[
0
]
h
.
systemUserSelect
=
&
systemUser
h
.
Proxy
(
context
.
TODO
())
...
...
@@ -259,12 +287,9 @@ func (h *interactiveHandler) displayAssets(assets model.AssetList) {
func
(
h
*
interactiveHandler
)
displayNodes
(
nodes
[]
model
.
Node
)
{
tree
:=
ConstructAssetNodeTree
(
nodes
)
tipHeaderMsg
:=
i18n
.
T
(
"Node: [ ID.Name(Asset amount) ]"
)
tipEndMsg
:=
i18n
.
T
(
"Tips: Enter g+NodeID to display the host under the node, such as g1"
)
_
,
err
:=
io
.
WriteString
(
h
.
term
,
"
\n\r
"
+
tipHeaderMsg
)
_
,
err
:=
io
.
WriteString
(
h
.
term
,
"
\n\r
"
+
getI18nFromMap
(
"NodeHeaderTip"
))
_
,
err
=
io
.
WriteString
(
h
.
term
,
tree
.
String
())
_
,
err
=
io
.
WriteString
(
h
.
term
,
tipEndMsg
+
"
\n\r
"
)
_
,
err
=
io
.
WriteString
(
h
.
term
,
getI18nFromMap
(
"NodeEndTip"
)
+
"
\n\r
"
)
if
err
!=
nil
{
logger
.
Info
(
"displayAssetNodes err:"
,
err
)
}
...
...
@@ -277,7 +302,7 @@ func (h *interactiveHandler) refreshAssetsAndNodesData() {
h
.
loadAllAssets
()
}
h
.
loadUserNodes
(
"2"
)
_
,
err
:=
io
.
WriteString
(
h
.
term
,
i18n
.
T
(
"Refresh d
one"
)
+
"
\n\r
"
)
_
,
err
:=
io
.
WriteString
(
h
.
term
,
getI18nFromMap
(
"RefreshD
one"
)
+
"
\n\r
"
)
if
err
!=
nil
{
logger
.
Error
(
"refresh Assets Nodes err:"
,
err
)
}
...
...
@@ -363,7 +388,10 @@ func (h *interactiveHandler) searchNodeAssets(num int) (assets model.AssetList)
func
(
h
*
interactiveHandler
)
ProxyAsset
(
assetSelect
model
.
Asset
)
{
systemUsers
:=
service
.
GetUserAssetSystemUsers
(
h
.
user
.
ID
,
assetSelect
.
ID
)
systemUserSelect
:=
h
.
chooseSystemUser
(
systemUsers
)
systemUserSelect
,
ok
:=
h
.
chooseSystemUser
(
assetSelect
,
systemUsers
)
if
!
ok
{
return
}
h
.
systemUserSelect
=
&
systemUserSelect
h
.
assetSelect
=
&
assetSelect
h
.
Proxy
(
context
.
Background
())
...
...
pkg/proxy/proxy.go
View file @
50194e95
...
...
@@ -21,6 +21,8 @@ type ProxyServer struct {
User
*
model
.
User
Asset
*
model
.
Asset
SystemUser
*
model
.
SystemUser
cacheSSHClient
*
srvconn
.
SSHClient
}
// getSystemUserAuthOrManualSet 获取系统用户的认证信息或手动设置
...
...
@@ -105,6 +107,7 @@ func (p *ProxyServer) getSSHConn() (srvConn *srvconn.ServerSSHConnection, err er
ReuseConnection
:
conf
.
ReuseConnection
,
CloseOnce
:
new
(
sync
.
Once
),
}
srvConn
.
SetSSHClient
(
p
.
cacheSSHClient
)
err
=
srvConn
.
Connect
(
pty
.
Window
.
Height
,
pty
.
Window
.
Width
,
pty
.
Term
)
return
}
...
...
@@ -129,12 +132,22 @@ func (p *ProxyServer) getTelnetConn() (srvConn *srvconn.ServerTelnetConnection,
// getServerConn 获取获取server连接
func
(
p
*
ProxyServer
)
getServerConn
()
(
srvConn
srvconn
.
ServerConnection
,
err
error
)
{
done
:=
make
(
chan
struct
{})
defer
func
()
{
utils
.
IgnoreErrWriteString
(
p
.
UserConn
,
"
\r\n
"
)
close
(
done
)
}()
go
p
.
sendConnectingMsg
(
done
,
config
.
GetConf
()
.
SSHTimeout
*
time
.
Second
)
if
p
.
cacheSSHClient
==
nil
{
done
:=
make
(
chan
struct
{})
defer
func
()
{
utils
.
IgnoreErrWriteString
(
p
.
UserConn
,
"
\r\n
"
)
close
(
done
)
}()
go
p
.
sendConnectingMsg
(
done
,
config
.
GetConf
()
.
SSHTimeout
*
time
.
Second
)
}
else
{
reuseMsg
:=
fmt
.
Sprintf
(
i18n
.
T
(
"Reuse SSH connections (%s@%s) [Number of connections: %d]"
),
p
.
SystemUser
.
Username
,
p
.
Asset
.
Hostname
,
p
.
cacheSSHClient
.
RefCount
())
utils
.
IgnoreErrWriteString
(
p
.
UserConn
,
reuseMsg
+
"
\r\n
"
)
logger
.
Infof
(
"Request %s: Reuse connection for SSH. SSH client %p current ref: %d"
,
p
.
UserConn
.
ID
(),
p
.
cacheSSHClient
,
p
.
cacheSSHClient
.
RefCount
())
}
if
p
.
SystemUser
.
Protocol
==
"telnet"
{
return
p
.
getTelnetConn
()
}
else
{
...
...
@@ -193,6 +206,17 @@ func (p *ProxyServer) checkRequiredSystemUserInfo() error {
logger
.
Errorf
(
"Get asset %s systemuser username err: %s"
,
p
.
Asset
.
Hostname
,
err
)
return
err
}
if
config
.
GetConf
()
.
ReuseConnection
{
key
:=
srvconn
.
MakeReuseSSHClientKey
(
p
.
User
,
p
.
Asset
,
p
.
SystemUser
)
cacheSSHClient
,
ok
:=
srvconn
.
GetClientFromCache
(
key
)
if
ok
{
p
.
cacheSSHClient
=
cacheSSHClient
logger
.
Infof
(
"Reuse connection for SFTP: %s->%s@%s. SSH client %p current ref: %d"
,
p
.
User
.
Username
,
p
.
SystemUser
.
Username
,
p
.
Asset
.
IP
,
cacheSSHClient
,
cacheSSHClient
.
RefCount
())
return
nil
}
}
if
err
:=
p
.
getSystemUserAuthOrManualSet
();
err
!=
nil
{
logger
.
Errorf
(
"Get asset %s systemuser password/PrivateKey err: %s"
,
p
.
Asset
.
Hostname
,
err
)
return
err
...
...
pkg/service/assets.go
View file @
50194e95
...
...
@@ -11,7 +11,7 @@ func GetSystemUserAssetAuthInfo(systemUserID, assetID string) (info model.System
Url
:=
fmt
.
Sprintf
(
SystemUserAssetAuthURL
,
systemUserID
,
assetID
)
_
,
err
:=
authClient
.
Get
(
Url
,
&
info
)
if
err
!=
nil
{
logger
.
Error
(
"Get system user %s asset %s auth info failed"
,
systemUserID
,
assetID
)
logger
.
Error
f
(
"Get system user %s asset %s auth info failed"
,
systemUserID
,
assetID
)
}
return
}
...
...
pkg/srvconn/connmanager.go
View file @
50194e95
...
...
@@ -5,6 +5,7 @@ import (
"fmt"
"net"
"strconv"
"strings"
"sync"
"time"
...
...
@@ -42,7 +43,7 @@ type SSHClient struct {
closed
chan
struct
{}
}
func
(
s
*
SSHClient
)
r
efCount
()
int
{
func
(
s
*
SSHClient
)
R
efCount
()
int
{
if
s
.
isClosed
()
{
return
0
}
...
...
@@ -273,34 +274,33 @@ func newClient(asset *model.Asset, systemUser *model.SystemUser, timeout time.Du
func
NewClient
(
user
*
model
.
User
,
asset
*
model
.
Asset
,
systemUser
*
model
.
SystemUser
,
timeout
time
.
Duration
,
useCache
bool
)
(
client
*
SSHClient
,
err
error
)
{
key
:=
fmt
.
Sprintf
(
"%s_%s_%s"
,
user
.
ID
,
asset
.
ID
,
systemUser
.
ID
)
switch
{
case
useCache
:
client
=
getClientFromCache
(
key
)
if
client
!=
nil
{
if
systemUser
.
Username
==
""
{
systemUser
.
Username
=
client
.
username
}
logger
.
Infof
(
"Reuse connection: %s->%s@%s. SSH client %p current ref: %d"
,
user
.
Username
,
client
.
username
,
asset
.
IP
,
client
,
client
.
refCount
())
return
client
,
nil
}
}
client
,
err
=
newClient
(
asset
,
systemUser
,
timeout
)
if
err
==
nil
&&
useCache
{
key
:=
MakeReuseSSHClientKey
(
user
,
asset
,
systemUser
)
setClientCache
(
key
,
client
)
}
return
}
func
getClientFromCache
(
key
string
)
(
client
*
SSHClient
)
{
func
searchSSHClientFromCache
(
prefixKey
string
)
(
client
*
SSHClient
,
ok
bool
)
{
clientLock
.
Lock
()
defer
clientLock
.
Unlock
()
client
,
ok
:=
sshClients
[
key
]
if
!
ok
{
return
nil
for
key
,
cacheClient
:=
range
sshClients
{
if
strings
.
HasPrefix
(
key
,
prefixKey
)
{
cacheClient
.
increaseRef
()
return
cacheClient
,
true
}
}
return
}
func
GetClientFromCache
(
key
string
)
(
client
*
SSHClient
,
ok
bool
)
{
clientLock
.
Lock
()
defer
clientLock
.
Unlock
()
client
,
ok
=
sshClients
[
key
]
if
ok
{
client
.
increaseRef
()
}
client
.
increaseRef
()
return
}
...
...
@@ -317,7 +317,7 @@ func RecycleClient(client *SSHClient) {
return
}
client
.
decreaseRef
()
if
client
.
r
efCount
()
==
0
{
if
client
.
R
efCount
()
==
0
{
clientLock
.
Lock
()
delete
(
sshClients
,
client
.
key
)
clientLock
.
Unlock
()
...
...
@@ -328,6 +328,10 @@ func RecycleClient(client *SSHClient) {
logger
.
Infof
(
"Success to close SSH client %p"
,
client
)
}
}
else
{
logger
.
Debugf
(
"SSH client %p ref -1. current ref: %d"
,
client
,
client
.
r
efCount
())
logger
.
Debugf
(
"SSH client %p ref -1. current ref: %d"
,
client
,
client
.
R
efCount
())
}
}
func
MakeReuseSSHClientKey
(
user
*
model
.
User
,
asset
*
model
.
Asset
,
systemUser
*
model
.
SystemUser
)
string
{
return
fmt
.
Sprintf
(
"%s_%s_%s_%s"
,
user
.
ID
,
asset
.
ID
,
systemUser
.
ID
,
systemUser
.
Username
)
}
pkg/srvconn/sftpconn.go
View file @
50194e95
...
...
@@ -589,10 +589,42 @@ func (u *UserSftp) SendFTPLog(dataChan <-chan *model.FTPLog) {
}
func
(
u
*
UserSftp
)
GetSftpClient
(
asset
*
model
.
Asset
,
sysUser
*
model
.
SystemUser
)
(
conn
*
SftpConn
,
err
error
)
{
sshClient
,
err
:=
NewClient
(
u
.
User
,
asset
,
sysUser
,
u
.
Overtime
,
u
.
ReuseConnection
)
if
err
!=
nil
{
return
var
(
sshClient
*
SSHClient
ok
bool
)
if
u
.
ReuseConnection
{
key
:=
MakeReuseSSHClientKey
(
u
.
User
,
asset
,
sysUser
)
switch
sysUser
.
Username
{
case
""
:
sshClient
,
ok
=
searchSSHClientFromCache
(
key
)
if
ok
{
sysUser
.
Username
=
sshClient
.
username
}
default
:
sshClient
,
ok
=
GetClientFromCache
(
key
)
}
if
!
ok
{
sshClient
,
err
=
NewClient
(
u
.
User
,
asset
,
sysUser
,
u
.
Overtime
,
u
.
ReuseConnection
)
if
err
!=
nil
{
logger
.
Errorf
(
"Get new SSH client err: %s"
,
err
)
return
}
}
else
{
logger
.
Infof
(
"Reuse connection for SFTP: %s->%s@%s. SSH client %p current ref: %d"
,
u
.
User
.
Username
,
sshClient
.
username
,
asset
.
IP
,
sshClient
,
sshClient
.
RefCount
())
}
}
else
{
sshClient
,
err
=
NewClient
(
u
.
User
,
asset
,
sysUser
,
u
.
Overtime
,
u
.
ReuseConnection
)
if
err
!=
nil
{
logger
.
Errorf
(
"Get new SSH client err: %s"
,
err
)
return
}
}
sftpClient
,
err
:=
sftp
.
NewClient
(
sshClient
.
client
)
if
err
!=
nil
{
logger
.
Errorf
(
"SSH client %p start sftp client session err %s"
,
sshClient
,
err
)
...
...
pkg/srvconn/sshconn.go
View file @
50194e95
...
...
@@ -25,6 +25,12 @@ type ServerSSHConnection struct {
stdout
io
.
Reader
}
func
(
sc
*
ServerSSHConnection
)
SetSSHClient
(
client
*
SSHClient
)
{
if
client
!=
nil
{
sc
.
client
=
client
}
}
func
(
sc
*
ServerSSHConnection
)
Protocol
()
string
{
return
"ssh"
}
...
...
@@ -57,10 +63,12 @@ func (sc *ServerSSHConnection) invokeShell(h, w int, term string) (err error) {
}
func
(
sc
*
ServerSSHConnection
)
Connect
(
h
,
w
int
,
term
string
)
(
err
error
)
{
sc
.
client
,
err
=
NewClient
(
sc
.
User
,
sc
.
Asset
,
sc
.
SystemUser
,
sc
.
Timeout
(),
sc
.
ReuseConnection
)
if
err
!=
nil
{
logger
.
Errorf
(
"New SSH client err: %s"
,
err
)
return
if
sc
.
client
==
nil
{
sc
.
client
,
err
=
NewClient
(
sc
.
User
,
sc
.
Asset
,
sc
.
SystemUser
,
sc
.
Timeout
(),
sc
.
ReuseConnection
)
if
err
!=
nil
{
logger
.
Errorf
(
"New SSH client err: %s"
,
err
)
return
}
}
err
=
sc
.
invokeShell
(
h
,
w
,
term
)
if
err
!=
nil
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment