commit 3849aab9b592dd64fcfa3ab28e937d611323133a Author: amorozov Date: Mon Mar 3 00:42:00 2025 +0300 initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3ef7e82 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Force checkout as Unix endline style +text eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..17d5f0a --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +bats-core/ +bats/ +target/ +/.vscode/ +build-windows.yaml diff --git a/AGENT.README.md b/AGENT.README.md new file mode 100644 index 0000000..1e11440 --- /dev/null +++ b/AGENT.README.md @@ -0,0 +1,265 @@ +# Docker image for Jenkins agents connected over SSH + +[![Join the chat at https://gitter.im/jenkinsci/docker](https://badges.gitter.im/jenkinsci/docker.svg)](https://gitter.im/jenkinsci/docker?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![GitHub stars](https://img.shields.io/github/stars/jenkinsci/docker-ssh-agent?label=GitHub%20stars)](https://github.com/jenkinsci/docker-ssh-agent) +[![Docker Pulls](https://img.shields.io/docker/pulls/jenkins/ssh-agent.svg)](https://hub.docker.com/r/jenkins/ssh-agent/) +[![GitHub release](https://img.shields.io/github/release/jenkinsci/docker-ssh-agent.svg?label=changelog)](https://github.com/jenkinsci/docker-ssh-agent/releases) + +A [Jenkins](https://jenkins.io) agent image which allows using SSH to establish the connection. +It can be used together with the [SSH Build Agents plugin](https://plugins.jenkins.io/ssh-slaves) or other similar plugins. + +See [Jenkins Distributed builds](https://wiki.jenkins-ci.org/display/JENKINS/Distributed+builds) for more info. + +## Running + +### Running with the SSH Build Agents plugin + +To run a Docker container + +```bash +docker run -d --rm --name=agent --publish 2200:22 -e "JENKINS_AGENT_SSH_PUBKEY=" jenkins/ssh-agent +``` + + - `-d`: To start a container in detached mode, use the `-d` option. Containers started in detached mode exit when the root process used to run the container exits, unless you also specify the --rm option. + - `--rm`: If you use -d with --rm, the container is removed when it exits or when the daemon exits, whichever happens first. + - `--name`: Assigns a name to the container. If you do not specify a name, Docker generates a random name. + - `--publish 2200:22`: Publishes the host port 2200 to the agent container port 22 (SSH) to allow connection from the host with `ssh jenkins@localhost -p 2200` + +Please note none of these options are mandatory, they are just examples. + +You will then be able to connect this agent using the [SSH Build Agents plugin](https://plugins.jenkins.io/ssh-slaves) as "jenkins" with the matching private key. + +When using the Linux image, you have to set the value of the `Remote root directory` to `/home/jenkins/agent` in the agent configuration UI. + +![Remote root directory with a Linux agent](docs/ssh-plugin-remote-root-directory-linux.png "Remote root directory with a Linux agent") + +When using the Windows image, you have to set the value of the `Remote root directory` to `C:/Users/jenkins/Work` in the agent configuration UI. + +![Remote root directory with a Windows agent](docs/ssh-plugin-remote-root-directory-windows.png "Remote root directory with a Windows agent") + +If you intend to use another directory than `/home/jenkins/agent` under Linux or `C:/Users/jenkins/Work` under Windows, don't forget to add it as a data volume. + +```bash +docker run -v docker-volume-for-jenkins-ssh-agent:/home/jenkins/agent:rw jenkins/ssh-agent "" +``` + +### How to use this image with Docker Plugin + +To use this image with [Docker Plugin](https://plugins.jenkins.io/docker-plugin), you need to pass the public SSH key using environment variable `JENKINS_AGENT_SSH_PUBKEY` and not as a startup argument. + +In _Environment_ field of the Docker Template (advanced section), just add: + + JENKINS_AGENT_SSH_PUBKEY= + +Don't put quotes around the public key. + +Please note that you have to set the value of the `Remote File System Root` to `/home/jenkins/agent` in the Docker Agent Template configuration UI. + +![Remote File System Root](docs/docker-plugin-remote-filesystem-root.png "Remote File System Root directory") + +If you intend to use another directory than `/home/jenkins/agent`, don't forget to add it as a data volume. + +![Docker Volumes mounts](docs/docker-plugin-volumes.png "Docker Volumes mounts") + +You should be all set. + +## Extending the image +Should you need to extend the image, you could use something along those lines: + +```Dockerfile +FROM jenkins/ssh-agent:debian-jdk17 as ssh-agent +# [...] +COPY --chown=jenkins mykey "${JENKINS_AGENT_HOME}"/.ssh/mykey +# [...] +``` + +## Configurations + +The image has several supported configurations, which can be accessed via the following tags: + +`${IMAGE_VERSION}` can be found on the [releases](https://github.com/jenkinsci/docker-ssh-agent/releases) page. + +* `latest`, `latest-jdk11`, `jdk11`, `latest-bookworm-jdk11`, `bookworm-jdk11`, `latest-debian-jdk11`, `debian-jdk11`, `${IMAGE_VERSION}`, `${IMAGE_VERSION}-jdk11`, ([Dockerfile](debian/Dockerfile)) +* `latest-jdk17`, `jdk17`, `latest-bookworm-jdk17`, `bookworm-jdk17`, `latest-debian-jdk17`, `debian-jdk17`, `${IMAGE_VERSION}-jdk17`, ([Dockerfile](debian/Dockerfile)) +* `nanoserver-1809`, `nanoserver-ltsc2019`, `nanoserver-1809-jdk11`, `nanoserver-ltsc2019-jdk11`, `${IMAGE_VERSION}-nanoserver-1809`, `${IMAGE_VERSION}-nanoserver-ltsc2019`, `${IMAGE_VERSION}-nanoserver-1809-jdk11`, `${IMAGE_VERSION}-nanoserver-ltsc2019-jdk11` ([Dockerfile](windows/nanoserver-ltsc2019/Dockerfile)) +* `windowsservercore-1809`, `windowsservercore-ltsc2019`, `windowsservercore-1809-jdk11`, `windowsservercore-ltsc2019-jdk11`, `${IMAGE_VERSION}-windowsservercore-1809`, `${IMAGE_VERSION}-windowsservercore-ltsc2019`, `${IMAGE_VERSION}-windowsservercore-1809-jdk11`, `${IMAGE_VERSION}-windowsservercore-ltsc2019-jdk11` ([Dockerfile](windows/windowsservercore-ltsc2019/Dockerfile)) + +## Building instructions + +### Pre-requisites + +Should you want to build this image on your machine (before submitting a pull request for example), please have a look at the pre-requisites: + +* A GNU/Linux machine with [Docker](https://docs.docker.com/engine/install/), a macOS machine with [Docker Desktop](https://docs.docker.com/desktop/install/mac-install/), or a Windows machine with [Docker for Windows](https://docs.docker.com/docker-for-windows/) installed +* Docker BuildX plugin [installed](https://github.com/docker/buildx#installing) on older versions of Docker (from `19.03`). Docker Buildx is included in recent versions of Docker Desktop for Windows, macOS, and Linux. Docker Linux packages also include Docker Buildx when installed using the DEB or RPM packages. +* [GNU Make](https://www.gnu.org/software/make/) [installed](https://command-not-found.com/make) +* jq [installed](https://command-not-found.com/jq) +* yq [installed](https://github.com/mikefarah/yq) (for Windows) +* [GNU Bash](https://www.gnu.org/software/bash/) [installed](https://command-not-found.com/bash) +* git [installed](https://command-not-found.com/git) +* curl [installed](https://command-not-found.com/curl) + +### Building + +#### Target images + +If you want to see the target images that will be built, you can issue the following command: + +```bash +make list +alpine_jdk11 +alpine_jdk17 +debian_jdk11 +debian_jdk17 +``` + +#### Building a specific image + +If you want to build a specific image, you can issue the following command: + +```bash +make build-_ +``` + +That would give for JDK 17 on Alpine Linux: + +```bash +make build-alpine_jdk17 +``` + +#### Building images supported by your current architecture + +Then, you can build the images supported by your current architecture by running: + +```bash +make build +``` + +#### Testing all images + +If you want to test these images, you can run: + +```bash +make test +``` +#### Testing a specific image + +If you want to test a specific image, you can run: + +```bash +make test-_ +``` + +That would give for JDK 17 on Alpine Linux: + +```bash +make test-alpine_jdk17 +``` + +#### Building all images + +You can build all images (even those unsupported by your current architecture) by running: + +```bash +make every-build +``` + +#### Other `make` targets + +`show` gives us a detailed view of the images that will be built, with the tags, platforms, and Dockerfiles. + +```bash +make show +{ + "group": { + "default": { + "targets": [ + "alpine_jdk17", + "alpine_jdk11", + "debian_jdk11", + "debian_jdk17", + ] + } + }, + "target": { + "alpine_jdk11": { + "context": ".", + "dockerfile": "alpine/Dockerfile", + "tags": [ + "docker.io/jenkins/ssh-agent:alpine-jdk11", + "docker.io/jenkins/ssh-agent:latest-alpine-jdk11" + ], + "platforms": [ + "linux/amd64" + ], + "output": [ + "type=docker" + ] + }, + [...] +``` + +`bats` is a dependency target. It will update the [`bats` submodule](https://github.com/bats-core/bats-core) and run the tests. + +```bash +make bats +make: 'bats' is up to date. +``` + +`publish` allows the publication of all images targeted by 'linux' to a registry. + +`docker-init` is dedicated to Jenkins infrastructure for initializing docker and isn't required in other contexts. + +### Building and testing on Windows + +#### Building all images + +Run `.\build.ps1` to launch the build of the images corresponding to the "windows" target of docker-bake.hcl. + +Internally, the first time you'll run this script and if there is no build-windows.yaml file in your repository, it will use a combination of `docker buildx bake` and `yq` to generate a build-windows.yaml docker compose file containing all Windows image definitions from docker-bake.hcl. Then it will run `docker compose` on this file to build these images. + +You can modify this docker compose file as you want, then rerun `.\build.ps1`. +It won't regenerate the docker compose file from docker-bake.hcl unless you add the `-OverwriteDockerComposeFile` build.ps1 parameter: `.\build.ps1 -OverwriteDockerComposeFile`. + +Note: you can generate this docker compose file from docker-bake.hcl yourself with the following command (require `docker buildx` and `yq`): + +```console +# - Use docker buildx bake to output image definitions from the "windows" bake target +# - Convert with yq to the format expected by docker compose +# - Store the result in the docker compose file + +$ docker buildx bake --progress=plain --file=docker-bake.hcl windows --print ` + | yq --prettyPrint '.target[] | del(.output) | {(. | key): {\"image\": .tags[0], \"build\": .}}' | yq '{\"services\": .}' ` + | Out-File -FilePath build-windows.yaml +``` + +Note that you don't need build.ps1 to build (or to publish) your images from this docker compose file, you can use `docker compose --file=build-windows.yaml build`. + +#### Testing all images + +Run `.\build.ps1 test` if you also want to run the tests harness suit. + +Run `.\build.ps1 test -TestsDebug 'debug'` to also get commands & stderr of tests, displayed on top of them. +You can set it to `'verbose'` to also get stdout of every test command. + +Note that instead of passing `-TestsDebug` parameter to build.ps1, you can set the $env:TESTS_DEBUG environment variable to the desired value. + +Also note that contrary to the Linux part, you have to build the images before testing them. + +#### Dry run + +Add the `-DryRun` parameter to print out any build, publish or tests commands instead of executing them: `.\build.ps1 test -DryRun` + +#### Building and testing a specific image + +You can build (and test) only one image type by setting `-ImageType` to a combination of Windows flavors ("nanoserver" & "windowsservercore") and Windows versions ("1809", "ltsc2019", "ltsc2022"). + +Ex: `.\build.ps1 -ImageType 'nanoserver-ltsc2019'` + +Warning: trying to build `windowsservercore-1809` will fail as there is no corresponding image from Microsoft. + +## Changelog + +See [GitHub Releases](https://github.com/jenkinsci/docker-ssh-agent/releases/latest). +Note that the changelogs and release tags were introduced in Dec 2019, and there are no entries for previous releases. +Please consult with the commit history if needed. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1e87c5a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,136 @@ +# MIT License +# +# Copyright (c) 2019-2022 Fabio Kruger and other contributors +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +ARG JAVA_VERSION=17.0.14_7 +FROM docker:dind AS jre-build + +SHELL ["/bin/ash", "-eo", "pipefail", "-c"] + +# This Build ARG is populated by Docker +# Ref. https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope +ARG TARGETPLATFORM + +COPY jdk-download-url.sh /usr/bin/local/jdk-download-url.sh +COPY jdk-download.sh /usr/bin/local/jdk-download.sh + +RUN chmod +x /usr/bin/local/jdk-download.sh && \ + chmod +x /usr/bin/local/jdk-download-url.sh && \ + cat /usr/bin/local/jdk-download.sh && \ + cat /usr/bin/local/jdk-download-url.sh && \ + echo "ASH HERE: $(which ash)" + +ARG JAVA_VERSION=17.0.14_7 +# hadolint ignore=DL3018 +RUN apk add --no-cache \ + ca-certificates \ + jq \ + curl + +RUN /usr/bin/local/jdk-download.sh alpine + +ENV PATH="/opt/jdk-${JAVA_VERSION}/bin:${PATH}" + +RUN case "$(jlink --version 2>&1)" in \ + "17."*) set -- "--compress=2" ;; \ + # the compression argument is different for JDK21 + "21."*) set -- "--compress=zip-6" ;; \ + *) echo "ERROR: unmanaged jlink version pattern" && exit 1 ;; \ + esac; \ + jlink \ + --strip-java-debug-attributes \ + "$1" \ + --add-modules ALL-MODULE-PATH \ + --no-man-pages \ + --no-header-files \ + --output /javaruntime + +FROM docker:dind AS build + +ARG user=jenkins +ARG group=jenkins +ARG uid=1000 +ARG gid=1000 +ARG JENKINS_AGENT_HOME=/home/${user} + +ENV JENKINS_AGENT_HOME=${JENKINS_AGENT_HOME} + +ARG AGENT_WORKDIR="${JENKINS_AGENT_HOME}"/agent +# Persist agent workdir path through an environment variable for people extending the image +ENV AGENT_WORKDIR=${AGENT_WORKDIR} + +RUN addgroup -g "${gid}" "${group}" \ + # Set the home directory (h), set user and group id (u, G), set the shell, don't ask for password (D) + && adduser -h "${JENKINS_AGENT_HOME}" -u "${uid}" -G "${group}" -s /bin/bash -D "${user}" \ + # Unblock user + && passwd -u "${user}" \ + # Prepare subdirectories + && mkdir -p "${JENKINS_AGENT_HOME}/.ssh/" "${JENKINS_AGENT_HOME}/.jenkins/" "${AGENT_WORKDIR}" \ + && chown -R "${uid}":"${gid}" "${JENKINS_AGENT_HOME}" "${AGENT_WORKDIR}" + +RUN addgroup docker || true && \ + addgroup ${user} docker + +RUN apk add --no-cache \ + bash \ + git-lfs \ + less \ + musl-locales \ + netcat-openbsd \ + openssh \ + patch + +# setup SSH server +RUN sed -i /etc/ssh/sshd_config \ + -e 's/#PermitRootLogin.*/PermitRootLogin no/' \ + -e 's/#PasswordAuthentication.*/PasswordAuthentication no/' \ + -e 's/#SyslogFacility.*/SyslogFacility AUTH/' \ + -e 's/#LogLevel.*/LogLevel INFO/' \ + -e 's/#PermitUserEnvironment.*/PermitUserEnvironment yes/' \ + && mkdir /var/run/sshd + +# Install JDK + +ENV JAVA_HOME=/opt/java/openjdk +COPY --from=jre-build /javaruntime "$JAVA_HOME" +ENV PATH="${JAVA_HOME}/bin:${PATH}" + +# VOLUME directive must happen after setting up permissions and content +VOLUME "${AGENT_WORKDIR}" "${JENKINS_AGENT_HOME}"/.jenkins "/tmp" "/run" "/var/run" +WORKDIR "${JENKINS_AGENT_HOME}" + +# Alpine's ssh doesn't use $PATH defined in /etc/environment, so we define `$PATH` in `~/.ssh/environment` +# The file path has been created earlier in the file by `mkdir -p` and we also have configured sshd so that it will +# allow environment variables to be sourced (see `sed` command related to `PermitUserEnvironment`) +RUN echo "PATH=${PATH}" >> ${JENKINS_AGENT_HOME}/.ssh/environment +COPY setup-sshd /usr/local/bin/setup-sshd + +EXPOSE 22 + +ENTRYPOINT ["setup-sshd"] + +LABEL \ + org.opencontainers.image.vendor="Jenkins project" \ + org.opencontainers.image.title="Official Jenkins SSH Agent Docker image" \ + org.opencontainers.image.description="A Jenkins agent image which allows using SSH to establish the connection" \ + org.opencontainers.image.url="https://www.jenkins.io/" \ + org.opencontainers.image.source="https://github.com/jenkinsci/docker-ssh-agent" \ + org.opencontainers.image.licenses="MIT" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..13b9d40 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2015-2019 Jenkins project contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2f2579f --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# Склейка DinD и Jenkins SSH Agent. + +- Берем [Jenkins SSH Agent](https://github.com/jenkinsci/docker-ssh-agent), а именно [Alpine Dockerfile](https://github.com/jenkinsci/docker-ssh-agent/blob/master/alpine/Dockerfile) +- Изменяем базовый образ на `docker:dind` (по-умолчанию он на базе alpine) +- Добавляем в `Dockerfile` под создание пользователя `jenkins` строки: +```Dockerfile +RUN addgroup docker || true && \ + addgroup ${user} docker +``` +- В `setup-sshd` вписываем строки: +```shell +# dockerd's patch +dockerd-entrypoint.sh +``` +- Собираем, тэгаем и пушим diff --git a/dockerd-sshd-entrypoint.sh b/dockerd-sshd-entrypoint.sh new file mode 100644 index 0000000..da537b2 --- /dev/null +++ b/dockerd-sshd-entrypoint.sh @@ -0,0 +1,5 @@ +dockerd-entrypoint.sh +addgroup docker || true +addgroup jenkins docker + +setup-sshd $@ \ No newline at end of file diff --git a/docs/docker-plugin-remote-filesystem-root.png b/docs/docker-plugin-remote-filesystem-root.png new file mode 100644 index 0000000..db5808c Binary files /dev/null and b/docs/docker-plugin-remote-filesystem-root.png differ diff --git a/docs/docker-plugin-volumes.png b/docs/docker-plugin-volumes.png new file mode 100644 index 0000000..7f43444 Binary files /dev/null and b/docs/docker-plugin-volumes.png differ diff --git a/docs/ssh-plugin-remote-root-directory-linux.png b/docs/ssh-plugin-remote-root-directory-linux.png new file mode 100644 index 0000000..e8dbf37 Binary files /dev/null and b/docs/ssh-plugin-remote-root-directory-linux.png differ diff --git a/docs/ssh-plugin-remote-root-directory-windows.png b/docs/ssh-plugin-remote-root-directory-windows.png new file mode 100644 index 0000000..10ff623 Binary files /dev/null and b/docs/ssh-plugin-remote-root-directory-windows.png differ diff --git a/jdk-download-url.sh b/jdk-download-url.sh new file mode 100644 index 0000000..dbf175b --- /dev/null +++ b/jdk-download-url.sh @@ -0,0 +1,106 @@ +#!/bin/sh + +# Check if at least one argument was passed to the script +# If one argument was passed and JAVA_VERSION is set, assign the argument to OS +# If two arguments were passed, assign them to JAVA_VERSION and OS respectively +# If three arguments were passed, assign them to JAVA_VERSION, OS and ARCHS respectively +# If not, check if JAVA_VERSION and OS are already set. If they're not set, exit the script with an error message +if [ $# -eq 1 ] && [ -n "$JAVA_VERSION" ]; then + OS=$1 +elif [ $# -eq 2 ]; then + JAVA_VERSION=$1 + OS=$2 +elif [ $# -eq 3 ]; then + JAVA_VERSION=$1 + OS=$2 + ARCHS=$3 +elif [ -z "$JAVA_VERSION" ] && [ -z "$OS" ]; then + echo "Error: No Java version and OS specified. Please set the JAVA_VERSION and OS environment variables or pass them as arguments." >&2 + exit 1 +elif [ -z "$JAVA_VERSION" ]; then + echo "Error: No Java version specified. Please set the JAVA_VERSION environment variable or pass it as an argument." >&2 + exit 1 +elif [ -z "$OS" ]; then + OS=$1 + if [ -z "$OS" ]; then + echo "Error: No OS specified. Please set the OS environment variable or pass it as an argument." >&2 + exit 1 + fi +fi + +# Check if ARCHS is set. If it's not set, assign the current architecture to it +if [ -z "$ARCHS" ]; then + ARCHS=$(uname -m | sed -e 's/x86_64/x64/' -e 's/armv7l/arm/') +else + # Convert ARCHS to an array + OLD_IFS=$IFS + IFS=',' + set -- "$ARCHS" + ARCHS="" + for arch in "$@"; do + ARCHS="$ARCHS $arch" + done + IFS=$OLD_IFS +fi + +# Check if jq and curl are installed +# If they are not installed, exit the script with an error message +if ! command -v jq >/dev/null 2>&1 || ! command -v curl >/dev/null 2>&1; then + echo "jq and curl are required but not installed. Exiting with status 1." >&2 + exit 1 +fi + +# Replace underscores with plus signs in JAVA_VERSION +ARCHIVE_DIRECTORY=$(echo "$JAVA_VERSION" | tr '_' '+') + +# URL encode ARCHIVE_DIRECTORY +ENCODED_ARCHIVE_DIRECTORY=$(echo "$ARCHIVE_DIRECTORY" | xargs -I {} printf %s {} | jq "@uri" -jRr) + +# Determine the OS type for the URL +OS_TYPE="linux" +if [ "$OS" = "alpine" ]; then + OS_TYPE="alpine-linux" +fi +if [ "$OS" = "windows" ]; then + OS_TYPE="windows" +fi + +# Initialize a variable to store the URL for the first architecture +FIRST_ARCH_URL="" + +# Loop over the array of architectures +for ARCH in $ARCHS; do + # Fetch the download URL from the Adoptium API + URL="https://api.adoptium.net/v3/binary/version/jdk-${ENCODED_ARCHIVE_DIRECTORY}/${OS_TYPE}/${ARCH}/jdk/hotspot/normal/eclipse?project=jdk" + + if ! RESPONSE=$(curl -fsI "$URL"); then + echo "Error: Failed to fetch the URL for architecture ${ARCH} from ${URL}. Exiting with status 1." >&2 + echo "Response: $RESPONSE" >&2 + exit 1 + fi + + # Extract the redirect URL from the HTTP response + REDIRECTED_URL=$(echo "$RESPONSE" | grep -i location | awk '{print $2}' | tr -d '\r') + + # If no redirect URL was found, exit the script with an error message + if [ -z "$REDIRECTED_URL" ]; then + echo "Error: No redirect URL found for architecture ${ARCH} from ${URL}. Exiting with status 1." >&2 + echo "Response: $RESPONSE" >&2 + exit 1 + fi + + # Use curl to check if the URL is reachable + # If the URL is not reachable, print an error message and exit the script with status 1 + if ! curl -v -fs "$REDIRECTED_URL" >/dev/null 2>&1; then + echo "${REDIRECTED_URL}" is not reachable for architecture "${ARCH}". >&2 + exit 1 + fi + + # If FIRST_ARCH_URL is empty, store the current URL + if [ -z "$FIRST_ARCH_URL" ]; then + FIRST_ARCH_URL=$REDIRECTED_URL + fi +done + +# If all downloads are successful, print the URL for the first architecture +echo "$FIRST_ARCH_URL" diff --git a/jdk-download.sh b/jdk-download.sh new file mode 100644 index 0000000..5132492 --- /dev/null +++ b/jdk-download.sh @@ -0,0 +1,51 @@ +#!/bin/sh +set -x +# Check if curl and tar are installed +if ! command -v curl >/dev/null 2>&1 || ! command -v tar >/dev/null 2>&1 ; then + echo "curl and tar are required but not installed. Exiting with status 1." >&2 + exit 1 +fi + +# Set the OS to "standard" by default +OS="standard" + +# If a second argument is provided, use it as the OS +if [ $# -eq 1 ]; then + OS=$1 +fi + +# Call jdk-download-url.sh with JAVA_VERSION and OS as arguments +# The two scripts should be in the same directory. +# That's why we're trying to find the directory of the current script and use it to call the other script. +SCRIPT_DIR=$(cd "$(dirname "$0")" || exit; pwd) +if ! DOWNLOAD_URL=$("${SCRIPT_DIR}"/jdk-download-url.sh "${JAVA_VERSION}" "${OS}"); then + echo "Error: Failed to fetch the URL. Exiting with status 1." >&2 + exit 1 +fi + +# Use curl to download the JDK archive from the URL +if ! curl --silent --location --output /tmp/jdk.tar.gz "${DOWNLOAD_URL}"; then + echo "Error: Failed to download the JDK archive. Exiting with status 1." >&2 + exit 1 +fi + +# Extract the archive to the /opt/ directory +if ! tar -xzf /tmp/jdk.tar.gz -C /opt/; then + echo "Error: Failed to extract the JDK archive. Exiting with status 1." >&2 + exit 1 +fi + +# Get the name of the extracted directory +EXTRACTED_DIR=$(tar -tzf /tmp/jdk.tar.gz | head -n 1 | cut -f1 -d"/") + +# Rename the extracted directory to /opt/jdk-${JAVA_VERSION} +if ! mv "/opt/${EXTRACTED_DIR}" "/opt/jdk-${JAVA_VERSION}"; then + echo "Error: Failed to rename the extracted directory. Exiting with status 1." >&2 + exit 1 +fi + +# Remove the downloaded archive +if ! rm -f /tmp/jdk.tar.gz; then + echo "Error: Failed to remove the downloaded archive. Exiting with status 1." >&2 + exit 1 +fi diff --git a/setup-sshd b/setup-sshd new file mode 100644 index 0000000..2580a1b --- /dev/null +++ b/setup-sshd @@ -0,0 +1,85 @@ +#!/usr/bin/env bash + +set -ex + +# The MIT License +# +# Copyright (c) 2015, CloudBees, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +# Usage: +# docker run jenkins/ssh-agent +# or +# docker run -e "JENKINS_AGENT_SSH_PUBKEY=" jenkins/ssh-agent + +write_key() { + local ID_GROUP + + # As user, group, uid, gid and JENKINS_AGENT_HOME can be overridden at build, + # we need to find the values for JENKINS_AGENT_HOME + # ID_GROUP contains the user:group of JENKINS_AGENT_HOME directory + ID_GROUP=$(stat -c '%U:%G' "${JENKINS_AGENT_HOME}") + + mkdir -p "${JENKINS_AGENT_HOME}/.ssh" + echo "$1" > "${JENKINS_AGENT_HOME}/.ssh/authorized_keys" + chown -Rf "${ID_GROUP}" "${JENKINS_AGENT_HOME}/.ssh" + chmod 0700 -R "${JENKINS_AGENT_HOME}/.ssh" +} + +if [[ ${JENKINS_AGENT_SSH_PUBKEY} == ssh-* ]]; then + write_key "${JENKINS_AGENT_SSH_PUBKEY}" +fi +if [[ ${JENKINS_SLAVE_SSH_PUBKEY} == ssh-* ]]; then + write_key "${JENKINS_SLAVE_SSH_PUBKEY}" +fi + +# ensure variables passed to docker container are also exposed to ssh sessions +env | grep _ >> /etc/environment + +if [[ $# -gt 0 ]]; then + echo "${0##*/} params: $@" + + if [[ $1 == ssh-* ]]; then + echo "Authorizing ssh pubkey found in params." + write_key "$1" + shift 1 + elif [[ "$@" == "/usr/sbin/sshd -D -p 22" ]]; then + # neutralize default jenkins docker-plugin command + # we will run sshd at the end anyway + echo "Ignoring provided sshd command." + + # if unquoted (4 tokens) shift extra 3 + [[ "$2" == "-D" ]] && shift 3 + + shift 1 + else + echo "Executing params: '$@'" + exec "$@" + fi +fi + +# generate host keys if not present +ssh-keygen -A + +# theswayfarer's patch +dockerd-entrypoint.sh + +# do not detach (-D), log to stderr (-e), passthrough other arguments +exec /usr/sbin/sshd -D -e "${@}" diff --git a/setup-sshd.ps1 b/setup-sshd.ps1 new file mode 100644 index 0000000..d53ce13 --- /dev/null +++ b/setup-sshd.ps1 @@ -0,0 +1,110 @@ +# The MIT License +# +# Copyright (c) 2019-2020, Alex Earl +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +# Usage: +# docker run jenkins/ssh-agent +# or +# docker run -e "JENKINS_AGENT_SSH_PUBKEY=" jenkins/ssh-agent +# or +# docker run -e "JENKINS_AGENT_SSH_PUBKEY=" -e "JENKINS_AGENT_SSH_KNOWNHOST_0=" -e "JENKINS_AGENT_SSH_KNOWNHOST_n=" jenkins/ssh-agent + +[CmdletBinding()] +Param( + [Parameter(Position = 0, ValueFromRemainingArguments = $true)] + [string] $Cmd +) + +function Get-SSHDir { + return Join-Path "C:/Users/$env:JENKINS_AGENT_USER" '.ssh' +} + +function Check-SSHDir { + $sshDir = Get-SSHDir + if(-not (Test-Path $sshDir)) { + New-Item -Type Directory -Path $sshDir | Out-Null + icacls.exe $sshDir /setowner $env:JENKINS_AGENT_USER | Out-Null + icacls.exe $sshDir /grant $('{0}:(CI)(OI)(F)' -f $env:JENKINS_AGENT_USER) /grant "administrators:(CI)(OI)(F)" | Out-Null + icacls.exe $sshDir /inheritance:r | Out-Null + } +} + +function Write-Key($Key) { + # this writes the key and sets the permissions correctly for pubkey auth + $authorizedKeys = Join-Path (Get-SSHDir) 'authorized_keys' + Set-Content -Path $authorizedKeys -Value "$Key" -Encoding UTF8 + + icacls.exe $authorizedKeys /setowner $env:JENKINS_AGENT_USER | Out-Null +} + +function Write-HostKey($Key) { + # this writes the key and sets the permissions + $knownHosts = Join-Path (Get-SSHDir) 'known_hosts' + Set-Content -Path $knownHosts -Value "$Key" -Encoding UTF8 + + icacls.exe $knownHosts /setowner $env:JENKINS_AGENT_USER | Out-Null +} + +# Give the user Full Access to the home directory +icacls.exe "C:/Users/$env:JENKINS_AGENT_USER" /grant "${env:JENKINS_AGENT_USER}:(CI)(OI)(F)" | Out-Null + +# check the .ssh dir permissions +Check-SSHDir + +if($env:JENKINS_AGENT_SSH_PUBKEY -match "^ssh-.*") { + Write-Key $env:JENKINS_AGENT_SSH_PUBKEY +} + +$index = 0 +$knownHostKeyVar = Get-ChildItem -Path "env:JENKINS_AGENT_SSH_KNOWNHOST_$index" -ErrorAction 'SilentlyContinue' +while($null -ne $knownHostKeyVar) { + Write-HostKey $knownHostKeyVar.Value + $index++ + $knownHostKeyVar = Get-ChildItem env: -Name "JENKINS_AGENT_SSH_KNOWNHOST_$index" +} + +# ensure variables passed to docker container are also exposed to ssh sessions +Get-ChildItem env: | ForEach-Object { setx /m $_.Name $_.Value | Out-Null } + +if(![System.String]::IsNullOrWhiteSpace($Cmd)) { + Write-Host "$($MyInvocation.MyCommand.Name) param: '$Cmd'" + if($Cmd -match "^ssh-.*") { + Write-Host "Authorizing ssh pubkey found in params." + Write-Key $Cmd + } elseif($Cmd -match "^/usr/sbin/sshd") { + # neutralize default jenkins docker-plugin command + # we will run sshd at the end anyway + Write-Host "Ignoring provided (linux) sshd command." + } else { + Write-Host "Executing param: $Cmd" + & $Cmd + exit + } +} + +Start-Service sshd + +# dump network information +ipconfig +netstat -a + +# aside from forwarding ssh logs, this keeps the container open +Get-Content -Path "C:\ProgramData\ssh\logs\sshd.log" -Wait