A little cow B, operation and maintenance team, there is no operation and maintenance platform is not enough, web terminal is an important part of the operation and maintenance of the platform, where the recommended blood gateone, on github 5K stars, unfortunately, the author is not updated, but fundamental, it still my mind web terminal no.1.
Click on the web directly to the terminal button to open a ssh terminal, the experience is very nice:
1. Install
1.1 Environment
# cat /etc/redhat-release
CentOS Linux release 7.4.1708 (Core)
# python -V
Python 2.7.5
1.2. Installation
# yum -y install epel-release
# yum -y install python-setuptools python-pip
# python -m pip install --upgrade pip
# pip install --upgrade setuptools
1.3. The latest version pulled master
# python setup.py install
# pip uninstall tornado
# pip install tornado==4.5.3
1.4. Configuration
/etc/gateone/conf.d/10server.conf
"disable_ssl": true,
"embedded": true,
"js_init": "{showToolbar: false, showTitle: false}",
"origins": ["*"],
"port": 80,
"session_timeout": 0,
At this point, execution gateone to start.
2. Log-free secret
Step 1 is completed, only to http: // gateone-serverip access, the need for further input ip \ port \ username \ password, log on to the server you want to register.
To avoid dense login, complete the following configuration and access the following manner.
2.1. Gateone-server and your target server configuration ssh login-free dense (filled with methods, a little here)
2.2. Copy ssh keys at /var/lib/gateone/users/ANONYMOUS/.ssh directory, file and generates .default_ids
#cd /var/lib/gateone/users/ANONYMOUS/.ssh
# cp /root/.ssh/id* .
# echo "id_rsa" > .default_ids
Login dense web free access:
http://gateone-serverip/?location=mylocation& ssh=ssh://root@target-ip
3. Page Optimization
3.1. Can not enter from the top of the page, below
The program calculates the ranks of the problem, and finally fixed the entry into force of the ranks, you can modify the code to make up:
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};
// 增加结束
After 3.2.ssh prompted many to streamline the
Only rows 2 3.1 As before compacted FIG streamline:
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. Close the error tone (BELL)
Each time a wrong input, there will be sound, while also following pop-up captions, annoying
关闭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. Integration with django project
4.1 modify the configuration
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