Merge pull request #3624 from jumpserver/dev

Dev
pull/3671/head
BaiJiangJie 2020-01-10 18:27:49 +08:00 committed by GitHub
commit bd19f8afe8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 186 additions and 161 deletions

245
README.md
View File

@ -17,156 +17,155 @@ Jumpserver 采纳分布式架构,支持多机房跨区域部署,支持横向
## 核心功能列表 ## 核心功能列表
<table class="subscription-level-table"> <table>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-first-td-background-style" rowspan="4">身份验证 Authentication</td> <td rowspan="7">身份认证<br>Authentication</td>
<td class="features-second-td-background-style" rowspan="3" >登录认证 <td rowspan="4">登录认证</td>
</td> <td>资源统一登录与认证</td>
<td class="features-third-td-background-style">资源统一登录和认证
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-third-td-background-style">LDAP 认证 <td>LDAP/AD 认证</td>
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-third-td-background-style">支持 OpenID实现单点登录 <td>RADIUS 认证</td>
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-second-td-background-style">多因子认证 <td>OpenID 认证(实现单点登录)</td>
</td>
<td class="features-third-td-background-style">MFAGoogle Authenticator
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-first-td-background-style" rowspan="9">账号管理 Account</td> <td rowspan="2">MFA认证</td>
<td class="features-second-td-background-style" rowspan="2">集中账号管理 <td>MFA 二次认证Google Authenticator</td>
</td>
<td class="features-third-td-background-style">管理用户管理
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-third-td-background-style">系统用户管理 <td>RADIUS 二次认证</td>
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-second-td-background-style" rowspan="4">统一密码管理 <td>登录复核X-PACK</td>
</td> <td>用户登录行为受管理员的监管与控制</td>
<td class="features-third-td-background-style">资产密码托管
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-third-td-background-style">自动生成密码 <td rowspan="11">账号管理<br>Account</td>
</td> <td rowspan="2">集中账号</td>
<td>管理用户管理</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-third-td-background-style">密码自动推送 <td>系统用户管理</td>
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-third-td-background-style">密码过期设置 <td rowspan="4">统一密码</td>
</td> <td>资产密码托管</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-outline-td-background-style" rowspan="2">批量密码变更(X-PACK) <td>自动生成密码</td>
</td>
<td class="features-outline-td-background-style">定期批量修改密码
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-outline-td-background-style">生成随机密码 <td>自动推送密码</td>
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-outline-td-background-style">多云环境的资产纳管(X-PACK) <td>密码过期设置</td>
</td>
<td class="features-outline-td-background-style">对私有云、公有云资产统一纳管
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-first-td-background-style" rowspan="9">授权控制 Authorization</td> <td rowspan="2">批量改密X-PACK</td>
<td class="features-second-td-background-style" rowspan="3">资产授权管理 <td>定期批量改密</td>
</td>
<td class="features-third-td-background-style">资产树
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-third-td-background-style">资产或资产组灵活授权 <td>多种密码策略</td>
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-third-td-background-style">节点内资产自动继承授权 <td>多云纳管X-PACK</td>
</td> <td>对私有云、公有云资产自动统一纳管</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-outline-td-background-style">RemoteApp(X-PACK) <td>收集用户X-PACK</td>
</td> <td>自定义任务定期收集主机用户</td>
<td class="features-outline-td-background-style">实现更细粒度的应用级授权
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-outline-td-background-style">组织管理(X-PACK) <td>密码匣子X-PACK</td>
</td> <td>统一对资产主机的用户密码进行查看、更新、测试操作</td>
<td class="features-outline-td-background-style">实现多租户管理,权限隔离
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-second-td-background-style">多维度授权 <td rowspan="15">授权控制<br>Authorization</td>
</td> <td>多维授权</td>
<td class="features-third-td-background-style">可对用户、用户组或系统角色授权 <td>对用户、用户组、资产、资产节点、应用以及系统用户进行授权</td>
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-second-td-background-style">指令限制 <td rowspan="4">资产授权</td>
</td> <td>资产以树状结构进行展示</td>
<td class="features-third-td-background-style">限制特权指令使用,支持黑白名单
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-second-td-background-style">统一文件传输 <td>资产和节点均可灵活授权</td>
</td>
<td class="features-third-td-background-style">SFTP 文件上传/下载
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-second-td-background-style">文件管理 <td>节点内资产自动继承授权</td>
</td>
<td class="features-third-td-background-style">Web SFTP 文件管理
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-first-td-background-style" rowspan="6">安全审计 Audit</td> <td>子节点自动继承父节点授权</td>
<td class="features-second-td-background-style" rowspan="2">会话管理
</td>
<td class="features-third-td-background-style">在线会话管理
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-third-td-background-style">历史会话管理 <td rowspan="2">应用授权</td>
</td> <td>实现更细粒度的应用级授权</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-second-td-background-style" rowspan="2">录像管理 <td>MySQL 数据库应用、RemoteApp 远程应用X-PACK</td>
</td>
<td class="features-third-td-background-style">Linux 录像支持
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-third-td-background-style">Windows 录像支持 <td>动作授权</td>
</td> <td>实现对授权资产的文件上传、下载以及连接动作的控制</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-second-td-background-style">指令审计 <td>时间授权</td>
</td> <td>实现对授权资源使用时间段的限制</td>
<td class="features-third-td-background-style">指令记录
</td>
</tr> </tr>
<tr class="subscription-level-tr-border"> <tr>
<td class="features-second-td-background-style">文件传输审计 <td>特权指令</td>
</td> <td>实现对特权指令的使用(支持黑白名单)</td>
<td class="features-third-td-background-style">上传/下载记录审计 </tr>
</td> <tr>
<td>命令过滤</td>
<td>实现对授权系统用户所执行的命令进行控制</td>
</tr>
<tr>
<td>文件传输</td>
<td>SFTP 文件上传/下载</td>
</tr>
<tr>
<td>文件管理</td>
<td>实现 Web SFTP 文件管理</td>
</tr>
<tr>
<td>工单管理X-PACK</td>
<td>支持对用户登录请求行为进行控制</td>
</tr>
<tr>
<td>组织管理X-PACK</td>
<td>实现多租户管理与权限隔离</td>
</tr>
<tr>
<td rowspan="7">安全审计<br>Audit</td>
<td>操作审计</td>
<td>用户操作行为审计</td>
</tr>
<tr>
<td rowspan="2">会话审计</td>
<td>在线会话内容审计</td>
</tr>
<tr>
<td>历史会话内容审计</td>
</tr>
<tr>
<td rowspan="2">录像审计</td>
<td>支持对 Linux、Windows 等资产操作的录像进行回放审计</td>
</tr>
<tr>
<td>支持对 RemoteAppX-PACK、MySQL 等应用操作的录像进行回放审计</td>
</tr>
<tr>
<td>指令审计</td>
<td>支持对资产和应用等操作的命令进行审计</td>
</tr>
<tr>
<td>文件传输</td>
<td>可对文件的上传、下载记录进行审计</td>
</tr> </tr>
</table> </table>

View File

@ -10,6 +10,7 @@ from django.dispatch import receiver
from common.utils import get_logger, timeit from common.utils import get_logger, timeit
from common.decorator import on_transaction_commit from common.decorator import on_transaction_commit
from .models import Asset, SystemUser, Node, AuthBook from .models import Asset, SystemUser, Node, AuthBook
from .utils import TreeService
from .tasks import ( from .tasks import (
update_assets_hardware_info_util, update_assets_hardware_info_util,
test_asset_connectivity_util, test_asset_connectivity_util,
@ -131,16 +132,22 @@ def on_asset_nodes_add(sender, instance=None, action='', model=None, pk_set=None
if action != "post_add": if action != "post_add":
return return
logger.debug("Assets node add signal recv: {}".format(action)) logger.debug("Assets node add signal recv: {}".format(action))
queryset = model.objects.filter(pk__in=pk_set).values_list('id', flat=True) queryset = model.objects.filter(pk__in=pk_set).values_list('key', flat=True)
if model == Node: if model == Node:
nodes = queryset nodes = queryset
assets = [instance] assets = [instance]
else: else:
nodes = [instance] nodes = [instance]
assets = queryset assets = queryset
# 节点资产发生变化时,将资产关联到节点关联的系统用户, 只关注新增的 # 节点资产发生变化时,将资产关联到节点及祖先节点关联的系统用户, 只关注新增的
nodes_ancestors_keys = set()
node_tree = TreeService.new()
for node in nodes:
ancestors_keys = node_tree.ancestors_ids(nid=node)
nodes_ancestors_keys.update(ancestors_keys)
system_users = SystemUser.objects.filter(nodes__key__in=nodes_ancestors_keys)
system_users_assets = defaultdict(set) system_users_assets = defaultdict(set)
system_users = SystemUser.objects.filter(nodes__in=nodes)
for system_user in system_users: for system_user in system_users:
system_users_assets[system_user].update(set(assets)) system_users_assets[system_user].update(set(assets))
for system_user, _assets in system_users_assets.items(): for system_user, _assets in system_users_assets.items():

View File

@ -84,11 +84,15 @@ class TreeService(Tree):
children_ids = self.all_children_ids(nid, with_self=with_self) children_ids = self.all_children_ids(nid, with_self=with_self)
return [self.get_node(i, deep=deep) for i in children_ids] return [self.get_node(i, deep=deep) for i in children_ids]
def ancestors(self, nid, with_self=False, deep=False): def ancestors_ids(self, nid, with_self=True):
ancestor_ids = list(self.rsearch(nid)) ancestor_ids = list(self.rsearch(nid))
ancestor_ids.pop() ancestor_ids.pop()
if not with_self: if not with_self:
ancestor_ids.pop(0) ancestor_ids.pop(0)
return ancestor_ids
def ancestors(self, nid, with_self=False, deep=False):
ancestor_ids = self.ancestors_ids(nid, with_self=with_self)
return [self.get_node(i, deep=deep) for i in ancestor_ids] return [self.get_node(i, deep=deep) for i in ancestor_ids]
def get_node_full_tag(self, nid): def get_node_full_tag(self, nid):

View File

@ -27,13 +27,16 @@ class LDAPAuthorizationBackend(LDAPBackend):
is_valid = getattr(user, 'is_valid', None) is_valid = getattr(user, 'is_valid', None)
return is_valid or is_valid is None return is_valid or is_valid is None
def pre_check(self, username): def pre_check(self, username, password):
if not settings.AUTH_LDAP: if not settings.AUTH_LDAP:
return False return False
logger.info('Authentication LDAP backend') logger.info('Authentication LDAP backend')
if not username: if not username:
logger.info('Authenticate failed: username is None') logger.info('Authenticate failed: username is None')
return False return False
if not password:
logger.info('Authenticate failed: password is None')
return False
if settings.AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS: if settings.AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS:
user_model = self.get_user_model() user_model = self.get_user_model()
exist = user_model.objects.filter(username=username).exists() exist = user_model.objects.filter(username=username).exists()
@ -44,7 +47,7 @@ class LDAPAuthorizationBackend(LDAPBackend):
return True return True
def authenticate(self, request=None, username=None, password=None, **kwargs): def authenticate(self, request=None, username=None, password=None, **kwargs):
match = self.pre_check(username) match = self.pre_check(username, password)
if not match: if not match:
return None return None
ldap_user = LDAPUser(self, username=username.strip(), request=request) ldap_user = LDAPUser(self, username=username.strip(), request=request)

View File

@ -35,6 +35,7 @@ REPLAY_STORAGE_TYPE_SWIFT_FIELDS = [
{'name': 'SECRET_KEY', 'write_only': True}, {'name': 'SECRET_KEY', 'write_only': True},
{'name': 'REGION'}, {'name': 'REGION'},
{'name': 'ENDPOINT'}, {'name': 'ENDPOINT'},
{'name': 'PROTOCOL'},
] ]
REPLAY_STORAGE_TYPE_OSS_FIELDS = [ REPLAY_STORAGE_TYPE_OSS_FIELDS = [
{'name': 'BUCKET'}, {'name': 'BUCKET'},

View File

@ -145,6 +145,12 @@ class ReplayStorageSwiftForm(BaseReplayStorageForm):
swift_endpoint = forms.CharField( swift_endpoint = forms.CharField(
max_length=128, label=_('Endpoint'), required=False, max_length=128, label=_('Endpoint'), required=False,
) )
swift_protocol = forms.ChoiceField(
choices=(
('HTTP', 'http'),
('HTTPS', 'https')
), initial='http', label=_('Protocol'), required=True,
)
class CommandStorageTypeESForm(BaseCommandStorageForm): class CommandStorageTypeESForm(BaseCommandStorageForm):

View File

@ -362,18 +362,23 @@ class ReplayStorage(CommonModelMixin):
return self.name return self.name
def convert_type(self): def convert_type(self):
s3_type_list = [ s3_type_list = [const.REPLAY_STORAGE_TYPE_CEPH]
const.REPLAY_STORAGE_TYPE_CEPH, const.REPLAY_STORAGE_TYPE_SWIFT
]
tp = self.type tp = self.type
if tp in s3_type_list: if tp in s3_type_list:
tp = const.REPLAY_STORAGE_TYPE_S3 tp = const.REPLAY_STORAGE_TYPE_S3
return tp return tp
def get_extra_config(self):
extra_config = {'TYPE': self.convert_type()}
if self.type == const.REPLAY_STORAGE_TYPE_SWIFT:
extra_config.update({'signer': 'S3SignerType'})
return extra_config
@property @property
def config(self): def config(self):
config = self.meta config = self.meta
config.update({'TYPE': self.convert_type()}) extra_config = self.get_extra_config()
config.update(extra_config)
return config return config
def in_defaults(self): def in_defaults(self):