Merge pull request #8951 from jumpserver/dev

v2.27.0-rc1
pull/9057/head
Jiangjie.Bai 2022-10-13 15:05:53 +08:00 committed by GitHub
commit e6d50cc8b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
72 changed files with 800 additions and 463 deletions

View File

@ -8,3 +8,4 @@ celerybeat.pid
### Vagrant ###
.vagrant/
apps/xpack/.git

1
.gitattributes vendored
View File

@ -1,3 +1,4 @@
*.mmdb filter=lfs diff=lfs merge=lfs -text
*.mo filter=lfs diff=lfs merge=lfs -text
*.ipdb filter=lfs diff=lfs merge=lfs -text

View File

@ -42,3 +42,4 @@ version-resolver:
template: |
## 版本变化 Whats Changed
$CHANGES

1
.gitignore vendored
View File

@ -41,3 +41,4 @@ release/*
releashe
/apps/script.py
data/*

View File

@ -126,3 +126,4 @@ enforcement ladder](https://github.com/mozilla/diversity).
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@ -23,3 +23,4 @@ When reporting issues, always include:
Because the issues are open to the public, when submitting files, be sure to remove any sensitive information, e.g. user name, password, IP address, and company name. You can
replace those parts with "REDACTED" or other strings like "****".

View File

@ -1,4 +1,15 @@
FROM python:3.8-slim as stage-build
ARG TARGETARCH
ARG VERSION
ENV VERSION=$VERSION
WORKDIR /opt/jumpserver
ADD . .
RUN cd utils && bash -ixeu build.sh
FROM python:3.8-slim
ARG TARGETARCH
MAINTAINER JumpServer Team <ibuler@qq.com>
ARG BUILD_DEPENDENCIES=" \
@ -21,9 +32,9 @@ ARG DEPENDENCIES=" \
sshpass"
ARG TOOLS=" \
ca-certificates \
curl \
default-mysql-client \
iproute2 \
iputils-ping \
locales \
procps \
@ -33,21 +44,22 @@ ARG TOOLS=" \
unzip \
wget"
RUN sed -i 's@http://.*.debian.org@http://mirrors.ustc.edu.cn@g' /etc/apt/sources.list \
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
sed -i 's@http://.*.debian.org@http://mirrors.ustc.edu.cn@g' /etc/apt/sources.list \
&& rm -f /etc/apt/apt.conf.d/docker-clean \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& apt-get update \
&& apt-get -y install --no-install-recommends ${BUILD_DEPENDENCIES} \
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
&& apt-get -y install --no-install-recommends ${TOOLS} \
&& localedef -c -f UTF-8 -i zh_CN zh_CN.UTF-8 \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& mkdir -p /root/.ssh/ \
&& echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null" > /root/.ssh/config \
&& sed -i "s@# alias l@alias l@g" ~/.bashrc \
&& echo "set mouse-=a" > ~/.vimrc \
&& echo "no" | dpkg-reconfigure dash \
&& echo "zh_CN.UTF-8" | dpkg-reconfigure locales \
&& rm -rf /var/lib/apt/lists/*
ARG TARGETARCH
ARG ORACLE_LIB_MAJOR=19
ARG ORACLE_LIB_MINOR=10
ENV ORACLE_FILE="instantclient-basiclite-linux.${TARGETARCH:-amd64}-${ORACLE_LIB_MAJOR}.${ORACLE_LIB_MINOR}.0.0.0dbru.zip"
@ -68,21 +80,18 @@ ARG PIP_MIRROR=https://pypi.douban.com/simple
ENV PIP_MIRROR=$PIP_MIRROR
ARG PIP_JMS_MIRROR=https://pypi.douban.com/simple
ENV PIP_JMS_MIRROR=$PIP_JMS_MIRROR
# 因为以 jms 或者 jumpserver 开头的 mirror 上可能没有
RUN pip install --upgrade pip==20.2.4 setuptools==49.6.0 wheel==0.34.2 -i ${PIP_MIRROR} \
&& pip install --no-cache-dir $(grep -E 'jms|jumpserver' requirements/requirements.txt) -i ${PIP_JMS_MIRROR} \
&& pip install --no-cache-dir -r requirements/requirements.txt -i ${PIP_MIRROR} \
&& rm -rf ~/.cache/pip
ARG VERSION
ENV VERSION=$VERSION
RUN --mount=type=cache,target=/root/.cache/pip \
set -ex \
&& pip config set global.index-url ${PIP_MIRROR} \
&& pip install --upgrade pip \
&& pip install --upgrade setuptools wheel \
&& pip install $(grep -E 'jms|jumpserver' requirements/requirements.txt) -i ${PIP_JMS_MIRROR} \
&& pip install -r requirements/requirements.txt
ADD . .
RUN cd utils \
&& bash -ixeu build.sh \
&& mv ../release/jumpserver /opt/jumpserver \
&& rm -rf /tmp/build \
&& echo > /opt/jumpserver/config.yml
COPY --from=stage-build /opt/jumpserver/release/jumpserver /opt/jumpserver
RUN echo > /opt/jumpserver/config.yml \
&& rm -rf /tmp/build
WORKDIR /opt/jumpserver
VOLUME /opt/jumpserver/data

94
Dockerfile.loong64 Normal file
View File

@ -0,0 +1,94 @@
FROM python:3.8-slim as stage-build
ARG TARGETARCH
ARG VERSION
ENV VERSION=$VERSION
WORKDIR /opt/jumpserver
ADD . .
RUN cd utils && bash -ixeu build.sh
FROM python:3.8-slim
ARG TARGETARCH
MAINTAINER JumpServer Team <ibuler@qq.com>
ARG BUILD_DEPENDENCIES=" \
g++ \
make \
pkg-config"
ARG DEPENDENCIES=" \
default-libmysqlclient-dev \
freetds-dev \
libpq-dev \
libffi-dev \
libldap2-dev \
libsasl2-dev \
libxml2-dev \
libxmlsec1-dev \
libxmlsec1-openssl \
libaio-dev \
openssh-client \
sshpass"
ARG TOOLS=" \
ca-certificates \
curl \
default-mysql-client \
iputils-ping \
locales \
netcat \
redis-server \
telnet \
vim \
unzip \
wget"
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
set -ex \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& apt-get update \
&& apt-get -y install --no-install-recommends ${BUILD_DEPENDENCIES} \
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
&& apt-get -y install --no-install-recommends ${TOOLS} \
&& mkdir -p /root/.ssh/ \
&& echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null" > /root/.ssh/config \
&& sed -i "s@# alias l@alias l@g" ~/.bashrc \
&& echo "set mouse-=a" > ~/.vimrc \
&& echo "no" | dpkg-reconfigure dash \
&& echo "zh_CN.UTF-8" | dpkg-reconfigure locales \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /tmp/build
COPY ./requirements ./requirements
ARG PIP_MIRROR=https://pypi.douban.com/simple
ENV PIP_MIRROR=$PIP_MIRROR
ARG PIP_JMS_MIRROR=https://pypi.douban.com/simple
ENV PIP_JMS_MIRROR=$PIP_JMS_MIRROR
RUN --mount=type=cache,target=/root/.cache/pip \
set -ex \
&& pip config set global.index-url ${PIP_MIRROR} \
&& pip install --upgrade pip \
&& pip install --upgrade setuptools wheel \
&& pip install https://download.jumpserver.org/pypi/simple/cryptography/cryptography-36.0.1-cp38-cp38-linux_loongarch64.whl \
&& pip install https://download.jumpserver.org/pypi/simple/greenlet/greenlet-1.1.2-cp38-cp38-linux_loongarch64.whl \
&& pip install $(grep 'PyNaCl' requirements/requirements.txt) \
&& GRPC_PYTHON_BUILD_SYSTEM_OPENSSL=true pip install grpcio \
&& pip install $(grep -E 'jms|jumpserver' requirements/requirements.txt) -i ${PIP_JMS_MIRROR} \
&& pip install -r requirements/requirements.txt
COPY --from=stage-build /opt/jumpserver/release/jumpserver /opt/jumpserver
RUN echo > /opt/jumpserver/config.yml \
&& rm -rf /tmp/build
WORKDIR /opt/jumpserver
VOLUME /opt/jumpserver/data
VOLUME /opt/jumpserver/logs
ENV LANG=zh_CN.UTF-8
EXPOSE 8070
EXPOSE 8080
ENTRYPOINT ["./entrypoint.sh"]

View File

@ -672,3 +672,4 @@ may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View File

@ -22,9 +22,7 @@ JumpServer 使用 Python 开发,配备了业界领先的 Web Terminal 方案
JumpServer 采纳分布式架构,支持多机房跨区域部署,支持横向扩展,无资产数量及并发限制。
改变世界,从一点点开始 ...
> 如需进一步了解 JumpServer 开源项目,推荐阅读 [JumpServer 的初心和使命](https://mp.weixin.qq.com/s/S6q_2rP_9MwaVwyqLQnXzA)
### 特色优势
@ -102,7 +100,7 @@ JumpServer 采纳分布式架构,支持多机房跨区域部署,支持横向
- [沐瞳游戏通过JumpServer管控多项目分布式资产](https://blog.fit2cloud.com/?p=3213)
- [携程JumpServer 堡垒机部署与运营实战](https://blog.fit2cloud.com/?p=851)
- [大智慧JumpServer 堡垒机让“大智慧”的混合 IT 运维更智慧](https://blog.fit2cloud.com/?p=882)
- [小红书:JumpServer堡垒机大规模资产跨版本迁移之路](https://blog.fit2cloud.com/?p=516)
- [小红书:JumpServer 堡垒机大规模资产跨版本迁移之路](https://blog.fit2cloud.com/?p=516)
- [中手游JumpServer堡垒机助力中手游提升多云环境下安全运维能力](https://blog.fit2cloud.com/?p=732)
- [中通快递JumpServer主机安全运维实践](https://blog.fit2cloud.com/?p=708)
- [东方明珠JumpServer高效管控异构化、分布式云端资产](https://blog.fit2cloud.com/?p=687)

View File

@ -92,4 +92,3 @@ Licensed under The GNU General Public License version 3 (GPLv3) (the "License")
https://www.gnu.org/licenses/gpl-3.0.htmll
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

View File

@ -18,3 +18,4 @@ All security bugs should be reported to the contact as below:
- ibuler@fit2cloud.com
- support@fit2cloud.com
- 400-052-0755

56
Vagrantfile vendored
View File

@ -1,56 +0,0 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.
# Every Vagrant development environment requires a box. You can search for
# boxes at https://vagrantcloud.com/search.
config.vm.box_check_update = false
config.vm.box = "centos/7"
config.vm.hostname = "jumpserver"
config.vm.network "private_network", ip: "172.17.8.101"
config.vm.provider "virtualbox" do |vb|
vb.memory = "4096"
vb.cpus = 2
vb.name = "jumpserver"
end
config.vm.synced_folder ".", "/vagrant", type: "rsync",
rsync__verbose: true,
rsync__exclude: ['.git*', 'node_modules*','*.log','*.box','Vagrantfile']
config.vm.provision "shell", inline: <<-SHELL
## 设置yum的阿里云源
sudo curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
sudo sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/CentOS-Base.repo
sudo curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
sudo yum makecache
## 安装依赖包
sudo yum install -y python36 python36-devel python36-pip \
libtiff-devel libjpeg-devel libzip-devel freetype-devel \
lcms2-devel libwebp-devel tcl-devel tk-devel sshpass \
openldap-devel mariadb-devel mysql-devel libffi-devel \
openssh-clients telnet openldap-clients gcc
## 配置pip阿里云源
mkdir /home/vagrant/.pip
cat << EOF | sudo tee -a /home/vagrant/.pip/pip.conf
[global]
timeout = 6000
index-url = https://mirrors.aliyun.com/pypi/simple/
[install]
use-mirrors = true
mirrors = https://mirrors.aliyun.com/pypi/simple/
trusted-host=mirrors.aliyun.com
EOF
python3.6 -m venv /home/vagrant/venv
source /home/vagrant/venv/bin/activate
echo 'source /home/vagrant/venv/bin/activate' >> /home/vagrant/.bash_profile
SHELL
end

View File

@ -4,6 +4,7 @@ from orgs.mixins.api import OrgBulkModelViewSet
from rest_framework.decorators import action
from rest_framework.response import Response
from common.tree import TreeNodeSerializer
from common.mixins.api import SuggestionMixin
from .. import serializers

View File

@ -7,3 +7,7 @@ from django.apps import AppConfig
class ApplicationsConfig(AppConfig):
name = 'applications'
verbose_name = _('Applications')
def ready(self):
from . import signal_handlers
super().ready()

View File

@ -83,9 +83,3 @@ class AppType(models.TextChoices):
if AppCategory.is_xpack(category):
return True
return tp in ['oracle', 'postgresql', 'sqlserver']
class OracleVersion(models.TextChoices):
version_11g = '11g', '11g'
version_12c = '12c', '12c'
version_other = 'other', _('Other')

View File

@ -10,9 +10,7 @@ from common.mixins import CommonModelMixin
from common.tree import TreeNode
from common.utils import is_uuid
from assets.models import Asset, SystemUser
from ..const import OracleVersion
from ..utils import KubernetesTree
from .. import const
@ -175,6 +173,7 @@ class ApplicationTreeNodeMixin:
return pid
def as_tree_node(self, pid, k8s_as_tree=False):
from ..utils import KubernetesTree
if self.type == const.AppType.k8s and k8s_as_tree:
node = KubernetesTree(pid).as_tree_node(self)
else:
@ -304,15 +303,6 @@ class Application(CommonModelMixin, OrgModelMixin, ApplicationTreeNodeMixin):
target_ip = self.attrs.get('host')
return target_ip
def get_target_protocol_for_oracle(self):
""" Oracle 类型需要单独处理,因为要携带版本号 """
if not self.is_type(self.APP_TYPE.oracle):
return
version = self.attrs.get('version', OracleVersion.version_12c)
if version == OracleVersion.version_other:
return
return 'oracle_%s' % version
class ApplicationUser(SystemUser):
class Meta:

View File

@ -2,15 +2,9 @@ from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer
from applications.const import OracleVersion
__all__ = ['OracleSerializer']
class OracleSerializer(DBSerializer):
version = serializers.ChoiceField(
choices=OracleVersion.choices, default=OracleVersion.version_12c,
allow_null=True, label=_('Version'),
help_text=_('Magnus currently supports only 11g and 12c connections')
)
port = serializers.IntegerField(default=1521, label=_('Port'), allow_null=True)

View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
#

View File

@ -208,7 +208,8 @@ class SystemUserTaskApi(generics.CreateAPIView):
class SystemUserCommandFilterRuleListApi(generics.ListAPIView):
rbac_perms = {
'list': 'assets.view_commandfilterule'
'list': 'assets.view_commandfilterule',
'GET': 'assets.view_commandfilterule',
}
def get_serializer_class(self):
@ -223,12 +224,14 @@ class SystemUserCommandFilterRuleListApi(generics.ListAPIView):
if not system_user:
system_user_id = self.request.query_params.get('system_user_id')
asset_id = self.request.query_params.get('asset_id')
node_id = self.request.query_params.get('node_id')
application_id = self.request.query_params.get('application_id')
rules = CommandFilterRule.get_queryset(
user_id=user_id,
user_group_id=user_group_id,
system_user_id=system_user_id,
asset_id=asset_id,
node_id=node_id,
application_id=application_id
)
return rules

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.15 on 2022-10-09 09:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0091_auto_20220629_1826'),
]
operations = [
migrations.AddField(
model_name='commandfilter',
name='nodes',
field=models.ManyToManyField(blank=True, related_name='cmd_filters', to='assets.Node', verbose_name='Nodes'),
),
]

View File

@ -116,7 +116,7 @@ class NodesRelationMixin:
nodes = []
for node in self.get_nodes():
_nodes = node.get_ancestors(with_self=True)
nodes.append(_nodes)
nodes.extend(list(_nodes))
if flat:
nodes = list(reduce(lambda x, y: set(x) | set(y), nodes))
return nodes

View File

@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy as _
from users.models import User, UserGroup
from applications.models import Application
from ..models import SystemUser, Asset
from ..models import SystemUser, Asset, Node
from common.utils import lazyproperty, get_logger, get_object_or_none
from orgs.mixins.models import OrgModelMixin
@ -33,6 +33,10 @@ class CommandFilter(OrgModelMixin):
'users.UserGroup', related_name='cmd_filters', blank=True,
verbose_name=_("User group"),
)
nodes = models.ManyToManyField(
'assets.Node', related_name='cmd_filters', blank=True,
verbose_name=_("Nodes")
)
assets = models.ManyToManyField(
'assets.Asset', related_name='cmd_filters', blank=True,
verbose_name=_("Asset")
@ -189,7 +193,8 @@ class CommandFilterRule(OrgModelMixin):
@classmethod
def get_queryset(cls, user_id=None, user_group_id=None, system_user_id=None,
asset_id=None, application_id=None, org_id=None):
asset_id=None, node_id=None, application_id=None, org_id=None):
# user & user_group
user_groups = []
user = get_object_or_none(User, pk=user_id)
if user:
@ -198,8 +203,18 @@ class CommandFilterRule(OrgModelMixin):
if user_group:
org_id = user_group.org_id
user_groups.append(user_group)
system_user = get_object_or_none(SystemUser, pk=system_user_id)
# asset & node
nodes = []
asset = get_object_or_none(Asset, pk=asset_id)
if asset:
nodes.extend(asset.get_all_nodes())
node = get_object_or_none(Node, pk=node_id)
if node:
org_id = node.org_id
nodes.extend(list(node.get_ancestors(with_self=True)))
system_user = get_object_or_none(SystemUser, pk=system_user_id)
application = get_object_or_none(Application, pk=application_id)
q = Q()
if user:
@ -212,6 +227,8 @@ class CommandFilterRule(OrgModelMixin):
if asset:
org_id = asset.org_id
q |= Q(assets=asset)
if nodes:
q |= Q(nodes__in=set(nodes))
if application:
org_id = application.org_id
q |= Q(applications=application)

View File

@ -21,7 +21,7 @@ class CommandFilterSerializer(BulkOrgResourceModelSerializer):
'comment', 'created_by',
]
fields_fk = ['rules']
fields_m2m = ['users', 'user_groups', 'system_users', 'assets', 'applications']
fields_m2m = ['users', 'user_groups', 'system_users', 'nodes', 'assets', 'applications']
fields = fields_small + fields_fk + fields_m2m
extra_kwargs = {
'rules': {'read_only': True},

View File

@ -62,12 +62,15 @@ class ConnectionTokenMixin:
def get_smart_endpoint(self, protocol, asset=None, application=None):
if asset:
target_instance = asset
target_ip = asset.get_target_ip()
elif application:
target_instance = application
target_ip = application.get_target_ip()
else:
target_instance = None
target_ip = ''
endpoint = EndpointRule.match_endpoint(target_ip, protocol, self.request)
endpoint = EndpointRule.match_endpoint(target_instance, target_ip, protocol, self.request)
return endpoint
@staticmethod

View File

@ -55,7 +55,9 @@ class OAuth2AuthCallbackView(View):
)
logger.debug(log_prompt.format('Redirect'))
return HttpResponseRedirect(settings.AUTH_OAUTH2_AUTHENTICATION_FAILURE_REDIRECT_URI)
# OAuth2 服务端认证成功, 但是用户被禁用了, 这时候需要调用服务端的logout
redirect_url = settings.AUTH_OAUTH2_PROVIDER_END_SESSION_ENDPOINT
return HttpResponseRedirect(redirect_url)
class OAuth2EndSessionView(View):

View File

@ -271,7 +271,10 @@ class Saml2AuthCallbackView(View, PrepareRequestMixin):
auth.login(self.request, user)
logger.debug(log_prompt.format('Redirect'))
next_url = saml_instance.redirect_to(post_data.get('RelayState', '/'))
redir = post_data.get('RelayState')
if not redir or len(redir) == 0:
redir = "/"
next_url = saml_instance.redirect_to(redir)
return HttpResponseRedirect(next_url)
@csrf_exempt

View File

@ -30,7 +30,9 @@ class CeleryBaseService(BaseService):
'-l', 'INFO',
'-c', str(self.num),
'-Q', self.queue,
'-n', f'{self.queue}@{server_hostname}'
'--heartbeat-interval', '10',
'-n', f'{self.queue}@{server_hostname}',
'--without-mingle',
]
return cmd

View File

@ -19,11 +19,13 @@ class FlowerService(BaseService):
'celery',
'-A', 'ops',
'flower',
'-l', 'INFO',
'-logging=info',
'--url_prefix=/core/flower',
'--auto_refresh=False',
'--max_tasks=1000',
'--tasks_columns=uuid,name,args,state,received,started,runtime,worker'
'--persistent=True',
'-db=/opt/jumpserver/data/flower.db',
'--state_save_interval=600000'
]
return cmd

View File

@ -33,7 +33,7 @@ class EBCCipher:
def __padding(val):
# padding
val = bytes(val)
while len(val) % 16 != 0:
while len(val) == 0 or len(val) % 16 != 0:
val += b'\0'
return val

View File

@ -53,7 +53,9 @@ class Subscription:
error(msg, item)
logger.error('Subscribe handler handle msg error: {}'.format(e))
except Exception as e:
logger.error('Consume msg error: {}'.format(e))
# 正常的 websocket 断开时, redis 会断开连接,避免日志太多
# logger.error('Consume msg error: {}'.format(e))
pass
try:
complete()

View File

@ -82,7 +82,8 @@ class PiicoSM4EcbCrypto(BaseCrypto):
return self.cipher.encrypt(self.to_16(data))
def _decrypt(self, data: bytes) -> bytes:
return self.cipher.decrypt(data)
bs = self.cipher.decrypt(data)
return bs.rstrip(b'\0')
class AESCrypto:
@ -232,6 +233,8 @@ class Crypto:
return self.cryptos[0]
def encrypt(self, text):
if text is None:
return text
return self.encryptor.encrypt(text)
def decrypt(self, text):
@ -241,7 +244,7 @@ class Crypto:
if origin_text:
# 有时不同算法解密不报错,但是返回空字符串
return origin_text
except (TypeError, ValueError, UnicodeDecodeError, IndexError):
except Exception:
continue

View File

@ -35,7 +35,7 @@ def random_string(length, lower=True, upper=True, digit=True, special_char=False
if special_char:
spc = random.choice(string_punctuation)
i = random.choice(range(len(password)))
i = random.choice(range(1, len(password)))
password[i] = spc
password = ''.join(password)

View File

@ -484,6 +484,9 @@ class Config(dict):
'SERVER_REPLAY_STORAGE': {},
'SECURITY_DATA_CRYPTO_ALGO': None,
'GMSSL_ENABLED': False,
# Magnus 组件需要监听的端口范围
'MAGNUS_DB_PORTS_START': 30000,
'MAGNUS_DB_PORTS_LIMIT_COUNT': 1000,
# 记录清理清理
'LOGIN_LOG_KEEP_DAYS': 200,

View File

@ -161,6 +161,8 @@ AUTH_OAUTH2_CLIENT_SECRET = CONFIG.AUTH_OAUTH2_CLIENT_SECRET
AUTH_OAUTH2_CLIENT_ID = CONFIG.AUTH_OAUTH2_CLIENT_ID
AUTH_OAUTH2_SCOPE = CONFIG.AUTH_OAUTH2_SCOPE
AUTH_OAUTH2_USER_ATTR_MAP = CONFIG.AUTH_OAUTH2_USER_ATTR_MAP
AUTH_OAUTH2_LOGOUT_COMPLETELY = CONFIG.AUTH_OAUTH2_LOGOUT_COMPLETELY
AUTH_OAUTH2_PROVIDER_END_SESSION_ENDPOINT = CONFIG.AUTH_OAUTH2_PROVIDER_END_SESSION_ENDPOINT
AUTH_OAUTH2_AUTH_LOGIN_CALLBACK_URL_NAME = 'authentication:oauth2:login-callback'
AUTH_OAUTH2_AUTHENTICATION_REDIRECT_URI = '/'
AUTH_OAUTH2_AUTHENTICATION_FAILURE_REDIRECT_URI = '/'

View File

@ -177,3 +177,7 @@ HELP_SUPPORT_URL = CONFIG.HELP_SUPPORT_URL
SESSION_RSA_PRIVATE_KEY_NAME = 'jms_private_key'
SESSION_RSA_PUBLIC_KEY_NAME = 'jms_public_key'
# Magnus DB Port
MAGNUS_DB_PORTS_START = CONFIG.MAGNUS_DB_PORTS_START
MAGNUS_DB_PORTS_LIMIT_COUNT = CONFIG.MAGNUS_DB_PORTS_LIMIT_COUNT

View File

@ -9,7 +9,6 @@ from .base import (
)
from ..const import CONFIG, PROJECT_DIR
REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
@ -64,7 +63,6 @@ SWAGGER_SETTINGS = {
'DEFAULT_INFO': 'jumpserver.views.swagger.api_info',
}
# Captcha settings, more see https://django-simple-captcha.readthedocs.io/en/latest/advanced.html
CAPTCHA_IMAGE_SIZE = (180, 38)
CAPTCHA_FOREGROUND_COLOR = '#001100'
@ -81,7 +79,6 @@ BOOTSTRAP3 = {
'required_css_class': 'required',
}
# Django channels support websocket
if not REDIS_USE_SSL:
redis_ssl = None
@ -108,7 +105,6 @@ CHANNEL_LAYERS = {
}
ASGI_APPLICATION = 'jumpserver.routing.application'
# Dump all celery log to here
CELERY_LOG_DIR = os.path.join(PROJECT_DIR, 'data', 'celery')
@ -131,6 +127,7 @@ CELERY_TASK_EAGER_PROPAGATES = True
CELERY_WORKER_REDIRECT_STDOUTS = True
CELERY_WORKER_REDIRECT_STDOUTS_LEVEL = "INFO"
CELERY_TASK_SOFT_TIME_LIMIT = 3600
CELERY_WORKER_CANCEL_LONG_RUNNING_TASKS_ON_CONNECTION_LOSS = True
if REDIS_USE_SSL:
CELERY_BROKER_USE_SSL = CELERY_REDIS_BACKEND_USE_SSL = {
@ -148,3 +145,7 @@ REDIS_PORT = CONFIG.REDIS_PORT
REDIS_PASSWORD = CONFIG.REDIS_PASSWORD
DJANGO_REDIS_SCAN_ITERSIZE = 1000
# GM DEVICE
PIICO_DEVICE_ENABLE = CONFIG.PIICO_DEVICE_ENABLE
PIICO_DRIVER_PATH = CONFIG.PIICO_DRIVER_PATH

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4d5dcc300fa64f04b513b670af0d9717fcdb108b54ddf264971b355cc72178de
size 132193
oid sha256:529cf82721ab8594a7ee8e4e1bff1f80fb702d3bfcbb5fb6e7bfb8b897d4920b
size 132560

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-09-15 15:52+0800\n"
"POT-Creation-Date: 2022-09-22 19:01+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -23,13 +23,13 @@ msgid "Acls"
msgstr "Acls"
#: acls/models/base.py:25 acls/serializers/login_asset_acl.py:47
#: applications/models/application.py:220 assets/models/asset.py:138
#: applications/models/application.py:219 assets/models/asset.py:138
#: assets/models/base.py:175 assets/models/cluster.py:18
#: assets/models/cmd_filter.py:27 assets/models/domain.py:23
#: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
#: orgs/models.py:70 perms/models/base.py:83 rbac/models/role.py:29
#: settings/models.py:33 settings/serializers/sms.py:6
#: terminal/models/endpoint.py:10 terminal/models/endpoint.py:86
#: terminal/models/endpoint.py:14 terminal/models/endpoint.py:87
#: terminal/models/storage.py:26 terminal/models/task.py:16
#: terminal/models/terminal.py:100 users/forms/profile.py:33
#: users/models/group.py:15 users/models/user.py:669
@ -38,12 +38,12 @@ msgid "Name"
msgstr "名前"
#: acls/models/base.py:27 assets/models/cmd_filter.py:84
#: assets/models/user.py:251 terminal/models/endpoint.py:89
#: assets/models/user.py:251 terminal/models/endpoint.py:90
msgid "Priority"
msgstr "優先順位"
#: acls/models/base.py:28 assets/models/cmd_filter.py:84
#: assets/models/user.py:251 terminal/models/endpoint.py:90
#: assets/models/user.py:251 terminal/models/endpoint.py:91
msgid "1-100, the lower the value will be match first"
msgstr "1-100、低い値は最初に一致します"
@ -53,7 +53,7 @@ msgstr "1-100、低い値は最初に一致します"
msgid "Active"
msgstr "アクティブ"
#: acls/models/base.py:32 applications/models/application.py:233
#: acls/models/base.py:32 applications/models/application.py:232
#: assets/models/asset.py:143 assets/models/asset.py:231
#: assets/models/backup.py:54 assets/models/base.py:180
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:48
@ -61,7 +61,7 @@ msgstr "アクティブ"
#: assets/models/domain.py:65 assets/models/group.py:23
#: assets/models/label.py:23 ops/models/adhoc.py:38 orgs/models.py:73
#: perms/models/base.py:93 rbac/models/role.py:37 settings/models.py:38
#: terminal/models/endpoint.py:23 terminal/models/endpoint.py:96
#: terminal/models/endpoint.py:22 terminal/models/endpoint.py:97
#: terminal/models/storage.py:29 terminal/models/terminal.py:114
#: tickets/models/comment.py:32 tickets/models/ticket/general.py:288
#: users/models/group.py:16 users/models/user.py:706
@ -241,7 +241,7 @@ msgstr ""
msgid "Time Period"
msgstr "期間"
#: applications/apps.py:9 applications/models/application.py:64
#: applications/apps.py:9 applications/models/application.py:62
msgid "Applications"
msgstr "アプリケーション"
@ -260,11 +260,7 @@ msgstr "リモートアプリ"
msgid "Custom"
msgstr "カスタム"
#: applications/const.py:91 rbac/tree.py:29
msgid "Other"
msgstr "その他"
#: applications/models/account.py:12 applications/models/application.py:237
#: applications/models/account.py:12 applications/models/application.py:236
#: assets/models/backup.py:32 assets/models/cmd_filter.py:45
#: authentication/models.py:67 authentication/models.py:95
#: perms/models/application_permission.py:28
@ -282,9 +278,8 @@ msgstr "アプリケーション"
msgid "System user"
msgstr "システムユーザー"
#: applications/models/account.py:17
#: applications/serializers/attrs/application_type/oracle.py:13
#: assets/models/authbook.py:21 settings/serializers/auth/cas.py:18
#: applications/models/account.py:17 assets/models/authbook.py:21
#: settings/serializers/auth/cas.py:18
msgid "Version"
msgstr "バージョン"
@ -300,7 +295,7 @@ msgstr "アプリケーションアカウントの秘密を表示できます"
msgid "Can change application account secret"
msgstr "アプリケーションアカウントの秘密を変更できます"
#: applications/models/application.py:222
#: applications/models/application.py:221
#: applications/serializers/application.py:101 assets/models/label.py:21
#: perms/models/application_permission.py:21
#: perms/serializers/application/user_permission.py:33
@ -309,7 +304,7 @@ msgstr "アプリケーションアカウントの秘密を変更できます"
msgid "Category"
msgstr "カテゴリ"
#: applications/models/application.py:225
#: applications/models/application.py:224
#: applications/serializers/application.py:103 assets/models/backup.py:49
#: assets/models/cmd_filter.py:82 assets/models/user.py:250
#: authentication/models.py:70 perms/models/application_permission.py:24
@ -323,21 +318,21 @@ msgstr "カテゴリ"
msgid "Type"
msgstr "タイプ"
#: applications/models/application.py:229 assets/models/asset.py:217
#: applications/models/application.py:228 assets/models/asset.py:217
#: assets/models/domain.py:29 assets/models/domain.py:64
msgid "Domain"
msgstr "ドメイン"
#: applications/models/application.py:231 xpack/plugins/cloud/models.py:33
#: applications/models/application.py:230 xpack/plugins/cloud/models.py:33
#: xpack/plugins/cloud/serializers/account.py:63
msgid "Attrs"
msgstr "ツールバーの"
#: applications/models/application.py:241
#: applications/models/application.py:240
msgid "Can match application"
msgstr "アプリケーションを一致させることができます"
#: applications/models/application.py:320
#: applications/models/application.py:310
msgid "Application user"
msgstr "アプリケーションユーザー"
@ -393,7 +388,7 @@ msgstr "クラスター"
#: applications/serializers/attrs/application_category/db.py:11
#: ops/models/adhoc.py:157 settings/serializers/auth/radius.py:14
#: settings/serializers/auth/sms.py:65 terminal/models/endpoint.py:11
#: settings/serializers/auth/sms.py:65 terminal/models/endpoint.py:15
#: xpack/plugins/cloud/serializers/account_attrs.py:72
msgid "Host"
msgstr "ホスト"
@ -402,7 +397,7 @@ msgstr "ホスト"
#: applications/serializers/attrs/application_type/mongodb.py:10
#: applications/serializers/attrs/application_type/mysql.py:10
#: applications/serializers/attrs/application_type/mysql_workbench.py:22
#: applications/serializers/attrs/application_type/oracle.py:16
#: applications/serializers/attrs/application_type/oracle.py:10
#: applications/serializers/attrs/application_type/pgsql.py:10
#: applications/serializers/attrs/application_type/redis.py:10
#: applications/serializers/attrs/application_type/sqlserver.py:10
@ -497,10 +492,6 @@ msgstr "Mysql workbench のユーザー名"
msgid "Mysql workbench password"
msgstr "Mysql workbench パスワード"
#: applications/serializers/attrs/application_type/oracle.py:14
msgid "Magnus currently supports only 11g and 12c connections"
msgstr "現在、Magnusは11gおよび12cバージョンへの接続のみをサポートしています"
#: applications/serializers/attrs/application_type/vmware_client.py:26
msgid "Vmware username"
msgstr "Vmware ユーザー名"
@ -3457,6 +3448,10 @@ msgstr "監査ビュー"
msgid "System setting"
msgstr "システム設定"
#: rbac/tree.py:29
msgid "Other"
msgstr "その他"
#: rbac/tree.py:37
msgid "Accounts"
msgstr "アカウント"
@ -4956,58 +4951,34 @@ msgstr "ストレージが無効です"
msgid "Command record"
msgstr "コマンドレコード"
#: terminal/models/endpoint.py:13
#: terminal/models/endpoint.py:17
msgid "HTTPS Port"
msgstr "HTTPS ポート"
#: terminal/models/endpoint.py:14 terminal/models/terminal.py:107
#: terminal/models/endpoint.py:18 terminal/models/terminal.py:107
msgid "HTTP Port"
msgstr "HTTP ポート"
#: terminal/models/endpoint.py:15 terminal/models/terminal.py:106
#: terminal/models/endpoint.py:19 terminal/models/terminal.py:106
msgid "SSH Port"
msgstr "SSH ポート"
#: terminal/models/endpoint.py:16
#: terminal/models/endpoint.py:20
msgid "RDP Port"
msgstr "RDP ポート"
#: terminal/models/endpoint.py:17
msgid "MySQL Port"
msgstr "MySQL ポート"
#: terminal/models/endpoint.py:18
msgid "MariaDB Port"
msgstr "MariaDB ポート"
#: terminal/models/endpoint.py:19
msgid "PostgreSQL Port"
msgstr "PostgreSQL ポート"
#: terminal/models/endpoint.py:20
msgid "Redis Port"
msgstr "Redis ポート"
#: terminal/models/endpoint.py:21
msgid "Oracle 11g Port"
msgstr "Oracle 11g ポート"
#: terminal/models/endpoint.py:22
msgid "Oracle 12c Port"
msgstr "Oracle 12c ポート"
#: terminal/models/endpoint.py:28 terminal/models/endpoint.py:94
#: terminal/models/endpoint.py:27 terminal/models/endpoint.py:95
#: terminal/serializers/endpoint.py:57 terminal/serializers/storage.py:38
#: terminal/serializers/storage.py:50 terminal/serializers/storage.py:80
#: terminal/serializers/storage.py:90 terminal/serializers/storage.py:98
msgid "Endpoint"
msgstr "エンドポイント"
#: terminal/models/endpoint.py:87
#: terminal/models/endpoint.py:88
msgid "IP group"
msgstr "IP グループ"
#: terminal/models/endpoint.py:99
#: terminal/models/endpoint.py:100
msgid "Endpoint rule"
msgstr "エンドポイントルール"
@ -5188,9 +5159,19 @@ msgstr "レベル"
msgid "Batch danger command alert"
msgstr "一括危険コマンド警告"
#: terminal/serializers/endpoint.py:12
msgid "Oracle port"
msgstr ""
#: terminal/serializers/endpoint.py:14
msgid "Magnus listen db port"
msgstr "Magnus がリッスンするデータベース ポート"
#: terminal/serializers/endpoint.py:17
msgid "Magnus Listen port range"
msgstr "Magnus がリッスンするポート範囲"
#: terminal/serializers/endpoint.py:19
msgid ""
"The range of ports that Magnus listens on is modified in the configuration "
"file"
msgstr "Magnus がリッスンするポート範囲を構成ファイルで変更してください"
#: terminal/serializers/endpoint.py:51
msgid ""
@ -5321,6 +5302,16 @@ msgstr "見つかりません"
msgid "view"
msgstr "表示"
#: terminal/utils/db_port_mapper.py:77
msgid ""
"No ports can be used, check and modify the limit on the number of ports that "
"Magnus listens on in the configuration file."
msgstr "使用できるポートがありません。設定ファイルで Magnus がリッスンするポート数の制限を確認して変更してください. "
#: terminal/utils/db_port_mapper.py:79
msgid "All available port count: {}, Already use port count: {}"
msgstr "使用可能なすべてのポート数: {}、すでに使用しているポート数: {}"
#: tickets/apps.py:7
msgid "Tickets"
msgstr "チケット"

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:618dce0f2521591410fb16b3d0afa536333d2e4f317d75d1955fe413dc0e64cb
size 108949
oid sha256:a346a8166af782cbc41eac33475b4cfac2e3713b26f84ddf6fa532742133b89d
size 109216

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-09-15 15:52+0800\n"
"POT-Creation-Date: 2022-09-22 19:01+0800\n"
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n"
@ -22,13 +22,13 @@ msgid "Acls"
msgstr "访问控制"
#: acls/models/base.py:25 acls/serializers/login_asset_acl.py:47
#: applications/models/application.py:220 assets/models/asset.py:138
#: applications/models/application.py:219 assets/models/asset.py:138
#: assets/models/base.py:175 assets/models/cluster.py:18
#: assets/models/cmd_filter.py:27 assets/models/domain.py:23
#: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
#: orgs/models.py:70 perms/models/base.py:83 rbac/models/role.py:29
#: settings/models.py:33 settings/serializers/sms.py:6
#: terminal/models/endpoint.py:10 terminal/models/endpoint.py:86
#: terminal/models/endpoint.py:14 terminal/models/endpoint.py:87
#: terminal/models/storage.py:26 terminal/models/task.py:16
#: terminal/models/terminal.py:100 users/forms/profile.py:33
#: users/models/group.py:15 users/models/user.py:669
@ -37,12 +37,12 @@ msgid "Name"
msgstr "名称"
#: acls/models/base.py:27 assets/models/cmd_filter.py:84
#: assets/models/user.py:251 terminal/models/endpoint.py:89
#: assets/models/user.py:251 terminal/models/endpoint.py:90
msgid "Priority"
msgstr "优先级"
#: acls/models/base.py:28 assets/models/cmd_filter.py:84
#: assets/models/user.py:251 terminal/models/endpoint.py:90
#: assets/models/user.py:251 terminal/models/endpoint.py:91
msgid "1-100, the lower the value will be match first"
msgstr "优先级可选范围为 1-100 (数值越小越优先)"
@ -52,7 +52,7 @@ msgstr "优先级可选范围为 1-100 (数值越小越优先)"
msgid "Active"
msgstr "激活中"
#: acls/models/base.py:32 applications/models/application.py:233
#: acls/models/base.py:32 applications/models/application.py:232
#: assets/models/asset.py:143 assets/models/asset.py:231
#: assets/models/backup.py:54 assets/models/base.py:180
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:48
@ -60,7 +60,7 @@ msgstr "激活中"
#: assets/models/domain.py:65 assets/models/group.py:23
#: assets/models/label.py:23 ops/models/adhoc.py:38 orgs/models.py:73
#: perms/models/base.py:93 rbac/models/role.py:37 settings/models.py:38
#: terminal/models/endpoint.py:23 terminal/models/endpoint.py:96
#: terminal/models/endpoint.py:22 terminal/models/endpoint.py:97
#: terminal/models/storage.py:29 terminal/models/terminal.py:114
#: tickets/models/comment.py:32 tickets/models/ticket/general.py:288
#: users/models/group.py:16 users/models/user.py:706
@ -236,7 +236,7 @@ msgstr ""
msgid "Time Period"
msgstr "时段"
#: applications/apps.py:9 applications/models/application.py:64
#: applications/apps.py:9 applications/models/application.py:62
msgid "Applications"
msgstr "应用管理"
@ -255,11 +255,7 @@ msgstr "远程应用"
msgid "Custom"
msgstr "自定义"
#: applications/const.py:91 rbac/tree.py:29
msgid "Other"
msgstr "其它"
#: applications/models/account.py:12 applications/models/application.py:237
#: applications/models/account.py:12 applications/models/application.py:236
#: assets/models/backup.py:32 assets/models/cmd_filter.py:45
#: authentication/models.py:67 authentication/models.py:95
#: perms/models/application_permission.py:28
@ -277,9 +273,8 @@ msgstr "应用程序"
msgid "System user"
msgstr "系统用户"
#: applications/models/account.py:17
#: applications/serializers/attrs/application_type/oracle.py:13
#: assets/models/authbook.py:21 settings/serializers/auth/cas.py:18
#: applications/models/account.py:17 assets/models/authbook.py:21
#: settings/serializers/auth/cas.py:18
msgid "Version"
msgstr "版本"
@ -295,7 +290,7 @@ msgstr "可以查看应用账号密码"
msgid "Can change application account secret"
msgstr "可以查看应用账号密码"
#: applications/models/application.py:222
#: applications/models/application.py:221
#: applications/serializers/application.py:101 assets/models/label.py:21
#: perms/models/application_permission.py:21
#: perms/serializers/application/user_permission.py:33
@ -304,7 +299,7 @@ msgstr "可以查看应用账号密码"
msgid "Category"
msgstr "类别"
#: applications/models/application.py:225
#: applications/models/application.py:224
#: applications/serializers/application.py:103 assets/models/backup.py:49
#: assets/models/cmd_filter.py:82 assets/models/user.py:250
#: authentication/models.py:70 perms/models/application_permission.py:24
@ -318,21 +313,21 @@ msgstr "类别"
msgid "Type"
msgstr "类型"
#: applications/models/application.py:229 assets/models/asset.py:217
#: applications/models/application.py:228 assets/models/asset.py:217
#: assets/models/domain.py:29 assets/models/domain.py:64
msgid "Domain"
msgstr "网域"
#: applications/models/application.py:231 xpack/plugins/cloud/models.py:33
#: applications/models/application.py:230 xpack/plugins/cloud/models.py:33
#: xpack/plugins/cloud/serializers/account.py:63
msgid "Attrs"
msgstr "属性"
#: applications/models/application.py:241
#: applications/models/application.py:240
msgid "Can match application"
msgstr "匹配应用"
#: applications/models/application.py:320
#: applications/models/application.py:310
msgid "Application user"
msgstr "应用用户"
@ -388,7 +383,7 @@ msgstr "集群"
#: applications/serializers/attrs/application_category/db.py:11
#: ops/models/adhoc.py:157 settings/serializers/auth/radius.py:14
#: settings/serializers/auth/sms.py:65 terminal/models/endpoint.py:11
#: settings/serializers/auth/sms.py:65 terminal/models/endpoint.py:15
#: xpack/plugins/cloud/serializers/account_attrs.py:72
msgid "Host"
msgstr "主机"
@ -397,7 +392,7 @@ msgstr "主机"
#: applications/serializers/attrs/application_type/mongodb.py:10
#: applications/serializers/attrs/application_type/mysql.py:10
#: applications/serializers/attrs/application_type/mysql_workbench.py:22
#: applications/serializers/attrs/application_type/oracle.py:16
#: applications/serializers/attrs/application_type/oracle.py:10
#: applications/serializers/attrs/application_type/pgsql.py:10
#: applications/serializers/attrs/application_type/redis.py:10
#: applications/serializers/attrs/application_type/sqlserver.py:10
@ -492,10 +487,6 @@ msgstr "Mysql 工作台 用户名"
msgid "Mysql workbench password"
msgstr "Mysql 工作台 密码"
#: applications/serializers/attrs/application_type/oracle.py:14
msgid "Magnus currently supports only 11g and 12c connections"
msgstr "目前 Magnus 只支持连接 11g、12c 版本"
#: applications/serializers/attrs/application_type/vmware_client.py:26
msgid "Vmware username"
msgstr "Vmware 用户名"
@ -3414,6 +3405,10 @@ msgstr "审计台"
msgid "System setting"
msgstr "系统设置"
#: rbac/tree.py:29
msgid "Other"
msgstr "其它"
#: rbac/tree.py:37
msgid "Accounts"
msgstr "账号管理"
@ -4879,58 +4874,34 @@ msgstr "存储无效"
msgid "Command record"
msgstr "命令记录"
#: terminal/models/endpoint.py:13
#: terminal/models/endpoint.py:17
msgid "HTTPS Port"
msgstr "HTTPS 端口"
#: terminal/models/endpoint.py:14 terminal/models/terminal.py:107
#: terminal/models/endpoint.py:18 terminal/models/terminal.py:107
msgid "HTTP Port"
msgstr "HTTP 端口"
#: terminal/models/endpoint.py:15 terminal/models/terminal.py:106
#: terminal/models/endpoint.py:19 terminal/models/terminal.py:106
msgid "SSH Port"
msgstr "SSH 端口"
#: terminal/models/endpoint.py:16
#: terminal/models/endpoint.py:20
msgid "RDP Port"
msgstr "RDP 端口"
#: terminal/models/endpoint.py:17
msgid "MySQL Port"
msgstr "MySQL 端口"
#: terminal/models/endpoint.py:18
msgid "MariaDB Port"
msgstr "MariaDB 端口"
#: terminal/models/endpoint.py:19
msgid "PostgreSQL Port"
msgstr "PostgreSQL 端口"
#: terminal/models/endpoint.py:20
msgid "Redis Port"
msgstr "Redis 端口"
#: terminal/models/endpoint.py:21
msgid "Oracle 11g Port"
msgstr "Oracle 11g 端口"
#: terminal/models/endpoint.py:22
msgid "Oracle 12c Port"
msgstr "Oracle 12c 端口"
#: terminal/models/endpoint.py:28 terminal/models/endpoint.py:94
#: terminal/models/endpoint.py:27 terminal/models/endpoint.py:95
#: terminal/serializers/endpoint.py:57 terminal/serializers/storage.py:38
#: terminal/serializers/storage.py:50 terminal/serializers/storage.py:80
#: terminal/serializers/storage.py:90 terminal/serializers/storage.py:98
msgid "Endpoint"
msgstr "端点"
#: terminal/models/endpoint.py:87
#: terminal/models/endpoint.py:88
msgid "IP group"
msgstr "IP 组"
#: terminal/models/endpoint.py:99
#: terminal/models/endpoint.py:100
msgid "Endpoint rule"
msgstr "端点规则"
@ -5111,9 +5082,19 @@ msgstr "级别"
msgid "Batch danger command alert"
msgstr "批量危险命令告警"
#: terminal/serializers/endpoint.py:12
msgid "Oracle port"
msgstr ""
#: terminal/serializers/endpoint.py:14
msgid "Magnus listen db port"
msgstr "Magnus 监听的数据库端口"
#: terminal/serializers/endpoint.py:17
msgid "Magnus Listen port range"
msgstr "Magnus 监听的端口范围"
#: terminal/serializers/endpoint.py:19
msgid ""
"The range of ports that Magnus listens on is modified in the configuration "
"file"
msgstr "请在配置文件中修改 Magnus 监听的端口范围"
#: terminal/serializers/endpoint.py:51
msgid ""
@ -5242,6 +5223,16 @@ msgstr "没有发现"
msgid "view"
msgstr "查看"
#: terminal/utils/db_port_mapper.py:77
msgid ""
"No ports can be used, check and modify the limit on the number of ports that "
"Magnus listens on in the configuration file."
msgstr "没有端口可以使用,检查并修改配置文件中 Magnus 监听的端口数量限制。"
#: terminal/utils/db_port_mapper.py:79
msgid "All available port count: {}, Already use port count: {}"
msgstr "所有可用端口数量:{},已使用端口数量:{}"
#: tickets/apps.py:7
msgid "Tickets"
msgstr "工单管理"

View File

@ -2,7 +2,7 @@
{% load i18n %}
<head>
<title>{% trans 'Task log' %}</title>
<script src="{% static 'js/jquery-3.1.1.min.js' %}"></script>
<script src="{% static 'js/jquery-3.6.1.min.js' %}"></script>
<script src="{% static 'js/plugins/xterm/xterm.js' %}"></script>
<script src="{% static 'js/plugins/xterm/addons/fit/fit.js' %}"></script>
<link rel="stylesheet" href="{% static 'js/plugins/xterm/xterm.css' %}" />

File diff suppressed because one or more lines are too long

2
apps/static/js/jquery-3.6.1.min.js vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@
<link href="{% static 'css/plugins/vaildator/jquery.validator.css' %}" rel="stylesheet">
<link href="{% static 'css/plugins/datatables/datatables.min.css' %}" rel="stylesheet">
<!-- scripts -->
<script src="{% static 'js/jquery-3.1.1.min.js' %}"></script>
<script src="{% static 'js/jquery-3.6.1.min.js' %}"></script>
<script src="{% static 'js/plugins/sweetalert/sweetalert.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
<script src="{% static 'js/plugins/datatables/datatables.min.js' %}"></script>

View File

@ -8,3 +8,4 @@ from .storage import *
from .status import *
from .sharing import *
from .endpoint import *
from .db_listen_port import *

View File

@ -0,0 +1,36 @@
# coding: utf-8
#
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
from ..utils import db_port_manager, DBPortManager
from applications import serializers
db_port_manager: DBPortManager
__all__ = ['DBListenPortViewSet']
class DBListenPortViewSet(GenericViewSet):
rbac_perms = {
'GET': 'applications.view_application',
'list': 'applications.view_application',
'db_info': 'applications.view_application',
}
http_method_names = ['get', 'post']
def list(self, request, *args, **kwargs):
ports = db_port_manager.get_already_use_ports()
return Response(data=ports, status=status.HTTP_200_OK)
@action(methods=['get'], detail=False, url_path='db-info')
def db_info(self, request, *args, **kwargs):
port = request.query_params.get("port")
db = db_port_manager.get_db_by_port(port)
serializer = serializers.AppSerializer(instance=db)
return Response(data=serializer.data, status=status.HTTP_200_OK)

View File

@ -47,11 +47,12 @@ class SmartEndpointViewMixin:
return Endpoint.match_by_instance_label(self.target_instance, self.target_protocol)
def match_endpoint_by_target_ip(self):
# 用来方便测试
target_ip = self.request.GET.get('target_ip', '')
target_ip = self.request.GET.get('target_ip', '') # 支持target_ip参数用来方便测试
if not target_ip and callable(getattr(self.target_instance, 'get_target_ip', None)):
target_ip = self.target_instance.get_target_ip()
endpoint = EndpointRule.match_endpoint(target_ip, self.target_protocol, self.request)
endpoint = EndpointRule.match_endpoint(
self.target_instance, target_ip, self.target_protocol, self.request
)
return endpoint
def get_target_instance(self):
@ -83,12 +84,7 @@ class SmartEndpointViewMixin:
return instance
def get_target_protocol(self):
protocol = None
if isinstance(self.target_instance, Application) and self.target_instance.is_type(Application.APP_TYPE.oracle):
protocol = self.target_instance.get_target_protocol_for_oracle()
if not protocol:
protocol = self.request.GET.get('protocol')
return protocol
return self.request.GET.get('protocol')
class EndpointViewSet(SmartEndpointViewMixin, JMSBulkModelViewSet):

View File

@ -169,6 +169,14 @@ class CommandStore(object):
return self.es.index(index=self.index, doc_type=self.doc_type, body=data)
def filter(self, query: dict, from_=None, size=None, sort=None):
try:
data = self._filter(query, from_, size, sort)
except Exception as e:
logger.error('ES filter error: {}'.format(e))
data = []
return data
def _filter(self, query: dict, from_=None, size=None, sort=None):
body = self.get_query_body(**query)
data = self.es.search(
@ -184,9 +192,14 @@ class CommandStore(object):
return Command.from_multi_dict(source_data)
def count(self, **query):
try:
body = self.get_query_body(**query)
data = self.es.count(index=self.query_index, doc_type=self.doc_type, body=body)
return data["count"]
count = data["count"]
except Exception as e:
logger.error('ES count error: {}'.format(e))
count = 0
return count
def __getattr__(self, item):
return getattr(self.es, item)

View File

@ -0,0 +1,37 @@
# Generated by Django 3.2.15 on 2022-10-09 09:55
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('terminal', '0052_auto_20220713_1417'),
]
operations = [
migrations.RemoveField(
model_name='endpoint',
name='mariadb_port',
),
migrations.RemoveField(
model_name='endpoint',
name='mysql_port',
),
migrations.RemoveField(
model_name='endpoint',
name='oracle_11g_port',
),
migrations.RemoveField(
model_name='endpoint',
name='oracle_12c_port',
),
migrations.RemoveField(
model_name='endpoint',
name='postgresql_port',
),
migrations.RemoveField(
model_name='endpoint',
name='redis_port',
),
]

View File

@ -1,25 +1,24 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core.validators import MinValueValidator, MaxValueValidator
from applications.models import Application
from ..utils import db_port_manager, DBPortManager
from common.db.models import JMSModel
from common.db.fields import PortField
from common.utils.ip import contains_ip
db_port_manager: DBPortManager
class Endpoint(JMSModel):
name = models.CharField(max_length=128, verbose_name=_('Name'), unique=True)
host = models.CharField(max_length=256, blank=True, verbose_name=_('Host'))
# disabled value=0
# value=0 表示 disabled
https_port = PortField(default=443, verbose_name=_('HTTPS Port'))
http_port = PortField(default=80, verbose_name=_('HTTP Port'))
ssh_port = PortField(default=2222, verbose_name=_('SSH Port'))
rdp_port = PortField(default=3389, verbose_name=_('RDP Port'))
mysql_port = PortField(default=33060, verbose_name=_('MySQL Port'))
mariadb_port = PortField(default=33061, verbose_name=_('MariaDB Port'))
postgresql_port = PortField(default=54320, verbose_name=_('PostgreSQL Port'))
redis_port = PortField(default=63790, verbose_name=_('Redis Port'))
oracle_11g_port = PortField(default=15211, verbose_name=_('Oracle 11g Port'))
oracle_12c_port = PortField(default=15212, verbose_name=_('Oracle 12c Port'))
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
default_id = '00000000-0000-0000-0000-000000000001'
@ -31,12 +30,14 @@ class Endpoint(JMSModel):
def __str__(self):
return self.name
def get_port(self, protocol):
return getattr(self, f'{protocol}_port', 0)
def get_oracle_port(self, version):
protocol = f'oracle_{version}'
return self.get_port(protocol)
def get_port(self, target_instance, protocol):
if protocol in ['https', 'http', 'ssh', 'rdp']:
port = getattr(self, f'{protocol}_port', 0)
elif isinstance(target_instance, Application) and target_instance.category_db:
port = db_port_manager.get_port_by_db(target_instance)
else:
port = 0
return port
def is_default(self):
return str(self.id) == self.default_id
@ -46,10 +47,10 @@ class Endpoint(JMSModel):
return
return super().delete(using, keep_parents)
def is_valid_for(self, protocol):
def is_valid_for(self, target_instance, protocol):
if self.is_default():
return True
if self.host and self.get_port(protocol) != 0:
if self.host and self.get_port(target_instance, protocol) != 0:
return True
return False
@ -103,19 +104,19 @@ class EndpointRule(JMSModel):
return f'{self.name}({self.priority})'
@classmethod
def match(cls, target_ip, protocol):
def match(cls, target_instance, target_ip, protocol):
for endpoint_rule in cls.objects.all().prefetch_related('endpoint'):
if not contains_ip(target_ip, endpoint_rule.ip_group):
continue
if not endpoint_rule.endpoint:
continue
if not endpoint_rule.endpoint.is_valid_for(protocol):
if not endpoint_rule.endpoint.is_valid_for(target_instance, protocol):
continue
return endpoint_rule
@classmethod
def match_endpoint(cls, target_ip, protocol, request=None):
endpoint_rule = cls.match(target_ip, protocol)
def match_endpoint(cls, target_instance, target_ip, protocol, request=None):
endpoint_rule = cls.match(target_instance, target_ip, protocol)
if endpoint_rule:
endpoint = endpoint_rule.endpoint
else:

View File

@ -2,25 +2,31 @@ from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from common.drf.serializers import BulkModelSerializer
from acls.serializers.rules import ip_group_child_validator, ip_group_help_text
from ..utils import db_port_manager
from ..models import Endpoint, EndpointRule
__all__ = ['EndpointSerializer', 'EndpointRuleSerializer']
class EndpointSerializer(BulkModelSerializer):
# 解决 luna 处理繁琐的问题oracle_port 返回匹配到的端口
oracle_port = serializers.SerializerMethodField(label=_('Oracle port'))
# 解决 luna 处理繁琐的问题, 返回 magnus 监听的当前 db 的 port
magnus_listen_db_port = serializers.SerializerMethodField(label=_('Magnus listen db port'))
magnus_listen_port_range = serializers.CharField(
max_length=128, default=db_port_manager.magnus_listen_port_range, read_only=True,
label=_('Magnus Listen port range'),
help_text=_(
'The range of ports that Magnus listens on is modified in the configuration file'
)
)
class Meta:
model = Endpoint
fields_mini = ['id', 'name']
fields_small = [
'host',
'https_port', 'http_port', 'ssh_port',
'rdp_port', 'mysql_port', 'mariadb_port',
'postgresql_port', 'redis_port',
'oracle_11g_port', 'oracle_12c_port',
'oracle_port',
'https_port', 'http_port', 'ssh_port', 'rdp_port',
'magnus_listen_db_port', 'magnus_listen_port_range',
]
fields = fields_mini + fields_small + [
'comment', 'date_created', 'date_updated', 'created_by'
@ -30,19 +36,13 @@ class EndpointSerializer(BulkModelSerializer):
'http_port': {'default': 80},
'ssh_port': {'default': 2222},
'rdp_port': {'default': 3389},
'mysql_port': {'default': 33060},
'mariadb_port': {'default': 33061},
'postgresql_port': {'default': 54320},
'redis_port': {'default': 63790},
'oracle_11g_port': {'default': 15211},
'oracle_12c_port': {'default': 15212},
}
def get_oracle_port(self, obj: Endpoint):
def get_magnus_listen_db_port(self, obj: Endpoint):
view = self.context.get('view')
if not view or view.action not in ['smart']:
return 0
return obj.get_port(view.target_protocol)
return obj.get_port(view.target_instance, view.target_protocol)
class EndpointRuleSerializer(BulkModelSerializer):

View File

@ -1,2 +1,36 @@
# -*- coding: utf-8 -*-
#
from django.db.models.signals import post_save, post_delete
from common.signals import django_ready
from django.dispatch import receiver
from common.utils import get_logger
from .models import Application
from .utils import db_port_manager, DBPortManager
db_port_manager: DBPortManager
logger = get_logger(__file__)
@receiver(django_ready)
def init_db_port_mapper(sender, **kwargs):
logger.info('Init db port mapper')
db_port_manager.init()
@receiver(post_save, sender=Application)
def on_db_app_created(sender, instance: Application, created, **kwargs):
if not instance.category_db:
return
if not created:
return
db_port_manager.add(instance)
@receiver(post_delete, sender=Application)
def on_db_app_delete(sender, instance, **kwargs):
if not instance.category_db:
return
db_port_manager.pop(instance)

View File

@ -24,6 +24,7 @@ router.register(r'session-sharings', api.SessionSharingViewSet, 'session-sharing
router.register(r'session-join-records', api.SessionJoinRecordsViewSet, 'session-sharing-record')
router.register(r'endpoints', api.EndpointViewSet, 'endpoint')
router.register(r'endpoint-rules', api.EndpointRuleViewSet, 'endpoint-rule')
router.register(r'db-listen-ports', api.DBListenPortViewSet, 'db-listen-ports')
urlpatterns = [
path('my-sessions/', api.MySessionAPIView.as_view(), name='my-session'),

View File

@ -0,0 +1,4 @@
from .components import *
from .common import *
from .session_replay import *
from .db_port_mapper import *

View File

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
#
from common.utils import get_logger
from .. import const
from tickets.models import TicketSession
logger = get_logger(__name__)
class ComputeStatUtil:
# system status
@staticmethod
def _common_compute_system_status(value, thresholds):
if thresholds[0] <= value <= thresholds[1]:
return const.ComponentStatusChoices.normal.value
elif thresholds[1] < value <= thresholds[2]:
return const.ComponentStatusChoices.high.value
else:
return const.ComponentStatusChoices.critical.value
@classmethod
def _compute_system_stat_status(cls, stat):
system_stat_thresholds_mapper = {
'cpu_load': [0, 5, 20],
'memory_used': [0, 85, 95],
'disk_used': [0, 80, 99]
}
system_status = {}
for stat_key, thresholds in system_stat_thresholds_mapper.items():
stat_value = getattr(stat, stat_key)
if stat_value is None:
msg = 'stat: {}, stat_key: {}, stat_value: {}'
logger.debug(msg.format(stat, stat_key, stat_value))
stat_value = 0
status = cls._common_compute_system_status(stat_value, thresholds)
system_status[stat_key] = status
return system_status
@classmethod
def compute_component_status(cls, stat):
if not stat:
return const.ComponentStatusChoices.offline
system_status_values = cls._compute_system_stat_status(stat).values()
if const.ComponentStatusChoices.critical in system_status_values:
return const.ComponentStatusChoices.critical
elif const.ComponentStatusChoices.high in system_status_values:
return const.ComponentStatusChoices.high
else:
return const.ComponentStatusChoices.normal
def is_session_approver(session_id, user_id):
ticket = TicketSession.get_ticket_by_session_id(session_id)
if not ticket:
return False
ok = ticket.has_all_assignee(user_id)
return ok

View File

@ -1,124 +1,13 @@
# -*- coding: utf-8 -*-
#
import os
from itertools import groupby, chain
from django.conf import settings
from django.core.files.storage import default_storage
import jms_storage
from itertools import groupby
from common.utils import get_logger
from . import const
from .models import ReplayStorage
from tickets.models import TicketSession, TicketStep, TicketAssignee
from tickets.const import StepState
logger = get_logger(__name__)
def find_session_replay_local(session):
# 存在外部存储上,所有可能的路径名
session_paths = session.get_all_possible_relative_path()
# 存在本地存储上,所有可能的路径名
local_paths = session.get_all_possible_local_path()
for _local_path in chain(session_paths, local_paths):
if default_storage.exists(_local_path):
url = default_storage.url(_local_path)
return _local_path, url
return None, None
def download_session_replay(session):
replay_storages = ReplayStorage.objects.all()
configs = {
storage.name: storage.config
for storage in replay_storages
if not storage.type_null_or_server
}
if settings.SERVER_REPLAY_STORAGE:
configs['SERVER_REPLAY_STORAGE'] = settings.SERVER_REPLAY_STORAGE
if not configs:
msg = "Not found replay file, and not remote storage set"
return None, msg
storage = jms_storage.get_multi_object_storage(configs)
# 获取外部存储路径名
session_path = session.find_ok_relative_path_in_storage(storage)
if not session_path:
msg = "Not found session replay file"
return None, msg
# 通过外部存储路径名后缀,构造真实的本地存储路径
local_path = session.get_local_path_by_relative_path(session_path)
# 保存到storage的路径
target_path = os.path.join(default_storage.base_location, local_path)
target_dir = os.path.dirname(target_path)
if not os.path.isdir(target_dir):
os.makedirs(target_dir, exist_ok=True)
ok, err = storage.download(session_path, target_path)
if not ok:
msg = "Failed download replay file: {}".format(err)
logger.error(msg)
return None, msg
url = default_storage.url(local_path)
return local_path, url
def get_session_replay_url(session):
local_path, url = find_session_replay_local(session)
if local_path is None:
local_path, url = download_session_replay(session)
return local_path, url
class ComputeStatUtil:
# system status
@staticmethod
def _common_compute_system_status(value, thresholds):
if thresholds[0] <= value <= thresholds[1]:
return const.ComponentStatusChoices.normal.value
elif thresholds[1] < value <= thresholds[2]:
return const.ComponentStatusChoices.high.value
else:
return const.ComponentStatusChoices.critical.value
@classmethod
def _compute_system_stat_status(cls, stat):
system_stat_thresholds_mapper = {
'cpu_load': [0, 5, 20],
'memory_used': [0, 85, 95],
'disk_used': [0, 80, 99]
}
system_status = {}
for stat_key, thresholds in system_stat_thresholds_mapper.items():
stat_value = getattr(stat, stat_key)
if stat_value is None:
msg = 'stat: {}, stat_key: {}, stat_value: {}'
logger.debug(msg.format(stat, stat_key, stat_value))
stat_value = 0
status = cls._common_compute_system_status(stat_value, thresholds)
system_status[stat_key] = status
return system_status
@classmethod
def compute_component_status(cls, stat):
if not stat:
return const.ComponentStatusChoices.offline
system_status_values = cls._compute_system_stat_status(stat).values()
if const.ComponentStatusChoices.critical in system_status_values:
return const.ComponentStatusChoices.critical
elif const.ComponentStatusChoices.high in system_status_values:
return const.ComponentStatusChoices.high
else:
return const.ComponentStatusChoices.normal
class TypedComponentsStatusMetricsUtil(object):
def __init__(self):
self.components = []
@ -126,7 +15,7 @@ class TypedComponentsStatusMetricsUtil(object):
self.get_components()
def get_components(self):
from .models import Terminal
from ..models import Terminal
components = Terminal.objects.filter(is_deleted=False).order_by('type')
grouped_components = groupby(components, lambda c: c.type)
grouped_components = [(i[0], list(i[1])) for i in grouped_components]
@ -251,10 +140,3 @@ class ComponentsPrometheusMetricsUtil(TypedComponentsStatusMetricsUtil):
prometheus_metrics_text = '\n'.join(prometheus_metrics)
return prometheus_metrics_text
def is_session_approver(session_id, user_id):
ticket = TicketSession.get_ticket_by_session_id(session_id)
if not ticket:
return False
ok = ticket.has_all_assignee(user_id)
return ok

View File

@ -0,0 +1,112 @@
from django.utils.translation import ugettext_lazy as _
from django.core.cache import cache
from django.conf import settings
from common.decorator import Singleton
from common.utils import get_logger, get_object_or_none
from common.exceptions import JMSException
from applications.const import AppCategory
from applications.models import Application
from orgs.utils import tmp_to_root_org
logger = get_logger(__file__)
@Singleton
class DBPortManager(object):
""" 管理端口-数据库ID的映射, Magnus 要使用 """
CACHE_KEY = 'PORT_DB_MAPPER'
def __init__(self):
self.port_start = settings.MAGNUS_DB_PORTS_START
self.port_limit = settings.MAGNUS_DB_PORTS_LIMIT_COUNT
self.port_end = self.port_start + self.port_limit
# 可以使用的端口列表
self.all_available_ports = list(range(self.port_start, self.port_end))
@property
def magnus_listen_port_range(self):
return f'{self.port_start}-{self.port_end - 1}'
def init(self):
with tmp_to_root_org():
db_ids = Application.objects.filter(category=AppCategory.db).values_list('id', flat=True)
db_ids = [str(i) for i in db_ids]
mapper = dict(zip(self.all_available_ports, list(db_ids)))
self.set_mapper(mapper)
def add(self, db: Application):
mapper = self.get_mapper()
available_port = self.get_next_available_port()
mapper.update({available_port: str(db.id)})
self.set_mapper(mapper)
return True
def pop(self, db: Application):
mapper = self.get_mapper()
to_delete_port = self.get_port_by_db(db)
mapper.pop(to_delete_port, None)
self.set_mapper(mapper)
def get_port_by_db(self, db):
mapper = self.get_mapper()
for port, db_id in mapper.items():
if db_id == str(db.id):
return port
raise JMSException(
'Not matched db port, db id: {}, mapper length: {}'.format(db.id, len(mapper))
)
def get_db_by_port(self, port):
try:
port = int(port)
except Exception as e:
raise JMSException('Port type error: {}'.format(e))
mapper = self.get_mapper()
db_id = mapper.get(port, None)
if not db_id:
raise JMSException('Database not in port-db mapper, port: {}'.format(port))
with tmp_to_root_org():
db = get_object_or_none(Application, id=db_id)
if not db:
raise JMSException('Database not exists, db id: {}'.format(db_id))
return db
def get_next_available_port(self):
already_use_ports = self.get_already_use_ports()
available_ports = sorted(list(set(self.all_available_ports) - set(already_use_ports)))
if len(available_ports) <= 0:
msg = _('No ports can be used, check and modify the limit on the number '
'of ports that Magnus listens on in the configuration file.')
tips = _('All available port count: {}, Already use port count: {}').format(
len(self.all_available_ports), len(already_use_ports)
)
error = msg + tips
raise JMSException(error)
port = available_ports[0]
logger.debug('Get next available port: {}'.format(port))
return port
def get_already_use_ports(self):
mapper = self.get_mapper()
return sorted(list(mapper.keys()))
def get_mapper(self):
mapper = cache.get(self.CACHE_KEY, {})
if not mapper:
# redis 可能被清空,重新初始化一下
self.init()
return cache.get(self.CACHE_KEY, {})
def set_mapper(self, value):
"""
value: {
port: db_id
}
"""
cache.set(self.CACHE_KEY, value, timeout=None)
db_port_manager = DBPortManager()

View File

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
#
import os
from itertools import groupby, chain
from django.conf import settings
from django.core.files.storage import default_storage
import jms_storage
from common.utils import get_logger
from ..models import ReplayStorage
logger = get_logger(__name__)
def find_session_replay_local(session):
# 存在外部存储上,所有可能的路径名
session_paths = session.get_all_possible_relative_path()
# 存在本地存储上,所有可能的路径名
local_paths = session.get_all_possible_local_path()
for _local_path in chain(session_paths, local_paths):
if default_storage.exists(_local_path):
url = default_storage.url(_local_path)
return _local_path, url
return None, None
def download_session_replay(session):
replay_storages = ReplayStorage.objects.all()
configs = {
storage.name: storage.config
for storage in replay_storages
if not storage.type_null_or_server
}
if settings.SERVER_REPLAY_STORAGE:
configs['SERVER_REPLAY_STORAGE'] = settings.SERVER_REPLAY_STORAGE
if not configs:
msg = "Not found replay file, and not remote storage set"
return None, msg
storage = jms_storage.get_multi_object_storage(configs)
# 获取外部存储路径名
session_path = session.find_ok_relative_path_in_storage(storage)
if not session_path:
msg = "Not found session replay file"
return None, msg
# 通过外部存储路径名后缀,构造真实的本地存储路径
local_path = session.get_local_path_by_relative_path(session_path)
# 保存到storage的路径
target_path = os.path.join(default_storage.base_location, local_path)
target_dir = os.path.dirname(target_path)
if not os.path.isdir(target_dir):
os.makedirs(target_dir, exist_ok=True)
ok, err = storage.download(session_path, target_path)
if not ok:
msg = "Failed download replay file: {}".format(err)
logger.error(msg)
return None, msg
url = default_storage.url(local_path)
return local_path, url
def get_session_replay_url(session):
local_path, url = find_session_replay_local(session)
if local_path is None:
local_path, url = download_session_replay(session)
return local_path, url

View File

@ -19,7 +19,7 @@ class ApplyApplicationSerializer(BaseApplyAssetApplicationSerializer, TicketAppl
class Meta:
model = ApplyApplicationTicket
writeable_fields = [
'id', 'title', 'type', 'apply_category',
'id', 'title', 'type', 'apply_category', 'comment',
'apply_type', 'apply_applications', 'apply_system_users',
'apply_actions', 'apply_date_start', 'apply_date_expired', 'org_id'
]

View File

@ -23,7 +23,7 @@ class ApplyAssetSerializer(BaseApplyAssetApplicationSerializer, TicketApplySeria
model = ApplyAssetTicket
writeable_fields = [
'id', 'title', 'type', 'apply_nodes', 'apply_assets',
'apply_system_users', 'apply_actions',
'apply_system_users', 'apply_actions', 'comment',
'apply_date_start', 'apply_date_expired', 'org_id'
]
fields = TicketApplySerializer.Meta.fields + \

View File

@ -191,7 +191,7 @@ class UserExpirationReminderMsg(UserMessage):
class ResetSSHKeyMsg(UserMessage):
def get_html_msg(self) -> dict:
subject = _('Reset SSH Key')
update_url = urljoin(settings.SITE_URL, '/ui/#/users/profile/?activeTab=SSHUpdate')
update_url = urljoin(settings.SITE_URL, '/ui/#/profile/setting/?activeTab=SSHUpdate')
context = {
'name': self.user.name,
'url': update_url,

View File

@ -80,8 +80,6 @@ REDIS_PORT: 6379
# 启用定时任务
# PERIOD_TASK_ENABLED: True
#
# Windows 登录跳过手动输入密码
# WINDOWS_SKIP_ALL_MANUAL_PASSWORD: False
# 是否开启 Luna 水印
# SECURITY_WATERMARK_ENABLED: False
@ -97,3 +95,4 @@ REDIS_PORT: 6379
# 仅允许已存在的用户登录,不允许第三方认证后,自动创建用户
# ONLY_ALLOW_EXIST_USER_AUTH: False

View File

@ -1,2 +1 @@
 你想偷看啥
 What are you trying to peek at !!!

View File

@ -3,3 +3,4 @@
## 访问在线文档
[访问](https://docs.jumpserver.org)

View File

@ -22,3 +22,4 @@ elif [[ "$action" == "sleep" ]];then
else
python jms "${action}" "${service}"
fi

1
jms
View File

@ -189,3 +189,4 @@ if __name__ == '__main__':
collect_static()
else:
start_services()

View File

@ -0,0 +1 @@

View File

@ -62,7 +62,7 @@ jsonfield2==4.0.0.post0
geoip2==4.5.0
ipip-ipdb==1.6.1
# Django environment
Django==3.2.14
Django==3.2.15
django-bootstrap3==14.2.0
django-filter==2.4.0
django-formtools==2.2
@ -91,7 +91,7 @@ eventlet==0.33.1
greenlet==1.1.2
gunicorn==20.1.0
celery==5.2.7
flower==1.0.0
flower==1.2.0
django-celery-beat==2.3.0
kombu==5.2.4
# Auth
@ -133,6 +133,7 @@ pymssql==2.1.5
django-mysql==3.9.0
django-redis==5.2.0
python-redis-lock==3.7.0
pyOpenSSL==22.0.0
redis==4.3.3
pymongo==4.2.0
# Debug

View File

@ -6,6 +6,5 @@ import subprocess
if __name__ == '__main__':
subprocess.call('python3 jms start all', shell=True,
stdin=sys.stdin, stdout=sys.stdout)
subprocess.call('python3 jms start all', shell=True, stdin=sys.stdin, stdout=sys.stdout)

View File

@ -0,0 +1 @@

View File

@ -2,3 +2,4 @@ daemonize yes
port 6379
logfile "/opt/jumpserver/logs/redis.log"
dir /opt/jumpserver/