diff --git a/.github/workflows/jms-build-test.yml b/.github/workflows/jms-build-test.yml index 8b6c98376..4c6330b54 100644 --- a/.github/workflows/jms-build-test.yml +++ b/.github/workflows/jms-build-test.yml @@ -10,17 +10,17 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + - uses: docker/setup-qemu-action@v3 + - uses: docker/setup-buildx-action@v3 - - uses: docker/setup-qemu-action@v2 - - - uses: docker/setup-buildx-action@v2 - - - uses: docker/build-push-action@v3 + - name: Build CE Image + uses: docker/build-push-action@v5 with: context: . push: false + file: Dockerfile tags: jumpserver/core-ce:test - file: Dockerfile-ce + platforms: linux/amd64 build-args: | APT_MIRROR=http://deb.debian.org PIP_MIRROR=https://pypi.org/simple @@ -28,9 +28,22 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max - - uses: LouisBrunner/checks-action@v1.5.0 - if: always() + - name: Prepare EE Image + run: | + sed -i 's@^FROM registry.fit2cloud.com@# FROM registry.fit2cloud.com@g' Dockerfile-ee + sed -i 's@^COPY --from=build-xpack@# COPY --from=build-xpack@g' Dockerfile-ee + + - name: Build EE Image + uses: docker/build-push-action@v5 with: - token: ${{ secrets.GITHUB_TOKEN }} - name: Check Build - conclusion: ${{ job.status }} + context: . + push: false + file: Dockerfile-ee + tags: jumpserver/core-ee:test + platforms: linux/amd64 + build-args: | + APT_MIRROR=http://deb.debian.org + PIP_MIRROR=https://pypi.org/simple + PIP_JMS_MIRROR=https://pypi.org/simple + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/Dockerfile-ce b/Dockerfile similarity index 80% rename from Dockerfile-ce rename to Dockerfile index 5714a5465..23c6f2643 100644 --- a/Dockerfile-ce +++ b/Dockerfile @@ -1,6 +1,28 @@ FROM python:3.11-slim-bullseye as stage-1 ARG TARGETARCH +ARG DEPENDENCIES=" \ + ca-certificates \ + wget" + +RUN set -ex \ + && apt-get update \ + && apt-get -y install --no-install-recommends ${DEPENDENCIES} \ + && echo "no" | dpkg-reconfigure dash \ + && apt-get clean all \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /opt + +ARG CHECK_VERSION=v1.0.2 +RUN set -ex \ + && wget https://github.com/jumpserver-dev/healthcheck/releases/download/${CHECK_VERSION}/check-${CHECK_VERSION}-linux-${TARGETARCH}.tar.gz \ + && tar -xf check-${CHECK_VERSION}-linux-${TARGETARCH}.tar.gz \ + && mv check /usr/local/bin/ \ + && chown root:root /usr/local/bin/check \ + && chmod 755 /usr/local/bin/check \ + && rm -f check-${CHECK_VERSION}-linux-${TARGETARCH}.tar.gz + ARG VERSION ENV VERSION=$VERSION @@ -14,7 +36,6 @@ ARG TARGETARCH ARG BUILD_DEPENDENCIES=" \ g++ \ - make \ pkg-config" ARG DEPENDENCIES=" \ @@ -37,11 +58,11 @@ ARG TOOLS=" \ curl \ default-libmysqlclient-dev \ default-mysql-client \ - git \ - git-lfs \ - unzip \ - xz-utils \ - wget" + libldap2-dev \ + libsasl2-dev \ + libxml2-dev \ + libxmlsec1-dev \ + libxmlsec1-openssl" ARG APT_MIRROR=http://mirrors.ustc.edu.cn RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core-apt \ @@ -52,7 +73,6 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core-apt \ && 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} \ && echo "no" | dpkg-reconfigure dash WORKDIR /opt/jumpserver @@ -66,23 +86,22 @@ RUN --mount=type=cache,target=/root/.cache \ && pip install poetry -i ${PIP_MIRROR} \ && poetry config virtualenvs.create false \ && . /opt/py3/bin/activate \ - && poetry install + && poetry install --only=main FROM python:3.11-slim-bullseye ARG TARGETARCH -ENV LANG=zh_CN.UTF-8 \ +ENV LANG=en_US.UTF-8 \ PATH=/opt/py3/bin:$PATH ARG DEPENDENCIES=" \ libjpeg-dev \ + libldap2-dev \ libpq-dev \ libx11-dev \ - freerdp2-dev \ libxmlsec1-openssl" ARG TOOLS=" \ ca-certificates \ - curl \ default-libmysqlclient-dev \ default-mysql-client \ iputils-ping \ @@ -90,11 +109,7 @@ ARG TOOLS=" \ netcat-openbsd \ nmap \ openssh-client \ - patch \ - sshpass \ - telnet \ - vim \ - wget" + sshpass" ARG APT_MIRROR=http://mirrors.ustc.edu.cn RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core-apt \ @@ -108,7 +123,6 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core-apt \ && mkdir -p /root/.ssh/ \ && echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null\n\tCiphers +aes128-cbc\n\tKexAlgorithms +diffie-hellman-group1-sha1\n\tHostKeyAlgorithms +ssh-rsa" > /root/.ssh/config \ && echo "no" | dpkg-reconfigure dash \ - && echo "zh_CN.UTF-8" | dpkg-reconfigure locales \ && sed -i "s@# export @export @g" ~/.bashrc \ && sed -i "s@# alias @alias @g" ~/.bashrc @@ -121,6 +135,7 @@ RUN set -ex \ && rm -f /opt/receptor.tar.gz COPY --from=stage-2 /opt/py3 /opt/py3 +COPY --from=stage-1 /usr/local/bin /usr/local/bin COPY --from=stage-1 /opt/jumpserver/release/jumpserver /opt/jumpserver COPY --from=stage-1 /opt/jumpserver/release/jumpserver/apps/libs/ansible/ansible.cfg /etc/ansible/ diff --git a/Dockerfile-ee b/Dockerfile-ee index 2fbd4259d..5be062f97 100644 --- a/Dockerfile-ee +++ b/Dockerfile-ee @@ -1,5 +1,134 @@ ARG VERSION FROM registry.fit2cloud.com/jumpserver/xpack:${VERSION} as build-xpack -FROM registry.fit2cloud.com/jumpserver/core-ce:${VERSION} -COPY --from=build-xpack /opt/xpack /opt/jumpserver/apps/xpack \ No newline at end of file +FROM python:3.11-slim-bullseye as stage-1 +ARG TARGETARCH + +ARG DEPENDENCIES=" \ + ca-certificates \ + wget" + +RUN set -ex \ + && apt-get update \ + && apt-get -y install --no-install-recommends ${DEPENDENCIES} \ + && echo "no" | dpkg-reconfigure dash \ + && apt-get clean all \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /opt + +ARG CHECK_VERSION=v1.0.2 +RUN set -ex \ + && wget https://github.com/jumpserver-dev/healthcheck/releases/download/${CHECK_VERSION}/check-${CHECK_VERSION}-linux-${TARGETARCH}.tar.gz \ + && tar -xf check-${CHECK_VERSION}-linux-${TARGETARCH}.tar.gz \ + && mv check /usr/local/bin/ \ + && chown root:root /usr/local/bin/check \ + && chmod 755 /usr/local/bin/check \ + && rm -f check-${CHECK_VERSION}-linux-${TARGETARCH}.tar.gz + +ARG VERSION +ENV VERSION=$VERSION + +WORKDIR /opt/jumpserver + +ADD . . +COPY --from=build-xpack /opt/xpack /opt/jumpserver/apps/xpack + +RUN echo > /opt/jumpserver/config.yml \ + && cd utils && bash -ixeu build.sh + +FROM python:3.11-slim-bullseye as stage-2 +ARG TARGETARCH + +ARG BUILD_DEPENDENCIES=" \ + g++ \ + pkg-config" + +ARG DEPENDENCIES=" \ + default-libmysqlclient-dev \ + default-mysql-client \ + libldap2-dev \ + libsasl2-dev \ + libxml2-dev \ + libxmlsec1-dev \ + libxmlsec1-openssl" + +ARG APT_MIRROR=http://mirrors.ustc.edu.cn +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core-apt \ + --mount=type=cache,target=/var/lib/apt,sharing=locked,id=core-apt \ + sed -i "s@http://.*.debian.org@${APT_MIRROR}@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} \ + && echo "no" | dpkg-reconfigure dash + +WORKDIR /opt/jumpserver + +ARG PIP_MIRROR=https://pypi.tuna.tsinghua.edu.cn/simple +RUN --mount=type=cache,target=/root/.cache \ + --mount=type=bind,source=poetry.lock,target=/opt/jumpserver/poetry.lock \ + --mount=type=bind,source=pyproject.toml,target=/opt/jumpserver/pyproject.toml \ + set -ex \ + && python3 -m venv /opt/py3 \ + && pip install poetry -i ${PIP_MIRROR} \ + && poetry config virtualenvs.create false \ + && . /opt/py3/bin/activate \ + && poetry install + +FROM python:3.11-slim-bullseye +ARG TARGETARCH +ENV LANG=zh_CN.UTF-8 \ + PATH=/opt/py3/bin:$PATH + +ARG DEPENDENCIES=" \ + libldap2-dev \ + libx11-dev \ + libxmlsec1-openssl" + +ARG TOOLS=" \ + ca-certificates \ + curl \ + default-libmysqlclient-dev \ + default-mysql-client \ + iputils-ping \ + locales \ + nmap \ + openssh-client \ + patch \ + sshpass \ + telnet \ + vim \ + wget" + +ARG APT_MIRROR=http://mirrors.ustc.edu.cn +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core-apt \ + --mount=type=cache,target=/var/lib/apt,sharing=locked,id=core-apt \ + sed -i "s@http://.*.debian.org@${APT_MIRROR}@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 ${DEPENDENCIES} \ + && apt-get -y install --no-install-recommends ${TOOLS} \ + && mkdir -p /root/.ssh/ \ + && echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null\n\tCiphers +aes128-cbc\n\tKexAlgorithms +diffie-hellman-group1-sha1\n\tHostKeyAlgorithms +ssh-rsa" > /root/.ssh/config \ + && echo "no" | dpkg-reconfigure dash \ + && echo "zh_CN.UTF-8" | dpkg-reconfigure locales \ + && sed -i "s@# export @export @g" ~/.bashrc \ + && sed -i "s@# alias @alias @g" ~/.bashrc + +COPY --from=stage-2 /opt/py3 /opt/py3 +COPY --from=stage-1 /usr/local/bin /usr/local/bin +COPY --from=stage-1 /opt/jumpserver/release/jumpserver /opt/jumpserver + +WORKDIR /opt/jumpserver + +ARG VERSION +ENV VERSION=$VERSION + +VOLUME /opt/jumpserver/data + +EXPOSE 8080 + +ENTRYPOINT ["./entrypoint.sh"] diff --git a/README.md b/README.md index d9e8ff444..f3fa6c7e2 100644 --- a/README.md +++ b/README.md @@ -1,125 +1,222 @@ -

+

JumpServer -

-

广受欢迎的开源堡垒机

+ +### Popular Open-Source Bastion Host -

- License: GPLv3 - Docker pulls - Latest release - Stars -

+ +[![][license-shield]][license-link] +[![][docker-shield]][docker-link] +[![][github-release-shield]][github-release-link] +[![][github-stars-shield]][github-stars-link] +**English** · [简体中文](./README.zh-CN.md) · [Documents][docs-link] · [Report Bug][github-issues-link] · [Request Feature][github-issues-link] -

- 9 年时间,倾情投入,用心做好一款开源堡垒机。 -

+For 9 years, pouring heart and soul into creating a high-quality open-source bastion host.
------------------------------- -JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运维安全审计系统。 +
-JumpServer 堡垒机帮助企业以更安全的方式管控和登录各种类型的资产,包括: +# +![][image-dashboard] +_[To-do]: Need to design the graphics._ -- **SSH**: Linux / Unix / 网络设备 等; -- **Windows**: Web 方式连接 / 原生 RDP 连接; -- **数据库**: MySQL / MariaDB / PostgreSQL / Oracle / SQLServer / ClickHouse 等; -- **NoSQL**: Redis / MongoDB 等; -- **GPT**: ChatGPT 等; -- **云服务**: Kubernetes / VMware vSphere 等; -- **Web 站点**: 各类系统的 Web 管理后台; -- **应用**: 通过 Remote App 连接各类应用。 +
+Table of contents -## 产品特色 +#### TOC -- **开源**: 零门槛,线上快速获取和安装; -- **无插件**: 仅需浏览器,极致的 Web Terminal 使用体验; -- **分布式**: 支持分布式部署和横向扩展,轻松支持大规模并发访问; -- **多云支持**: 一套系统,同时管理不同云上面的资产; -- **多租户**: 一套系统,多个子公司或部门同时使用; -- **云端存储**: 审计录像云端存储,永不丢失; +- [Getting Started](#getting-started) +- [Introduction](#introduction) +- [Why JumpServer](#why-jumpserver) +- [Installation](#installation) +- [Product Architecture & Components](#product-architecture--components) +- [Features](#features) +- [Contributing](#contributing) +- [Security](#security) +- [License](#license) +#### -## UI 展示 +
-![UI展示](https://docs.jumpserver.org/zh/v3/img/dashboard.png) +
-## 在线体验 +## Getting Started -- 环境地址: +Step right into our online demonstration environment, where you can effortlessly experience our product without the need for time-consuming software installations. With just a few clicks, you'll quickly grasp the functionality and features of our product. In the demonstration environment, you can explore the various features of our product to your heart's content and experience our innovative design and exceptional performance. -| :warning: 注意 | -|:-----------------------------| -| 该环境仅作体验目的使用,我们会定时清理、重置数据! | -| 请勿修改体验环境用户的密码! | -| 请勿在环境中添加业务生产环境地址、用户名密码等敏感信息! | +Whether you're new to the experience or a seasoned expert, we invite you to join our Discord community right away! Here, our developers and enthusiastic users come together to offer support and assistance. No matter what challenges you encounter during your usage, we are committed to answering your questions and providing guidance. -## 快速开始 +| [![][demo-shield-badge]][demo-link] | No installation or registration necessary! Visit our website to experience it firsthand. | +| :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------- | +| [![][discord-shield-badge]][discord-link] | Join our Discord community! This is where you can connect with developers and other enthusiastic users of JumpServer. | -- [快速入门](https://docs.jumpserver.org/zh/v3/quick_start/) -- [产品文档](https://docs.jumpserver.org) -- [在线学习](https://edu.fit2cloud.com/page/2635362) -- [知识库](https://kb.fit2cloud.com/categories/jumpserver) +> \[!IMPORTANT] +> +> **Star Us**, You will receive all release notifications from GitHub without any delay \~ ⭐️ -## 案例研究 +![][image-star] -- [腾讯音乐娱乐集团:基于JumpServer的安全运维审计解决方案](https://blog.fit2cloud.com/?p=a04cdf0d-6704-4d18-9b40-9180baecd0e2) -- [腾讯海外游戏:基于JumpServer构建游戏安全运营能力](https://blog.fit2cloud.com/?p=3704) -- [万华化学:通过JumpServer管理全球化分布式IT资产,并且实现与云管平台的联动](https://blog.fit2cloud.com/?p=3504) -- [雪花啤酒:JumpServer堡垒机使用体会](https://blog.fit2cloud.com/?p=3412) -- [顺丰科技:JumpServer 堡垒机护航顺丰科技超大规模资产安全运维](https://blog.fit2cloud.com/?p=1147) -- [沐瞳游戏:通过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=732) -- [中通快递:JumpServer主机安全运维实践](https://blog.fit2cloud.com/?p=708) -- [东方明珠:JumpServer高效管控异构化、分布式云端资产](https://blog.fit2cloud.com/?p=687) -- [江苏农信:JumpServer堡垒机助力行业云安全运维](https://blog.fit2cloud.com/?p=666) +
+ Star History + + + + +
-## 社区交流 +> \[!TIP] +> +> This is a demonstration video that can quickly help you understand the page design and product features of JumpServer. -如果您在使用过程中有任何疑问或对建议,欢迎提交 [GitHub Issue](https://github.com/jumpserver/jumpserver/issues/new/choose)。 + +_[To-do]: Need to design the video._ -您也可以到我们的 [社区论坛](https://bbs.fit2cloud.com/c/js/5) 当中进行交流沟通。 +## Introduction -### 参与贡献 +JumpServer is a widely acclaimed open-source bastion host, serving as a professional operational security auditing system compliant with the 4A standards. It helps businesses securely manage and access all types of assets in a more secure manner, enabling pre-authorization, real-time monitoring, and post-audit capabilities. -欢迎提交 PR 参与贡献。 参考 [CONTRIBUTING.md](https://github.com/jumpserver/jumpserver/blob/dev/CONTRIBUTING.md) +JumpServer aims to become the industry's preferred platform, assisting businesses in securely and efficiently managing and accessing all types of assets. By offering a professional operational security auditing system compliant with 4A standards, JumpServer is committed to delivering advanced asset management and access solutions, meeting enterprises' needs for security, reliability, and efficiency. -## 组件项目 +JumpServer's vision is to become a leader in the enterprise-level asset management and access control field, providing comprehensive solutions for users to securely and efficiently manage and utilize their assets. Through continuous innovation and enhancement of product features, JumpServer is committed to driving the development of the entire industry and becoming a key supporter and promoter of enterprise digital transformation. -| 项目 | 状态 | 描述 | -|--------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------| -| [Lina](https://github.com/jumpserver/lina) | Lina release | JumpServer Web UI 项目 | -| [Luna](https://github.com/jumpserver/luna) | Luna release | JumpServer Web Terminal 项目 | -| [KoKo](https://github.com/jumpserver/koko) | Koko release | JumpServer 字符协议 Connector 项目 | -| [Lion](https://github.com/jumpserver/lion-release) | Lion release | JumpServer 图形协议 Connector 项目,依赖 [Apache Guacamole](https://guacamole.apache.org/) | -| [Razor](https://github.com/jumpserver/razor) | Chen | JumpServer RDP 代理 Connector 项目 | -| [Tinker](https://github.com/jumpserver/tinker) | Tinker | JumpServer 远程应用 Connector 项目 (Windows) | -| [Panda](https://github.com/jumpserver/Panda) | Panda | JumpServer 远程应用 Connector 项目 (Linux) | -| [Magnus](https://github.com/jumpserver/magnus-release) | Magnus release | JumpServer 数据库代理 Connector 项目 | -| [Chen](https://github.com/jumpserver/chen-release) | Chen release | JumpServer Web DB 项目,替代原来的 OmniDB | -| [Kael](https://github.com/jumpserver/kael) | Kael release | JumpServer 连接 GPT 资产的组件项目 | -| [Wisp](https://github.com/jumpserver/wisp) | Magnus release | JumpServer 各系统终端组件和 Core API 通信的组件项目 | -| [Clients](https://github.com/jumpserver/clients) | Clients release | JumpServer 客户端 项目 | -| [Installer](https://github.com/jumpserver/installer) | Installer release | JumpServer 安装包 项目 | +![][image-supported-asset-type] +_[To-do]: Need to design the graphics._ -## 安全说明 +## Why JumpServer +1. **Open Source**: JumpServer is an open-source software, meaning users can freely access, use, and modify its source code to meet individual needs, while also benefiting from community support and collaboration. +2. **Plugin-Free**: JumpServer provides comprehensive functionality without the need for additional plugins or extensions. This simplifies deployment and management processes, reducing potential compatibility and security risks. +3. **Distributed**: JumpServer supports a distributed architecture, allowing easy scaling across multiple nodes for high availability and fault tolerance. This makes it suitable for large-scale deployments and complex network environments. +4. **Multi-Cloud**: JumpServer offers support for various cloud platforms, including AWS, Azure, Google Cloud, etc., enabling users to manage and access assets seamlessly across different cloud environments. +5. **Cloud Storage**: JumpServer supports storing critical data such as audit logs and configuration files in the cloud, ensuring data security and reliability, as well as facilitating cross-region and cross-device access. +6. **Organizational**: JumpServer provides a flexible organizational structure, supporting multi-level organizational hierarchies and permission management. This allows administrators to finely control user access permissions, ensuring asset security and compliance. -JumpServer是一款安全产品,请参考 [基本安全建议](https://docs.jumpserver.org/zh/master/install/install_security/) -进行安装部署。如果您发现安全相关问题,请直接联系我们: +## Installation -- 邮箱:support@fit2cloud.com -- 电话:400-052-0755 +JumpServer supports multiple installation methods to cater to diverse user scenarios and preferences: -## License & Copyright +See Docs: https://docs.jumpserver.org/zh/v3/ +#### 1. Online +Ideal for users with internet access, this method involves downloading installation scripts or packages directly from the internet. It ensures easy access to the latest updates and dependencies during installation. + +Quick installation of JumpServer in just two steps: + +1. Prepare a 64-bit Linux host with at least 4 cores and 8 GB of RAM, which has internet access. +2. Execute the following command as the root user for one-click installation of JumpServer. + +```sh +curl -sSL https://github.com/jumpserver/jumpserver/releases/latest/download/quick_start.sh | bash +``` + +#### 2. Offline +Suited for environments without internet connectivity, this method allows users to download all necessary installation files and dependencies beforehand. It ensures seamless installation even in isolated or restricted network environments. + +Download offline package: https://community.fit2cloud.com/#/products/jumpserver/downloads + +#### 3. Kubernetes (K8s) +JumpServer supports installation on Kubernetes clusters. You can deploy JumpServer as containerized applications on Kubernetes, leveraging the scalability and management features of Kubernetes for running JumpServer. + +#### 4. All-in-One +This method provides a simplified installation process where all components of JumpServer are installed on a single server or machine. It's suitable for small-scale deployments or testing purposes where separate component deployment is not required. + +#### 5. Enterprise Edition Trial +JumpServer offers a trial version of its enterprise edition, allowing users to test out the enterprise features and functionalities before committing to a full deployment. This trial version typically comes with limited duration or features to provide a glimpse of the capabilities of the enterprise edition. + +Each installation method caters to different use cases and deployment scenarios, offering flexibility and options for users based on their requirements and infrastructure setup. + +Applying for the Enterprise Edition: https://jumpserver.org/enterprise.html + +## Product Architecture & Components +#### 1. Architecture Diagram +Below is the schematic diagram of the JumpServer system architecture, providing a more comprehensive understanding of the product features of JumpServer. +![][image-system-architecture] +_[To-do]: Need to design the graphics._ + +#### 2. Supporting Components +| Project | Status | Description | +|--------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------| +| [Lina](https://github.com/jumpserver/lina) | Lina release | JumpServer Web UI | +| [Luna](https://github.com/jumpserver/luna) | Luna release | JumpServer Web Terminal | +| [KoKo](https://github.com/jumpserver/koko) | Koko release | JumpServer Character Protocol Connector | +| [Lion](https://github.com/jumpserver/lion-release) | Lion release | JumpServer Graphical Protocol Connector, dependent on [Apache Guacamole](https://guacamole.apache.org/) | +| [Razor](https://github.com/jumpserver/razor) | Chen | JumpServer RDP Proxy Connector | +| [Tinker](https://github.com/jumpserver/tinker) | Tinker | JumpServer Remote Application Connector (Windows) | +| [Panda](https://github.com/jumpserver/Panda) | Panda | JumpServer Remote Application Connector (Linux) | +| [Magnus](https://github.com/jumpserver/magnus-release) | Magnus release | JumpServer Database Proxy Connector | +| [Chen](https://github.com/jumpserver/chen-release) | Chen release | JumpServer Web DB | +| [Kael](https://github.com/jumpserver/kael) | Kael release | JumpServer GPT Assets Connector | +| [Wisp](https://github.com/jumpserver/wisp) | Magnus release | JumpServer Inter-Project Communication Component with Core API | +| [Clients](https://github.com/jumpserver/clients) | Clients release | JumpServer Client | +| [Installer](https://github.com/jumpserver/installer) | Installer release | JumpServer Installation Tool | + +JumpServer consists of multiple key components, which collectively form the functional framework of JumpServer, providing users with comprehensive capabilities for operations management and security control. + +## Features +Below are the features supported by JumpServer, covering various aspects of operations management and security control, providing you with a comprehensive solution. + +1. User Authentication Supporting Integration with Multiple Single Sign-On Systems (SSO) +2. User Management Based on Role-based Access Control (RBAC) +3. Asset Management of Everything is an Asset +4. Asset Account Management +5. Asset Authorization Management +6. Asset Permission Management Based Access Control Logic (ACL) +7. Remote Application Management for Everything +8. Support for Multiple Asset Connection Methods +9. Comprehensive and Detailed User Behavior Audit System +10. Organization Management with Resource Isolation [![][version-ee-shield-badge]][official-website-en-link] +11. Ticket Management [![][version-ee-shield-badge]][official-website-en-link] + +For details, [See Docs][docs-link]. + +## Contributing +Welcome to submit PR to contribute. Please refer to [CONTRIBUTING.md][contributing-link] for guidelines. + +## Security +JumpServer is a secure product. Please refer to the Basic Security Recommendations for installation and deployment. If you encounter any security-related issues, please contact us directly: + +- Email: support@fit2cloud.com +- Phone: 400-052-0755 + +## License Copyright (c) 2014-2024 飞致云 FIT2CLOUD, All rights reserved. -Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in -compliance with the License. You may obtain a copy of the License at +Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.gnu.org/licenses/gpl-3.0.html -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. +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. + + + + +[official-website-en-link]: https://jumpserver.org/ +[docs-link]: https://docs.jumpserver.org/ +[community-link]: https://community.fit2cloud.com/#/products/jumpserver/downloads +[demo-link]: https://demo.jumpserver.org/ +[discord-link]: https://discord.gg/DVz6Hckx +[contributing-link]: https://github.com/jumpserver/jumpserver/blob/dev/CONTRIBUTING.md + + + +[license-link]: https://www.gnu.org/licenses/gpl-3.0.html +[docker-link]: https://hub.docker.com/u/jumpserver +[github-release-shield]: https://img.shields.io/github/v/release/jumpserver/jumpserver +[github-release-link]: https://github.com/jumpserver/jumpserver/releases/latest +[github-stars-shield]: https://img.shields.io/github/stars/jumpserver/jumpserver?color=%231890FF&style=flat-square +[github-stars-link]: https://github.com/jumpserver/jumpserver +[github-issues-link]: https://github.com/jumpserver/jumpserver/issues + + +[docker-shield]: https://img.shields.io/docker/pulls/jumpserver/jms_all.svg +[license-shield]: https://img.shields.io/github/license/jumpserver/jumpserver +[demo-shield-badge]: https://img.shields.io/badge/ONLINE-online?style=plastic&logo=jameson&logoColor=white&label=TRY%20JUMPSERVER&labelColor=black&color=%23148f76 +[discord-shield-badge]: https://img.shields.io/badge/JOIN_US_NOW-ONLINE?style=plastic&logo=discord&logoColor=white&label=DISCORD&labelColor=black&color=%23404eed +[version-ee-shield-badge]: https://img.shields.io/badge/Enterprise-black?style=flat-square&logo=vagrant + + +[image-jumpserver]: https://download.jumpserver.org/images/jumpserver-logo.svg +[image-dashboard]: https://github.com/jumpserver/jumpserver/assets/32935519/014c2230-82d3-4b53-b907-8149ce44bbd0 +[image-star]: https://github.com/jumpserver/jumpserver/assets/32935519/76158e65-783d-4f11-81cd-45556a388e63 +[image-supported-asset-type]: https://github.com/jumpserver/jumpserver/assets/32935519/8e769007-5449-4e86-b34b-d04e8e484257 +[image-system-architecture]: https://github.com/jumpserver/jumpserver/assets/32935519/8a720b4e-19ed-4e3c-a8aa-325d7581005a + + diff --git a/apps/accounts/apps.py b/apps/accounts/apps.py index 56ca232f7..f24698b03 100644 --- a/apps/accounts/apps.py +++ b/apps/accounts/apps.py @@ -4,6 +4,7 @@ from django.apps import AppConfig class AccountsConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'accounts' + verbose_name = 'App Accounts' def ready(self): from . import signal_handlers # noqa diff --git a/apps/accounts/migrations/0002_auto_20220616_0021.py b/apps/accounts/migrations/0002_auto_20220616_0021.py index 155800064..cef9c230f 100644 --- a/apps/accounts/migrations/0002_auto_20220616_0021.py +++ b/apps/accounts/migrations/0002_auto_20220616_0021.py @@ -28,9 +28,9 @@ class Migration(migrations.Migration): ('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')), ('name', models.CharField(max_length=128, verbose_name='Name')), - ('is_periodic', models.BooleanField(default=False, verbose_name='Periodic perform')), - ('interval', models.IntegerField(blank=True, default=24, null=True, verbose_name='Cycle perform')), - ('crontab', models.CharField(blank=True, max_length=128, null=True, verbose_name='Regularly perform')), + ('is_periodic', models.BooleanField(default=False, verbose_name='Periodic run')), + ('interval', models.IntegerField(blank=True, default=24, null=True, verbose_name='Interval')), + ('crontab', models.CharField(blank=True, max_length=128, null=True, verbose_name='Crontab')), ('types', models.JSONField(default=list)), ('recipients', models.ManyToManyField(blank=True, related_name='recipient_escape_route_plans', to=settings.AUTH_USER_MODEL, verbose_name='Recipient')), diff --git a/apps/accounts/migrations/0006_gatheredaccount.py b/apps/accounts/migrations/0006_gatheredaccount.py index af0b2a048..f7058864e 100644 --- a/apps/accounts/migrations/0006_gatheredaccount.py +++ b/apps/accounts/migrations/0006_gatheredaccount.py @@ -24,9 +24,9 @@ class Migration(migrations.Migration): ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), ('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')), ('present', models.BooleanField(default=True, verbose_name='Present')), - ('date_last_login', models.DateTimeField(null=True, verbose_name='Date last login')), + ('date_last_login', models.DateTimeField(null=True, verbose_name='Date login')), ('username', models.CharField(blank=True, db_index=True, max_length=32, verbose_name='Username')), - ('address_last_login', models.CharField(default='', max_length=39, verbose_name='Address last login')), + ('address_last_login', models.CharField(default='', max_length=39, verbose_name='Address login')), ('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.asset', verbose_name='Asset')), ], options={ diff --git a/apps/accounts/migrations/0020_alter_accountbackupautomation_backup_type_and_more.py b/apps/accounts/migrations/0020_alter_accountbackupautomation_backup_type_and_more.py index 7e88fa547..dda414f94 100644 --- a/apps/accounts/migrations/0020_alter_accountbackupautomation_backup_type_and_more.py +++ b/apps/accounts/migrations/0020_alter_accountbackupautomation_backup_type_and_more.py @@ -18,11 +18,11 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='accountbackupautomation', name='is_password_divided_by_email', - field=models.BooleanField(default=True, verbose_name='Is Password Divided'), + field=models.BooleanField(default=True, verbose_name='Password divided'), ), migrations.AlterField( model_name='accountbackupautomation', name='is_password_divided_by_obj_storage', - field=models.BooleanField(default=True, verbose_name='Is Password Divided'), + field=models.BooleanField(default=True, verbose_name='Password divided'), ), ] diff --git a/apps/accounts/models/automations/backup_account.py b/apps/accounts/models/automations/backup_account.py index 177e0dfa1..36c6fb1c2 100644 --- a/apps/accounts/models/automations/backup_account.py +++ b/apps/accounts/models/automations/backup_account.py @@ -24,9 +24,9 @@ logger = get_logger(__file__) class AccountBackupAutomation(PeriodTaskModelMixin, JMSOrgBaseModel): types = models.JSONField(default=list) backup_type = models.CharField(max_length=128, choices=AccountBackupType.choices, - default=AccountBackupType.email.value, verbose_name=_('Backup Type')) - is_password_divided_by_email = models.BooleanField(default=True, verbose_name=_('Is Password Divided')) - is_password_divided_by_obj_storage = models.BooleanField(default=True, verbose_name=_('Is Password Divided')) + default=AccountBackupType.email.value, verbose_name=_('Backup type')) + is_password_divided_by_email = models.BooleanField(default=True, verbose_name=_('Password divided')) + is_password_divided_by_obj_storage = models.BooleanField(default=True, verbose_name=_('Password divided')) recipients_part_one = models.ManyToManyField( 'users.User', related_name='recipient_part_one_plans', blank=True, verbose_name=_("Recipient part one") @@ -37,14 +37,15 @@ class AccountBackupAutomation(PeriodTaskModelMixin, JMSOrgBaseModel): ) obj_recipients_part_one = models.ManyToManyField( 'terminal.ReplayStorage', related_name='obj_recipient_part_one_plans', blank=True, - verbose_name=_("Object Storage Recipient part one") + verbose_name=_("Object storage recipient part one") ) obj_recipients_part_two = models.ManyToManyField( 'terminal.ReplayStorage', related_name='obj_recipient_part_two_plans', blank=True, - verbose_name=_("Object Storage Recipient part two") + verbose_name=_("Object storage recipient part two") + ) + zip_encrypt_password = fields.EncryptCharField( + max_length=4096, blank=True, null=True, verbose_name=_('Zip encrypt password') ) - zip_encrypt_password = fields.EncryptCharField(max_length=4096, blank=True, null=True, - verbose_name=_('Zip Encrypt Password')) def __str__(self): return f'{self.name}({self.org_id})' diff --git a/apps/accounts/models/automations/gather_account.py b/apps/accounts/models/automations/gather_account.py index bc56fa4ec..96e83cbcb 100644 --- a/apps/accounts/models/automations/gather_account.py +++ b/apps/accounts/models/automations/gather_account.py @@ -12,10 +12,10 @@ __all__ = ['GatherAccountsAutomation', 'GatheredAccount'] class GatheredAccount(JMSOrgBaseModel): present = models.BooleanField(default=True, verbose_name=_("Present")) - date_last_login = models.DateTimeField(null=True, verbose_name=_("Date last login")) + date_last_login = models.DateTimeField(null=True, verbose_name=_("Date login")) asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_("Asset")) username = models.CharField(max_length=32, blank=True, db_index=True, verbose_name=_('Username')) - address_last_login = models.CharField(max_length=39, default='', verbose_name=_("Address last login")) + address_last_login = models.CharField(max_length=39, default='', verbose_name=_("Address login")) @property def address(self): diff --git a/apps/accounts/serializers/account/account.py b/apps/accounts/serializers/account/account.py index 2230c8e92..62a18c03e 100644 --- a/apps/accounts/serializers/account/account.py +++ b/apps/accounts/serializers/account/account.py @@ -31,7 +31,9 @@ class AccountCreateUpdateSerializerMixin(serializers.Serializer): default=False, label=_("Push now"), write_only=True ) params = serializers.JSONField( - decoder=None, encoder=None, required=False, style={'base_template': 'textarea.html'} + decoder=None, encoder=None, required=False, + style={'base_template': 'textarea.html'}, + label=_('Params'), ) on_invalid = LabeledChoiceField( choices=AccountInvalidPolicy.choices, default=AccountInvalidPolicy.ERROR, @@ -225,7 +227,7 @@ class AccountSerializer(AccountCreateUpdateSerializerMixin, BaseAccountSerialize fields = BaseAccountSerializer.Meta.fields + [ 'su_from', 'asset', 'version', 'source', 'source_id', 'connectivity', - ] + AccountCreateUpdateSerializerMixin.Meta.fields + ] + list(set(AccountCreateUpdateSerializerMixin.Meta.fields) - {'params'}) read_only_fields = BaseAccountSerializer.Meta.read_only_fields + [ 'connectivity' ] diff --git a/apps/accounts/serializers/account/backup.py b/apps/accounts/serializers/account/backup.py index a67d834c6..d2aaf7ed1 100644 --- a/apps/accounts/serializers/account/backup.py +++ b/apps/accounts/serializers/account/backup.py @@ -35,8 +35,7 @@ class AccountBackupSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSer ] extra_kwargs = { 'name': {'required': True}, - 'periodic_display': {'label': _('Periodic perform')}, - 'executed_amount': {'label': _('Executed amount')}, + 'executed_amount': {'label': _('Executions')}, 'recipients': { 'label': _('Recipient'), 'help_text': _('Currently only mail sending is supported') diff --git a/apps/accounts/serializers/account/base.py b/apps/accounts/serializers/account/base.py index 23dec0d3e..3b47b7d02 100644 --- a/apps/accounts/serializers/account/base.py +++ b/apps/accounts/serializers/account/base.py @@ -22,7 +22,7 @@ class AuthValidateMixin(serializers.Serializer): ) passphrase = serializers.CharField( allow_blank=True, allow_null=True, required=False, max_length=512, - write_only=True, label=_('Key password') + write_only=True, label=_('Passphrase') ) @staticmethod diff --git a/apps/accounts/serializers/automations/base.py b/apps/accounts/serializers/automations/base.py index 1704c58a2..086f1b297 100644 --- a/apps/accounts/serializers/automations/base.py +++ b/apps/accounts/serializers/automations/base.py @@ -25,7 +25,8 @@ class BaseAutomationSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSe class Meta: read_only_fields = [ - 'date_created', 'date_updated', 'created_by', 'periodic_display', 'executed_amount' + 'date_created', 'date_updated', 'created_by', + 'periodic_display', 'executed_amount' ] fields = read_only_fields + [ 'id', 'name', 'is_periodic', 'interval', 'crontab', 'comment', @@ -34,8 +35,7 @@ class BaseAutomationSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSe extra_kwargs = { 'name': {'required': True}, 'type': {'read_only': True}, - 'periodic_display': {'label': _('Periodic perform')}, - 'executed_amount': {'label': _('Executed amount')}, + 'executed_amount': {'label': _('Executions')}, } def validate_name(self, name): diff --git a/apps/accounts/serializers/automations/push_account.py b/apps/accounts/serializers/automations/push_account.py index baf1994a4..b9982300b 100644 --- a/apps/accounts/serializers/automations/push_account.py +++ b/apps/accounts/serializers/automations/push_account.py @@ -7,7 +7,6 @@ from .change_secret import ( class PushAccountAutomationSerializer(ChangeSecretAutomationSerializer): - class Meta(ChangeSecretAutomationSerializer.Meta): model = PushAccountAutomation fields = [ diff --git a/apps/acls/apps.py b/apps/acls/apps.py index 291ce855e..e550e34e3 100644 --- a/apps/acls/apps.py +++ b/apps/acls/apps.py @@ -4,4 +4,4 @@ from django.utils.translation import gettext_lazy as _ class AclsConfig(AppConfig): name = 'acls' - verbose_name = _('Acls') + verbose_name = _('App Acls') diff --git a/apps/acls/const.py b/apps/acls/const.py index cccc906f4..3c03d8e2d 100644 --- a/apps/acls/const.py +++ b/apps/acls/const.py @@ -6,5 +6,5 @@ class ActionChoices(models.TextChoices): reject = 'reject', _('Reject') accept = 'accept', _('Accept') review = 'review', _('Review') - warning = 'warning', _('Warning') - notice = 'notice', _('Notifications') + warning = 'warning', _('Warn') + notice = 'notice', _('Notify') diff --git a/apps/applications/apps.py b/apps/applications/apps.py index c672bf36e..888e4739f 100644 --- a/apps/applications/apps.py +++ b/apps/applications/apps.py @@ -6,7 +6,7 @@ from django.utils.translation import gettext_lazy as _ class ApplicationsConfig(AppConfig): name = 'applications' - verbose_name = _('Applications') + verbose_name = _('App Applications') def ready(self): super().ready() diff --git a/apps/assets/apps.py b/apps/assets/apps.py index 9ebd7a93d..74260a689 100644 --- a/apps/assets/apps.py +++ b/apps/assets/apps.py @@ -6,7 +6,7 @@ from django.utils.translation import gettext_lazy as _ class AssetsConfig(AppConfig): name = 'assets' - verbose_name = _('App assets') + verbose_name = _('App Assets') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/apps/assets/const/base.py b/apps/assets/const/base.py index c318115b5..3236050ea 100644 --- a/apps/assets/const/base.py +++ b/apps/assets/const/base.py @@ -113,8 +113,7 @@ class BaseType(TextChoices): @classmethod def get_choices(cls): if not settings.XPACK_ENABLED: - return [ - (tp.value, tp.label) - for tp in cls.get_community_types() - ] - return cls.choices + choices = [(tp.value, tp.label) for tp in cls.get_community_types()] + else: + choices = cls.choices + return choices diff --git a/apps/assets/const/types.py b/apps/assets/const/types.py index 53f41c218..94a2c262c 100644 --- a/apps/assets/const/types.py +++ b/apps/assets/const/types.py @@ -166,23 +166,26 @@ class AllTypes(ChoicesMixin): @classmethod def category_types(cls): - return ( + types = [ (Category.HOST, HostTypes), (Category.DEVICE, DeviceTypes), (Category.DATABASE, DatabaseTypes), - (Category.CLOUD, CloudTypes), (Category.WEB, WebTypes), - (Category.GPT, GPTTypes), - (Category.CUSTOM, CustomTypes), - ) + ] + if settings.XPACK_ENABLED: + types.extend([ + (Category.CLOUD, CloudTypes), + (Category.CUSTOM, CustomTypes), + ]) + return types @classmethod def get_types(cls, exclude_custom=False): choices = [] for name, tp in dict(cls.category_types()).items(): - if name == Category.CUSTOM and exclude_custom: - continue + # if name == Category.CUSTOM and exclude_custom: + # continue choices.extend(tp.get_types()) return choices diff --git a/apps/assets/migrations/0002_auto_20180105_1807_squashed_0009_auto_20180307_1212.py b/apps/assets/migrations/0002_auto_20180105_1807_squashed_0009_auto_20180307_1212.py index 6392158fd..7cbf78a43 100644 --- a/apps/assets/migrations/0002_auto_20180105_1807_squashed_0009_auto_20180307_1212.py +++ b/apps/assets/migrations/0002_auto_20180105_1807_squashed_0009_auto_20180307_1212.py @@ -123,7 +123,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='asset', name='nodes', - field=models.ManyToManyField(default=assets.models.asset.default_node, related_name='assets', to='assets.Node', verbose_name='Node'), + field=models.ManyToManyField(default=assets.models.asset.default_node, related_name='assets', to='assets.Node', verbose_name='Nodes'), ), migrations.AddField( model_name='systemuser', diff --git a/apps/assets/migrations/0043_auto_20191114_1111.py b/apps/assets/migrations/0043_auto_20191114_1111.py index a07dee6bb..55a3eda47 100644 --- a/apps/assets/migrations/0043_auto_20191114_1111.py +++ b/apps/assets/migrations/0043_auto_20191114_1111.py @@ -13,11 +13,11 @@ class Migration(migrations.Migration): migrations.AddField( model_name='gathereduser', name='date_last_login', - field=models.DateTimeField(null=True, verbose_name='Date last login'), + field=models.DateTimeField(null=True, verbose_name='Date login'), ), migrations.AddField( model_name='gathereduser', name='ip_last_login', - field=models.CharField(default='', max_length=39, verbose_name='IP last login'), + field=models.CharField(default='', max_length=39, verbose_name='IP login'), ), ] diff --git a/apps/assets/migrations/0084_auto_20220112_1959.py b/apps/assets/migrations/0084_auto_20220112_1959.py index 25aedef77..71951bad3 100644 --- a/apps/assets/migrations/0084_auto_20220112_1959.py +++ b/apps/assets/migrations/0084_auto_20220112_1959.py @@ -20,9 +20,9 @@ class Migration(migrations.Migration): fields=[ ('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')), ('name', models.CharField(max_length=128, verbose_name='Name')), - ('is_periodic', models.BooleanField(default=False, verbose_name='Periodic perform')), - ('interval', models.IntegerField(blank=True, default=24, null=True, verbose_name='Cycle perform')), - ('crontab', models.CharField(blank=True, max_length=128, null=True, verbose_name='Regularly perform')), + ('is_periodic', models.BooleanField(default=False, verbose_name='Periodic run')), + ('interval', models.IntegerField(blank=True, default=24, null=True, verbose_name='Interval')), + ('crontab', models.CharField(blank=True, max_length=128, null=True, verbose_name='Crontab')), ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')), ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), diff --git a/apps/assets/migrations/0107_automation.py b/apps/assets/migrations/0107_automation.py index 6841cb912..e5909b104 100644 --- a/apps/assets/migrations/0107_automation.py +++ b/apps/assets/migrations/0107_automation.py @@ -24,9 +24,9 @@ class Migration(migrations.Migration): ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), ('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')), ('name', models.CharField(max_length=128, verbose_name='Name')), - ('is_periodic', models.BooleanField(default=False, verbose_name='Periodic perform')), - ('interval', models.IntegerField(blank=True, default=24, null=True, verbose_name='Cycle perform')), - ('crontab', models.CharField(blank=True, max_length=128, null=True, verbose_name='Regularly perform')), + ('is_periodic', models.BooleanField(default=False, verbose_name='Periodic run')), + ('interval', models.IntegerField(blank=True, default=24, null=True, verbose_name='Interval')), + ('crontab', models.CharField(blank=True, max_length=128, null=True, verbose_name='Crontab')), ('accounts', models.JSONField(default=list, verbose_name='Accounts')), ('type', models.CharField(max_length=16, verbose_name='Type')), ('is_active', models.BooleanField(default=True, verbose_name='Is active')), diff --git a/apps/assets/models/asset/common.py b/apps/assets/models/asset/common.py index 7d3fb5fd2..9be7c230b 100644 --- a/apps/assets/models/asset/common.py +++ b/apps/assets/models/asset/common.py @@ -161,8 +161,7 @@ class Asset(NodesRelationMixin, LabeledMixin, AbsConnectivity, JSONFilterMixin, platform = models.ForeignKey(Platform, on_delete=models.PROTECT, verbose_name=_("Platform"), related_name='assets') domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets', verbose_name=_("Domain"), on_delete=models.SET_NULL) - nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', - verbose_name=_("Node")) + nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', verbose_name=_("Nodes")) is_active = models.BooleanField(default=True, verbose_name=_('Is active')) gathered_info = models.JSONField(verbose_name=_('Gathered info'), default=dict, blank=True) # 资产的一些信息,如 硬件信息 custom_info = models.JSONField(verbose_name=_('Custom info'), default=dict) diff --git a/apps/assets/models/domain.py b/apps/assets/models/domain.py index e424a2d46..6b9a387d8 100644 --- a/apps/assets/models/domain.py +++ b/apps/assets/models/domain.py @@ -5,7 +5,7 @@ import random from django.db import models from django.utils.translation import gettext_lazy as _ -from common.utils import get_logger +from common.utils import get_logger, lazyproperty from labels.mixins import LabeledMixin from orgs.mixins.models import JMSOrgBaseModel from .gateway import Gateway @@ -29,6 +29,10 @@ class Domain(LabeledMixin, JMSOrgBaseModel): def select_gateway(self): return self.random_gateway() + @lazyproperty + def assets_amount(self): + return self.assets.count() + def random_gateway(self): gateways = [gw for gw in self.active_gateways if gw.is_connective] diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index 96371bcd2..2e2025dcf 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -125,7 +125,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer, ResourceLabelsMixin, Writa category = LabeledChoiceField(choices=Category.choices, read_only=True, label=_('Category')) type = LabeledChoiceField(choices=AllTypes.choices(), read_only=True, label=_('Type')) protocols = AssetProtocolsSerializer(many=True, required=False, label=_('Protocols'), default=()) - accounts = AssetAccountSerializer(many=True, required=False, allow_null=True, write_only=True, label=_('Account')) + accounts = AssetAccountSerializer(many=True, required=False, allow_null=True, write_only=True, label=_('Accounts')) nodes_display = serializers.ListField(read_only=False, required=False, label=_("Node path")) _accounts = None @@ -140,7 +140,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer, ResourceLabelsMixin, Writa ] read_only_fields = [ 'category', 'type', 'connectivity', 'auto_config', - 'date_verified', 'created_by', 'date_created', + 'date_verified', 'created_by', 'date_created', 'date_updated', ] fields = fields_small + fields_fk + fields_m2m + read_only_fields fields_unexport = ['auto_config'] @@ -149,7 +149,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer, ResourceLabelsMixin, Writa 'name': {'label': _("Name")}, 'address': {'label': _('Address')}, 'nodes_display': {'label': _('Node path')}, - 'nodes': {'allow_empty': True}, + 'nodes': {'allow_empty': True, 'label': _("Nodes")}, } def __init__(self, *args, **kwargs): diff --git a/apps/assets/serializers/automations/base.py b/apps/assets/serializers/automations/base.py index f720ed077..4622c0674 100644 --- a/apps/assets/serializers/automations/base.py +++ b/apps/assets/serializers/automations/base.py @@ -1,4 +1,4 @@ -from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy as _ from rest_framework import serializers from assets.models import Asset, Node, BaseAutomation, AutomationExecution @@ -31,7 +31,6 @@ class BaseAutomationSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSe extra_kwargs = { 'name': {'required': True}, 'type': {'read_only': True}, - 'periodic_display': {'label': _('Periodic perform')}, } diff --git a/apps/assets/serializers/domain.py b/apps/assets/serializers/domain.py index 4224e3983..278a0391d 100644 --- a/apps/assets/serializers/domain.py +++ b/apps/assets/serializers/domain.py @@ -18,12 +18,13 @@ class DomainSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer): gateways = ObjectRelatedField( many=True, required=False, label=_('Gateway'), queryset=Gateway.objects ) + assets_amount = serializers.IntegerField(label=_('Assets amount'), read_only=True) class Meta: model = Domain fields_mini = ['id', 'name'] fields_small = fields_mini + ['comment'] - fields_m2m = ['assets', 'gateways'] + fields_m2m = ['assets', 'gateways', 'assets_amount'] read_only_fields = ['date_created'] fields = fields_small + fields_m2m + read_only_fields extra_kwargs = { @@ -54,13 +55,12 @@ class DomainSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer): @classmethod def setup_eager_loading(cls, queryset): queryset = queryset \ + .annotate(assets_amount=Count('assets')) \ .prefetch_related('labels', 'labels__label') return queryset class DomainListSerializer(DomainSerializer): - assets_amount = serializers.IntegerField(label=_('Assets amount'), read_only=True) - class Meta(DomainSerializer.Meta): fields = list(set(DomainSerializer.Meta.fields + ['assets_amount']) - {'assets'}) diff --git a/apps/assets/serializers/platform.py b/apps/assets/serializers/platform.py index 4fb0418c6..50587071a 100644 --- a/apps/assets/serializers/platform.py +++ b/apps/assets/serializers/platform.py @@ -30,18 +30,43 @@ class PlatformAutomationSerializer(serializers.ModelSerializer): ] extra_kwargs = { # 启用资产探测 - "ping_enabled": {"label": _("Ping enabled")}, + "ping_enabled": {"label": _("Ping enabled"), "help_text": _("Enable asset detection")}, "ping_method": {"label": _("Ping method")}, - "gather_facts_enabled": {"label": _("Gather facts enabled")}, - "gather_facts_method": {"label": _("Gather facts method")}, - "verify_account_enabled": {"label": _("Verify account enabled")}, - "verify_account_method": {"label": _("Verify account method")}, - "change_secret_enabled": {"label": _("Change secret enabled")}, - "change_secret_method": {"label": _("Change secret method")}, - "push_account_enabled": {"label": _("Push account enabled")}, - "push_account_method": {"label": _("Push account method")}, - "gather_accounts_enabled": {"label": _("Gather accounts enabled")}, - "gather_accounts_method": {"label": _("Gather accounts method")}, + "gather_facts_enabled": { + "label": _("Gather facts enabled"), + "help_text": _("Enable asset information collection") + }, + "gather_facts_method": { + "label": _("Gather facts method"), + }, + "verify_account_enabled": { + "label": _("Verify account enabled"), + "help_text": _("Enable account verification") + }, + "verify_account_method": { + "label": _("Verify account method"), + }, + "change_secret_enabled": { + "label": _("Change secret enabled"), + "help_text": _("Enable account secret auto change") + }, + "change_secret_method": { + "label": _("Change secret method"), + }, + "push_account_enabled": { + "label": _("Push account enabled"), + "help_text": _("Enable account auto push") + }, + "push_account_method": { + "label": _("Push account method"), + }, + "gather_accounts_enabled": { + "label": _("Gather accounts enabled"), + "help_text": _("Enable account collection") + }, + "gather_accounts_method": { + "label": _("Gather accounts method"), + }, } @@ -158,13 +183,21 @@ class PlatformSerializer(ResourceLabelsMixin, WritableNestedModelSerializer): 'created_by', 'updated_by' ] fields = fields_small + [ - "protocols", "domain_enabled", "su_enabled", - "su_method", "automation", "comment", "custom_fields", - "labels" + "protocols", "domain_enabled", "su_enabled", "su_method", + "automation", "comment", "custom_fields", "labels" ] + read_only_fields extra_kwargs = { - "su_enabled": {"label": _('Su enabled')}, - "domain_enabled": {"label": _('Domain enabled')}, + "su_enabled": { + "label": _('Su enabled'), + "help_text": _( + "Login with account when accessing assets, then automatically switch to another, " + "similar to logging in with a regular account and then switching to root" + ) + }, + "domain_enabled": { + "label": _('Domain enabled'), + "help_text": _("Assets can be connected using a domain gateway") + }, "domain_default": {"label": _('Default Domain')}, } diff --git a/apps/audits/apps.py b/apps/audits/apps.py index ddaa535a7..1d56d13ad 100644 --- a/apps/audits/apps.py +++ b/apps/audits/apps.py @@ -6,7 +6,7 @@ from django.utils.translation import gettext_lazy as _ class AuditsConfig(AppConfig): name = 'audits' - verbose_name = _('Audits') + verbose_name = _('App Audits') def ready(self): from . import signal_handlers # noqa diff --git a/apps/audits/migrations/0005_auto_20190228_1715.py b/apps/audits/migrations/0005_auto_20190228_1715.py index aac747e9b..910644526 100644 --- a/apps/audits/migrations/0005_auto_20190228_1715.py +++ b/apps/audits/migrations/0005_auto_20190228_1715.py @@ -42,7 +42,7 @@ class Migration(migrations.Migration): default=True, max_length=2, verbose_name='Status')), ('datetime', models.DateTimeField(default=django.utils.timezone.now, - verbose_name='Date login')), + verbose_name='Login Date')), ], options={ 'ordering': ['-datetime', 'username'], diff --git a/apps/audits/migrations/0023_auto_20230906_1322.py b/apps/audits/migrations/0023_auto_20230906_1322.py index 34998318c..34928eef6 100644 --- a/apps/audits/migrations/0023_auto_20230906_1322.py +++ b/apps/audits/migrations/0023_auto_20230906_1322.py @@ -24,6 +24,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='userloginlog', name='datetime', - field=models.DateTimeField(db_index=True, default=django.utils.timezone.now, verbose_name='Date login'), + field=models.DateTimeField(db_index=True, default=django.utils.timezone.now, verbose_name='Login Date'), ), ] diff --git a/apps/audits/models.py b/apps/audits/models.py index 512f50bc1..13b0e9063 100644 --- a/apps/audits/models.py +++ b/apps/audits/models.py @@ -207,7 +207,7 @@ class UserLoginLog(models.Model): choices=LoginStatusChoices.choices, verbose_name=_("Status"), ) - datetime = models.DateTimeField(default=timezone.now, verbose_name=_("Date login"), db_index=True) + datetime = models.DateTimeField(default=timezone.now, verbose_name=_("Login Date"), db_index=True) backend = models.CharField( max_length=32, default="", verbose_name=_("Authentication backend") ) diff --git a/apps/audits/serializers.py b/apps/audits/serializers.py index 3111b0d99..9997e0126 100644 --- a/apps/audits/serializers.py +++ b/apps/audits/serializers.py @@ -131,7 +131,7 @@ class ActivityUnionLogSerializer(serializers.Serializer): def get_content(obj): if not obj['r_detail']: action = obj['r_action'].replace('_', ' ').capitalize() - ctn = _('User %s %s this resource') % (obj['r_user'], _(action)) + ctn = _('%s %s this resource') % (obj['r_user'], _(action).lower()) else: ctn = i18n_trans(obj['r_detail']) return ctn diff --git a/apps/authentication/apps.py b/apps/authentication/apps.py index 5a1c1966a..8f9c38c94 100644 --- a/apps/authentication/apps.py +++ b/apps/authentication/apps.py @@ -4,7 +4,7 @@ from django.utils.translation import gettext_lazy as _ class AuthenticationConfig(AppConfig): name = 'authentication' - verbose_name = _('Authentication') + verbose_name = _('App Authentication') def ready(self): from . import signal_handlers # noqa diff --git a/apps/authentication/backends/saml2/views.py b/apps/authentication/backends/saml2/views.py index 235dd2c38..aa676bef4 100644 --- a/apps/authentication/backends/saml2/views.py +++ b/apps/authentication/backends/saml2/views.py @@ -50,7 +50,7 @@ class PrepareRequestMixin: if idp_metadata_xml.strip(): xml_idp_settings = IdPMetadataParse.parse(idp_metadata_xml) except Exception as err: - logger.warning('Failed to get IDP metadata XML settings, error: %s', str(err)) + logger.warning('Failed to get IDP Metadata XML settings, error: %s', str(err)) url_idp_settings = None try: @@ -59,7 +59,7 @@ class PrepareRequestMixin: idp_metadata_url, timeout=20 ) except Exception as err: - logger.warning('Failed to get IDP metadata URL settings, error: %s', str(err)) + logger.warning('Failed to get IDP Metadata URL settings, error: %s', str(err)) idp_settings = url_idp_settings or xml_idp_settings diff --git a/apps/authentication/templates/authentication/_msg_different_city.html b/apps/authentication/templates/authentication/_msg_different_city.html index 02dbf4271..b3ca09432 100644 --- a/apps/authentication/templates/authentication/_msg_different_city.html +++ b/apps/authentication/templates/authentication/_msg_different_city.html @@ -7,7 +7,7 @@

{% trans 'Username' %}: {{ username }}
- {% trans 'Login time' %}: {{ time }}
+ {% trans 'Login Date' %}: {{ time }}
{% trans 'Login city' %}: {{ city }}({{ ip }})

diff --git a/apps/authentication/templates/authentication/login.html b/apps/authentication/templates/authentication/login.html index ed70b30f7..e7219de27 100644 --- a/apps/authentication/templates/authentication/login.html +++ b/apps/authentication/templates/authentication/login.html @@ -297,8 +297,7 @@