SSH

如何在VKUBE中使用shell/ssh远程连接功能

Tool Injection

VKUBE提供注入工具的功能,具体可以参考Tool Injection.

使用此功能,用户目前可以选择三个工具:

  • sshd:sshd(Secure Shell Daemon)是 OpenSSH 提供的 SSH 服务器进程,允许远程用户通过 SSH 协议安全地连接到 Linux 服务器。

  • ttyd:ttyd 是一个可以将命令行终端(TTY)通过 WebSocket 映射到网页的工具,使得用户可以通过浏览器访问服务器终端。

  • rsync:rsync(Remote Sync)是一个高效的远程同步工具,可用于本地和远程文件同步,支持增量复制和压缩传输。

VKUBE将会根据不同的用户选择做出以下的配置:

  • 选择sshd:VKUBE将会帮助用户在指定的某一个container中,下载并运行sshd,sshd只能通过public key/private key的形式进行验证加密,public key将会写入container的环境变量当中。此后,用户容器就会启动sshd,而用户可以通过ssh登录用户所指定的容器。

  • 选择ttyd:VKUBE将会帮助用户在指定的某一个container中,下载并运行ttyd,ttyd将会启动一个web网页形式的程序,可以让用户在网页上连接到对应的container上,用户需要用户填写用户名和密码,并且密码需要满足:至少一个大写字母,至少一个小写字母,至少一个数字,至少一个特殊字符。

  • 选择rsync:需要启用sshd才可以启用,VKUBE将会帮助用户在指定的某一个container中,下载并使用rsync。

下载并运行会通过以下脚本的执行得到。注意:VKUBE会尽力帮助用户下载和运行sshd,但是目前的测试当中比较新版本的ubuntu和apline、Debian为基础的镜像是合适的,其他的linux发行版操作系统有可能会不成功。而在Debian中,ttyd的下载是不行的。

Image customization

用户也可以在创建镜像的时候,自己进行启动sshd,例如以ubuntu基础镜像为主:

RUN /bin/sh -c service ssh start # buildkit

RUN /bin/sh -c echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config # buildkit

RUN /bin/sh -c echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config # buildkit

RUN /bin/sh -c echo "PasswordAuthentication no" >> /etc/ssh/sshd_config # buildkit

RUN apt-get update \
 && apt-get install -y openssh-server \
 && cp /etc/ssh/sshd_config /etc/ssh/sshd_config-original \
 && sed -i 's/^#\s*Port.*/Port 2222/' /etc/ssh/sshd_config \
 && sed -i 's/^#\s*PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config \
 && mkdir -p /root/.ssh \
 && chmod 700 /root/.ssh \
 && mkdir /var/run/sshd \
 && chmod 755 /var/run/sshd \
 && rm -rf /var/lib/apt/lists /var/cache/apt/archives

当然也可以直接使用我们的脚本拷贝到镜像当中,在docker build的时候直接执行并下载。

脚本

#!/bin/sh

# Check for root privileges
if [ "$(id -u)" -ne 0 ]; then
    echo "This script must be run as root" >&2
    exit 1
fi
# Function to get the correct package name for a tool based on the package manager
get_package_name() {
    local tool="$1"
    case "$tool" in
        openssh)
            case "$PACKAGE_MANAGER" in
                apt) echo "openssh-server" ;;
                dnf|yum) echo "openssh-server" ;;
                pacman) echo "openssh" ;;
                zypper) echo "openssh" ;;
                apk) echo "openssh" ;;
                emerge) echo "net-misc/openssh" ;;
                *) echo "$tool" ;;  # Default to the original name if unrecognized
            esac
            ;;
        rsync)
            case "$PACKAGE_MANAGER" in
                apt) echo "rsync" ;;
                dnf|yum) echo "rsync" ;;
                pacman) echo "rsync" ;;
                zypper) echo "rsync" ;;
                apk) echo "rsync" ;;
                emerge) echo "net-misc/rsync" ;;
                *) echo "$tool" ;;
            esac
            ;;
        ttyd)
            echo "ttyd"
            ;;
        # Add more tools here if needed
        *)
            echo "$tool"  # Default to the original name
            ;;
    esac
}

# Function to determine the package manager, update repositories, and install the specified package
determine_package_manager() {
    # Check if /etc/os-release exists
    if [ ! -f /etc/os-release ]; then
        echo "Cannot determine the OS. /etc/os-release file not found." >&2
        exit 1
    fi

    # Source the /etc/os-release file to get OS information
    . /etc/os-release

    # Initialize variables
    PACKAGE_MANAGER=""

    # Determine package manager based on ID and ID_LIKE
    case "$ID" in
        ubuntu|debian)
            PACKAGE_MANAGER="apt"
            ;;
        fedora)
            PACKAGE_MANAGER="dnf"
            ;;
        centos|rhel)
            if command -v dnf >/dev/null 2>&1; then
                PACKAGE_MANAGER="dnf"
            else
                PACKAGE_MANAGER="yum"
            fi
            ;;
        arch)
            PACKAGE_MANAGER="pacman"
            ;;
        opensuse*|suse)
            PACKAGE_MANAGER="zypper"
            ;;
        alpine)
            PACKAGE_MANAGER="apk"
            ;;
        gentoo)
            PACKAGE_MANAGER="emerge"
            ;;
        *)
            if [ -n "$ID_LIKE" ]; then
                case "$ID_LIKE" in
                    *debian*)
                        PACKAGE_MANAGER="apt"
                        ;;
                    *rhel*|*fedora*)
                        if command -v dnf >/dev/null 2>&1; then
                            PACKAGE_MANAGER="dnf"
                        else
                            PACKAGE_MANAGER="yum"
                        fi
                        ;;
                    *arch*)
                        PACKAGE_MANAGER="pacman"
                        ;;
                    *suse*)
                        PACKAGE_MANAGER="zypper"
                        ;;
                    *alpine*)
                        PACKAGE_MANAGER="apk"
                        ;;
                    *gentoo*)
                        PACKAGE_MANAGER="emerge"
                        ;;
                    *)
                        PACKAGE_MANAGER=""
                        ;;
                esac
            fi
            ;;
    esac
}

install_package() {
    local package_name=$1
    PACKAGE_INSTALL_NAME=$(get_package_name "$package_name")
    if [ -n "$PACKAGE_MANAGER" ]; then
        echo "Installing $PACKAGE_INSTALL_NAME using $PACKAGE_MANAGER"
        case "$PACKAGE_MANAGER" in
            apt)
                apt update && apt install -y "$PACKAGE_INSTALL_NAME"
                if [ "$package_name" = "openssh" ]; then
                    mkdir -p /run/sshd
                    chmod 755 /run/sshd
                fi
                ;;
            dnf)
                dnf makecache && dnf install -y "$PACKAGE_INSTALL_NAME"
                ;;
            yum)
                yum makecache fast && yum install -y "$PACKAGE_INSTALL_NAME"
                ;;
            pacman)
                pacman -Sy --noconfirm "$PACKAGE_INSTALL_NAME"
                ;;
            zypper)
                zypper refresh && zypper install -y "$PACKAGE_INSTALL_NAME"
                ;;
            apk)
                apk update && apk add "$PACKAGE_INSTALL_NAME"
                ;;
            emerge)
                emerge --sync && emerge "$PACKAGE_INSTALL_NAME"
                ;;
            *)
                echo "Error: Unsupported package manager." >&2
                exit 1
                ;;
        esac
    else
        echo "Unsupported or unknown Linux distribution: $NAME" >&2
        exit 1
    fi
}

create_user() {
    local username="$1"
    local password="$2"

    if [ -z "$username" ]; then
        echo "Error: VKUBE_TTYD_USER is required." >&2
        return 1
    fi

    if id "$username" >/dev/null 2>&1; then
        echo "User $username already exists."

        # Check if the existing user has a password (for Alpine, check /etc/shadow)
        user_info=$(getent shadow "$username")
        if [ -n "$user_info" ] && echo "$user_info" | cut -d: -f2 | grep -q "^\*$\|^!$"; then
            if [ -z "$password" ]; then
                echo "Error: User $username does not have a password. VKUBE_TTYD_PASSWORD is required." >&2
                return 1
            fi
        fi

        # If a password is provided, update it
        if [ -n "$password" ]; then
            echo "$username:$password" | chpasswd
            echo "Password for $username has been updated."
        fi
    else
        # If the user does not exist, a password must be entered
        if [ -z "$password" ]; then
            echo "Error: User $username does not exist. VKUBE_TTYD_PASSWORD is required to create a new user." >&2
            return 1
        fi

        # Create the user (handling different distributions)
        if command -v useradd >/dev/null 2>&1; then
            useradd -m -s /bin/bash "$username"
        elif command -v adduser >/dev/null 2>&1; then
            adduser -D -s /bin/sh "$username"
        else
            echo "Error: No compatible user creation command found." >&2
            return 1
        fi

        echo "$username:$password" | chpasswd
        echo "User $username has been created with a password."
    fi
    
    # Special handling for root user in Alpine Linux: Add pts/0 to /etc/securetty
    if [ "$username" = "root" ]; then
        echo "pts/0" >> /etc/securetty
    fi
}

determine_package_manager

# Install packages based on environment variables
[ -n "$VKUBE_SSH_PUB_KEY" ] && install_package openssh
[ -n "$VKUBE_RSYNC" ] && install_package rsync
[ -n "$VKUBE_TTYD_USER" ] && install_package ttyd

# Configure SSH if SSH key is provided
if [ -n "$VKUBE_SSH_PUB_KEY" ]; then
    ssh-keygen -A
    mkdir -p /root/.ssh
    echo "$VKUBE_SSH_PUB_KEY" > /root/.ssh/authorized_keys
    chmod 600 /root/.ssh/authorized_keys

    echo "PermitRootLogin prohibit-password" >> /etc/ssh/sshd_config
    echo "PasswordAuthentication no" >> /etc/ssh/sshd_config
    echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config

    /usr/sbin/sshd
    echo "SSHD service configured for key-based authentication only."
fi

# Configute TTYD if TTYD key is provided
if [ -n "$VKUBE_TTYD_USER" ]; then
    create_user "$VKUBE_TTYD_USER" "$VKUBE_TTYD_PASSWORD"
    /usr/bin/ttyd -W --port=27681 login > /dev/null 2>&1 < /dev/null &
fi
上次更新: 2025/9/9 上午2:24:03