From 06b3fc786d5729849a15b3604b45c3940737a8ed Mon Sep 17 00:00:00 2001 From: Nicolas Pastorello Date: Tue, 25 Aug 2026 13:52:03 -0300 Subject: [PATCH 1/6] feat: add JumpServer LXC script Adds JumpServer (PAM/bastion host): core (Python 3.14/uv), KoKo, Lion + guacd (compiled from Apache's release tarball), Lina/Luna, PostgreSQL, Redis and Nginx, all as systemd services in one LXC. Tested end-to-end on a real Proxmox host (repeated clean installs) before opening this. Notable fixes found along the way: - freerdp3-dev is the correct Debian 13 package name (not libfreerdp3-dev) - guacd's tarball needs its autotools timestamps normalized after extraction, and --disable-guacenc (Debian 13's libavcodec breaks guacenc's build under -Werror) - guacd is pinned to 1.5.5 deliberately: it's the exact version upstream's own Lion Dockerfile is built against, and Apache Guacamole has no GitHub Releases API to auto-track "latest" from - jumpserver/koko/lina/luna all publish two parallel release lines (3.10.x and 4.10.x); "latest" can resolve to the older-but-more- recently-published 3.10.x line, so fetches are scoped to v4.x via tag_prefix - requirements/static_files.sh must run before first start, or core crash-loops on a missing leak_passwords.db - the venv needs to be on PATH for jms to find gunicorn/celery/daphne - JumpServer v4.10.19's own admin/ChangeMe default 500s on login (an upstream bug in its weak-password check), so the script sets a generated admin password directly instead - galaxy.ansible.com occasionally 502s on collection install; retry a few times before failing the install outright --- ct/jumpserver.sh | 83 ++++++++++ install/jumpserver-install.sh | 303 ++++++++++++++++++++++++++++++++++ json/jumpserver.json | 51 ++++++ 3 files changed, 437 insertions(+) create mode 100644 ct/jumpserver.sh create mode 100644 install/jumpserver-install.sh create mode 100644 json/jumpserver.json diff --git a/ct/jumpserver.sh b/ct/jumpserver.sh new file mode 100644 index 0000000000..5b4c39173c --- /dev/null +++ b/ct/jumpserver.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +_cs_boot="${COMMUNITY_SCRIPTS_CORE_DIR:-$(dirname "${BASH_SOURCE[0]}")/../../core}/core/build.func" +source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/build.func") + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Nícolas Pastorello (opastorello) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://www.jumpserver.org/ | Github: https://github.com/jumpserver/jumpserver + +APP="JumpServer" +var_tags="${var_tags:-bastion-host;pam}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-8192}" +var_disk="${var_disk:-50}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" +#var_arm64="${var_arm64:-no}" # unset = ask the user; koko/lion ship arm64 binaries and guacd builds fine on arm64, but the combined stack has not actually been run on arm64 + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/jumpserver ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "jumpserver" "jumpserver/jumpserver" "" "" "v4."; then + msg_info "Stopping Services" + systemctl stop jumpserver koko lion + msg_ok "Stopped Services" + + create_backup /opt/jumpserver/config.yml /opt/jumpserver/data/media /opt/koko/config.yml /opt/lion/config.yml + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "jumpserver" "jumpserver/jumpserver" "tarball" "latest" "" "" "v4." + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "koko" "jumpserver/koko" "prebuild" "latest" "/opt/koko" "koko-v4.*-linux-$(arch_resolve amd64 arm64).tar.gz" "v4." + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "lion" "jumpserver/lion-release" "prebuild" "latest" "/opt/lion" "lion-v*-linux-$(arch_resolve amd64 arm64).tar.gz" + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "lina" "jumpserver/lina" "prebuild" "latest" "/opt/lina" "lina-v4.*.tar.gz" "v4." + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "luna" "jumpserver/luna" "prebuild" "latest" "/opt/luna" "luna-v4.*.tar.gz" "v4." + + restore_backup + + msg_info "Updating Python Environment" + cd /opt/jumpserver + $STD bash requirements/static_files.sh + $STD uv venv --python 3.14 + $STD uv pip install -r pyproject.toml + JMS_GALAXY_RETRIES=3 + until ANSIBLE_COLLECTIONS_PATHS="/opt/jumpserver/.venv/lib/python3.14/site-packages/ansible_collections" $STD .venv/bin/ansible-galaxy collection install -r requirements/collections.yml --force --ignore-certs; do + JMS_GALAXY_RETRIES=$((JMS_GALAXY_RETRIES - 1)) + if ((JMS_GALAXY_RETRIES <= 0)); then + msg_error "ansible-galaxy collection install failed after 3 attempts (Ansible Galaxy may be unreachable)" + exit 1 + fi + sleep 15 + done + msg_ok "Updated Python Environment" + + msg_info "Starting Services" + systemctl start jumpserver koko lion + msg_ok "Started Services" + msg_ok "Updated successfully!" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW}Access it using the following URL:${CL}" +echo -e "${GATEWAY}${BGN}http://${IP}${CL}" +echo -e "${INFO}${YW}SSH bastion (character protocols): ${CL}${BGN}ssh -p 2222 @${IP}${CL}" +echo -e "${INFO}${YW}Login credentials: ~/jumpserver.creds (inside the container)${CL}" diff --git a/install/jumpserver-install.sh b/install/jumpserver-install.sh new file mode 100644 index 0000000000..0b165b9bd3 --- /dev/null +++ b/install/jumpserver-install.sh @@ -0,0 +1,303 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Nícolas Pastorello (opastorello) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://www.jumpserver.org/ | Github: https://github.com/jumpserver/jumpserver + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y \ + build-essential \ + pkg-config \ + default-libmysqlclient-dev \ + freetds-dev \ + gettext \ + locales \ + libkrb5-dev \ + libldap2-dev \ + libsasl2-dev \ + libcairo2-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + uuid-dev \ + libavcodec-dev \ + libavformat-dev \ + libavutil-dev \ + libswscale-dev \ + freerdp3-dev \ + libwinpr3-dev \ + libpango1.0-dev \ + libssh2-1-dev \ + libtelnet-dev \ + libvncserver-dev \ + libwebsockets-dev \ + libpulse-dev \ + libssl-dev \ + libvorbis-dev \ + libwebp-dev \ + redis-server \ + nginx +msg_ok "Installed Dependencies" + +PG_VERSION="16" setup_postgresql +PG_DB_NAME="jumpserver" PG_DB_USER="jumpserver" setup_postgresql_db + +msg_info "Starting Redis" +systemctl enable -q --now redis-server +msg_ok "Started Redis" + +PYTHON_VERSION="3.14" setup_uv + +fetch_and_deploy_from_url "https://dlcdn.apache.org/guacamole/1.5.5/source/guacamole-server-1.5.5.tar.gz" "/opt/guacamole-server" + +msg_info "Building guacd" +cd /opt/guacamole-server +find . -exec touch -t 202401010000 {} + +touch -t 202401010001 configure.ac aclocal.m4 config.h.in +touch -t 202401010002 configure +find . -name "Makefile.in" -exec touch -t 202401010003 {} + +$STD ./configure --with-systemd-dir=/etc/systemd/system --disable-guacenc +$STD make -j"$(nproc)" +$STD make install +$STD ldconfig +systemctl enable -q --now guacd +msg_ok "Built guacd" + +fetch_and_deploy_gh_release "jumpserver" "jumpserver/jumpserver" "tarball" "latest" "" "" "v4." + +JMS_BOOTSTRAP_TOKEN=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32) + +msg_info "Configuring JumpServer Core" +cat </opt/jumpserver/config.yml +SECRET_KEY: $(openssl rand -base64 64 | tr -dc 'a-zA-Z0-9' | cut -c1-49) +BOOTSTRAP_TOKEN: ${JMS_BOOTSTRAP_TOKEN} +DB_ENGINE: postgresql +DB_HOST: 127.0.0.1 +DB_PORT: 5432 +DB_USER: jumpserver +DB_PASSWORD: ${PG_DB_PASS} +DB_NAME: jumpserver +REDIS_HOST: 127.0.0.1 +REDIS_PORT: 6379 +HTTP_BIND_HOST: 0.0.0.0 +HTTP_LISTEN_PORT: 8080 +WS_LISTEN_PORT: 8070 +LOG_LEVEL: ERROR +EOF +cd /opt/jumpserver +$STD bash requirements/static_files.sh +$STD uv venv --python 3.14 +$STD uv pip install -r pyproject.toml +JMS_GALAXY_RETRIES=3 +until ANSIBLE_COLLECTIONS_PATHS="/opt/jumpserver/.venv/lib/python3.14/site-packages/ansible_collections" $STD .venv/bin/ansible-galaxy collection install -r requirements/collections.yml --force --ignore-certs; do + JMS_GALAXY_RETRIES=$((JMS_GALAXY_RETRIES - 1)) + if ((JMS_GALAXY_RETRIES <= 0)); then + msg_error "ansible-galaxy collection install failed after 3 attempts (Ansible Galaxy may be unreachable)" + exit 1 + fi + sleep 15 +done +msg_ok "Configured JumpServer Core" + +msg_info "Creating Core Service" +cat </etc/systemd/system/jumpserver.service +[Unit] +Description=JumpServer Core Service +After=network.target postgresql.service redis-server.service + +[Service] +Type=simple +WorkingDirectory=/opt/jumpserver +Environment=ANSIBLE_COLLECTIONS_PATHS=/opt/jumpserver/.venv/lib/python3.14/site-packages/ansible_collections +Environment=PATH=/opt/jumpserver/.venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +ExecStart=/opt/jumpserver/.venv/bin/python3 /opt/jumpserver/jms start all +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now jumpserver +msg_ok "Created Core Service" + +msg_info "Securing Admin Account" +for _ in $(seq 1 90); do + curl -fsS -o /dev/null http://127.0.0.1:8080/api/health/ 2>/dev/null && break + sleep 5 +done +JMS_ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-16) +PYTHONPATH=/opt/jumpserver/apps DJANGO_SETTINGS_MODULE=jumpserver.settings $STD .venv/bin/python3 -c " +import django +django.setup() +from users.models import User +u = User.objects.get(username='admin') +u.set_password('${JMS_ADMIN_PASS}') +u.need_update_password = False +u.save() +" +cat <~/jumpserver.creds +JumpServer Admin Credentials +Username: admin +Password: ${JMS_ADMIN_PASS} +EOF +msg_ok "Secured Admin Account" + +fetch_and_deploy_gh_release "koko" "jumpserver/koko" "prebuild" "latest" "/opt/koko" "koko-v4.*-linux-$(arch_resolve amd64 arm64).tar.gz" "v4." + +msg_info "Configuring KoKo" +cat </opt/koko/config.yml +CORE_HOST: http://127.0.0.1:8080 +BOOTSTRAP_TOKEN: ${JMS_BOOTSTRAP_TOKEN} +REDIS_HOST: 127.0.0.1 +REDIS_PORT: 6379 +EOF +cat </etc/systemd/system/koko.service +[Unit] +Description=JumpServer KoKo Service +After=network.target jumpserver.service + +[Service] +Type=simple +WorkingDirectory=/opt/koko +ExecStart=/opt/koko/koko +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now koko +msg_ok "Configured KoKo" + +fetch_and_deploy_gh_release "lion" "jumpserver/lion-release" "prebuild" "latest" "/opt/lion" "lion-v*-linux-$(arch_resolve amd64 arm64).tar.gz" + +msg_info "Configuring Lion" +cat </opt/lion/config.yml +CORE_HOST: http://127.0.0.1:8080 +BOOTSTRAP_TOKEN: ${JMS_BOOTSTRAP_TOKEN} +GUA_HOST: 127.0.0.1 +GUA_PORT: 4822 +REDIS_HOST: 127.0.0.1 +REDIS_PORT: 6379 +EOF +cat </etc/systemd/system/lion.service +[Unit] +Description=JumpServer Lion Service +After=network.target guacd.service jumpserver.service + +[Service] +Type=simple +WorkingDirectory=/opt/lion +ExecStart=/opt/lion/lion +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now lion +msg_ok "Configured Lion" + +fetch_and_deploy_gh_release "lina" "jumpserver/lina" "prebuild" "latest" "/opt/lina" "lina-v4.*.tar.gz" "v4." +fetch_and_deploy_gh_release "luna" "jumpserver/luna" "prebuild" "latest" "/opt/luna" "luna-v4.*.tar.gz" "v4." + +msg_info "Configuring Nginx" +rm -f /etc/nginx/sites-enabled/default +cat </etc/nginx/sites-available/jumpserver +server { + listen 80 default_server; + client_max_body_size 4096m; + + location / { + rewrite ^/(.*)\$ /ui/\$1 last; + } + + location /ui/ { + try_files \$uri / /index.html; + alias /opt/lina/; + } + + location /luna/ { + try_files \$uri / /index.html; + alias /opt/luna/; + } + + location /static/ { + root /opt/jumpserver/data/; + } + + location /private-media/ { + internal; + alias /opt/jumpserver/data/media/; + } + + location /ws/ { + proxy_pass http://127.0.0.1:8080; + proxy_buffering off; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host \$host; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + } + + location ~ ^/(core|api|media)/ { + proxy_pass http://127.0.0.1:8080; + proxy_set_header Host \$http_host; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_ignore_client_abort on; + proxy_connect_timeout 600; + proxy_send_timeout 600; + proxy_read_timeout 600; + send_timeout 6000; + } + + location /koko/ { + proxy_pass http://127.0.0.1:5000; + proxy_buffering off; + proxy_http_version 1.1; + proxy_request_buffering off; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host \$host; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_ignore_client_abort on; + proxy_connect_timeout 600; + proxy_send_timeout 600; + proxy_read_timeout 600; + send_timeout 6000; + } + + location /lion/ { + proxy_pass http://127.0.0.1:8081; + proxy_buffering off; + proxy_http_version 1.1; + proxy_request_buffering off; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host \$host; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_ignore_client_abort on; + proxy_connect_timeout 600; + proxy_send_timeout 600; + proxy_read_timeout 600; + send_timeout 6000; + } +} +EOF +ln -sf /etc/nginx/sites-available/jumpserver /etc/nginx/sites-enabled/jumpserver +systemctl enable -q nginx +systemctl restart nginx +msg_ok "Configured Nginx" + +motd_ssh +customize +cleanup_lxc diff --git a/json/jumpserver.json b/json/jumpserver.json new file mode 100644 index 0000000000..3f945abd45 --- /dev/null +++ b/json/jumpserver.json @@ -0,0 +1,51 @@ +{ + "name": "JumpServer", + "slug": "jumpserver", + "categories": [6], + "date_created": "2026-08-25", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 80, + "documentation": "https://docs.jumpserver.org/", + "website": "https://www.jumpserver.org/", + "repository": "https://github.com/jumpserver/jumpserver", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/jumpserver.webp", + "description": "JumpServer is an open-source PAM (Privileged Access Management) / bastion host that provides unified, audited access to SSH, RDP, VNC, Telnet, Kubernetes and database assets through a single web console, with full session recording and playback.", + "install_methods": [ + { + "type": "default", + "script": "ct/jumpserver.sh", + "config_path": "/opt/jumpserver/config.yml", + "resources": { + "cpu": 4, + "ram": 8192, + "hdd": 50, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": "admin", + "password": null + }, + "notes": [ + { + "text": "The generated admin password is saved to ~/jumpserver.creds inside the container. This script sets it directly (bypassing upstream's own admin/ChangeMe default) because logging in with that default currently crashes with a server error on JumpServer v4.10.19 — its own weak-password check raises an exception the API can't render.", + "type": "info" + }, + { + "text": "The SSH bastion (character-protocol) endpoint is on port 2222, e.g. ssh -p 2222 @ — this is separate from the web console on port 80.", + "type": "info" + }, + { + "text": "First start takes several minutes: the core service runs database migrations, downloads a GeoIP database, and installs built-in RDP applets on its own before the web UI becomes reachable.", + "type": "info" + }, + { + "text": "This is a large, multi-service stack (core, KoKo, Lion + guacd, PostgreSQL, Redis, Nginx) — the default 4 vCPU / 8GB RAM / 50GB disk sizing reflects that, not a typical single-service app.", + "type": "info" + } + ] +} From ada92a2437e05e2d6ce2d2b360a24d0f23bd0d1c Mon Sep 17 00:00:00 2001 From: Nicolas Pastorello Date: Tue, 13 Oct 2026 14:44:24 -0300 Subject: [PATCH 2/6] feat(jumpserver): move to v5, building KoKo, Lina and Luna from source JumpServer v5 only ships Docker images: KoKo, Lina and Luna no longer publish prebuilt release assets, Lion is gone (KoKo now drives guacd itself) and the Luna release is the desktop client. Build the three from source at the core's tag, with Go, Zig/Ghostty, usql and k8s-bundle versions read from KoKo's own build files. Review follow-ups: guacd comes from the latest apache/guacamole-server tag, the bootstrap token is generated inside the core config step, nginx_enable_site replaces the hand-rolled enable/restart, and the sizing note is dropped from the JSON. --- ct/jumpserver.sh | 99 +++++++++++++++--- install/jumpserver-install.sh | 185 ++++++++++++++++++---------------- json/jumpserver.json | 10 +- 3 files changed, 192 insertions(+), 102 deletions(-) diff --git a/ct/jumpserver.sh b/ct/jumpserver.sh index 5b4c39173c..91aa198efc 100644 --- a/ct/jumpserver.sh +++ b/ct/jumpserver.sh @@ -15,7 +15,7 @@ var_disk="${var_disk:-50}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" -#var_arm64="${var_arm64:-no}" # unset = ask the user; koko/lion ship arm64 binaries and guacd builds fine on arm64, but the combined stack has not actually been run on arm64 +#var_arm64="${var_arm64:-no}" # unset = ask the user; every component builds from source with arm64-capable toolchains, but the stack has only been run on amd64 header_info "$APP" variables @@ -32,20 +32,34 @@ function update_script() { exit fi - if check_for_gh_release "jumpserver" "jumpserver/jumpserver" "" "" "v4."; then + if check_for_gh_tag "guacd" "apache/guacamole-server"; then + msg_info "Stopping guacd" + systemctl stop guacd + msg_ok "Stopped guacd" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_tag "guacd" "apache/guacamole-server" + + msg_info "Building guacd" + cd /opt/guacd + $STD autoreconf -fi + $STD ./configure --with-systemd-dir=/etc/systemd/system --disable-guacenc + $STD make -j"$(nproc)" + $STD make install + $STD ldconfig + systemctl daemon-reload + systemctl start guacd + msg_ok "Built guacd" + fi + + if check_for_gh_release "jumpserver" "jumpserver/jumpserver" "" "" "v5."; then msg_info "Stopping Services" - systemctl stop jumpserver koko lion + systemctl stop jumpserver koko msg_ok "Stopped Services" - create_backup /opt/jumpserver/config.yml /opt/jumpserver/data/media /opt/koko/config.yml /opt/lion/config.yml - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "jumpserver" "jumpserver/jumpserver" "tarball" "latest" "" "" "v4." - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "koko" "jumpserver/koko" "prebuild" "latest" "/opt/koko" "koko-v4.*-linux-$(arch_resolve amd64 arm64).tar.gz" "v4." - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "lion" "jumpserver/lion-release" "prebuild" "latest" "/opt/lion" "lion-v*-linux-$(arch_resolve amd64 arm64).tar.gz" - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "lina" "jumpserver/lina" "prebuild" "latest" "/opt/lina" "lina-v4.*.tar.gz" "v4." - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "luna" "jumpserver/luna" "prebuild" "latest" "/opt/luna" "luna-v4.*.tar.gz" "v4." + create_backup /opt/jumpserver/config.yml /opt/jumpserver/data /opt/koko/config.yml /opt/koko/data - restore_backup + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "jumpserver" "jumpserver/jumpserver" "tarball" "latest" "" "" "v5." + JMS_TAG="v$(<~/.jumpserver)" msg_info "Updating Python Environment" cd /opt/jumpserver @@ -61,10 +75,71 @@ function update_script() { fi sleep 15 done + cd /opt/jumpserver/apps + $STD ../.venv/bin/python3 manage.py compilemessages msg_ok "Updated Python Environment" + CLEAN_INSTALL=1 fetch_and_deploy_gh_tag "koko" "jumpserver/koko" "$JMS_TAG" + + GHOSTTY_COMMIT=$(grep -oP 'libghostty-vt:ghostty-\K[0-9a-f]+' /opt/koko/Dockerfile-base) + ZIG_VERSION=$(grep -oP 'libghostty-vt:ghostty-[0-9a-f]+-zig-\K[0-9.]+' /opt/koko/Dockerfile-base) + USQL_VERSION=$(grep -oP 'ARG USQL_VERSION=\K\S+' /opt/koko/Dockerfile-base) + K8S_BUNDLE_VERSION=$(grep -oP 'ARG K8S_BUNDLE_VERSION=\K\S+' /opt/koko/Dockerfile-base) + GO_VERSION=$(grep -oP '^go \K\S+' /opt/koko/go.mod) setup_go + + if [[ "$(cat ~/.libghostty-vt 2>/dev/null)" != "$GHOSTTY_COMMIT" ]]; then + fetch_and_deploy_from_url "https://ziglang.org/download/${ZIG_VERSION}/zig-$(arch_resolve x86_64 aarch64)-linux-${ZIG_VERSION}.tar.xz" "/opt/zig" + fetch_and_deploy_from_url "https://github.com/ghostty-org/ghostty/archive/${GHOSTTY_COMMIT}.tar.gz" "/opt/ghostty" + + msg_info "Building libghostty-vt" + rm -rf /opt/libghostty-vt + cd /opt/ghostty + $STD /opt/zig/zig build -Demit-lib-vt -Doptimize=ReleaseFast -Dcpu=baseline --prefix /opt/libghostty-vt + rm -rf /opt/ghostty /opt/zig ~/.cache/zig + echo "$GHOSTTY_COMMIT" >~/.libghostty-vt + msg_ok "Built libghostty-vt" + fi + + msg_info "Building KoKo" + cd /opt/koko + export PKG_CONFIG_PATH=/opt/libghostty-vt/share/pkgconfig + $STD make build VERSION="$JMS_TAG" + mv build/koko koko + rm -rf build + msg_ok "Built KoKo" + + fetch_and_deploy_gh_release "usql" "jumpserver-dev/usql" "prebuild" "$USQL_VERSION" "/opt/usql" "usql-*-linux-$(arch_resolve amd64 arm64).tar.gz" + fetch_and_deploy_gh_release "k8s-bundle" "jumpserver-dev/k8s-bundle" "binary" "$K8S_BUNDLE_VERSION" "" "k8s-bundle_$(arch_resolve amd64 arm64).deb" + + restore_backup + + NODE_VERSION="24" NODE_MODULE="corepack" setup_nodejs + + CLEAN_INSTALL=1 fetch_and_deploy_gh_tag "lina" "jumpserver/lina" "$JMS_TAG" "/opt/lina-src" + + msg_info "Building Lina" + cd /opt/lina-src + $STD yarn install --immutable + $STD yarn build:prod + rm -rf /opt/lina + mv lina /opt/lina + rm -rf /opt/lina-src + msg_ok "Built Lina" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_tag "luna" "jumpserver/luna" "$JMS_TAG" "/opt/luna-src" + + msg_info "Building Luna" + cd /opt/luna-src + export ELECTRON_SKIP_BINARY_DOWNLOAD=1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 HUSKY=0 + $STD pnpm --filter jumpserver-web... install --frozen-lockfile --config.node-linker=isolated + $STD pnpm --config.node-linker=isolated --filter jumpserver-web web:generate + rm -rf /opt/luna + mv .output/public /opt/luna + rm -rf /opt/luna-src + msg_ok "Built Luna" + msg_info "Starting Services" - systemctl start jumpserver koko lion + systemctl start jumpserver koko msg_ok "Started Services" msg_ok "Updated successfully!" fi diff --git a/install/jumpserver-install.sh b/install/jumpserver-install.sh index 0b165b9bd3..e89aace484 100644 --- a/install/jumpserver-install.sh +++ b/install/jumpserver-install.sh @@ -16,7 +16,11 @@ update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ + autoconf \ + automake \ + libtool \ pkg-config \ + xz-utils \ default-libmysqlclient-dev \ freetds-dev \ gettext \ @@ -24,6 +28,10 @@ $STD apt install -y \ libkrb5-dev \ libldap2-dev \ libsasl2-dev \ + libx11-dev \ + openssh-client \ + sshpass \ + bubblewrap \ libcairo2-dev \ libjpeg62-turbo-dev \ libpng-dev \ @@ -55,15 +63,13 @@ systemctl enable -q --now redis-server msg_ok "Started Redis" PYTHON_VERSION="3.14" setup_uv +NODE_VERSION="24" NODE_MODULE="corepack" setup_nodejs -fetch_and_deploy_from_url "https://dlcdn.apache.org/guacamole/1.5.5/source/guacamole-server-1.5.5.tar.gz" "/opt/guacamole-server" +fetch_and_deploy_gh_tag "guacd" "apache/guacamole-server" msg_info "Building guacd" -cd /opt/guacamole-server -find . -exec touch -t 202401010000 {} + -touch -t 202401010001 configure.ac aclocal.m4 config.h.in -touch -t 202401010002 configure -find . -name "Makefile.in" -exec touch -t 202401010003 {} + +cd /opt/guacd +$STD autoreconf -fi $STD ./configure --with-systemd-dir=/etc/systemd/system --disable-guacenc $STD make -j"$(nproc)" $STD make install @@ -71,11 +77,12 @@ $STD ldconfig systemctl enable -q --now guacd msg_ok "Built guacd" -fetch_and_deploy_gh_release "jumpserver" "jumpserver/jumpserver" "tarball" "latest" "" "" "v4." - -JMS_BOOTSTRAP_TOKEN=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32) +fetch_and_deploy_gh_release "jumpserver" "jumpserver/jumpserver" "tarball" "latest" "" "" "v5." +# KoKo, Lina and Luna must match the core release exactly +JMS_TAG="v$(<~/.jumpserver)" msg_info "Configuring JumpServer Core" +JMS_BOOTSTRAP_TOKEN=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32) cat </opt/jumpserver/config.yml SECRET_KEY: $(openssl rand -base64 64 | tr -dc 'a-zA-Z0-9' | cut -c1-49) BOOTSTRAP_TOKEN: ${JMS_BOOTSTRAP_TOKEN} @@ -105,6 +112,8 @@ until ANSIBLE_COLLECTIONS_PATHS="/opt/jumpserver/.venv/lib/python3.14/site-packa fi sleep 15 done +cd /opt/jumpserver/apps +$STD ../.venv/bin/python3 manage.py compilemessages msg_ok "Configured JumpServer Core" msg_info "Creating Core Service" @@ -128,29 +137,36 @@ EOF systemctl enable -q --now jumpserver msg_ok "Created Core Service" -msg_info "Securing Admin Account" -for _ in $(seq 1 90); do - curl -fsS -o /dev/null http://127.0.0.1:8080/api/health/ 2>/dev/null && break - sleep 5 -done -JMS_ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-16) -PYTHONPATH=/opt/jumpserver/apps DJANGO_SETTINGS_MODULE=jumpserver.settings $STD .venv/bin/python3 -c " -import django -django.setup() -from users.models import User -u = User.objects.get(username='admin') -u.set_password('${JMS_ADMIN_PASS}') -u.need_update_password = False -u.save() -" -cat <~/jumpserver.creds -JumpServer Admin Credentials -Username: admin -Password: ${JMS_ADMIN_PASS} -EOF -msg_ok "Secured Admin Account" - -fetch_and_deploy_gh_release "koko" "jumpserver/koko" "prebuild" "latest" "/opt/koko" "koko-v4.*-linux-$(arch_resolve amd64 arm64).tar.gz" "v4." +fetch_and_deploy_gh_tag "koko" "jumpserver/koko" "$JMS_TAG" + +# Toolchain pins come from KoKo's own build files so they follow each release +GHOSTTY_COMMIT=$(grep -oP 'libghostty-vt:ghostty-\K[0-9a-f]+' /opt/koko/Dockerfile-base) +ZIG_VERSION=$(grep -oP 'libghostty-vt:ghostty-[0-9a-f]+-zig-\K[0-9.]+' /opt/koko/Dockerfile-base) +USQL_VERSION=$(grep -oP 'ARG USQL_VERSION=\K\S+' /opt/koko/Dockerfile-base) +K8S_BUNDLE_VERSION=$(grep -oP 'ARG K8S_BUNDLE_VERSION=\K\S+' /opt/koko/Dockerfile-base) +GO_VERSION=$(grep -oP '^go \K\S+' /opt/koko/go.mod) setup_go + +fetch_and_deploy_from_url "https://ziglang.org/download/${ZIG_VERSION}/zig-$(arch_resolve x86_64 aarch64)-linux-${ZIG_VERSION}.tar.xz" "/opt/zig" +fetch_and_deploy_from_url "https://github.com/ghostty-org/ghostty/archive/${GHOSTTY_COMMIT}.tar.gz" "/opt/ghostty" + +msg_info "Building libghostty-vt" +cd /opt/ghostty +$STD /opt/zig/zig build -Demit-lib-vt -Doptimize=ReleaseFast -Dcpu=baseline --prefix /opt/libghostty-vt +rm -rf /opt/ghostty /opt/zig ~/.cache/zig +echo "$GHOSTTY_COMMIT" >~/.libghostty-vt +msg_ok "Built libghostty-vt" + +msg_info "Building KoKo" +cd /opt/koko +export PKG_CONFIG_PATH=/opt/libghostty-vt/share/pkgconfig +$STD make build VERSION="$JMS_TAG" +mv build/koko koko +rm -rf build +msg_ok "Built KoKo" + +fetch_and_deploy_gh_release "usql" "jumpserver-dev/usql" "prebuild" "$USQL_VERSION" "/opt/usql" "usql-*-linux-$(arch_resolve amd64 arm64).tar.gz" +ln -sf /opt/usql/usql /usr/local/bin/usql +fetch_and_deploy_gh_release "k8s-bundle" "jumpserver-dev/k8s-bundle" "binary" "$K8S_BUNDLE_VERSION" "" "k8s-bundle_$(arch_resolve amd64 arm64).deb" msg_info "Configuring KoKo" cat </opt/koko/config.yml @@ -158,15 +174,19 @@ CORE_HOST: http://127.0.0.1:8080 BOOTSTRAP_TOKEN: ${JMS_BOOTSTRAP_TOKEN} REDIS_HOST: 127.0.0.1 REDIS_PORT: 6379 +GUA_HOST: 127.0.0.1 +GUA_PORT: 4822 +WEB_PROXY_RECORDING_ENABLED: false EOF cat </etc/systemd/system/koko.service [Unit] Description=JumpServer KoKo Service -After=network.target jumpserver.service +After=network.target guacd.service jumpserver.service [Service] Type=simple WorkingDirectory=/opt/koko +Environment=LD_LIBRARY_PATH=/opt/libghostty-vt/lib ExecStart=/opt/koko/koko Restart=on-failure RestartSec=5 @@ -177,40 +197,53 @@ EOF systemctl enable -q --now koko msg_ok "Configured KoKo" -fetch_and_deploy_gh_release "lion" "jumpserver/lion-release" "prebuild" "latest" "/opt/lion" "lion-v*-linux-$(arch_resolve amd64 arm64).tar.gz" - -msg_info "Configuring Lion" -cat </opt/lion/config.yml -CORE_HOST: http://127.0.0.1:8080 -BOOTSTRAP_TOKEN: ${JMS_BOOTSTRAP_TOKEN} -GUA_HOST: 127.0.0.1 -GUA_PORT: 4822 -REDIS_HOST: 127.0.0.1 -REDIS_PORT: 6379 -EOF -cat </etc/systemd/system/lion.service -[Unit] -Description=JumpServer Lion Service -After=network.target guacd.service jumpserver.service - -[Service] -Type=simple -WorkingDirectory=/opt/lion -ExecStart=/opt/lion/lion -Restart=on-failure -RestartSec=5 +fetch_and_deploy_gh_tag "lina" "jumpserver/lina" "$JMS_TAG" "/opt/lina-src" + +msg_info "Building Lina" +cd /opt/lina-src +$STD yarn install --immutable +$STD yarn build:prod +rm -rf /opt/lina +mv lina /opt/lina +rm -rf /opt/lina-src +msg_ok "Built Lina" + +fetch_and_deploy_gh_tag "luna" "jumpserver/luna" "$JMS_TAG" "/opt/luna-src" + +msg_info "Building Luna" +cd /opt/luna-src +export ELECTRON_SKIP_BINARY_DOWNLOAD=1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 HUSKY=0 +$STD pnpm --filter jumpserver-web... install --frozen-lockfile --config.node-linker=isolated +$STD pnpm --config.node-linker=isolated --filter jumpserver-web web:generate +rm -rf /opt/luna +mv .output/public /opt/luna +rm -rf /opt/luna-src +msg_ok "Built Luna" -[Install] -WantedBy=multi-user.target +msg_info "Securing Admin Account" +for _ in $(seq 1 90); do + curl -fsS -o /dev/null http://127.0.0.1:8080/api/health/ 2>/dev/null && break + sleep 5 +done +JMS_ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-16) +cd /opt/jumpserver +PYTHONPATH=/opt/jumpserver/apps DJANGO_SETTINGS_MODULE=jumpserver.settings $STD .venv/bin/python3 -c " +import django +django.setup() +from users.models import User +u = User.objects.get(username='admin') +u.set_password('${JMS_ADMIN_PASS}') +u.need_update_password = False +u.save() +" +cat <~/jumpserver.creds +JumpServer Admin Credentials +Username: admin +Password: ${JMS_ADMIN_PASS} EOF -systemctl enable -q --now lion -msg_ok "Configured Lion" - -fetch_and_deploy_gh_release "lina" "jumpserver/lina" "prebuild" "latest" "/opt/lina" "lina-v4.*.tar.gz" "v4." -fetch_and_deploy_gh_release "luna" "jumpserver/luna" "prebuild" "latest" "/opt/luna" "luna-v4.*.tar.gz" "v4." +msg_ok "Secured Admin Account" msg_info "Configuring Nginx" -rm -f /etc/nginx/sites-enabled/default cat </etc/nginx/sites-available/jumpserver server { listen 80 default_server; @@ -246,13 +279,13 @@ server { proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host \$host; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-For \$remote_addr; } location ~ ^/(core|api|media)/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host \$http_host; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-For \$remote_addr; proxy_ignore_client_abort on; proxy_connect_timeout 600; proxy_send_timeout 600; @@ -268,23 +301,7 @@ server { proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host \$host; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; - proxy_ignore_client_abort on; - proxy_connect_timeout 600; - proxy_send_timeout 600; - proxy_read_timeout 600; - send_timeout 6000; - } - - location /lion/ { - proxy_pass http://127.0.0.1:8081; - proxy_buffering off; - proxy_http_version 1.1; - proxy_request_buffering off; - proxy_set_header Upgrade \$http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host \$host; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-For \$remote_addr; proxy_ignore_client_abort on; proxy_connect_timeout 600; proxy_send_timeout 600; @@ -293,9 +310,7 @@ server { } } EOF -ln -sf /etc/nginx/sites-available/jumpserver /etc/nginx/sites-enabled/jumpserver -systemctl enable -q nginx -systemctl restart nginx +nginx_enable_site "jumpserver" msg_ok "Configured Nginx" motd_ssh diff --git a/json/jumpserver.json b/json/jumpserver.json index 3f945abd45..708e9138c4 100644 --- a/json/jumpserver.json +++ b/json/jumpserver.json @@ -32,19 +32,19 @@ }, "notes": [ { - "text": "The generated admin password is saved to ~/jumpserver.creds inside the container. This script sets it directly (bypassing upstream's own admin/ChangeMe default) because logging in with that default currently crashes with a server error on JumpServer v4.10.19 — its own weak-password check raises an exception the API can't render.", + "text": "The generated admin password is saved to ~/jumpserver.creds inside the container.", "type": "info" }, { - "text": "The SSH bastion (character-protocol) endpoint is on port 2222, e.g. ssh -p 2222 @ — this is separate from the web console on port 80.", - "type": "info" + "text": "Upstream only ships JumpServer v5 as Docker images, so this script builds KoKo, Lina and Luna from source at the same tag as the core. Installs and updates take a long time and need the default resources.", + "type": "warning" }, { - "text": "First start takes several minutes: the core service runs database migrations, downloads a GeoIP database, and installs built-in RDP applets on its own before the web UI becomes reachable.", + "text": "The SSH bastion (character-protocol) endpoint is on port 2222, e.g. ssh -p 2222 @ — this is separate from the web console on port 80.", "type": "info" }, { - "text": "This is a large, multi-service stack (core, KoKo, Lion + guacd, PostgreSQL, Redis, Nginx) — the default 4 vCPU / 8GB RAM / 50GB disk sizing reflects that, not a typical single-service app.", + "text": "First start takes several minutes: the core service runs database migrations, downloads a GeoIP database, and installs built-in RDP applets on its own before the web UI becomes reachable.", "type": "info" } ] From ddd0d82c3c6fb85b006c2c7c955eaee302f0f007 Mon Sep 17 00:00:00 2001 From: Nicolas Pastorello Date: Tue, 13 Oct 2026 15:08:43 -0300 Subject: [PATCH 3/6] fix(jumpserver): issues found running the v5 install - guacd 1.6.0 fails to build against Debian 13's FreeRDP 3 under -Werror for deprecated APIs; allow those warnings. - guacd binds ::1 by default while KoKo dials 127.0.0.1; pin it to IPv4. - The core only accepts terminal registration for 5 minutes after it starts, so start the core and KoKo together after all builds finish. - KoKo links libghostty-vt statically; drop the LD_LIBRARY_PATH. - Append the admin credentials instead of overwriting the database ones. - Restore config and data before the update rebuilds anything. --- ct/jumpserver.sh | 10 +++++----- install/jumpserver-install.sh | 26 ++++++++++++++++++++------ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/ct/jumpserver.sh b/ct/jumpserver.sh index 91aa198efc..6b0c6963ce 100644 --- a/ct/jumpserver.sh +++ b/ct/jumpserver.sh @@ -42,7 +42,8 @@ function update_script() { msg_info "Building guacd" cd /opt/guacd $STD autoreconf -fi - $STD ./configure --with-systemd-dir=/etc/systemd/system --disable-guacenc + # FreeRDP 3.6+ deprecates APIs guacd still calls, which -Werror turns into build failures + $STD ./configure --with-systemd-dir=/etc/systemd/system --disable-guacenc CFLAGS="-O2 -Wno-error=deprecated-declarations" $STD make -j"$(nproc)" $STD make install $STD ldconfig @@ -60,6 +61,9 @@ function update_script() { CLEAN_INSTALL=1 fetch_and_deploy_gh_release "jumpserver" "jumpserver/jumpserver" "tarball" "latest" "" "" "v5." JMS_TAG="v$(<~/.jumpserver)" + CLEAN_INSTALL=1 fetch_and_deploy_gh_tag "koko" "jumpserver/koko" "$JMS_TAG" + + restore_backup msg_info "Updating Python Environment" cd /opt/jumpserver @@ -79,8 +83,6 @@ function update_script() { $STD ../.venv/bin/python3 manage.py compilemessages msg_ok "Updated Python Environment" - CLEAN_INSTALL=1 fetch_and_deploy_gh_tag "koko" "jumpserver/koko" "$JMS_TAG" - GHOSTTY_COMMIT=$(grep -oP 'libghostty-vt:ghostty-\K[0-9a-f]+' /opt/koko/Dockerfile-base) ZIG_VERSION=$(grep -oP 'libghostty-vt:ghostty-[0-9a-f]+-zig-\K[0-9.]+' /opt/koko/Dockerfile-base) USQL_VERSION=$(grep -oP 'ARG USQL_VERSION=\K\S+' /opt/koko/Dockerfile-base) @@ -111,8 +113,6 @@ function update_script() { fetch_and_deploy_gh_release "usql" "jumpserver-dev/usql" "prebuild" "$USQL_VERSION" "/opt/usql" "usql-*-linux-$(arch_resolve amd64 arm64).tar.gz" fetch_and_deploy_gh_release "k8s-bundle" "jumpserver-dev/k8s-bundle" "binary" "$K8S_BUNDLE_VERSION" "" "k8s-bundle_$(arch_resolve amd64 arm64).deb" - restore_backup - NODE_VERSION="24" NODE_MODULE="corepack" setup_nodejs CLEAN_INSTALL=1 fetch_and_deploy_gh_tag "lina" "jumpserver/lina" "$JMS_TAG" "/opt/lina-src" diff --git a/install/jumpserver-install.sh b/install/jumpserver-install.sh index e89aace484..62f161ac16 100644 --- a/install/jumpserver-install.sh +++ b/install/jumpserver-install.sh @@ -70,10 +70,18 @@ fetch_and_deploy_gh_tag "guacd" "apache/guacamole-server" msg_info "Building guacd" cd /opt/guacd $STD autoreconf -fi -$STD ./configure --with-systemd-dir=/etc/systemd/system --disable-guacenc +# FreeRDP 3.6+ deprecates APIs guacd still calls, which -Werror turns into build failures +$STD ./configure --with-systemd-dir=/etc/systemd/system --disable-guacenc CFLAGS="-O2 -Wno-error=deprecated-declarations" $STD make -j"$(nproc)" $STD make install $STD ldconfig +# guacd binds "localhost" (::1 here) by default, but KoKo dials 127.0.0.1 +mkdir -p /etc/guacamole +cat </etc/guacamole/guacd.conf +[server] +bind_host = 127.0.0.1 +bind_port = 4822 +EOF systemctl enable -q --now guacd msg_ok "Built guacd" @@ -134,7 +142,7 @@ RestartSec=5 [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now jumpserver +systemctl enable -q jumpserver msg_ok "Created Core Service" fetch_and_deploy_gh_tag "koko" "jumpserver/koko" "$JMS_TAG" @@ -186,7 +194,6 @@ After=network.target guacd.service jumpserver.service [Service] Type=simple WorkingDirectory=/opt/koko -Environment=LD_LIBRARY_PATH=/opt/libghostty-vt/lib ExecStart=/opt/koko/koko Restart=on-failure RestartSec=5 @@ -194,7 +201,7 @@ RestartSec=5 [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now koko +systemctl enable -q koko msg_ok "Configured KoKo" fetch_and_deploy_gh_tag "lina" "jumpserver/lina" "$JMS_TAG" "/opt/lina-src" @@ -220,11 +227,18 @@ mv .output/public /opt/luna rm -rf /opt/luna-src msg_ok "Built Luna" -msg_info "Securing Admin Account" +msg_info "Starting Services" +# The core only accepts terminal registration for 5 minutes after it starts, +# so KoKo has to come up right behind it instead of after the long builds +systemctl start jumpserver for _ in $(seq 1 90); do curl -fsS -o /dev/null http://127.0.0.1:8080/api/health/ 2>/dev/null && break sleep 5 done +systemctl start koko +msg_ok "Started Services" + +msg_info "Securing Admin Account" JMS_ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-16) cd /opt/jumpserver PYTHONPATH=/opt/jumpserver/apps DJANGO_SETTINGS_MODULE=jumpserver.settings $STD .venv/bin/python3 -c " @@ -236,7 +250,7 @@ u.set_password('${JMS_ADMIN_PASS}') u.need_update_password = False u.save() " -cat <~/jumpserver.creds +cat <>~/jumpserver.creds JumpServer Admin Credentials Username: admin Password: ${JMS_ADMIN_PASS} From b4bd95248b97d6adf4f5d92bc8fbbcaadd11fc5b Mon Sep 17 00:00:00 2001 From: Nicolas Pastorello Date: Tue, 13 Oct 2026 15:44:18 -0300 Subject: [PATCH 4/6] fix(jumpserver): keep nuxt generate from blocking on its telemetry prompt --- ct/jumpserver.sh | 3 ++- install/jumpserver-install.sh | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ct/jumpserver.sh b/ct/jumpserver.sh index 6b0c6963ce..75ad4e54a1 100644 --- a/ct/jumpserver.sh +++ b/ct/jumpserver.sh @@ -130,7 +130,8 @@ function update_script() { msg_info "Building Luna" cd /opt/luna-src - export ELECTRON_SKIP_BINARY_DOWNLOAD=1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 HUSKY=0 + # NUXT_TELEMETRY_DISABLED stops nuxt generate from waiting on a consent prompt + export ELECTRON_SKIP_BINARY_DOWNLOAD=1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 HUSKY=0 NUXT_TELEMETRY_DISABLED=1 $STD pnpm --filter jumpserver-web... install --frozen-lockfile --config.node-linker=isolated $STD pnpm --config.node-linker=isolated --filter jumpserver-web web:generate rm -rf /opt/luna diff --git a/install/jumpserver-install.sh b/install/jumpserver-install.sh index 62f161ac16..84dfe4b097 100644 --- a/install/jumpserver-install.sh +++ b/install/jumpserver-install.sh @@ -219,7 +219,8 @@ fetch_and_deploy_gh_tag "luna" "jumpserver/luna" "$JMS_TAG" "/opt/luna-src" msg_info "Building Luna" cd /opt/luna-src -export ELECTRON_SKIP_BINARY_DOWNLOAD=1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 HUSKY=0 +# NUXT_TELEMETRY_DISABLED stops nuxt generate from waiting on a consent prompt +export ELECTRON_SKIP_BINARY_DOWNLOAD=1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 HUSKY=0 NUXT_TELEMETRY_DISABLED=1 $STD pnpm --filter jumpserver-web... install --frozen-lockfile --config.node-linker=isolated $STD pnpm --config.node-linker=isolated --filter jumpserver-web web:generate rm -rf /opt/luna From d09af48a339aed8656ede7a9fdcea48837e7c7f8 Mon Sep 17 00:00:00 2001 From: Nicolas Pastorello Date: Tue, 13 Oct 2026 16:03:54 -0300 Subject: [PATCH 5/6] fix(jumpserver): generate the terminal host key before the core starts The core creates TERMINAL_HOST_KEY in a django_ready handler that runs after the one loading settings, so on first boot the service caches an empty key and KoKo exits with 'ssh: no key found'. Run upgrade_db and the admin step (which boots Django) before starting the service. --- install/jumpserver-install.sh | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/install/jumpserver-install.sh b/install/jumpserver-install.sh index 84dfe4b097..bc67e678a4 100644 --- a/install/jumpserver-install.sh +++ b/install/jumpserver-install.sh @@ -228,18 +228,14 @@ mv .output/public /opt/luna rm -rf /opt/luna-src msg_ok "Built Luna" -msg_info "Starting Services" -# The core only accepts terminal registration for 5 minutes after it starts, -# so KoKo has to come up right behind it instead of after the long builds -systemctl start jumpserver -for _ in $(seq 1 90); do - curl -fsS -o /dev/null http://127.0.0.1:8080/api/health/ 2>/dev/null && break - sleep 5 -done -systemctl start koko -msg_ok "Started Services" +msg_info "Initializing Database" +cd /opt/jumpserver +$STD .venv/bin/python3 jms upgrade_db +msg_ok "Initialized Database" msg_info "Securing Admin Account" +# Booting Django here (outside upgrade_db) also generates TERMINAL_HOST_KEY, +# so the core service loads it on start instead of caching an empty one JMS_ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-16) cd /opt/jumpserver PYTHONPATH=/opt/jumpserver/apps DJANGO_SETTINGS_MODULE=jumpserver.settings $STD .venv/bin/python3 -c " @@ -258,6 +254,17 @@ Password: ${JMS_ADMIN_PASS} EOF msg_ok "Secured Admin Account" +msg_info "Starting Services" +# The core only accepts terminal registration for 5 minutes after it starts, +# so KoKo has to come up right behind it instead of after the long builds +systemctl start jumpserver +for _ in $(seq 1 90); do + curl -fsS -o /dev/null http://127.0.0.1:8080/api/health/ 2>/dev/null && break + sleep 5 +done +systemctl start koko +msg_ok "Started Services" + msg_info "Configuring Nginx" cat </etc/nginx/sites-available/jumpserver server { From d2d73da30203db85348328946af00f2c13e71569 Mon Sep 17 00:00:00 2001 From: Nicolas Pastorello Date: Tue, 13 Oct 2026 16:21:04 -0300 Subject: [PATCH 6/6] fix(jumpserver): always refetch Lina and Luna sources on update Their source trees are removed after building, so a matching version marker would skip the fetch and leave the build step without sources. --- ct/jumpserver.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ct/jumpserver.sh b/ct/jumpserver.sh index 75ad4e54a1..c5ea85a204 100644 --- a/ct/jumpserver.sh +++ b/ct/jumpserver.sh @@ -115,6 +115,10 @@ function update_script() { NODE_VERSION="24" NODE_MODULE="corepack" setup_nodejs + # Lina and Luna sources are deleted after each build, so their version + # markers must not let the fetch be skipped + rm -f ~/.lina ~/.luna + CLEAN_INSTALL=1 fetch_and_deploy_gh_tag "lina" "jumpserver/lina" "$JMS_TAG" "/opt/lina-src" msg_info "Building Lina"