From 6646e6cb9fbd8f8925d85032ad38d8fc0672e751 Mon Sep 17 00:00:00 2001 From: Chuck Lantz Date: Thu, 3 Jun 2021 19:14:10 -0700 Subject: [PATCH] Update sshd-debian.sh to restore environment variables for login shells (#871) --- .github/workflows/script-library.yml | 10 +- script-library/README.md | 2 +- script-library/docs/sshd.md | 11 ++- script-library/sshd-debian.sh | 93 +++++++++++++------ .../test/{ => regression}/Dockerfile | 2 +- .../alpine/.devcontainer.json | 5 +- .../debian/.devcontainer.json | 4 +- .../redhat/.devcontainer.json | 4 +- .../test/{ => regression}/run-scripts.sh | 0 script-library/test/regression/test.sh | 17 ++++ script-library/test/sshd/Dockerfile | 20 ++++ .../test/sshd/debian/.devcontainer.json | 18 ++++ .../test/sshd/test-in-container-for-user.sh | 71 ++++++++++++++ script-library/test/sshd/test-in-container.sh | 18 ++++ script-library/test/sshd/test-prep.sh | 24 +++++ .../test/sshd/test-user-secrets.json | 42 +++++++++ 16 files changed, 293 insertions(+), 48 deletions(-) rename script-library/test/{ => regression}/Dockerfile (96%) rename script-library/test/{ => regression}/alpine/.devcontainer.json (67%) rename script-library/test/{ => regression}/debian/.devcontainer.json (82%) rename script-library/test/{ => regression}/redhat/.devcontainer.json (78%) rename script-library/test/{ => regression}/run-scripts.sh (100%) create mode 100755 script-library/test/regression/test.sh create mode 100644 script-library/test/sshd/Dockerfile create mode 100644 script-library/test/sshd/debian/.devcontainer.json create mode 100755 script-library/test/sshd/test-in-container-for-user.sh create mode 100755 script-library/test/sshd/test-in-container.sh create mode 100755 script-library/test/sshd/test-prep.sh create mode 100644 script-library/test/sshd/test-user-secrets.json diff --git a/.github/workflows/script-library.yml b/.github/workflows/script-library.yml index f19be361..1eb59ef7 100644 --- a/.github/workflows/script-library.yml +++ b/.github/workflows/script-library.yml @@ -13,7 +13,7 @@ jobs: if: "!contains(github.event.head_commit.message, 'Automated update') && !contains(github.event.head_commit.message, 'CI ignore')" strategy: matrix: - os: [debian, alpine, redhat] + os: [debian, ubuntu, alpine, centos] fail-fast: false runs-on: ubuntu-latest steps: @@ -34,13 +34,7 @@ jobs: id: test_script_library run: | set -e - cd script-library - if [ "${{ matrix.os }}" = "redhat" ]; then - IMAGE_TO_TEST="centos:7" - else - IMAGE_TO_TEST=${{ matrix.os }} - fi - docker build --build-arg DISTRO=${{ matrix.os }} --build-arg IMAGE_TO_TEST=$IMAGE_TO_TEST -f test/Dockerfile . + bash script-library/test/regression/test.sh "${{ matrix.os }}" copy-scripts: name: Copy and commit script-library updates diff --git a/script-library/README.md b/script-library/README.md index 28e1bcbb..4f234e11 100644 --- a/script-library/README.md +++ b/script-library/README.md @@ -141,7 +141,7 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ ## Testing -The `test` sub-folder includes Debian, Alpine, and RedHat based dev containers that can be used to test the scripts. +The `test/regression` sub-folder includes Debian, Alpine, and RedHat based dev containers that can be used to test the scripts. ## Contributing diff --git a/script-library/docs/sshd.md b/script-library/docs/sshd.md index c355b513..c8a423fe 100644 --- a/script-library/docs/sshd.md +++ b/script-library/docs/sshd.md @@ -11,7 +11,7 @@ ## Syntax ```text -./sshd-debian.sh [SSH Port] [Non-root user] [Start SSHD now flag] [New password for user] +./sshd-debian.sh [SSH Port] [Non-root user] [Start SSHD now flag] [New password for user] [Fix environment flag] ``` |Argument|Default|Description| @@ -20,6 +20,7 @@ | Non-root user |`automatic`| Specifies a user in the container other than root that will use SSH. A value of `automatic` will cause the script to check for a user called `vscode`, then `node`, `codespace`, and finally a user with a UID of `1000` before falling back to `root`. | |Start SSHD now flag |`false`| Flag (`true`/`false`) that specifies whether the SSH server should be started after the script runs.| |New password for user|`skip`| Sets a new password for the specified non-root user. A value of `skip` will skip this step. A value of `random` will generate a random password and print it out when the script finishes. | +|Fix environment flag|`true`|Connections using the SSH daemon use "fresh" login shells that will not contain any environment or `PATH` updates made in the image. By default the script will wire into login shell creation, add any environment variables not already set, and base the `PATH` off of what was set in the Dockerifle. Set to false if you experience unexpected problems. | ## Usage @@ -45,7 +46,7 @@ Usage: 3. Your container/codespace now has a running SSH server in it. Use a **local terminal** (or other tool) to connect to it using the command and password from step 2. e.g. ```bash - ssh -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null codespace@localhost + ssh -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o GlobalKnownHostsFile=/dev/null codespace@localhost ``` ...where `codespace` above is the user you are running as in the container (e.g. `codespace`, `vscode`, `node`, or `root`) and `2222` after `-p` is the **local address port** from step 2. @@ -94,7 +95,7 @@ Usage: 5. Connect to the container or [codespace using VS Code](https://docs.github.com/en/github/developing-online-with-codespaces/connecting-to-your-codespace-from-visual-studio-code). You can now SSH into the container on port `2222`. For example, if you are in the container as the `vscode` user, run the following command in a **local terminal**: ```bash - ssh -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null vscode@localhost + ssh -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o GlobalKnownHostsFile=/dev/null vscode@localhost ``` ...where `vscode` above is the user you are running as in the container (e.g. `codespace`, `vscode`, `node`, or `root`). @@ -140,7 +141,7 @@ If you already have a running container, you can use the script to spin up SSH i [SSHFS](https://en.wikipedia.org/wiki/SSHFS) allows you to mount a remote filesystem to your local machine with nothing but a SSH connection. Here's how to use it with a dev container. -1. Follow the steps in one of the previous sections to ensure you can connect to the dev contaienr using the normal `ssh` client. +1. Follow the steps in one of the previous sections to ensure you can connect to the dev container using the normal `ssh` client. 2. Install a SSHFS client. @@ -154,7 +155,7 @@ If you already have a running container, you can use the script to spin up SSH i ``` mkdir -p ~/sshfs/devcontainer - sshfs "vscode@localhost:/workspaces" "$HOME/sshfs/devcontainer" -p 2222 -o follow_symlinks -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -C + sshfs "vscode@localhost:/workspaces" "$HOME/sshfs/devcontainer" -p 2222 -o follow_symlinks -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o GlobalKnownHostsFile=/dev/null -C ``` ...where `vscode` above is the user you are running as in the container (e.g. `codespace`, `vscode`, `node`, or `root`) and `2222` after the `-p` is the same local port you used in the `ssh` command in step 1. diff --git a/script-library/sshd-debian.sh b/script-library/sshd-debian.sh index 84b41c06..b7b77e44 100755 --- a/script-library/sshd-debian.sh +++ b/script-library/sshd-debian.sh @@ -7,7 +7,7 @@ # Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/sshd.md # Maintainer: The VS Code and Codespaces Teams # -# Syntax: ./sshd-debian.sh [SSH Port (don't use 22)] [non-root user] [start sshd now flag] [new password for user] +# Syntax: ./sshd-debian.sh [SSH Port (don't use 22)] [non-root user] [start sshd now flag] [new password for user] [fix environment flag] # # Note: You can change your user's password with "sudo passwd $(whoami)" (or just "passwd" if running as root). @@ -15,6 +15,7 @@ SSHD_PORT=${1:-"2222"} USERNAME=${2:-"automatic"} START_SSHD=${3:-"false"} NEW_PASSWORD=${4:-"skip"} +FIX_ENVIRONMENT=${5:-"true"} set -e @@ -23,12 +24,6 @@ if [ "$(id -u)" -ne 0 ]; then exit 1 fi -# SSH uses a login shells, so we need to ensure these get the same initial PATH as non-login shells. -# /etc/profile wipes out the path which is a problem when the PATH was modified using the ENV directive in a Dockerfile. -rm -f /etc/profile.d/00-restore-env.sh -echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh -chmod +x /etc/profile.d/00-restore-env.sh - # Determine the appropriate non-root user if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then USERNAME="" @@ -61,57 +56,103 @@ apt-get-update-if-needed() export DEBIAN_FRONTEND=noninteractive # Install openssh-server openssh-client -if ! dpkg -s openssh-server openssh-client > /dev/null 2>&1; then +if ! dpkg -s openssh-server openssh-client lsof jq > /dev/null 2>&1; then apt-get-update-if-needed - apt-get -y install --no-install-recommends openssh-server openssh-client + apt-get -y install --no-install-recommends openssh-server openssh-client lsof jq fi # Generate password if new password set to the word "random" if [ "${NEW_PASSWORD}" = "random" ]; then NEW_PASSWORD="$(openssl rand -hex 16)" EMIT_PASSWORD="true" +elif [ "${NEW_PASSWORD}" != "skip" ]; then + # If new password not set to skip, set it for the specified user + echo "${USERNAME}:${NEW_PASSWORD}" | chpasswd fi -# If new password not set to skip, set it for the specified user -if [ "${NEW_PASSWORD}" != "skip" ]; then - echo "${USERNAME}:${NEW_PASSWORD}" | chpasswd - if [ "${NEW_PASSWORD}" != "root" ]; then - usermod -aG ssh ${USERNAME} - fi +# Add user to ssh group +if [ "${USERNAME}" != "root" ]; then + usermod -aG ssh ${USERNAME} fi # Setup sshd mkdir -p /var/run/sshd -sed -i 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' /etc/pam.d/sshd +sed -i 's/session\s*required\s*pam_loginuid\.so/session optional pam_loginuid.so/g' /etc/pam.d/sshd sed -i 's/#*PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config sed -i -E "s/#*\s*Port\s+.+/Port ${SSHD_PORT}/g" /etc/ssh/sshd_config +# Need to UsePAM so /etc/environment is processed +sed -i -E "s/#?\s*UsePAM\s+.+/UsePAM yes/g" /etc/ssh/sshd_config -# Write out a script that can be referenced as an ENTRYPOINT to auto-start sshd +# Script to store variables that exist at the time the ENTRYPOINT is fired +STORE_ENV_SCRIPT="$(cat << 'EOF' +# Wire in codespaces secret processing to zsh if present (since may have been added to image after script was run) +if [ -f /etc/zsh/zlogin ] && ! grep '/etc/profile.d/00-restore-secrets.sh' /etc/zsh/zlogin > /dev/null 2>&1; then + echo -e "if [ -f /etc/profile.d/00-restore-secrets.sh ]; then . /etc/profile.d/00-restore-secrets.sh; fi\n$(cat /etc/zsh/zlogin 2>/dev/null || echo '')" | sudoIf tee /etc/zsh/zlogin > /dev/null +fi +EOF +)" + +# Script to ensure login shells get the latest Codespaces secrets +RESTORE_SECRETS_SCRIPT="$(cat << 'EOF' +#!/bin/sh +if [ "${CODESPACES}" != "true" ] || [ "${VSCDC_FIXED_SECRETS}" = "true" ] || [ ! -z "${GITHUB_CODESPACES_TOKEN}" ]; then + # Not codespaces, already run, or secrets already in environment, so return + return +fi +if [ -f /workspaces/.codespaces/shared/.user-secrets.json ]; then + $(cat /workspaces/.codespaces/shared/.user-secrets.json | jq -r '.[] | select (.type=="EnvironmentVariable") | "export "+.name+"=\""+.value+"\""') +fi +export VSCDC_FIXED_SECRETS=true +EOF +)" + +# Write out a scripts that can be referenced as an ENTRYPOINT to auto-start sshd and fix login environments tee /usr/local/share/ssh-init.sh > /dev/null \ -<< EOF +<< 'EOF' #!/usr/bin/env bash +# This script is intended to be run as root with a container that runs as root (even if you connect with a different user) +# However, it supports running as a user other than root if passwordless sudo is configured for that same user. + set -e -if [ "\$(id -u)" -ne 0 ]; then - sudo /etc/init.d/ssh start > /tmp/sshd.log 2>&1 -else - /etc/init.d/ssh start > /tmp/sshd.log 2>&1 +sudoIf() +{ + if [ "$(id -u)" -ne 0 ]; then + sudo "$@" + else + "$@" + fi +} + +EOF +if [ "${FIX_ENVIRONMENT}" = "true" ]; then + echo "${STORE_ENV_SCRIPT}" >> /usr/local/share/ssh-init.sh + echo "${RESTORE_SECRETS_SCRIPT}" > /etc/profile.d/00-restore-secrets.sh + chmod +x /etc/profile.d/00-restore-secrets.sh + # Wire in zsh if present + if type zsh > /dev/null 2>&1; then + echo -e "if [ -f /etc/profile.d/00-restore-secrets.sh ]; then . /etc/profile.d/00-restore-secrets.sh; fi\n$(cat /etc/zsh/zlogin 2>/dev/null || echo '')" > /etc/zsh/zlogin + fi fi +tee -a /usr/local/share/ssh-init.sh > /dev/null \ +<< 'EOF' + +# ** Start SSH server ** +sudoIf /etc/init.d/ssh start 2>&1 | sudoIf tee /tmp/sshd.log > /dev/null set +e -exec "\$@" +exec "$@" EOF chmod +x /usr/local/share/ssh-init.sh -chown ${USERNAME}:ssh /usr/local/share/ssh-init.sh # If we should start sshd now, do so if [ "${START_SSHD}" = "true" ]; then /usr/local/share/ssh-init.sh fi -# Write out result +# Output success details echo -e "Done!\n\n- Port: ${SSHD_PORT}\n- User: ${USERNAME}" if [ "${EMIT_PASSWORD}" = "true" ]; then echo "- Password: ${NEW_PASSWORD}" fi -echo -e "\nForward port ${SSHD_PORT} to your local machine and run:\n\n ssh -p ${SSHD_PORT} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${USERNAME}@localhost\n" +echo -e "\nForward port ${SSHD_PORT} to your local machine and run:\n\n ssh -p ${SSHD_PORT} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o GlobalKnownHostsFile=/dev/null ${USERNAME}@localhost\n" diff --git a/script-library/test/Dockerfile b/script-library/test/regression/Dockerfile similarity index 96% rename from script-library/test/Dockerfile rename to script-library/test/regression/Dockerfile index 97a0115e..2c94c7bb 100644 --- a/script-library/test/Dockerfile +++ b/script-library/test/regression/Dockerfile @@ -8,7 +8,7 @@ ARG IMAGE_TO_TEST=debian:10 FROM ${IMAGE_TO_TEST} COPY *.sh /tmp/ -COPY test/*.sh /tmp +COPY test/regression/*.sh /tmp/ RUN chmod +x /tmp/*.sh ARG DISTRO="debian" diff --git a/script-library/test/alpine/.devcontainer.json b/script-library/test/regression/alpine/.devcontainer.json similarity index 67% rename from script-library/test/alpine/.devcontainer.json rename to script-library/test/regression/alpine/.devcontainer.json index a4a42e59..057b9390 100644 --- a/script-library/test/alpine/.devcontainer.json +++ b/script-library/test/regression/alpine/.devcontainer.json @@ -2,15 +2,14 @@ "name": "Script Tester - Alpine", "build": { "dockerfile": "../Dockerfile", - "context": "../../", + "context": "../../../", "args": { "IMAGE_TO_TEST": "alpine", "USERNAME": "vscode", "DISTRO": "alpine" } }, - - "workspaceMount": "source=${localWorkspaceFolder}/../..,target=/workspace,type=bind", + "workspaceMount": "source=${localWorkspaceFolder}/../../..,target=/workspace,type=bind", "workspaceFolder": "/workspace", "remoteUser": "vscode" diff --git a/script-library/test/debian/.devcontainer.json b/script-library/test/regression/debian/.devcontainer.json similarity index 82% rename from script-library/test/debian/.devcontainer.json rename to script-library/test/regression/debian/.devcontainer.json index 2c9bf8ad..d81a640b 100644 --- a/script-library/test/debian/.devcontainer.json +++ b/script-library/test/regression/debian/.devcontainer.json @@ -2,7 +2,7 @@ "name": "Script Tester - Debian", "build": { "dockerfile": "../Dockerfile", - "context": "../../", + "context": "../../../", "args": { "IMAGE_TO_TEST": "debian", "DISTRO": "debian", @@ -13,7 +13,7 @@ } }, "remoteUser": "vscode", - "workspaceMount": "source=${localWorkspaceFolder}/../..,target=/workspace,type=bind", + "workspaceMount": "source=${localWorkspaceFolder}/../../..,target=/workspace,type=bind", "workspaceFolder": "/workspace", "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker-host.sock,type=bind" ], "runArgs": ["--init", "--security-opt", "seccomp=unconfined"], diff --git a/script-library/test/redhat/.devcontainer.json b/script-library/test/regression/redhat/.devcontainer.json similarity index 78% rename from script-library/test/redhat/.devcontainer.json rename to script-library/test/regression/redhat/.devcontainer.json index 50f981f3..57f2ade0 100644 --- a/script-library/test/redhat/.devcontainer.json +++ b/script-library/test/regression/redhat/.devcontainer.json @@ -2,7 +2,7 @@ "name": "Script Tester - RedHat", "build": { "dockerfile": "../Dockerfile", - "context": "../../", + "context": "../../../", "args": { "IMAGE_TO_TEST": "centos:7", "USERNAME": "vscode", @@ -11,7 +11,7 @@ }, "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker-host.sock,type=bind" ], - "workspaceMount": "source=${localWorkspaceFolder}/../..,target=/workspace,type=bind", + "workspaceMount": "source=${localWorkspaceFolder}/../../..,target=/workspace,type=bind", "workspaceFolder": "/workspace", "overrideCommand": false, diff --git a/script-library/test/run-scripts.sh b/script-library/test/regression/run-scripts.sh similarity index 100% rename from script-library/test/run-scripts.sh rename to script-library/test/regression/run-scripts.sh diff --git a/script-library/test/regression/test.sh b/script-library/test/regression/test.sh new file mode 100755 index 00000000..206ba2b0 --- /dev/null +++ b/script-library/test/regression/test.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +IMAGE_TO_TEST="${1:-debian}" + +if [ "$IMAGE_TO_TEST" = "centos" ]; then + DISTRO="redhat" +elif [ "${IMAGE_TO_TEST}" = "ubuntu" ]; then + DISTRO="debian" +else + DISTRO=$IMAGE_TO_TEST +fi + +set -e + +cd "$(cd "$(dirname "$0")" && pwd)/../.." +echo -e "๐Ÿงช Testing image $IMAGE_TO_TEST (${DISTRO}-like)..." +docker build --build-arg DISTRO=$DISTRO --build-arg IMAGE_TO_TEST=$IMAGE_TO_TEST -f test/regression/Dockerfile . +echo -e "\n๐ŸŽ‰ All tests passed!" diff --git a/script-library/test/sshd/Dockerfile b/script-library/test/sshd/Dockerfile new file mode 100644 index 00000000..4e1e3c89 --- /dev/null +++ b/script-library/test/sshd/Dockerfile @@ -0,0 +1,20 @@ +ARG IMAGE_TO_TEST="mcr.microsoft.com/vscode/devcontainers/base:0-focal" +FROM $IMAGE_TO_TEST + +ENV PATH=/front-dockerfile-pre-script:$PATH:/back-dockerfile-pre-script +ENV this_var=true + +ARG DEFAULT_SHELL_TYPE=bash +COPY sshd-debian.sh test/sshd/*.sh /tmp/scripts/ +ARG current_user=$USER +USER root +RUN apt-get update \ + && bash /tmp/scripts/sshd-debian.sh \ + && bash /tmp/scripts/test-prep.sh ${DEFAULT_SHELL_TYPE} +USER $current_user + +ENV that_var=true +ENV PATH=/front-dockerfile-post-script:$PATH:/back-dockerfile-post-script + +ENTRYPOINT [ "/usr/local/share/ssh-init.sh" ] +CMD [ "sleep", "infinity" ] \ No newline at end of file diff --git a/script-library/test/sshd/debian/.devcontainer.json b/script-library/test/sshd/debian/.devcontainer.json new file mode 100644 index 00000000..a0c3d14f --- /dev/null +++ b/script-library/test/sshd/debian/.devcontainer.json @@ -0,0 +1,18 @@ +{ + "name": "Test SSHD script", + "build": { + "dockerfile": "../Dockerfile", + "context": "../../../", + "args": { + "IMAGE_TO_TEST": "mcr.microsoft.com/vscode/devcontainers/base:0-buster", + "DEFAULT_SHELL_TYPE": "bash" + } + }, + "remoteUser": "vscode", + "userEnvProbe": "none", + + "overrideCommand": false, + "forwardPorts": [2222], + "workspaceMount": "source=${localWorkspaceFolder}/../../..,target=/workspace,type=bind", + "workspaceFolder": "/workspace" +} \ No newline at end of file diff --git a/script-library/test/sshd/test-in-container-for-user.sh b/script-library/test/sshd/test-in-container-for-user.sh new file mode 100755 index 00000000..2559845d --- /dev/null +++ b/script-library/test/sshd/test-in-container-for-user.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +USERNAME=${1:-$(whoami)} +RUN_CODESPACES_TESTS=${2:-false} +set -e + +# Work around wierdness with $HOME in images where this is set by something else +if [ "${USERNAME}" = "root" ]; then + HOME="/root" +fi +if [ ! -f $HOME/.ssh/test.id_rsa ]; then + mkdir -p $HOME/.ssh + chmod 700 $HOME/.ssh + yes | ssh-keygen -q -b 2048 -t rsa -N '' -C "$USERNAME@localhost" -f $HOME/.ssh/test.id_rsa + cat $HOME/.ssh/test.id_rsa.pub >> $HOME/.ssh/authorized_keys + chmod 644 $HOME/.ssh/authorized_keys +fi + +check_result() { + local shell_to_test=$1 + local teset_result=$2 + local regex=$3 + if echo "$test_result" | grep -E "$regex" > /dev/null 2>&1; then + echo " โœ”๏ธ $regex passed." + return 0 + fi + echo " โŒ $regex failed!" + return 1 +} + +run_test() { + # Processing is async, so sleep for 1s + sleep 1s + + local shell_to_test=$1 + local test_result="$(ssh -q -p 2222 -i $HOME/.ssh/test.id_rsa -o StrictHostKeyChecking=no -o GlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null $USERNAME@localhost $shell_to_test -lic 'env' 2> /dev/null)" + echo -e "\n๐Ÿงช Run \"${USERNAME}\" user tests for $1...\n(*) Environment for $shell_to_test:\n$test_result" + echo "(*) /etc/environment contents:" + cat /etc/environment + echo "(*) Running environment var tests for $1..." + check_result "$shell_to_test" "$test_result" 'this_var="?true"?' + check_result "$shell_to_test" "$test_result" 'that_var="?true"?' + if [ "${RUN_CODESPACES_TESTS}" = "true" ]; then + check_result "$shell_to_test" "$test_result" 'CODESPACES="?true"?' + check_result "$shell_to_test" "$test_result" "GITHUB_TOKEN=.*" + check_result "$shell_to_test" "$test_result" "GITHUB_CODESPACE_TOKEN=.*" + check_result "$shell_to_test" "$test_result" "GIT_COMMITTER_NAME=.*" + check_result "$shell_to_test" "$test_result" "GIT_COMMITTER_EMAIL=.*" + fi + echo "(*) Running PATH tests for $1..." + check_result "$shell_to_test" "$test_result" "PATH=.*/front-dockerfile-pre-script:.*:/back-dockerfile-pre-script.*" + check_result "$shell_to_test" "$test_result" "PATH=.*/front-dockerfile-post-script:.*:/back-dockerfile-post-script.*" + check_result "$shell_to_test" "$test_result" "PATH=.*(/front-profile-post-export:|/front-profile-begin:).*(:/back-profile-post-export|:/back-profile-begin).*" + if [ "$shell_to_test" != "zsh" ]; then + check_result "$shell_to_test" "$test_result" "PATH=.*/front-profile-pre-rc:.*:/back-profile-pre-rc.*" + check_result "$shell_to_test" "$test_result" "PATH=.*/front-pre-profile\.d:.*:/back-pre-profile\.d.*" + fi + check_result "$shell_to_test" "$test_result" "PATH=.*/front-profile-end:.*:/back-profile-end.*" + if [ "$shell_to_test" != "sh" ]; then + check_result "$shell_to_test" "$test_result" "PATH=.*/front-rc:.*" + check_result "$shell_to_test" "$test_result" "PATH=.*:/back-rc.*" + fi + echo "๐Ÿ‘ All $1 tests passed for user \"$USERNAME\". " +} + +run_test bash +if type zsh > /dev/null 2>&1; then + run_test zsh +fi +run_test sh + +echo -e "\n๐Ÿ† All shell tests passed for user \"$USERNAME\"!" diff --git a/script-library/test/sshd/test-in-container.sh b/script-library/test/sshd/test-in-container.sh new file mode 100755 index 00000000..a3a21f2e --- /dev/null +++ b/script-library/test/sshd/test-in-container.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +RUN_CODESPACES_TESTS=${1:-false} + +cd "$(cd "$(dirname "$0")" && pwd)" +set -e + +if [ "$(id -u)" -ne 0 ]; then + echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' + exit 1 +fi + +./test-in-container-for-user.sh root $RUN_CODESPACES_TESTS +USERNAME="$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd 2>/dev/null)" +if [ "${USERNAME}" != "" ]; then + su $USERNAME -c ./test-in-container-for-user.sh $USERNAME $RUN_CODESPACES_TESTS +fi + +echo -e "\n๐Ÿ™Œ Tests passed for all users!" diff --git a/script-library/test/sshd/test-prep.sh b/script-library/test/sshd/test-prep.sh new file mode 100755 index 00000000..2ceb144d --- /dev/null +++ b/script-library/test/sshd/test-prep.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +DEFAULT_SHELL_TYPE=$1 + +set -e + +apt-get update +apt-get -y install zsh + +echo -e "export PATH=/front-profile-begin:\$PATH:/back-profile-begin\n$(cat /etc/profile)" > /etc/profile +echo -e "export PATH=/front-profile-begin:\$PATH:/back-profile-begin\n$(cat /etc/zsh/zlogin)" > /etc/zsh/zlogin +sed -i 's%export PATH$%&\nexport PATH=/front-profile-post-export:$PATH:/back-profile-post-export%g' /etc/profile +sed -i 's%if \[ "\${PS1-}" \]; then%export PATH=/front-profile-pre-rc:$PATH:/back-profile-pre-rc\n&%g' /etc/profile +sed -i 's%if \[ -d /etc/profile\.d \]; then%export PATH=/front-pre-profile.d:$PATH:/back-pre-profile.d\n&%g' /etc/profile +echo "export PATH=/front-profile-end:\$PATH:/back-profile-end" | tee -a /etc/zsh/zlogin >> /etc/profile +echo "export PATH=/front-rc:\$PATH:/back-rc" | tee -a /etc/zsh/zshrc >> /etc/bash.bashrc +echo "etc_env_var=true" >> /etc/environment + +echo -e "vscode\nvscode" | passwd root 2>&1 +USERNAME="$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd 2>/dev/null)" +if [ "${USERNAME}" != "" ]; then + chown $USERNAME /usr/local/share/ssh-init.sh + echo -e "vscode\nvscode" | passwd $USERNAME 2>&1 + chsh -s $(which $DEFAULT_SHELL_TYPE) $USERNAME +fi diff --git a/script-library/test/sshd/test-user-secrets.json b/script-library/test/sshd/test-user-secrets.json new file mode 100644 index 00000000..1bb89991 --- /dev/null +++ b/script-library/test/sshd/test-user-secrets.json @@ -0,0 +1,42 @@ +[ + { + "type": "EnvironmentVariable", + "name": "GITHUB_TOKEN", + "value": "foo" + }, + { + "type": "EnvironmentVariable", + "name": "GITHUB_CODESPACE_TOKEN", + "value": "foo" + }, + { + "type": "EnvironmentVariable", + "name": "GIT_COMMITTER_NAME", + "value": "foo" + }, + { + "type": "EnvironmentVariable", + "name": "GIT_COMMITTER_EMAIL", + "value": "foo@foo.com" + }, + { + "type": "ContainerRegistry", + "name": "docker.pkg.github.com", + "value": "foo" + }, + { + "type": "EnvironmentVariable", + "name": "GH_TOKEN", + "value": "foo" + }, + { + "type": "EnvironmentVariable", + "name": "GH_USER", + "value": "foo" + }, + { + "type": "ContainerRegistry", + "name": "foo@https://index.docker.io/v1/", + "value": "foo@https://index.docker.io/v1/" + } +] \ No newline at end of file