From 44d55dd6f0b34b061066e6a2c6aafb160f86fd0d Mon Sep 17 00:00:00 2001 From: Liana Date: Wed, 11 Dec 2024 13:40:38 -0600 Subject: [PATCH] Add devserver container --- apps/devserver/Dockerfile | 64 +++++++++++++ apps/devserver/ci/goss.yaml | 5 + apps/devserver/ci/latest.sh | 3 + apps/devserver/metadata.yaml | 11 +++ apps/devserver/root/entrypoint.sh | 20 ++++ apps/devserver/root/etc/motd | 1 + apps/devserver/root/keygen.sh | 32 +++++++ apps/devserver/root/openssh.sh | 151 ++++++++++++++++++++++++++++++ 8 files changed, 287 insertions(+) create mode 100644 apps/devserver/Dockerfile create mode 100644 apps/devserver/ci/goss.yaml create mode 100755 apps/devserver/ci/latest.sh create mode 100644 apps/devserver/metadata.yaml create mode 100644 apps/devserver/root/entrypoint.sh create mode 100644 apps/devserver/root/etc/motd create mode 100644 apps/devserver/root/keygen.sh create mode 100644 apps/devserver/root/openssh.sh diff --git a/apps/devserver/Dockerfile b/apps/devserver/Dockerfile new file mode 100644 index 0000000..5a484a2 --- /dev/null +++ b/apps/devserver/Dockerfile @@ -0,0 +1,64 @@ +FROM docker.io/library/python:3.12-slim-bookworm + +LABEL \ + maintainer="Liana64" \ + org.opencontainers.image.source="https://github.com/RareCompute/containers" + +ARG TARGETPLATFORM +ARG VERSION +ARG CHANNEL +ARG DEBIAN_FRONTEND=noninteractive + +ENV \ + NVIDIA_DRIVER_CAPABILITIES="compute,video,utility,graphics" \ + PATH="/opt/venv/bin:$PATH" \ + UMASK="0002" \ + LANG=C.UTF-8 \ + TZ="Etc/UTC" \ + PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PYTHONFAULTHANDLER=1 \ + PIP_ROOT_USER_ACTION=ignore \ + PIP_NO_CACHE_DIR=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + PIP_BREAK_SYSTEM_PACKAGES=1 \ + UV_HTTP_TIMEOUT=1000 + +ENV \ + USER_NAME=rare \ + UID=900 \ + GID=900 \ + LISTENPORT=2222 + +USER root +WORKDIR /config + +COPY ./apps/devserver/root/ / + +RUN \ + apt-get update && apt-get install -y --no-install-recommends \ + curl wget unzip build-essential catatonit jq lsb-release \ + nano vim tree tmux git htop net-tools sudo \ + socat rsync aria2 restic \ + less man bat ffmpeg ripgrep \ + #cuda-toolkit nvidia-container-toolkit \ + openssh-server pciutils \ + && chmod 755 /entrypoint.sh \ + && chmod 755 /openssh.sh \ + && chmod 755 /keygen.sh \ + && printf "UpdateMethod=docker\nBranch=master\nPackageVersion=%s\nPackageAuthor=[RareCompute](https://github.com/RareCompute)\n" "${VERSION}" > /config/package_info \ + && chown -R ${UID}:${GID} /config && chmod -R 755 /config \ + && curl -LsSf https://astral.sh/uv/0.5.6/install.sh | sh \ + && . $HOME/.local/bin/env \ + && uv venv --no-python-downloads /opt/venv \ + && . /opt/venv/bin/activate \ + && uv pip install \ + tensorflow torch torchvision torchaudio \ + numpy pandas matplotlib scikit-learn \ + networkx tqdm pydot \ + && apt-get autoremove -y \ + && apt-get clean \ + && rm -rf /root/.cache /var/lib/apt/lists/* /tmp/* /var/tmp/* \ + && chsh -s /bin/bash + +ENTRYPOINT ["/usr/bin/catatonit", "--", "/entrypoint.sh"] diff --git a/apps/devserver/ci/goss.yaml b/apps/devserver/ci/goss.yaml new file mode 100644 index 0000000..ddfc75c --- /dev/null +++ b/apps/devserver/ci/goss.yaml @@ -0,0 +1,5 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/goss-org/goss/master/docs/schema.yaml +file: + /app/LICENSE: + exists: true diff --git a/apps/devserver/ci/latest.sh b/apps/devserver/ci/latest.sh new file mode 100755 index 0000000..0bdfee5 --- /dev/null +++ b/apps/devserver/ci/latest.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +version="0.0.1" +printf "%s" "${version}" diff --git a/apps/devserver/metadata.yaml b/apps/devserver/metadata.yaml new file mode 100644 index 0000000..912e482 --- /dev/null +++ b/apps/devserver/metadata.yaml @@ -0,0 +1,11 @@ +--- +#yamllint disable +app: devserver +semver: true +channels: + - name: stable + platforms: ["linux/amd64"] + stable: true + tests: + enabled: false + type: cli diff --git a/apps/devserver/root/entrypoint.sh b/apps/devserver/root/entrypoint.sh new file mode 100644 index 0000000..7794527 --- /dev/null +++ b/apps/devserver/root/entrypoint.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +echo -n 'Rare Compute Devserver ' +echo ${VERSION} +echo ' +───────────────────────────────────────' +echo " +Username: ${USER_NAME} +User UID: ${UID} +User GID: ${GID} +───────────────────────────────────────" +groupadd --gid ${GID} "${USER_NAME}" \ +&& useradd --uid ${UID} --gid ${GID} --create-home -d /config --shell /bin/bash "${USER_NAME}" \ + +exec \ + /openssh.sh + +exec \ + gosu $USER_NAME \ + /bin/bash diff --git a/apps/devserver/root/etc/motd b/apps/devserver/root/etc/motd new file mode 100644 index 0000000..ab17369 --- /dev/null +++ b/apps/devserver/root/etc/motd @@ -0,0 +1 @@ +Welcome to Rare Compute diff --git a/apps/devserver/root/keygen.sh b/apps/devserver/root/keygen.sh new file mode 100644 index 0000000..2a286d6 --- /dev/null +++ b/apps/devserver/root/keygen.sh @@ -0,0 +1,32 @@ +#! /bin/bash +# Source: https://github.com/linuxserver/docker-openssh-server/blob/master/root/keygen.sh + +# selection menu +echo "Please select your key type to generate" +printf "1.) ed25519\n2.) rsa\n3.) ecdsa\n4.) dsa\n[default ed25519]:" +read opt +case $opt in + "ed25519"|1) TYPE="ed25519";; + "rsa"|2) TYPE="rsa";; + "ecdsa"|3) TYPE="ecdsa" BITS="-b 521";; + "dsa"|4) TYPE="dsa";; + *) echo "blank or unknown option choosing ed25519" && TYPE="ed25519";; +esac +# rsa bit selection +if [[ "$TYPE" == "rsa" ]]; then + echo "Please select RSA bits" + printf "1.) 4096\n2.) 2048\n3.) 1024\n[default 4096]:" + read opt + case $opt in + 4096|1) BITS="-b 4096";; + 2048|2) BITS="-b 2048";; + 1024|3) BITS="-b 1024";; + *) echo "blank or unknown option choosing 4096" && BITS="-b 4096";; + esac +fi + +# key generation +echo "" +echo "YOUR KEYS ARE BELOW. PLEASE TAKE A COPY OF THEM AS THEY WILL NOT PERSIST ONCE THIS TERMINAL IS CLOSED." +echo "" +echo /tmp/stderr{,.pub} | xargs -n 1 ln -sf /dev/stderr && yes | ssh-keygen -t ${TYPE} ${BITS} -N '' -qf /tmp/stderr > /dev/null diff --git a/apps/devserver/root/openssh.sh b/apps/devserver/root/openssh.sh new file mode 100644 index 0000000..cf73770 --- /dev/null +++ b/apps/devserver/root/openssh.sh @@ -0,0 +1,151 @@ +#!/usr/bin/bash +# See: https://github.com/linuxserver/docker-openssh-server/blob/master/root/etc/s6-overlay/s6-rc.d/init-openssh-server-config/run + +# create folders +mkdir -p \ + /config/{.ssh,logs/openssh,sshd} + +USER_NAME=${USER_NAME:-rare} +echo "User name is set to $USER_NAME" + +# set password for abc to unlock it and set sudo access +sed -i "/${USER_NAME} ALL.*/d" /etc/sudoers +if [[ "$SUDO_ACCESS" == "true" ]]; then + if [[ -n "$USER_PASSWORD" || (-n "$USER_PASSWORD_FILE" && -f "$USER_PASSWORD_FILE") ]]; then + echo "${USER_NAME} ALL=(ALL) ALL" >> /etc/sudoers + echo "sudo is enabled with password." + else + echo "${USER_NAME} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + echo "sudo is enabled without password." + fi +else + echo "sudo is disabled." +fi + +if [[ -n "$USER_PASSWORD_FILE" ]] && [[ -f "$USER_PASSWORD_FILE" ]]; then + USER_PASSWORD=$(cat "$USER_PASSWORD_FILE") + echo "User password is retrieved from file." +fi + +USER_PASSWORD=${USER_PASSWORD:-$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c"${1:-8}";echo;)} +echo "${USER_NAME}:${USER_PASSWORD}" | chpasswd + +# Migration +if [[ -f /config/ssh_host_keys/sshd_config ]]; then + mv /config/ssh_host_keys/sshd_config /config/sshd/sshd_config + sed -i 's/Include \/etc\/ssh\/sshd_config.d\/\*.conf/#Include \/etc\/ssh\/sshd_config.d\/\*.conf/' /config/sshd/sshd_config + echo "This file has been moved to /config/sshd/sshd_config" > /config/ssh_host_keys/sshd_config_README + chmod 600 /config/ssh_host_keys/ssh_host_*_key + chmod 644 /config/ssh_host_keys/ssh_host_*_key.pub +fi + +if [[ ! -f /config/sshd/sshd_config ]]; then + sed -i '/#PidFile/c\PidFile \/config\/sshd.pid' /etc/ssh/sshd_config + sed -i 's/Include \/etc\/ssh\/sshd_config.d\/\*.conf/#Include \/etc\/ssh\/sshd_config.d\/\*.conf/' /etc/ssh/sshd_config + cp -a /etc/ssh/sshd_config /config/sshd/sshd_config +fi + +if [[ ! -d /config/ssh_host_keys ]]; then + mkdir -p /config/ssh_host_keys + ssh-keygen -A + cp /etc/ssh/ssh_host_* /config/ssh_host_keys +fi + +# display SSH host public key(s) +echo "SSH host public key(s):" +cat /config/ssh_host_keys/ssh_host_*.pub + +# custom port +if [[ -n "${LISTEN_PORT}" ]]; then + sed -i "s/^#Port [[:digit:]]\+/Port ${LISTEN_PORT}"/ /config/sshd/sshd_config + sed -i "s/^Port [[:digit:]]\+/Port ${LISTEN_PORT}"/ /config/sshd/sshd_config + echo "sshd is listening on port ${LISTEN_PORT}" +else + sed -i "s/^#Port [[:digit:]]\+/Port 2222"/ /config/sshd/sshd_config + sed -i "s/^Port [[:digit:]]\+/Port 2222"/ /config/sshd/sshd_config + echo "sshd is listening on port 2222" +fi + +# password access +if [[ "$PASSWORD_ACCESS" == "true" ]]; then + sed -i '/^#PasswordAuthentication/c\PasswordAuthentication yes' /config/sshd/sshd_config + sed -i '/^PasswordAuthentication/c\PasswordAuthentication yes' /config/sshd/sshd_config + chown root:"${USER_NAME}" \ + /etc/shadow + echo "User/password ssh access is enabled." +else + sed -i '/^PasswordAuthentication/c\PasswordAuthentication no' /config/sshd/sshd_config + chown root:root \ + /etc/shadow + echo "User/password ssh access is disabled." +fi + +# set umask for sftp +UMASK=${UMASK:-022} +sed -i "s|/usr/lib/ssh/sftp-server$|/usr/lib/ssh/sftp-server -u ${UMASK}|g" /config/sshd/sshd_config + +# set key auth in file +if [[ ! -f /config/.ssh/authorized_keys ]]; then + touch /config/.ssh/authorized_keys +fi + +if [[ -n "$PUBLIC_KEY" ]]; then + if ! grep -q "${PUBLIC_KEY}" /config/.ssh/authorized_keys; then + echo "$PUBLIC_KEY" >> /config/.ssh/authorized_keys + echo "Public key from env variable added" + fi +fi + +if [[ -n "$PUBLIC_KEY_URL" ]]; then + PUBLIC_KEY_DOWNLOADED=$(curl -s "$PUBLIC_KEY_URL") + if ! grep -q "$PUBLIC_KEY_DOWNLOADED" /config/.ssh/authorized_keys; then + echo "$PUBLIC_KEY_DOWNLOADED" >> /config/.ssh/authorized_keys + echo "Public key downloaded from '$PUBLIC_KEY_URL' added" + fi +fi + +if [[ -n "$PUBLIC_KEY_FILE" ]] && [[ -f "$PUBLIC_KEY_FILE" ]]; then + PUBLIC_KEY2=$(cat "$PUBLIC_KEY_FILE") + if ! grep -q "$PUBLIC_KEY2" /config/.ssh/authorized_keys; then + echo "$PUBLIC_KEY2" >> /config/.ssh/authorized_keys + echo "Public key from file added" + fi +fi + +if [[ -d "$PUBLIC_KEY_DIR" ]]; then + for F in "${PUBLIC_KEY_DIR}"/*; do + PUBLIC_KEYN=$(cat "$F") + if ! grep -q "$PUBLIC_KEYN" /config/.ssh/authorized_keys; then + echo "$PUBLIC_KEYN" >> /config/.ssh/authorized_keys + echo "Public key from file '$F' added" + fi + done +fi + +# back up old log files processed by logrotate +if [[ -f /config/logs/openssh/openssh.log ]]; then + mv /config/logs/openssh /config/logs/openssh.old.logs + mkdir -p /config/logs/openssh +fi + +# add log file info +if [[ ! -f /config/logs/loginfo.txt ]]; then + echo "The current log file is named \"current\". The rotated log files are gzipped, named with a TAI64N timestamp and a \".s\" extension" > /config/logs/loginfo.txt +fi + +# permissions +chown -R "${UID}":"${GID}" \ + /config +chmod go-w \ + /config +chmod 700 \ + /config/.ssh +chmod 600 \ + /config/.ssh/authorized_keys + +chown -R root:"${GID}" \ + /config/sshd +chmod 750 \ + /config/sshd +chmod 640 \ + /config/sshd/sshd_config