稍微牛B的运维团队,没有运维平台是不行的,web terminal是运维平台的重要组成部分,这里吐血推荐gateone,github上5K颗星,遗憾的是作者不更新了,但瑕不掩瑜,它依然是我心目中web terminal no.1。
web上直接点击终端按钮,打开一个ssh终端,体验还是很nice的:
1.安装
1.1.环境
# cat /etc/redhat-release
CentOS Linux release 7.4.1708 (Core)
# python -V
Python 2.7.5
1.2.安装
# yum -y install epel-release
# yum -y install python-setuptools python-pip
# python -m pip install --upgrade pip
# pip install --upgrade setuptools
1.3.拉取master最新版本
# python setup.py install
# pip uninstall tornado
# pip install tornado==4.5.3
1.4.配置
/etc/gateone/conf.d/10server.conf
"disable_ssl": true,
"embedded": true,
"js_init": "{showToolbar: false, showTitle: false}",
"origins": ["*"],
"port": 80,
"session_timeout": 0,
此时,执行gateone即可启动。
2.免密登录
步骤1完成后,只能以http://gateone-serverip访问,需要进一步输入ip\port\username\password,以登录你想登录的服务器。
要想免密登录,需要完成以下配置,并以以下方式访问。
2.1.将gateone-server与你的目标服务器配置ssh免密登录(满大街都是方法,这里略)
2.2.在/var/lib/gateone/users/ANONYMOUS/.ssh目录下拷贝ssh秘钥,并生成.default_ids文件
#cd /var/lib/gateone/users/ANONYMOUS/.ssh
# cp /root/.ssh/id* .
# echo "id_rsa" > .default_ids
web免密登录访问方式:
http://gateone-serverip/?location=mylocation& ssh=ssh://root@target-ip
3.页面优化
3.1.不能从页面最上方输入,如下
程序中行列计算有问题,最后以固定的行列生效了,可以修改代码弥补:
gateone.js
3911 newWorkspace: function() {
3944 sidebarWidth = go.toolbar.clientWidth || go.sideinfo.clientHeight; // clientHeight is ……
3945 v.updateDimensions(); // 增加一行
3946 // Prepare the workspace div for the grid
3947 if (go.prefs.showTitle || go.prefs.showToolbar) {
gateone_utils_extra.js
157 getEmDimensions: function(elem, /*opt*/where) {
// 增加开始
242 if (nodeWidth < 7.2) {
243 nodeWidth = 7.2;
244 }
245 if (nodeHeight < 14.05) {
246 nodeHeight = 14.05;
247 }
248 u.prevEmDimensions = {'w': nodeWidth, 'h': nodeHeight};
// 增加结束
3.2.ssh后提示太多,精简之
精简前如3.1上图,精简后只有2行:
ssh://root@target-ip
Last login: Mon Jul ?1 19:37:47 2019 from gateone-serverip
ssh_connect.py
以下有#的都注释,同时增加2行
336 if identities:
337 #print(_(
338 # "The following SSH identities are being used for this "
339 # "connection:"))
345 #print(_("\t\x1b[1m%s\x1b[0m" % os.path.split(identity)[1]))
384 if not os.path.exists(socket_path):
385 args.insert(0, "-M")
386 else:
387 # print("\x1b]0;%s@%s (child)\007" % (user, host))
388 # print(_(
389 # "\x1b]_;notice|Existing ssh session detected for ssh://%s@%s:%s;"
390 # " utilizing existing tunnel.\007" % (user, host, port)
391 # ))
392 pass // 新增一行
393 socket = socket.replace(r'%SHORT_SOCKET%', hashed)
805 while not validated:
806 if not url:
807 #url = raw_input(_(
808 # "[Press Shift-F1 for help]\n\nHost/IP or ssh:// URL%s: " %
809 # default_host_str))
810 url = raw_input(_("")) // 新增一行
881 if protocol == 'ssh':
882 #print(_('Connecting to ssh://%s@%s:%s' % (user, host, port)))
883 # Set title
884 # print("\x1b]0;ssh://%s@%s\007" % (user, host))
885 # Special escape handler (so the rest of the plugin knows the
886 # connect string)
887 connect_string = "{0}@{1}:{2}".format(user, host, port)
888 #print(
889 # "\x1b]_;ssh|set;connect_string;{0}\007".format(connect_string))
890 openssh_connect(user, host, port,
3.3.关闭错误提示音(BELL)
每次一个错误输入,都会有声音,同时还会弹出以下字幕,有点烦人
关闭BELL
terminal.js
35 go.prefs.audibleBell = go.prefs.audibleBell || true; // If false, the bell sound will not be played (visual notification will still occur),
改为go.prefs.audibleBell = false
2377 bellAction: function(bellObj) {
2378 /**:GateOne.Terminal.bellAction(bellObj)
2379
2380 Attached to the 'terminal:bell' WebSocket action; plays a bell sound and pops up a message indiciating which terminal issued a bell.
2381 */
2382 /*
2383 var term = bellObj.term;
2384 go.Terminal.playBell();
2385 v.displayMessage(gettext("Bell in: ") + term + ": " + go.Terminal.terminals[term].title);
2386 */
2387 pass
2388 },
389 // Load the bell sound from the cache. If that fails ask the server to send us the file.
390 /* if (go.prefs.bellSound.length) {
391 logDebug("Existing bell sound found");
392 go.Terminal.loadBell({'mimetype': go.prefs.bellSoundType, 'data_uri': go.prefs.bellSound});
393 } else {
394 logDebug("Attempting to download our bell sound...");
395 go.ws.send(JSON.stringify({'terminal:get_bell': null}));
396 } */
4.与django项目集成
4.1.修改配置
20authentication.conf
"auth": "api",
4.2.创建秘钥
# gateone --new_api_key
生成以下文件
# pwd
/etc/gateone/conf.d
# cat 30api_keys.conf
// This file contains the key and secret pairs used by Gate One's API authentication method.
{
"*": {
"gateone": {
"api_keys": {
"YmNmZGFlMzExYzRmNDBlOGFlNDA4YjEzOGFkYzVkAAAAA": "NjBiOTRlNDM4NzRmNDQ2Y2FmNThjOGNmNDBlNDc0BBBBB"
}
}
}
}
4.3.前端页面button
<button type="button" onclick="get_terminal(\'' + hostip + '\')">终端</button>
<script>
function get_terminal(host_ip) {
window.open('/assets/get_terminal?host_ip=' + host_ip)
}
</script>
4.4.views方法
def get_terminal(request):
host_ip = request.GET['host_ip']
api_key = "YmNmZGFlMzExYzRmNDBlOGFlNDA4YjEzOGFkYzVkAAAAA"
api_secret = "NjBiOTRlNDM4NzRmNDQ2Y2FmNThjOGNmNDBlNDc0BBBBB"
gateone_owner = "meishidong"
timestamp = str(int(time.time() * 1000))
signature = create_signature(api_secret, api_key, gateone_owner, timestamp)
gateone_url = "http://gateone-serverip/"
ssh_url = "ssh://root@" + host_ip
return render(request, "assets/terminal.html", {
"api_key": api_key,
"timestamp": timestamp,
"signature": signature,
"gateone_url": gateone_url,
"ssh_url": ssh_url,
"upn": gateone_owner,
"title": host_ip.split('.')[2] + '.' + host_ip.split('.')[3],
"location": host_ip.replace('.','-') + "_" + str(int(time.time()))
})
4.5.与django项目集成,模板页面terminal.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body>
<script src="http://gateone-serverip/static/gateone.js"></script>
<div id="gateone_container" style="width: 60em; height: 30em;">
<div id="gateone"></div>
</div>
<script type="text/javascript">
window.onload = function() {
// Initialize Gate One:
var apiauth = {
'api_key':'{{api_key}}',
'timestamp':'{{timestamp}}',
'api_version':'1.0',
'upn':'{{upn}}',
'signature':'{{signature}}',
'signature_method':'HMAC-SHA1',
};
GateOne.location = "{{ location }}";
GateOne.init({
auth: apiauth,
url: '{{ gateone_url }}',
autoConnectURL:'{{ssh_url}}',
goDiv:'#gateone',
showToolbar:false,
showTitle: false,
embedded: false,
fillContainer: true,
showAppChooser: false
});
}
</script>
</body>
</html>
4.6.与django项目集成,upn用户key处理
gateone服务器目录/var/lib/gateone/users下会自动生成meishidong目录
# cd /var/lib/gateone/users/meishidong/.ssh
# scp id* ../../meishidong/.ssh/
# scp .default_ids ../../meishidong/.ssh/
4.7.页面调整
4.7.1集成到应用后,默认显示“Gate One - Applicaiton”页面,点击进入目标服务器;
要跳过“Gate One - Applicaiton”页面:
ssh.js
105 // Connect to the given ssh:// URL if we were given an 'ssh' query string variable (e.g. ……
106 /* if (sshQueryString) {
107 if (u.isArray(sshQueryString)) {
108 // Assume it's all one-time only if multiple ssh:// URLs were provided
109 sshOnce = 'true';
110 sshQueryString.forEach(function(str) {
111 handleQueryString(str);
112 });
113 } else {
114 handleQueryString(sshQueryString);
115 }
116 } */
117 // 新增以下三行
118 if (go.prefs.autoConnectURL) {
119 handleQueryString(go.prefs.autoConnectURL);
120 }
4.7.2取消刚连接时弹出的“1:Gate One”
terminal.js
874 var //displayText = termObj.id.split('term')[1] + ": " + go.Terminal.terminals[term].title,
875 //termInfoDiv = u.createElement('div', {'id': 'terminfo', 'class': '?terminfo'}),
876 marginFix = Math.round(go.Terminal.terminals[term].title.length/2),
877 infoContainer = u.createElement('div', {'id': 'infocontainer', 'class': '?term_infocontainer ?halfsectrans'});
878 //termInfoDiv.innerHTML = displayText;
over