From e5ce5766beb569dc8035cde804f6df5c461a85c8 Mon Sep 17 00:00:00 2001 From: Tad Wang Date: Mon, 29 Feb 2016 01:27:51 +0800 Subject: [PATCH 1/9] add document for nginx ssl setting #88 add document for nginx ssl configuration. #88 --- docs/nginx-with-ssl-configuration.md | 75 ++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 docs/nginx-with-ssl-configuration.md diff --git a/docs/nginx-with-ssl-configuration.md b/docs/nginx-with-ssl-configuration.md new file mode 100644 index 000000000..93fcb7e46 --- /dev/null +++ b/docs/nginx-with-ssl-configuration.md @@ -0,0 +1,75 @@ +# 使用Nginx搭建SSL配置 + +跳板机是所有服务器的入口,所以,它的安全至关重要。因此,建议把`Jumpserver`搭建在内网环境中,并且加上SSL证书,保证数据传输的安全。 + +## nginx的安装 + +不同的操作系统及版本,安装方法都不太一样。我们以`Debian`为例。 + +``` +apt-get update +apt-get install -y nginx +``` + +更多安装示例请参考 [Nginx官方安装指南](https://www.nginx.com/resources/wiki/start/topics/tutorials/install/) + +## Nginx中的SSL的配置 + +* 编辑 `/etc/nginx/sites-enabled/default` 或者指定的`Jumpserver`的配置文件 + +* 示例如下 + +``` +server { + listen 443; + listen 80; + server_name YOUR_DOMAIN; + ssl_certificate /etc/nginx/certs/YOUR_DOMAIN_CRT.crt; + ssl_certificate_key /etc/nginx/certs/YOUR_DOMAIN_KEY.key; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + ssl on ; + + if ($ssl_protocol = "") { + rewrite ^ https://$host$request_uri? permanent; + } + + location / { + proxy_set_header Connection ""; + proxy_http_version 1.1; + proxy_pass http://JUMPSERVER_HOST:WEB_PORT; + } + + location /_ws/ { + keepalive_timeout 600s; + send_timeout 600s; + proxy_connect_timeout 7d; + proxy_send_timeout 7d; + proxy_read_timeout 7d; + rewrite ^/_ws(/.*)$ $1 break; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://JUMPSERVER_HOST:WS_PORT; + } +} + +``` + +* 请替换如下表格的关键字 + + +关键字 | 示例 | 说明 +------------- | ------------- |------- +`YOUR_DOMAIN` | example.com | `Jumpserver`的域名 +`YOUR_DOMAIN_CRT` | /etc/nginx/certs/example.crt | SSL证书的CRT文件 +`YOUR_DOMAIN_KEY` | /etc/nginx/certs/example.key | SSL证书的KEY文件 +`JUMPSERVER_HOST` | 127.0.0.1 | `Jumpserver`服务器IP +`WEB_PORT ` | 80 | `Jumpserver`网页监听端口 +`WS_PORT ` | 3000 | websocket端口,`Jumpserver` 默认为3000 + +* 此配置会强制使用`https`, 建议加上(即if判断的那三行)。 \ No newline at end of file From 5ab882ae662d1b4f7bf025fff934add0bf296bb4 Mon Sep 17 00:00:00 2001 From: Tad Wang Date: Mon, 29 Feb 2016 01:37:20 +0800 Subject: [PATCH 2/9] update configuration doc typo of KEYWORD of SSL configuration --- docs/nginx-with-ssl-configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/nginx-with-ssl-configuration.md b/docs/nginx-with-ssl-configuration.md index 93fcb7e46..7d218ebea 100644 --- a/docs/nginx-with-ssl-configuration.md +++ b/docs/nginx-with-ssl-configuration.md @@ -24,8 +24,8 @@ server { listen 443; listen 80; server_name YOUR_DOMAIN; - ssl_certificate /etc/nginx/certs/YOUR_DOMAIN_CRT.crt; - ssl_certificate_key /etc/nginx/certs/YOUR_DOMAIN_KEY.key; + ssl_certificate YOUR_DOMAIN_CRT; + ssl_certificate_key YOUR_DOMAIN_KEY; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; From 054edeefb6a40c4dc7719207aba6c732b9ebe10c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=88=90=E7=BB=B4?= Date: Mon, 29 Feb 2016 15:03:48 +0800 Subject: [PATCH 3/9] try fix tornado connection timeout --- run_websocket.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/run_websocket.py b/run_websocket.py index ec6b2f73f..18aeb4802 100755 --- a/run_websocket.py +++ b/run_websocket.py @@ -7,8 +7,10 @@ import os import sys import os.path import threading -import datetime import re +import functools + +from django.core.signals import request_started, request_finished import tornado.ioloop import tornado.options @@ -20,10 +22,10 @@ import tornado.httpclient from tornado.websocket import WebSocketClosedError from tornado.options import define, options -from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE, IN_CREATE, IN_MODIFY, AsyncNotifier +from pyinotify import WatchManager, ProcessEvent, IN_DELETE, IN_CREATE, IN_MODIFY, AsyncNotifier import select -from connect import Tty, User, Asset, PermRole, logger, get_object, PermRole, gen_resource +from connect import Tty, User, Asset, PermRole, logger, get_object, gen_resource from connect import TtyLog, Log, Session, user_have_perm, get_group_user_perm, MyRunner, ExecLog try: @@ -36,6 +38,16 @@ define("port", default=3000, help="run on the given port", type=int) define("host", default='0.0.0.0', help="run port on given host", type=str) +def django_request_support(func): + @functools.wraps(func) + def _deco(*args, **kwargs): + request_started.send_robust() + response = func(*args, **kwargs) + request_finished.send_robust() + return response + return _deco + + def require_auth(role='user'): def _deco(func): def _deco2(request, *args, **kwargs): @@ -56,6 +68,7 @@ def require_auth(role='user'): request.user = user if role == 'admin': if user.role in ['SU', 'GA']: + request_finished.send_robust() return func(request, *args, **kwargs) logger.debug('Websocket: user [ %s ] is not admin.' % user.username) else: @@ -67,6 +80,7 @@ def require_auth(role='user'): except AttributeError: pass logger.warning('Websocket: Request auth failed.') + return _deco2 return _deco @@ -127,6 +141,7 @@ class MonitorHandler(tornado.websocket.WebSocketHandler): def check_origin(self, origin): return True + @django_request_support @require_auth('admin') def open(self): # 获取监控的path @@ -178,6 +193,7 @@ class WebTty(Tty): class WebTerminalKillHandler(tornado.web.RequestHandler): + @django_request_support @require_auth('admin') def get(self): ws_id = self.get_argument('id') @@ -207,6 +223,7 @@ class ExecHandler(tornado.websocket.WebSocketHandler): def check_origin(self, origin): return True + @django_request_support @require_auth('user') def open(self): logger.debug('Websocket: Open exec request') @@ -287,6 +304,7 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler): def check_origin(self, origin): return True + @django_request_support @require_auth('user') def open(self): logger.debug('Websocket: Open request') From 8c552ccc451f64af1dbdcab293074f5215e833bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=88=90=E7=BB=B4?= Date: Mon, 29 Feb 2016 16:06:20 +0800 Subject: [PATCH 4/9] fix signal send --- run_websocket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_websocket.py b/run_websocket.py index 18aeb4802..9071ed0dd 100755 --- a/run_websocket.py +++ b/run_websocket.py @@ -41,7 +41,7 @@ define("host", default='0.0.0.0', help="run port on given host", type=str) def django_request_support(func): @functools.wraps(func) def _deco(*args, **kwargs): - request_started.send_robust() + request_started.send_robust(func) response = func(*args, **kwargs) request_finished.send_robust() return response From e886b55727933d92213cc38dedde8db142c925f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=88=90=E7=BB=B4?= Date: Mon, 29 Feb 2016 16:09:01 +0800 Subject: [PATCH 5/9] fix signal send --- run_websocket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_websocket.py b/run_websocket.py index 9071ed0dd..56b05a928 100755 --- a/run_websocket.py +++ b/run_websocket.py @@ -43,7 +43,7 @@ def django_request_support(func): def _deco(*args, **kwargs): request_started.send_robust(func) response = func(*args, **kwargs) - request_finished.send_robust() + request_finished.send_robust(func) return response return _deco From 5d28a6e402f2715895060a84d58a6a52ae951ab7 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 29 Feb 2016 16:35:09 +0800 Subject: [PATCH 6/9] =?UTF-8?q?fix(invalid=20connection)=20=E5=AE=9A?= =?UTF-8?q?=E6=9C=9F=E5=A4=84=E7=90=86=E9=95=BF=E6=97=B6=E9=97=B4=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 减少了处理时间间隔 2. 处理策略更改为 超过 1小时没有动的连接就干掉 --- jlog/log_api.py | 6 +----- jumpserver/settings.py | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/jlog/log_api.py b/jlog/log_api.py index 6f2d6edbe..bbbf94763 100644 --- a/jlog/log_api.py +++ b/jlog/log_api.py @@ -80,15 +80,11 @@ def renderTemplate(script_path, time_file_path, dimensions=(24, 80), templatenam def kill_invalid_connection(): - long_time_logs = [] unfinished_logs = Log.objects.filter(is_finished=False) now = datetime.datetime.now() now_timestamp = int(time.mktime(now.timetuple())) - for log in unfinished_logs: - if (now - log.start_time).days > 1: - long_time_logs.append(log) - for log in long_time_logs: + for log in unfinished_logs: try: log_file_mtime = int(os.stat(log.log_path).st_mtime) except OSError: diff --git a/jumpserver/settings.py b/jumpserver/settings.py index 24c0ea27a..3b9a9cadb 100644 --- a/jumpserver/settings.py +++ b/jumpserver/settings.py @@ -155,5 +155,5 @@ BOOTSTRAP_COLUMN_COUNT = 10 CRONJOBS = [ ('0 1 * * *', 'jasset.asset_api.asset_ansible_update_all'), - ('1 * * * *', 'jlog.log_api.kill_invalid_connection'), + ('*/10 * * * *', 'jlog.log_api.kill_invalid_connection'), ] From 3c610668b57fa61f3349bcb5d6a9b14cd419560a Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 29 Feb 2016 17:07:53 +0800 Subject: [PATCH 7/9] =?UTF-8?q?fix(invalid=20connection=20again)=20?= =?UTF-8?q?=E5=AE=9A=E6=9C=9F=E5=A4=84=E7=90=86=E9=95=BF=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 减少了处理时间间隔 2. 处理策略更改为 超过 1小时没有动的连接就干掉 --- jlog/log_api.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/jlog/log_api.py b/jlog/log_api.py index bbbf94763..43925353c 100644 --- a/jlog/log_api.py +++ b/jlog/log_api.py @@ -91,12 +91,17 @@ def kill_invalid_connection(): log_file_mtime = 0 if (now_timestamp - log_file_mtime) > 3600: - try: - os.kill(int(log.pid), 9) - except OSError: - pass + if log.login_type == 'ssh': + try: + os.kill(int(log.pid), 9) + except OSError: + pass + elif (now - log.start_time).days < 1: + continue log.is_finished = True log.end_time = now log.save() + + From d7bb4c10051a8f34869d2f3a91ad55d7d04ff158 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 29 Feb 2016 17:48:14 +0800 Subject: [PATCH 8/9] =?UTF-8?q?fix(kill=20invalid=20connection)=20?= =?UTF-8?q?=E7=B4=A7=E6=80=A5=E5=A4=84=E7=90=86=E8=B6=85=E6=97=B6=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 紧急修复异常bug 2. 修改处理策略 3. 每10分钟处理一次 ssh连接:超过1小时没有操作就干掉 web:超过1天的就设置完成 --- jlog/log_api.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/jlog/log_api.py b/jlog/log_api.py index 43925353c..e7e43e7e6 100644 --- a/jlog/log_api.py +++ b/jlog/log_api.py @@ -15,6 +15,7 @@ from struct import unpack from subprocess import Popen from sys import platform, prefix, stderr from tempfile import NamedTemporaryFile +from jumpserver.api import logger from jinja2 import FileSystemLoader, Template from jinja2.environment import Environment @@ -86,7 +87,7 @@ def kill_invalid_connection(): for log in unfinished_logs: try: - log_file_mtime = int(os.stat(log.log_path).st_mtime) + log_file_mtime = int(os.stat('%s.log' % log.log_path).st_mtime) except OSError: log_file_mtime = 0 @@ -94,6 +95,7 @@ def kill_invalid_connection(): if log.login_type == 'ssh': try: os.kill(int(log.pid), 9) + except OSError: pass elif (now - log.start_time).days < 1: @@ -102,6 +104,7 @@ def kill_invalid_connection(): log.is_finished = True log.end_time = now log.save() + logger.warn('kill log %s' % log.log_path) From b4d74bc5890748cec5652479c75bb8ff79b9781c Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 29 Feb 2016 17:53:15 +0800 Subject: [PATCH 9/9] =?UTF-8?q?hot=5Ffix(kill=20invalid=20connection)=20?= =?UTF-8?q?=E7=B4=A7=E6=80=A5=E4=BF=AE=E5=A4=8D=E8=B6=85=E6=97=B6=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E8=BF=9E=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修改日志参数 2. 修改处理间隔,每10分钟处理一次 3. 修改处理策略 ssh: 1小时不操作,就kill掉 web: 超过1天,就设置完成 --- jlog/log_api.py | 22 +++++++++++----------- jumpserver/settings.py | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/jlog/log_api.py b/jlog/log_api.py index 6f2d6edbe..7089346bc 100644 --- a/jlog/log_api.py +++ b/jlog/log_api.py @@ -15,6 +15,7 @@ from struct import unpack from subprocess import Popen from sys import platform, prefix, stderr from tempfile import NamedTemporaryFile +from jumpserver.api import logger from jinja2 import FileSystemLoader, Template from jinja2.environment import Environment @@ -80,27 +81,26 @@ def renderTemplate(script_path, time_file_path, dimensions=(24, 80), templatenam def kill_invalid_connection(): - long_time_logs = [] unfinished_logs = Log.objects.filter(is_finished=False) now = datetime.datetime.now() now_timestamp = int(time.mktime(now.timetuple())) - for log in unfinished_logs: - if (now - log.start_time).days > 1: - long_time_logs.append(log) - for log in long_time_logs: + for log in unfinished_logs: try: - log_file_mtime = int(os.stat(log.log_path).st_mtime) + log_file_mtime = int(os.stat('%s.log' % log.log_path).st_mtime) except OSError: log_file_mtime = 0 if (now_timestamp - log_file_mtime) > 3600: - try: - os.kill(int(log.pid), 9) - except OSError: - pass + if log.login_type == 'ssh': + try: + os.kill(int(log.pid), 9) + except OSError: + pass + elif (now - log.start_time).days < 1: + continue log.is_finished = True log.end_time = now log.save() - + logger.warn('kill log %s' % log.log_path) diff --git a/jumpserver/settings.py b/jumpserver/settings.py index d1c8a8f40..333f1447f 100644 --- a/jumpserver/settings.py +++ b/jumpserver/settings.py @@ -153,5 +153,5 @@ BOOTSTRAP_COLUMN_COUNT = 10 CRONJOBS = [ ('0 1 * * *', 'jasset.asset_api.asset_ansible_update_all'), - ('1 * * * *', 'jlog.log_api.kill_invalid_connection'), + ('*/10 * * * *', 'jlog.log_api.kill_invalid_connection'), ]