0x01 前言

Jumpserver 作为一款国产的堡垒机,经过长时间的发展,目前已完善堡垒机应有的功能。但在实际使用的过程中依旧发现一些不足,具体使用的文章将在后续逐一发布,而今天这篇文章主要编写单机部署的流程。

Jumpserver 的官网如下:

这款软件是开源的,可以在这里找到源码:

同时,开发商飞致云提供有偿的技术支持、定制化与物理堡垒机出售等服务。

0x02 架构

以下是简单的架构图:

图中各组件的用途如下:

  • Jumpserver:核心功能组件,用于管理整套系统
  • KOKO:SSH Server,主要用于 SSH 与 Telnet 协议
  • Guacamole:主要处理 RDP 与 VNC 协议
  • LUNA:Web Terminal Server 前端

用户通过 IP 地址或者域名与方向代理器通讯,如上图中的 Nginx。Nginx 与 Jumpserver 进行通讯进行鉴权,如果是资产管理或用户管理等操作,也一样需要与 Jumpserver 进行通讯。

当用户需要打开 Web Terminal 时,如通过浏览器使用 SSH、Telnet、RDP、VNC 协议登入资产时, Nginx 则与 LUNA 进行通讯,此时用户会通过 WebSocket 进行连接,而 Jumpserver 则会通过 API 调用 Guacamole 与 KOKO。

目前暂支持用浏览器通过 WebSocket 与用户通讯的方式使用 RDP 与 VNC 协议,而 SSH 协议则支持直接连接至 KOKO 所暴露的端口。但在实际使用中,我会将 KOKO 的端口用 Nginx 代理。

单机部署的结构较为简单,只需要根据官方教程将所有组件部署完成即可。

0x03 准备

在部署过程中需要使用 YUM 安装多个 RPM 包、需要使用 python pip 安装几十个依赖包,还需要使用 Docker 部署两个容器。

为了提高软件的安装速度,需要使用以下镜像:

而 Docker 可以使用微软 Azure 的镜像:

在部署过程中需要安装 Mariadb,此时可以使用以下 repo 文件:

[mariadb]
name = MariaDB
baseurl = https://mirrors.ustc.edu.cn/mariadb/yum/10.2/centos7-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1

如果你不太了解上面所说的,可以尝试逐一执行这些命令:

删除 yum 源:

rm -rf /etc/yum.repos.d/Centos*.repo

写入清华镜像站 Centos 的内容:

cat > /etc/yum.repos.d/centos.repo << EOF
[base]
name=CentOS-\$releasever - Base
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos/\$releasever/os/\$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7

#released updates
[updates]
name=CentOS-\$releasever - Updates
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos/\$releasever/updates/\$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7

#additional packages that may be useful
[extras]
name=CentOS-\$releasever - Extras
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos/\$releasever/extras/\$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7

#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-\$releasever - Plus
baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos/\$releasever/centosplus/\$basearch/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7

EOF

安装 epel 源

yum install epel-release -y

写入清华镜像站 epel 的内容:

cat > /etc/yum.repos.d/epel.repo << EOF
[epel]
name=Extra Packages for Enterprise Linux 7 - \$basearch
baseurl=https://mirrors.tuna.tsinghua.edu.cn/epel/7/\$basearch
failovermethod=priority
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7

EOF

写入 USTC 镜像站 epel 的内容:

cat > /etc/yum.repos.d/mariadb.repo << EOF
[mariadb]
name = MariaDB
baseurl = https://mirrors.ustc.edu.cn/mariadb/yum/10.2/centos7-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=1

EOF

最后添加 docker-ce 稳定版的清华镜像:

cat > /etc/yum.repos.d/docker.repo << EOF
[docker-ce-stable]
name=Docker CE Stable - \$basearch
baseurl=https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/7/\$basearch/stable
enabled=1
gpgcheck=0
gpgkey=https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/gpg

EOF

在这里之后要安装 docker-ce:

yum clean all && yum install docker-ce -y

在启动 docker 前,建议使用 Azure docker 仓库镜像:

mkdir /etc/docker/ && cat > /etc/docker/daemon.json << EOF
{
  "registry-mirrors": [
    "https://dockerhub.azk8s.cn"
  ]
}

EOF

还需要开启系统的 IPv4 转发功能以及调整最大 fd 数量:

cat >> /etc/sysctl.conf << EOF
net.ipv4.ip_forward = 1
fs.nr_open = 10245760000

EOF

使其立即生效:

sysctl -p

启动 docker:

systemctl enable docker \
  && systemctl restart docker \
  && systemctl status docker

至此,准备工作全部完成。

在测试环境中,可以关闭 selinux 以及防火墙;在生产环境中,可以关掉 selinux,但务必打开防火墙并使用白名单模式放行 IP 以及 端口:

sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config \
  && setenforce 0 \
  && systemctl disable firewalld \
  && systemctl stop firewalld

建议重启系统,其实不重启也可以。

0x04 Jumpserver

首先使用 yum 安装各类依赖包:

yum install -y wget gcc git redis python36 python36-devel \
  openssl-devel GeoIP-devel \
  MariaDB-client MariaDB-server MariaDB-devel MariaDB-shared

启动 redis:

systemctl enable redis \
  && systemctl start redis

启动 MariaDB:

systemctl enable mariadb \
  && systemctl start mariadb

生成 MariaDB 密码并初始化数据库:

DB_PASSWORD=`cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 24` \
  && echo -e "\033[31m 你的数据库密码是 $DB_PASSWORD \033[0m" \
  && mysql -uroot -e "create database jumpserver default charset 'utf8'; \
  grant all on jumpserver.* to 'jumpserver'@'127.0.0.1' identified by '$DB_PASSWORD'; \
  flush privileges;"

请将数据库密码复制并即在合适的地方备用:

创建 python3 虚拟环境:

cd /opt \
  && python3.6 -m venv py3 \
  && source /opt/py3/bin/activate

如果需要退出虚拟环境或进入虚拟环境,可执行以下命令:

# 进入虚拟环境
source /opt/py3/bin/activate

# 推出虚拟环境
deactivate

在 opt 目录下 clone jumpserver 的源码到本地,建议使用 git clone,如果遇到网络问题,可以使用 http 或 https proxy;如果服务器无法联网,也可以下载 zip 压缩包到本地压缩,但是在后期升级时会比较繁琐。

# 配置 git 使用 proxy,若网络较好,可以跳过
# 请根据实际情况修正 proxy 服务器地址
git config --global http.proxy http://10.1.1.11:10809

然后克隆源码到本地:

cd /opt \
  && git clone --depth=1 \
  https://github.com/jumpserver/jumpserver.git

安装 RPM 依赖包:

yum -y install $(cat /opt/jumpserver/requirements/rpm_requirements.txt)

配置 PYPI 镜像:

# 升级
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pip -U

# 配置全局默认仓库
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

安装 Python 库依赖:

pip install wheel \
  && pip install setuptools \
  && pip install -r /opt/jumpserver/requirements/requirements.txt

最终的结果如下:

如果出现类似首两行的版本错误,可以忽略。

至此,Jumpserver 基础环境已经完成部署,接下来要对他进行配置。

首先进入 Jumpserver 的目录,复制一份配置文件:

cd /opt/jumpserver \
  && cp config_example.yml config.yml

然后生成 SECRET_KEY 和 BOOTSTRAP_TOKEN,同时将它们写入 bashrc 文件中用于日后的变量赋值:

# SECRET_KEY
SECRET_KEY=`cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 50` \
  && echo "SECRET_KEY=$SECRET_KEY" >> ~/.bashrc

# BOOTSTRAP_TOKEN
BOOTSTRAP_TOKEN=`cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 16` \
  && echo "BOOTSTRAP_TOKEN=$BOOTSTRAP_TOKEN" >> ~/.bashrc

# 打印 SECRET_KEY
echo -e "\033[31m 你的SECRET_KEY是 $SECRET_KEY \033[0m"

# 打印 BOOTSTRAP_TOKEN
echo -e "\033[31m 你的BOOTSTRAP_TOKEN是 $BOOTSTRAP_TOKEN \033[0m"

接下来对配置文件进行修改,你也可以使用 vim 等方式打开文件逐一修改,可以使用官方文档里的 sed 命令修改。其实配置文件内没有很多需要修改的参数,可以直接使用 sed 修改。

# 填充 SECRET_KEY
sed -i "s/SECRET_KEY:/SECRET_KEY: $SECRET_KEY/g" /opt/jumpserver/config.yml

# 填充 BOOTSTRAP_TOKEN
sed -i "s/BOOTSTRAP_TOKEN:/BOOTSTRAP_TOKEN: $BOOTSTRAP_TOKEN/g" /opt/jumpserver/config.yml

# 关闭 DEBUG 模式
sed -i "s/# DEBUG: true/DEBUG: false/g" /opt/jumpserver/config.yml

# 修改日志级别
sed -i "s/# LOG_LEVEL: DEBUG/LOG_LEVEL: ERROR/g" /opt/jumpserver/config.yml

# 设定当浏览器关闭时将 session 设为已过期
sed -i "s/# SESSION_EXPIRE_AT_BROWSER_CLOSE: false/SESSION_EXPIRE_AT_BROWSER_CLOSE: true/g" /opt/jumpserver/config.yml

# 填充 DB_PASSWORD
sed -i "s/DB_PASSWORD: /DB_PASSWORD: $DB_PASSWORD/g" /opt/jumpserver/config.yml

至此,所有 Jumpserver 核心部分的安装与配置工作以完成,最后启动即可。在启动的时候要确认处于 py3 虚拟环境内:

source /opt/py3/bin/activate \
  && /opt/jumpserver/jms start -d

启动的时候可能会出现以下错误:

[root@b6ae77 ~]# source /opt/py3/bin/activate \
>   && /opt/jumpserver/jms start -d
Traceback (most recent call last):
  File "/opt/jumpserver/jms", line 16, in <module>
    import daemon
ModuleNotFoundError: No module named 'daemon'

请在 py3 虚拟环境中执行:

pip install python-daemon

最后再执行上面的启动命令即可,最终的结果如下:

最后还需要将启动命令加入到 rc.local 文件:

echo 'source /opt/py3/bin/activate && /opt/jumpserver/jms start -d' >> /etc/rc.local \
  && chmod +x /etc/rc.d/rc.local

到这里,Jumpserver 核心部分的所有工作已经完成。

0x05 前端

前端包含两部分,分别是 LUNA 和 Nginx,首先部署 LUNA。此时如果还在 Python 的虚拟环境里,可以退出:

(py3) [root@b6ae77 jumpserver]# deactivate
[root@b6ae77 jumpserver]#

然后下载 LUNA 并解压:

cd /opt \
  && wget https://demo.jumpserver.org/download/luna/1.5.5/luna.tar.gz \
  && tar xf luna.tar.gz \
  && chown -R root:root luna

因为下一篇文章会详细讲解分布式部署的流程,而分布式部署需要使用 Nginx stream 模块,但是这个模块并不是 Nginx 默认的模块,所以需要自行编译。

为了尽可能减少区别,我决定在这一篇文章里也使用自行编译的 Nginx。其实过程及其简单,只需要执行以下命令即可:

curl https://nginx.org/download/nginx-1.16.1.tar.gz -o /tmp/nginx-1.16.1.tar.gz \
  && tar zxvf /tmp/nginx-1.16.1.tar.gz -C /tmp/ \
  && cd /tmp/nginx-1.16.1/ \
  && ./configure \
  --prefix=/usr/local/nginx \
  --sbin-path=/usr/sbin/nginx \
  --conf-path=/usr/local/nginx/nginx.conf \
  --pid-path=/var/run/nginx.pid \
  --error-log-path=/var/log/nginx/error.log \
  --http-log-path=/var/log/nginx/access.log \
  --lock-path=/var/lock/nginx.lock \
  --with-stream \
  --with-http_realip_module \
  --with-http_ssl_module \
  --with-http_stub_status_module \
  --with-http_sub_module \
  --with-http_v2_module \
  --with-http_geoip_module \
  --with-file-aio \
  --http-client-body-temp-path=/var/tmp/nginx/client_body \
  --http-proxy-temp-path=/var/tmp/nginx/proxy \
  --http-fastcgi-temp-path=/var/tmp/nginx/fastcgi \
  --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi \
  --http-scgi-temp-path=/var/tmp/nginx/scgi \
  && make -j 2 \
  && make install \
  && groupadd nginx \
  && useradd -M -s /sbin/nologin -g nginx nginx \
  && mkdir -p /var/tmp/nginx/client_body \
  && mkdir -p /usr/local/nginx/conf.d

nginx.conf 的配置文件比较简单,如果有需要,请自行定制:

cat > /usr/local/nginx/nginx.conf << EOF
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '\$remote_addr - \$remote_user [\$time_local] "\$request" '
                      '\$status \$body_bytes_sent "\$http_referer" '
                      '"\$http_user_agent" "\$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include             /usr/local/nginx/mime.types;
    default_type        application/octet-stream;

    include /usr/local/nginx/conf.d/*.conf;
}

EOF

还需要添加jumpserver.conf配置文件:

cat > /usr/local/nginx/conf.d/jumpserver.conf << EOF
server {
  listen 80;

  # 录像及文件上传大小限制
  client_max_body_size 100m;

  location /luna/ {
    try_files $uri / /index.html;

    # luna 路径, 如果修改安装目录, 此处需要修改
    alias /opt/luna/;
  }

  location /media/ {
    add_header Content-Encoding gzip;

    # 录像位置, 如果修改安装目录, 此处需要修改
    root /opt/jumpserver/data/;
  }

  location /static/ {
    # 静态资源, 如果修改安装目录, 此处需要修改
    root /opt/jumpserver/data/;
  }

  location /koko/ {
    proxy_pass       http://localhost:5000;
    proxy_buffering off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    access_log off;
  }

  location /ws/ {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://localhost:8070;
    proxy_http_version 1.1;
    proxy_buffering off;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }

  location /guacamole/ {
    proxy_pass       http://localhost:8081/;
    proxy_buffering off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $http_connection;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    access_log off;
  }

  location / {
    proxy_pass http://localhost:8080;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}
EOF

最后启动即可:

echo 'nginx' >> /etc/rc.local \
  && nginx

前端的部分也已经完成,通过浏览器打开 Jumpserver 的页面并输入默认的超级用户账号与密码即可登入:

  • 默认账号:admin
  • 默认密码:admin

打开 web终端 可以测试 LUNA 是否工作正常:

0x06 终端

终端包含 KOKO 与 Guacamole,这两个终端正如本文开头所说的,分别对应 CLI 与 GUI 界面。在部署前先检查服务器 IP 是否正确以及系统变量中是否存在 BOOTSTRAP_TOKEN:

Server_IP=`ip addr | grep 'state UP' -A2 | grep inet \
  | egrep -v '(127.0.0.1|inet6|docker)' | awk '{print $2}' | tr -d "addr:" \
  | head -n 1 | cut -d / -f1` \
  && echo -e "\033[31m 你的服务器IP是 $Server_IP \033[0m" \
  && echo -e "\033[31m 你的BOOTSTRAP_TOKEN是 $BOOTSTRAP_TOKEN \033[0m"

如果值不对或没有值,可以手动指定:

# 手动指定 Server_IP
Server_IP=10.1.1.1

# 手动指定 BOOTSTRAP_TOKEN
BOOTSTRAP_TOKEN=aaabbbccc

然后执行以下命令即可完成部署:

# KOKO
docker run --name jms_koko -d -p 2222:2222 -p 127.0.0.1:5000:5000 \
  -e CORE_HOST=http://$Server_IP:8080 \
  -e BOOTSTRAP_TOKEN=$BOOTSTRAP_TOKEN \
  --restart=always \
  jumpserver/jms_koko:1.5.5

# Guacamole
docker run --name jms_guacamole -d -p 127.0.0.1:8081:8080 \
  -e JUMPSERVER_SERVER=http://$Server_IP:8080 \
  -e BOOTSTRAP_TOKEN=$BOOTSTRAP_TOKEN \
  --restart=always \
  jumpserver/jms_guacamole:1.5.5

使用以下命令检查运行情况:

docker ps

现在再回到 Jumpserver 前端,进入终端列表,应该可以看到刚才新增的两个终端:

Guacamole 需要添加资产才能测试,但是 SSH 可以立刻测试,KOKO 的默认端口是 TCP 2222,使用以下命令即可登入:

ssh [email protected] -p 2222

如果测试成功,说明服务以完成部署。

0x07 结语

因为篇幅的关系,这里没有详细描述使用方面的信息,下一篇文章将讲解分布式部署的案例与部署方式。

在部署的时候要注意各个组件的版本尽可能一致,以免出现不必要的问题。

更重要的是需要自行考量安全问题,尤其是各个端口的放行与拦截,建议为 Jumpserver 使用白名单模式的防火墙。