Jumpserver容器化部署手册

2020-06-12

组件说明

  • Jumpserver 为管理后台, 管理员可以通过 Web 页面进行资产管理、用户管理、资产授权等操作, 用户可以通过 Web 页面进行资产登录, 文件管理等操作
  • koko 为 SSH Server 和 Web Terminal Server 。用户可以使用自己的账户通过 SSH 或者 Web Terminal 访问 SSH 协议和 Telnet 协议资产
  • Luna 为 Web Terminal Server 前端页面, 用户使用 Web Terminal 方式登录所需要的组件
  • Guacamole 为 RDP 协议和 VNC 协议资产组件, 用户可以通过 Web Terminal 来连接 RDP 协议和 VNC 协议资产 (暂时只能通过 Web Terminal 来访问)

端口说明

  • Jumpserver 默认 Web 端口为 8080/tcp, 默认 WS 端口为 8070/tcp, 配置文件 jumpserver/config.yml
  • koko 默认 SSH 端口为 2222/tcp, 默认 Web Terminal 端口为 5000/tcp 配置文件在 koko/config.yml
  • Guacamole 默认端口为 8080tcp, 配置文件 /config/tomcat9/conf/server.xml
  • Nginx 默认端口为 80/tcp
  • Redis 默认端口为 6379/tcp

镜像制作

Jumpserver镜像

官方镜像不可用,需下载源码包自行构建,GitHub下载Jumpserver-1.5.4 Release包;源码下载地址:https://github.com/jumpserver/jumpserver/archive/1.5.4.tar.gz

解压源码包,并使用以下Dockerfile、entrypoint.sh替换源码中的文件,执行docker build命令。

Dockerfile

FROM registry.fit2cloud.com/public/python:v3
MAINTAINER Jumpserver Team <ibuler@qq.com>

WORKDIR /opt/jumpserver
RUN useradd jumpserver

COPY ./requirements /tmp/requirements

RUN yum -y install epel-release && \
      echo -e "[mysql]\nname=mysql\nbaseurl=https://mirrors.tuna.tsinghua.edu.cn/mysql/yum/mysql57-community-el6/\ngpgcheck=0\nenabled=1" > /etc/yum.repos.d/mysql.repo
RUN cd /tmp/requirements && yum -y install $(cat rpm_requirements.txt)
RUN cd /tmp/requirements && pip install --upgrade pip setuptools==33.1.1 && \
    pip install -i https://mirrors.aliyun.com/pypi/simple/ -r requirements.txt || pip install -r requirements.txt
RUN mkdir -p /root/.ssh/ && echo -e "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null" > /root/.ssh/config

COPY . /opt/jumpserver
# RUN echo > config.yml
VOLUME /opt/jumpserver/data
VOLUME /opt/jumpserver/logs

ENV LANG=zh_CN.UTF-8
ENV LC_ALL=zh_CN.UTF-8

EXPOSE 8070
EXPOSE 8080
ENTRYPOINT ["./entrypoint.sh"]

entrypoint.sh

#!/bin/bash
function cleanup()
{
    local pids=`jobs -p`
    if [[ "${pids}" != ""  ]]; then
        kill ${pids} >/dev/null 2>/dev/null
    fi
}

SECRET_KEY=${SECRET_KEY:=`cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 50`}
BOOTSTRAP_TOKEN=${BOOTSTRAP_TOKEN:=`cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 16`}
DEBUG=${DEBUG:="false"}
LOG_LEVEL=${LOG_LEVEL:="ERROR"}
SESSION_EXPIRE_ABC=${SESSION_EXPIRE_ABC:="true"}
DB_HOST=${DB_HOST:="127.0.0.1"}
DB_PORT=${DB_PORT:="3306"}
DB_USER=${DB_USER:="jumpserver"}
DB_PASSWORD=${DB_PASSWORD:=""}
DB_NAME=${DB_NAME:="jumpserver"}
REDIS_HOST=${REDIS_HOST:="127.0.0.1"}
REDIS_PORT=${REDIS_PORT:="6379"}

if [ ! -f "/opt/jumpserver/config.yml" ]; then
    cp /opt/jumpserver/config_example.yml /opt/jumpserver/config.yml
    sed -i "s/SECRET_KEY:/SECRET_KEY: $SECRET_KEY/g" /opt/jumpserver/config.yml
    sed -i "s/BOOTSTRAP_TOKEN:/BOOTSTRAP_TOKEN: $BOOTSTRAP_TOKEN/g" /opt/jumpserver/config.yml
    sed -i "s/# DEBUG: true/DEBUG: $DEBUG/g" /opt/jumpserver/config.yml
    sed -i "s/# LOG_LEVEL: DEBUG/LOG_LEVEL: $LOG_LEVEL/g" /opt/jumpserver/config.yml
    sed -i "s/# SESSION_EXPIRE_AT_BROWSER_CLOSE: false/SESSION_EXPIRE_AT_BROWSER_CLOSE: $SESSION_EXPIRE_ABC/g" /opt/jumpserver/config.yml
    sed -i "s/DB_ENGINE: mysql/DB_ENGINE: $DB_ENGINE/g" /opt/jumpserver/config.yml
    sed -i "s/DB_HOST: 127.0.0.1/DB_HOST: $DB_HOST/g" /opt/jumpserver/config.yml
    sed -i "s/DB_PORT: 3306/DB_PORT: $DB_PORT/g" /opt/jumpserver/config.yml
    sed -i "s/DB_USER: jumpserver/DB_USER: $DB_USER/g" /opt/jumpserver/config.yml
    sed -i "s/DB_PASSWORD: /DB_PASSWORD: $DB_PASSWORD/g" /opt/jumpserver/config.yml
    sed -i "s/DB_NAME: jumpserver/DB_NAME: $DB_NAME/g" /opt/jumpserver/config.yml
    sed -i "s/REDIS_HOST: 127.0.0.1/REDIS_HOST: $REDIS_HOST/g" /opt/jumpserver/config.yml
    sed -i "s/REDIS_PORT: 6379/REDIS_PORT: $REDIS_PORT/g" /opt/jumpserver/config.yml
    sed -i "s/# REDIS_PASSWORD: /REDIS_PASSWORD: $REDIS_PASSWORD/g" /opt/jumpserver/config.yml
fi

if [[ "$REDIS_PASSWORD" != "" ]]; then
    sed -i "s/# REDIS_PASSWORD: /REDIS_PASSWORD: $REDIS_PASSWORD/g" /opt/jumpserver/config.yml
fi

if [[ "$AUTH_LDAP_SYNC_IS_PERIODIC" == "True" ]]; then
    AUTH_LDAP_SEARCH_PAGED_SIZE=${AUTH_LDAP_SEARCH_PAGED_SIZE:="1000"}
    AUTH_LDAP_SYNC_INTERVAL=${AUTH_LDAP_SYNC_INTERVAL:="12"}
    AUTH_LDAP_SYNC_CRONTAB=${AUTH_LDAP_SYNC_CRONTAB:="* 6 * * *"}
    sed -i "s/# AUTH_LDAP_SYNC_IS_PERIODIC: True/AUTH_LDAP_SYNC_IS_PERIODIC: $AUTH_LDAP_SYNC_IS_PERIODIC/g" /opt/jumpserver/config.yml
    sed -i "s/# AUTH_LDAP_SYNC_INTERVAL: 12/AUTH_LDAP_SYNC_INTERVAL: $AUTH_LDAP_SYNC_INTERVAL/g" /opt/jumpserver/config.yml
    sed -i "s/# AUTH_LDAP_SYNC_CRONTAB: * 6 * * */AUTH_LDAP_SYNC_CRONTAB: $AUTH_LDAP_SYNC_CRONTAB/g" /opt/jumpserver/config.yml
fi

if [[ "$AUTH_LDAP_SEARCH_PAGED_SIZE" != "" ]]; then
    sed -i "s/# AUTH_LDAP_SEARCH_PAGED_SIZE: 1000/AUTH_LDAP_SEARCH_PAGED_SIZE: $AUTH_LDAP_SEARCH_PAGED_SIZE/g" /opt/jumpserver/config.yml
fi

if [[ "$AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS" != "" ]]; then
    sed -i "s/# AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS: False/AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS: $AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS/g" /opt/jumpserver/config.yml
fi

if [[ "$AUTH_LDAP_OPTIONS_OPT_REFERRALS" != "" ]]; then
    sed -i "s/# AUTH_LDAP_OPTIONS_OPT_REFERRALS: -1/AUTH_LDAP_OPTIONS_OPT_REFERRALS: $AUTH_LDAP_OPTIONS_OPT_REFERRALS/g" /opt/jumpserver/config.yml
fi

service="all"
if [[ "$1" != "" ]];then
    service=$1
fi

trap cleanup EXIT
if [[ "$1" == "bash" ]];then
    bash
else
    python jms start ${service}
fi

koko镜像

镜像版本:jumpserver/jms_koko:1.5.4

Guacamole镜像

镜像版本:jumpserver/jms_guacamole:1.5.4

Nginx镜像

官方镜像即可,容器化部署时主要用作jumpserver前端工程及静态文件访问。

Redis镜像

官方镜像即可。

集群部署

创建PVC

  • kubectl apply -f jms-data-pvc.yaml:主要用于录像存放

jms-data-pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: jms-core-data-pvc
  namespace: ${namespace}
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 500Mi
  storageClassName: nfs-client
 
  • kubectl apply -f jms-log-pvc.yaml:主要用于日志存放

jms-log-pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: jms-core-log-pvc
  namespace: ${namespace}
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 500Mi
  storageClassName: nfs-client
 
  • kubectl apply -f jms-luna-pvc.yaml:用于前端工程luna存放

jms-luna-pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: jms-static-luna-pvc
  namespace: ${namespace}
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 100Mi
  storageClassName: nfs-client

部署Jumpserver核心服务

  • kubectl apply -f jms-core-deployment.yaml

jms-core-deployment.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: jms-core
  name: jms-core
  namespace: ${namespace}
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: jms-core
    spec:
      containers:
      - env:
        - name: DB_HOST
          value: ${DB_HOST}
        - name: DB_USER
          value: jumpserver
        - name: DB_NAME
          value: jumpserver
        - name: DB_PASSWORD
          value: ${DB_PASSWORD}
        - name: DEBUG
          value: "true"
        - name: REDIS_HOST
          value: redis
        - name: LOG_LEVEL
          value: DEBUG
        - name: SECRET_KEY
          value: ${SECRET_KEY}
        - name: BOOTSTRAP_TOKEN
          value: ${BOOTSTRAP_TOKEN}
        image: ${docker_repo}
        name: jms-core
        ports:
        - containerPort: 8080
          name: jms-core-web
          protocol: TCP
        - containerPort: 8070
          name: jms-core-ws
          protocol: TCP
        resources:
          limits:
            cpu: "2"
            memory: 4Gi
          requests:
            cpu: "2"
            memory: 4Gi
        volumeMounts:
        - mountPath: /opt/jumpserver/data
          name: jms-core-data-pv
        - mountPath: /opt/jumpserver/logs
          name: jms-core-log-pv
      imagePullSecrets:
      - name: docker-repo
      volumes:
      - name: jms-core-data-pv
        persistentVolumeClaim:
          claimName: jms-core-data-pvc
      - name: jms-core-log-pv
        persistentVolumeClaim:
          claimName: jms-core-log-pvc
  • kubectl apply -f jms-core-service.yaml

jms-core-service.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: jms-core
  name: jms-service
  namespace: ${namespace}
spec:
  ports:
    - port: 8080
      name: jms-core-web
      targetPort: jms-core-web
    - port: 8070
      name: jms-core-ws
      targetPort: jms-core-ws
  selector:
    app: jms-core
 

部署Nginx:用于访问jumpserver静态文件及前端工程luna

  • kubectl create configmap jms-static-etc –from-file nginx.conf:根据以下nginx.conf文件创建Nginx配置 configmap

nginx.conf

user  nginx;
worker_processes  auto;
 
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
 
 
events {
    worker_connections  1024;
}
 
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
 
    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;
 
    keepalive_timeout  65;
 
    # 关闭版本显示
    server_tokens off;
 
    # include /etc/nginx/conf.d/*.conf;
 
    server {
        listen 80;
        server_name ${server_name}
 
        client_max_body_size 100m;
 
        location /luna/ {
            try_files $uri / /index.html;
            alias /opt/luna/;  # luna 路径
        }
 
        location /media/ {
            add_header Content-Encoding gzip;
            root /opt/jumpserver/data/;  # 录像位置, 如果修改安装目录, 此处需要修改
        }  
 
        location /static/ {
            root /opt/jumpserver/data/;  # 静态资源, 如果修改安装目录, 此处需要修改
        }
    }
}
 
  • kubectl apply -f jms-static-deployment.yaml

jms-statick-deployment.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: jms-static
  namespace: ${namespace} 
  labels:
    app: jms-static
spec:
  replicas: 1
  template:
    metadata:
      labels:
       app: jms-static
    spec:
      containers:
       - name: jms-static
         image: ${docker-repo}
         volumeMounts:
          - mountPath: /opt/luna/
            name: jms-static-luna-pv
          - mountPath: /etc/nginx/nginx.conf
            subPath: nginx.conf
            name: jms-static-nginx
          - mountPath: /opt/jumpserver/data/
            name: jms-core-data-pv
         ports:
          - containerPort: 80
         resources:
           limits:
             cpu: "1"
             memory: 1Gi
           requests:
             cpu: "1"
             memory: 1Gi
      imagePullSecrets:
        - name: docker-repo
      volumes:
         - name: jms-static-luna-pv
           persistentVolumeClaim:
            claimName: jms-static-luna-pvc
         - name: jms-core-data-pv
           persistentVolumeClaim:
            claimName: jms-core-data-pvc
         - name: jms-static-nginx
           configMap:
             name: jms-static-etc
             items:
             - key: nginx.conf
               path: nginx.conf 

部署KoKo组件:SSH Server 和 Web Terminal Server

  • kubectl apply -f jms-koko-deployment.yaml

jms-koko-deployment.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: jms-koko
  namespace: ${namespace}
  labels:
    app: jms-koko
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: jms-koko
    spec:
      containers:
        - name: jms-koko
          image: ${docker-repo}
          ports:
            - containerPort: 2222
              name: jms-koko-ssh
            - containerPort: 5000
              name: jms-koko-ws
          resources: 
            limits:
              cpu: "1"
              memory: 1Gi
            requests:
              cpu: "1"
              memory: 1Gi
          env:
            - name: CORE_HOST
              value: "http://jms-service:8080/"
            - name: BOOTSTRAP_TOKEN
              value: ${BOOTSTRAP_TOKEN}
      imagePullSecrets:
        - name: docker-repo
      nodeSelector:
        cloud: dmz
 
 
  • kubectl apply -f jms-koko-service.yaml

jms-koko-service.yaml

---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: jms-koko
  name: jms-koko-ssh
  namespace: ${namespace}
spec:
  ports:
    - port: 22222 #自定义端口
      name: jms-koko-ssh
      targetPort: jms-koko-ssh
  selector:
    app: jms-koko
  type: NodePort
 
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: jms-koko
  name: jms-koko-web
  namespace: ${namespace}
spec:
  ports:
    - port: 5000
      name: jms-koko-ws
      targetPort: jms-koko-ws
  selector:
    app: jms-koko
  type: ClusterIP

部署Guacamole组件:RDP 协议和 VNC 协议资产组件

  • kubectl apply -f jms-gua-deployment.yaml

jms-gua-deployment.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: jms-gua
  namespace: ${namespace} 
  labels:
    app: jms-gua
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: jms-gua
    spec:
      containers:
        - name: jms-guacamole
          image: ${docker-repo}
          ports:
            - containerPort: 8080
              name: jms-guardc
          resources: 
            limits:
              cpu: "1"
              memory: 1Gi
            requests:
              cpu: "1"
              memory: 1Gi
          env:
            - name: JUMPSERVER_SERVER
              value: "http://jms-service:8080/"
            - name: BOOTSTRAP_TOKEN
              value: ${BOOTSTRAP_TOKEN}
            - name: JUMPSERVER_KEY_DIR
              value: /config/guacamole/key
      imagePullSecrets:
        - name: docker-repo
 
  • kubectl apply -f jms-gua-service.yaml

jms-gua-service.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: jms-gua
  name: jms-gua
  namespace: ${namespace} 
spec:
  ports:
    - port: 8080
      name: gua-web
      targetPort: jms-guardc
  selector:
    app: jms-gua

创建Ingress:服务入口

  • kubectl apply -f jumpserver-ingress.yaml

jumpserver-ingress.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: jms
  namespace: ${namespace}
spec:
  rules:
  - host: jump.xxx.com   #自定义
    http:
      paths:
      - backend:
          serviceName: jms-service
          servicePort: 8080
        path: /
      - backend:
          serviceName: jms-service
          servicePort: 8070
        path: /ws/
      - backend:
          serviceName: jms-koko-web
          servicePort: 5000
        path: /koko/
      - backend:
          serviceName: jms-static-service
          servicePort: 80
        path: /luna/
      - backend:
          serviceName: jms-static-service
          servicePort: 80
        path: /media/
      - backend:
          serviceName: jms-static-service
          servicePort: 80
        path: /static/
  tls:
  - hosts:
    - jump.xxx.com
    secretName: xxx-tls   #自定义
status:
  loadBalancer: {}

由于Guacamole组件为独立部署的web应用,Ingress无法直接转发;需要创建middleware和ingressroute进行转发。

  • kubectl apply -f jumpserver-traefik-middleware.yaml

jumpserver-traefik-middleware.yaml

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: replace-gua-path
  namespace: ${namespace}
spec:
  stripPrefix:
    prefixes:
    - /guacamole
  • kubectl apply -f jumpserver-traefik-ingressroute.yaml

jumpserver-traefik-ingressroute.yaml

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: jump-test
  namespace: ${namespace}
spec:
  entryPoints:
  - web
  routes:
  - kind: Rule
    match: Host(`jump.xxx.com`) && PathPrefix(`/guacamole`)
    middlewares:
    - name: replace-gua-path
    services:
    - kind: Service
      name: jms-gua
      port: 8080