From 9b1bf7278e899b7eabc17e3ca0da333b9a52128f Mon Sep 17 00:00:00 2001 From: Burnett01 Date: Mon, 1 Sep 2025 13:08:48 +0000 Subject: [PATCH] feat: rsync-docker as first-party code, configureable strict host keys checking --- .dockerignore | 5 +++++ .editorconfig | 15 +++++++++++++++ Dockerfile | 13 ++++++++----- LICENSE | 1 + action.yml | 4 ++++ docker-rsync/agent-add | 6 ++++++ docker-rsync/agent-askpass | 5 +++++ docker-rsync/agent-start | 21 +++++++++++++++++++++ docker-rsync/agent-stop | 37 +++++++++++++++++++++++++++++++++++++ docker-rsync/ssh-init | 5 +++++ entrypoint.sh | 30 +++++++++++++++++++++--------- 11 files changed, 128 insertions(+), 14 deletions(-) create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100755 docker-rsync/agent-add create mode 100755 docker-rsync/agent-askpass create mode 100755 docker-rsync/agent-start create mode 100755 docker-rsync/agent-stop create mode 100755 docker-rsync/ssh-init diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..28f64f3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +Dockerfile +LICENSE +*.md +.git* +.github* diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8447008 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +tab_width = 4 +indent_size = 4 +indent_style = space +max_line_length = 9999 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{yml,yaml}] +tab_width = 2 +indent_size = 2 diff --git a/Dockerfile b/Dockerfile index b6ab743..a64e2a4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,14 @@ -# drinternet/rsync@v1.5.1 -FROM drinternet/rsync@sha256:e61f4047577b566872764fa39299092adeab691efb3884248dbd6495dc926527 +FROM alpine:3.22.1 as base + +RUN apk update && apk add --no-cache --upgrade rsync openssh-client openssl -# always force-upgrade rsync to get the latest security fixes -RUN apk update && apk add --no-cache --upgrade rsync openssl RUN rm -rf /var/cache/apk/* -# Copy entrypoint +COPY docker-rsync/* /bin/ +RUN chmod +x /bin/agent-* /bin/hosts-* + +FROM base as build + COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh diff --git a/LICENSE b/LICENSE index aa38d49..0e45d68 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2019-2022 Contention +Copyright (c) 2019-2025 Joshua Piper (Dr Internet) Copyright (c) 2019-2025 Burnett01 Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/action.yml b/action.yml index db35730..fdb51d1 100644 --- a/action.yml +++ b/action.yml @@ -13,6 +13,10 @@ inputs: description: 'Enables support for legacy RSA host keys on OpenSSH 8.8+' required: false default: 'false' + strict_hostkeys_checking: + description: 'Controls strict host keys checking' + required: false + default: 'false' path: description: 'The local path' required: false diff --git a/docker-rsync/agent-add b/docker-rsync/agent-add new file mode 100755 index 0000000..d1f5b77 --- /dev/null +++ b/docker-rsync/agent-add @@ -0,0 +1,6 @@ +#!/bin/sh + +set -euo pipefail + +source agent-start "${1:-default}" +cat - | tr -d '\r' | DISPLAY=1 SSH_ASKPASS=agent-askpass ssh-add - >/dev/null diff --git a/docker-rsync/agent-askpass b/docker-rsync/agent-askpass new file mode 100755 index 0000000..3757baf --- /dev/null +++ b/docker-rsync/agent-askpass @@ -0,0 +1,5 @@ +#!/bin/sh + +set -euo pipefail + +echo "$SSH_PASS" diff --git a/docker-rsync/agent-start b/docker-rsync/agent-start new file mode 100755 index 0000000..b63892b --- /dev/null +++ b/docker-rsync/agent-start @@ -0,0 +1,21 @@ +#!/bin/sh + +set -euo pipefail + +FOLDER=${1:-default} +STORE_PATH="/tmp/ssh-agent/$FOLDER" +mkdir -p "$STORE_PATH" + +if [ -z "$SSH_AGENT_PID" ]; then + if [ -f "$STORE_PATH/id" ]; then + SSH_AGENT_PID=$(cat "$STORE_PATH/id") + export SSH_AGENT_PID + + SSH_AUTH_SOCK=$(cat "$STORE_PATH/sock") + export SSH_AUTH_SOCK + else + eval "$(ssh-agent)" > /dev/null + echo "$SSH_AGENT_PID" > "$STORE_PATH"/id + echo "$SSH_AUTH_SOCK" > "$STORE_PATH"/sock + fi +fi diff --git a/docker-rsync/agent-stop b/docker-rsync/agent-stop new file mode 100755 index 0000000..621f746 --- /dev/null +++ b/docker-rsync/agent-stop @@ -0,0 +1,37 @@ +#!/bin/sh + +set -euo pipefail + +if [ ! -z "$SSH_AGENT_PID" ]; then + # Here, the environment is set already, just kill the script. + eval $(ssh-agent -k) >/dev/null + exit $? +else + # The env isn't set, construct the file path. + FOLDER=${1:-default} + STORE_PATH="/tmp/ssh-agent/$FOLDER" + if [ ! -d "$STORE_PATH" ]; then + echo "Store Path $STORE_PATH doesn't exist!" >&2 + exit 1 + fi + + # And check our files exist. + if [ -f "$STORE_PATH/id" ]; then + # Grab our PID and socket. + SSH_AGENT_PID=$(cat "$STORE_PATH/id") + export SSH_AGENT_PID + rm "$STORE_PATH/id" + + SSH_AUTH_SOCK=$(cat "$STORE_PATH/sock") + export SSH_AUTH_SOCK + rm "$STORE_PATH/sock" + + + rmdir "$STORE_PATH" + eval $(ssh-agent -k) >/dev/null + exit $? + else + echo "SSH_AGENT_PID not set, $STORE_PATH/id doesn't exist!" >&2 + exit 1 + fi +fi diff --git a/docker-rsync/ssh-init b/docker-rsync/ssh-init new file mode 100755 index 0000000..1efab13 --- /dev/null +++ b/docker-rsync/ssh-init @@ -0,0 +1,5 @@ +#!/bin/sh + +set -euo pipefail + +mkdir -m 700 ~/.ssh diff --git a/entrypoint.sh b/entrypoint.sh index b854a54..2b5e1f8 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,25 +1,37 @@ #!/bin/sh +set -euo pipefail + if [ -z "$(echo "$INPUT_REMOTE_PATH" | awk '{$1=$1};1')" ]; then echo "The remote_path can not be empty. see: github.com/Burnett01/rsync-deployments/issues/44" exit 1 fi +# Initialize SSH and known hosts. +source ssh-init + # Start the SSH agent and load key. source agent-start "$GITHUB_ACTION" -echo "$INPUT_REMOTE_KEY" | SSH_PASS="$INPUT_REMOTE_KEY_PASS" agent-add - -# Add strict errors. -set -eu +printf '%s' "$INPUT_REMOTE_KEY" | SSH_PASS="${INPUT_REMOTE_KEY_PASS}" agent-add >/dev/null 2>&1 # Variables. -LEGACY_RSA_HOSTKEYS="-o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa" -LEGACY_RSA_HOSTKEYS=$([ "$INPUT_LEGACY_ALLOW_RSA_HOSTKEYS" = "true" ] && echo "$LEGACY_RSA_HOSTKEYS" || echo "") +LEGACY_RSA_HOSTKEYS="" +if [ "${INPUT_LEGACY_ALLOW_RSA_HOSTKEYS:-false}" = "true" ]; then + LEGACY_RSA_HOSTKEYS="-o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa" +fi -SWITCHES="$INPUT_SWITCHES" -RSH="ssh -o StrictHostKeyChecking=no $LEGACY_RSA_HOSTKEYS -p $INPUT_REMOTE_PORT $INPUT_RSH" +STRICT_HOSTKEYS_CHECKING="-o StrictHostKeyChecking=no" +if [ "${INPUT_STRICT_HOSTKEYS_CHECKING:-false}" = "true" ]; then + STRICT_HOSTKEYS_CHECKING="-o StrictHostKeyChecking=yes" +fi + +RSH="ssh $STRICT_HOSTKEYS_CHECKING $LEGACY_RSA_HOSTKEYS -p $INPUT_REMOTE_PORT $INPUT_RSH" LOCAL_PATH="$GITHUB_WORKSPACE/$INPUT_PATH" DSN="$INPUT_REMOTE_USER@$INPUT_REMOTE_HOST" # Deploy. -sh -c "rsync $SWITCHES -e '$RSH' $LOCAL_PATH $DSN:$INPUT_REMOTE_PATH" +exec rsync "$INPUT_SWITCHES" -e "'$RSH'" "$LOCAL_PATH" "$DSN:$INPUT_REMOTE_PATH" + +# Clean up. +source agent-stop "$GITHUB_ACTION" +