diff --git a/doc/docs/en/deployment/docker-build.md b/doc/docs/en/deployment/docker-build.md index ea2f838e..06b6c0bf 100644 --- a/doc/docs/en/deployment/docker-build.md +++ b/doc/docs/en/deployment/docker-build.md @@ -19,6 +19,10 @@ docker buildx build --progress=plain --platform linux/amd64,linux/arm64 -t ccr.c # 📚 build documentation for multiple architectures docker buildx build --progress=plain --platform linux/amd64,linux/arm64 -t nexent/nexent-docs -f make/docs/Dockerfile . --push docker buildx build --progress=plain --platform linux/amd64,linux/arm64 -t ccr.ccs.tencentyun.com/nexent-hub/nexent-docs -f make/docs/Dockerfile . --push + +# 💻 build Ubuntu Terminal for multiple architectures +docker buildx build --progress=plain --platform linux/amd64,linux/arm64 -t nexent/nexent-terminal -f make/terminal/Dockerfile . --push +docker buildx build --progress=plain --platform linux/amd64,linux/arm64 -t ccr.ccs.tencentyun.com/nexent-hub/nexent-terminal -f make/terminal/Dockerfile . --push ``` ### 💻 Local Development Build @@ -35,6 +39,9 @@ docker build --progress=plain -t nexent/nexent-web -f make/web/Dockerfile . # 📚 Build documentation image (current architecture only) docker build --progress=plain -t nexent/nexent-docs -f make/docs/Dockerfile . + +# 💻 Build OpenSSH Server image (current architecture only) +docker build --progress=plain -t nexent/nexent-ubuntu-terminal -f make/terminal/Dockerfile . ``` ### 🧹 Clean up Docker resources @@ -66,6 +73,23 @@ docker builder prune -f && docker system prune -f - Built from `make/docs/Dockerfile` - Provides project documentation and API reference +#### OpenSSH Server Image (nexent/nexent-ubuntu-terminal) +- Ubuntu 24.04-based SSH server container +- Built from `make/terminal/Dockerfile` +- Pre-installed with Conda, Python, Git and other development tools +- Supports SSH key authentication with username `linuxserver.io` +- Provides complete development environment + +##### Pre-installed Tools and Features +- **Python Environment**: Python 3 + pip + virtualenv +- **Conda Management**: Miniconda3 environment management +- **Development Tools**: Git, Vim, Nano, Curl, Wget +- **Build Tools**: build-essential, Make +- **SSH Service**: Port 2222, root login and password authentication disabled +- **User Permissions**: `linuxserver.io` user has sudo privileges (no password required) +- **Timezone Setting**: Asia/Shanghai +- **Security Configuration**: SSH key authentication, 60-minute session timeout + ### 🏷️ Tagging Strategy Each image is pushed to two repositories: @@ -77,6 +101,7 @@ All images include: - `nexent/nexent-data-process` - Data processing service - `nexent/nexent-web` - Next.js frontend application - `nexent/nexent-docs` - Vitepress documentation site +- `nexent/nexent-ubuntu-terminal` - OpenSSH development server container ## 📚 Documentation Image Standalone Deployment diff --git a/doc/docs/en/getting-started/installation.md b/doc/docs/en/getting-started/installation.md index bd6fcffc..a26c8f2b 100644 --- a/doc/docs/en/getting-started/installation.md +++ b/doc/docs/en/getting-started/installation.md @@ -107,7 +107,7 @@ The deployment includes the following components: | MinIO API | 9000 | 9010 | Object storage API | | MinIO Console | 9001 | 9011 | Storage management UI | | Redis | 6379 | 6379 | Cache service | -| SSH Server | 2222 | 2222 | Terminal tool access | +| SSH Server | 22 | 2222 | Terminal tool access | For complete port mapping details, see our [Dev Container Guide](../deployment/devcontainer.md#port-mapping). diff --git a/doc/docs/zh/deployment/docker-build.md b/doc/docs/zh/deployment/docker-build.md index 6df8eff9..8fa33da4 100644 --- a/doc/docs/zh/deployment/docker-build.md +++ b/doc/docs/zh/deployment/docker-build.md @@ -23,6 +23,10 @@ docker buildx build --progress=plain --platform linux/amd64,linux/arm64 -t ccr.c # 📚 为多个架构构建文档 docker buildx build --progress=plain --platform linux/amd64,linux/arm64 -t nexent/nexent-docs -f make/docs/Dockerfile . --push docker buildx build --progress=plain --platform linux/amd64,linux/arm64 -t ccr.ccs.tencentyun.com/nexent-hub/nexent-docs -f make/docs/Dockerfile . --push + +# 💻 为多个架构构建 Ubuntu Terminal +docker buildx build --progress=plain --platform linux/amd64,linux/arm64 -t nexent/nexent-terminal -f make/terminal/Dockerfile . --push +docker buildx build --progress=plain --platform linux/amd64,linux/arm64 -t ccr.ccs.tencentyun.com/nexent-hub/nexent-terminal -f make/terminal/Dockerfile . --push ``` ## 💻 本地开发构建 @@ -39,6 +43,9 @@ docker build --progress=plain -t nexent/nexent-web -f make/web/Dockerfile . # 📚 构建文档镜像(仅当前架构) docker build --progress=plain -t nexent/nexent-docs -f make/docs/Dockerfile . + +# 💻 构建 OpenSSH Server 镜像(仅当前架构) +docker build --progress=plain -t nexent/nexent-ubuntu-terminal -f make/terminal/Dockerfile . ``` ## 🔧 镜像说明 @@ -63,6 +70,23 @@ docker build --progress=plain -t nexent/nexent-docs -f make/docs/Dockerfile . - 基于 `make/docs/Dockerfile` 构建 - 提供项目文档和 API 参考 +### OpenSSH Server 镜像 (nexent/nexent-ubuntu-terminal) +- 基于 Ubuntu 24.04 的 SSH 服务器容器 +- 基于 `make/terminal/Dockerfile` 构建 +- 预装 Conda、Python、Git 等开发工具 +- 支持 SSH 密钥认证,用户名为 `linuxserver.io` +- 提供完整的开发环境 + +#### 预装工具和特性 +- **Python 环境**: Python 3 + pip + virtualenv +- **Conda 管理**: Miniconda3 环境管理 +- **开发工具**: Git、Vim、Nano、Curl、Wget +- **构建工具**: build-essential、Make +- **SSH 服务**: 端口 2222,禁用 root 登录和密码认证 +- **用户权限**: `linuxserver.io` 用户具有 sudo 权限(无需密码) +- **时区设置**: Asia/Shanghai +- **安全配置**: SSH 密钥认证,会话超时 60 分钟 + ## 🏷️ 标签策略 每个镜像都会推送到两个仓库: @@ -74,6 +98,7 @@ docker build --progress=plain -t nexent/nexent-docs -f make/docs/Dockerfile . - `nexent/nexent-data-process` - 数据处理服务 - `nexent/nexent-web` - Next.js 前端应用 - `nexent/nexent-docs` - Vitepress 文档站点 +- `nexent/nexent-ubuntu-terminal` - OpenSSH 开发服务器容器 ## 📚 文档镜像独立部署 diff --git a/doc/docs/zh/getting-started/installation.md b/doc/docs/zh/getting-started/installation.md index 20333ae2..f7641abe 100644 --- a/doc/docs/zh/getting-started/installation.md +++ b/doc/docs/zh/getting-started/installation.md @@ -107,7 +107,7 @@ EXA_API_KEY=your_exa_key | MinIO API | 9000 | 9010 | 对象存储 API | | MinIO 控制台 | 9001 | 9011 | 存储管理 UI | | Redis | 6379 | 6379 | 缓存服务 | -| SSH 服务器 | 2222 | 2222 | 终端工具访问 | +| SSH 服务器 | 22 | 2222 | 终端工具访问 | 有关完整的端口映射详细信息,请参阅我们的 [开发容器指南](../deployment/devcontainer.md#port-mapping)。 diff --git a/docker/.env.beta b/docker/.env.beta index cc90fd66..6d3e5747 100644 --- a/docker/.env.beta +++ b/docker/.env.beta @@ -6,4 +6,4 @@ ELASTICSEARCH_IMAGE=docker.elastic.co/elasticsearch/elasticsearch:8.17.4 POSTGRESQL_IMAGE=postgres:15-alpine REDIS_IMAGE=redis:alpine MINIO_IMAGE=quay.io/minio/minio:RELEASE.2023-12-20T01-00-02Z -OPENSSH_SERVER_IMAGE=lscr.io/linuxserver/openssh-server:latest \ No newline at end of file +OPENSSH_SERVER_IMAGE=nexent-ubuntu-terminal:latest \ No newline at end of file diff --git a/docker/.env.general b/docker/.env.general index 0bb4923d..d7c579d6 100644 --- a/docker/.env.general +++ b/docker/.env.general @@ -6,4 +6,4 @@ ELASTICSEARCH_IMAGE=docker.elastic.co/elasticsearch/elasticsearch:8.17.4 POSTGRESQL_IMAGE=postgres:15-alpine REDIS_IMAGE=redis:alpine MINIO_IMAGE=quay.io/minio/minio:RELEASE.2023-12-20T01-00-02Z -OPENSSH_SERVER_IMAGE=lscr.io/linuxserver/openssh-server:latest +OPENSSH_SERVER_IMAGE=nexent-ubuntu-terminal:latest diff --git a/docker/.env.mainland b/docker/.env.mainland index a85a53ff..a4464577 100644 --- a/docker/.env.mainland +++ b/docker/.env.mainland @@ -6,4 +6,4 @@ ELASTICSEARCH_IMAGE=elastic.m.daocloud.io/elasticsearch/elasticsearch:8.17.4 POSTGRESQL_IMAGE=docker.m.daocloud.io/postgres:15-alpine REDIS_IMAGE=docker.m.daocloud.io/redis:alpine MINIO_IMAGE=quay.m.daocloud.io/minio/minio:RELEASE.2023-12-20T01-00-02Z -OPENSSH_SERVER_IMAGE=docker.m.daocloud.io/linuxserver/openssh-server:latest +OPENSSH_SERVER_IMAGE=nexent-ubuntu-terminal:latest diff --git a/docker/deploy.sh b/docker/deploy.sh index a7af674a..04174adb 100755 --- a/docker/deploy.sh +++ b/docker/deploy.sh @@ -156,9 +156,6 @@ generate_ssh_keys() { cp "openssh-server/ssh-keys/openssh_server_key.pub" "openssh-server/config/authorized_keys" chmod 644 "openssh-server/config/authorized_keys" - # Setup package installation script - setup_package_install_script - # Set SSH key path in environment SSH_PRIVATE_KEY_PATH="$(pwd)/openssh-server/ssh-keys/openssh_server_key" export SSH_PRIVATE_KEY_PATH @@ -185,7 +182,7 @@ generate_ssh_keys() { TEMP_OUTPUT="/tmp/ssh_keygen_output_$$.txt" # Generate ed25519 key pair using the openssh-server container - if docker run --rm -i --entrypoint //keygen.sh "$OPENSSH_SERVER_IMAGE" <<< "1" > "$TEMP_OUTPUT" 2>&1; then + if docker run --rm -i "$OPENSSH_SERVER_IMAGE" bash -c "ssh-keygen -t ed25519 -f /tmp/id_ed25519 -N '' && cat /tmp/id_ed25519 && echo '---' && cat /tmp/id_ed25519.pub" > "$TEMP_OUTPUT" 2>&1; then echo " 🔍 SSH key generation completed, extracting keys..." # Extract private key (everything between -----BEGIN and -----END) @@ -226,9 +223,6 @@ generate_ssh_keys() { cp "openssh-server/ssh-keys/openssh_server_key.pub" "openssh-server/config/authorized_keys" chmod 644 "openssh-server/config/authorized_keys" - # Setup package installation script - setup_package_install_script - # Set SSH key path in environment SSH_PRIVATE_KEY_PATH="$(pwd)/openssh-server/ssh-keys/openssh_server_key" export SSH_PRIVATE_KEY_PATH @@ -643,20 +637,7 @@ select_deployment_version() { echo "" } -pull_openssh_images() { - # Function to pull openssh images - echo "🐳 Pulling openssh-server image for Terminal tool..." - if ! docker pull "$OPENSSH_SERVER_IMAGE"; then - echo " ❌ ERROR Failed to pull openssh-server image: $OPENSSH_SERVER_IMAGE" - ERROR_OCCURRED=1 - return 1 - fi - echo " ✅ Successfully pulled openssh-server image" - echo "" - echo "--------------------------------" - echo "" -} setup_package_install_script() { # Function to setup package installation script @@ -714,6 +695,31 @@ select_terminal_tool() { export COMPOSE_PROFILES="${COMPOSE_PROFILES:+$COMPOSE_PROFILES,}terminal" echo "✅ Terminal tool enabled 🔧" echo " 🔧 Deploying an openssh-server container for secure command execution" + + # Ask user to specify directory mapping + default_terminal_dir="/opt/terminal" + echo " 📁 Terminal directory configuration:" + echo " • Container path: /opt/terminal (fixed)" + echo " • Host path: You can specify any directory on your host machine" + echo " • Default host path: /opt/terminal (recommended)" + echo "" + read -p " 📁 Enter host directory to mount (default: /opt/terminal): " terminal_mount_dir + terminal_mount_dir=$(sanitize_input "$terminal_mount_dir") + TERMINAL_MOUNT_DIR="${terminal_mount_dir:-$default_terminal_dir}" + + # Save to environment variables + export TERMINAL_MOUNT_DIR + + # Add to .env file + if grep -q "^TERMINAL_MOUNT_DIR=" .env; then + sed -i.bak "s~^TERMINAL_MOUNT_DIR=.*~TERMINAL_MOUNT_DIR=$TERMINAL_MOUNT_DIR~" .env + else + echo "TERMINAL_MOUNT_DIR=$TERMINAL_MOUNT_DIR" >> .env + fi + + echo " 📁 Terminal mount configuration:" + echo " • Host: $TERMINAL_MOUNT_DIR" + echo " • Container: /opt/terminal" else export ENABLE_TERMINAL_TOOL="false" echo "🚫 Terminal tool disabled" @@ -797,7 +803,6 @@ main_deploy() { generate_minio_ak_sk || { echo "❌ MinIO key generation failed"; exit 1; } if [ "$ENABLE_TERMINAL_TOOL" = "true" ]; then - pull_openssh_images || { echo "❌ Openssh image pull failed"; exit 1; } generate_ssh_keys || { echo "❌ SSH key generation failed"; exit 1; } fi diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index e631e669..39023d92 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -202,18 +202,11 @@ services: container_name: nexent-openssh-server hostname: nexent-openssh-server environment: - PUID: 1000 - PGID: 1000 - TZ: "Asia/Shanghai" - PUBLIC_KEY_FILE: /config/authorized_keys - SUDO_ACCESS: "true" - PASSWORD_ACCESS: "false" - LOG_STDOUT: "true" - DOCKER_MODS: linuxserver/mods:universal-package-install - INSTALL_PACKAGES: git|make|curl|vim|wget + - TZ=Asia/Shanghai + - DEV_USER=linuxserver.io volumes: - - ${ROOT_DIR}/openssh-server/config:/config - - ${ROOT_DIR}/openssh-server/config/custom-cont-init.d:/custom-cont-init.d:ro + - ${TERMINAL_MOUNT_DIR:-./workspace}:/opt/terminal + - ${ROOT_DIR}/openssh-server/config:/tmp/ssh_keys:ro # 只读挂载SSH公钥 networks: - nexent restart: always diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index e171ee82..2655fc08 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -217,20 +217,13 @@ services: container_name: nexent-openssh-server hostname: nexent-openssh-server environment: - PUID: 1000 - PGID: 1000 - TZ: "Asia/Shanghai" - PUBLIC_KEY_FILE: /config/authorized_keys - SUDO_ACCESS: "true" - PASSWORD_ACCESS: "false" - LOG_STDOUT: "true" - DOCKER_MODS: linuxserver/mods:universal-package-install - INSTALL_PACKAGES: git|make|curl|vim|wget + - TZ=Asia/Shanghai + - DEV_USER=linuxserver.io ports: - - "2222:2222" # SSH port + - "2222:22" # SSH port volumes: - - ${ROOT_DIR}/openssh-server/config:/config - - ${ROOT_DIR}/openssh-server/config/custom-cont-init.d:/custom-cont-init.d:ro + - ${TERMINAL_MOUNT_DIR:-./workspace}:/opt/terminal + - ${ROOT_DIR}/openssh-server/ssh-keys:/tmp/ssh_keys:ro networks: - nexent restart: always diff --git a/make/terminal/Dockerfile b/make/terminal/Dockerfile new file mode 100644 index 00000000..60ad23d4 --- /dev/null +++ b/make/terminal/Dockerfile @@ -0,0 +1,64 @@ +FROM ubuntu:24.04 + +# Set environment variables +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Asia/Shanghai +ENV CONDA_DIR=/opt/conda +ENV PATH=$CONDA_DIR/bin:$PATH + +# Install base tools and dependencies +RUN apt-get update && apt-get install -y \ + openssh-server \ + sudo \ + curl \ + wget \ + git \ + vim \ + nano \ + build-essential \ + python3 \ + python3-pip \ + python3-venv \ + ca-certificates \ + gnupg \ + lsb-release \ + software-properties-common \ + && rm -rf /var/lib/apt/lists/* + +# Create development user +ARG DEV_USER=linuxserver.io +RUN useradd -m -s /bin/bash $DEV_USER && \ + usermod -aG sudo $DEV_USER && \ + echo "$DEV_USER ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +# Configure SSH - disable root login + disable password authentication +RUN mkdir /var/run/sshd && \ + sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin no/' /etc/ssh/sshd_config && \ + sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config + +# Install Miniconda +RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /tmp/miniconda.sh && \ + bash /tmp/miniconda.sh -b -p $CONDA_DIR && \ + rm /tmp/miniconda.sh + +# Set conda permissions +RUN chown -R $DEV_USER:$DEV_USER $CONDA_DIR + +# Create .ssh directory +RUN mkdir -p /home/$DEV_USER/.ssh && \ + chown $DEV_USER:$DEV_USER /home/$DEV_USER/.ssh && \ + chmod 700 /home/$DEV_USER/.ssh + +# Create default working directory +RUN mkdir -p /opt/terminal && \ + chown $DEV_USER:$DEV_USER /opt/terminal + +# Set working directory +WORKDIR /opt + +# Entrypoint script +COPY make/terminal/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +EXPOSE 22 +ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/make/terminal/entrypoint.sh b/make/terminal/entrypoint.sh new file mode 100644 index 00000000..0584ea50 --- /dev/null +++ b/make/terminal/entrypoint.sh @@ -0,0 +1,46 @@ +#!/bin/bash +set -e + +# Get user name +DEV_USER=${DEV_USER:-linuxserver.io} +USER_HOME="/home/$DEV_USER" + +# Allow login (unlock) +passwd -u "$DEV_USER" 2>/dev/null || true + +# Ensure shell is available +usermod -s /bin/bash "$DEV_USER" 2>/dev/null || true + +# Ensure user .ssh directory exists +mkdir -p "$USER_HOME/.ssh" +chown $DEV_USER:$DEV_USER "$USER_HOME/.ssh" +chmod 700 "$USER_HOME/.ssh" + +# Configure public key authentication (Ed25519) +if [ -f /tmp/ssh_keys/openssh_server_key.pub ]; then + cp /tmp/ssh_keys/openssh_server_key.pub "$USER_HOME/.ssh/authorized_keys" + chown $DEV_USER:$DEV_USER "$USER_HOME/.ssh/authorized_keys" + chmod 600 "$USER_HOME/.ssh/authorized_keys" + echo "✅ SSH public key successfully configured (Ed25519)" +else + echo "⚠️ Warning: SSH public key file not found /tmp/ssh_keys/openssh_server_key.pub" + echo "⚠️ Unable to connect to container via SSH" +fi + +# Configure SSH timeout settings +echo "Configuring SSH timeout settings (60 minutes)..." +cat >> /etc/ssh/sshd_config << 'SSHD_EOF' + +# Nexent Terminal Tool - Session timeout configuration (60 minutes = 3600 seconds) +ClientAliveInterval 300 +ClientAliveCountMax 12 +SSHD_EOF + +echo "SSH timeout configuration applied successfully" + +# Start SSH service +if [ $# -gt 0 ]; then + exec "$@" +else + exec /usr/sbin/sshd -D -d +fi diff --git a/sdk/nexent/core/tools/terminal_tool.py b/sdk/nexent/core/tools/terminal_tool.py index 70e6b907..c6e2d497 100644 --- a/sdk/nexent/core/tools/terminal_tool.py +++ b/sdk/nexent/core/tools/terminal_tool.py @@ -37,7 +37,7 @@ def __init__(self, init_path: str = Field(description="Initial workspace path", default="/mnt/nexent"), observer: MessageObserver = Field(description="Message observer", default=None, exclude=True), ssh_host: str = Field(description="SSH host", default="nexent-openssh-server"), - ssh_port: int = Field(description="SSH port", default=2222), + ssh_port: int = Field(description="SSH port", default=22), ssh_user: str = Field(description="SSH username", default="linuxserver.io"), private_key_path: str = Field(description="Path to private key file", default="/opt/ssh-keys/openssh_server_key")): """Initialize the TerminalTool. @@ -46,7 +46,7 @@ def __init__(self, init_path (str): Initial workspace path. Defaults to "/mnt/nexent". observer (MessageObserver, optional): Message observer instance. Defaults to None. ssh_host (str): SSH server host. Defaults to "localhost". - ssh_port (int): SSH server port. Defaults to 2222. + ssh_port (int): SSH server port. Defaults to 22. ssh_user (str): SSH username. Defaults to "root". private_key_path (str): Path to SSH private key. Defaults to "~/.ssh/id_rsa". """