From 9baa6a5b2f5f1ffa468422c5821c0ea0e9f7472f Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Sun, 12 Jul 2020 22:08:12 +0200 Subject: [PATCH 01/32] Fixes #158: duplicate authorized keys --- files/create-sftp-user | 18 +++++++++++++----- tests/run | 29 +++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/files/create-sftp-user b/files/create-sftp-user index 5df16b7..874264c 100755 --- a/files/create-sftp-user +++ b/files/create-sftp-user @@ -81,12 +81,20 @@ else fi # Add SSH keys to authorized_keys with valid permissions -if [ -d "/home/$user/.ssh/keys" ]; then - for publickey in "/home/$user/.ssh/keys"/*; do - cat "$publickey" >> "/home/$user/.ssh/authorized_keys" +userKeysQueuedDir="/home/$user/.ssh/keys" +if [ -d "$userKeysQueuedDir" ]; then + userKeysAllowedFileTmp="$(mktemp)" + userKeysAllowedFile="/home/$user/.ssh/authorized_keys" + + for publickey in "$userKeysQueuedDir"/*; do + cat "$publickey" >> "$userKeysAllowedFileTmp" done - chown "$uid" "/home/$user/.ssh/authorized_keys" - chmod 600 "/home/$user/.ssh/authorized_keys" + + # Remove duplicate keys + sort < "$userKeysAllowedFileTmp" | uniq > "$userKeysAllowedFile" + + chown "$uid" "$userKeysAllowedFile" + chmod 600 "$userKeysAllowedFile" fi # Make sure dirs exists diff --git a/tests/run b/tests/run index 6f92d84..dce27bd 100755 --- a/tests/run +++ b/tests/run @@ -13,6 +13,8 @@ testDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" buildDir="$testDir/.." imageName="atmoz/sftp_test" buildOptions=(--tag "$imageName") +sshKeyPri="/tmp/atmoz_sftp_test_rsa" +sshKeyPub="/tmp/atmoz_sftp_test_rsa.pub" if [ "$argOutput" == "quiet" ]; then redirect="/dev/null" @@ -45,12 +47,12 @@ function oneTimeSetUp() { fi # Generate temporary ssh keys for testing - if [ ! -f "/tmp/atmoz_sftp_test_rsa" ]; then - ssh-keygen -t rsa -f "/tmp/atmoz_sftp_test_rsa" -N '' > "$redirect" 2>&1 + if [ ! -f "$sshKeyPri" ]; then + ssh-keygen -t rsa -f "$sshKeyPri" -N '' > "$redirect" 2>&1 fi # Private key can not be read by others (sshd will complain) - chmod go-rw "/tmp/atmoz_sftp_test_rsa" + chmod go-rw "$sshKeyPri" } function oneTimeTearDown() { @@ -104,7 +106,7 @@ function runSftpCommands() { done echo "$commands" | sftp \ - -i "/tmp/atmoz_sftp_test_rsa" \ + -i "$sshKeyPri" \ -oStrictHostKeyChecking=no \ -oUserKnownHostsFile=/dev/null \ -b - "$user@$ip" \ @@ -252,7 +254,7 @@ function testCreateUsersUsingCombo() { function testWriteAccessToAutocreatedDirs() { docker run --name "$containerName" -d \ - -v "/tmp/atmoz_sftp_test_rsa.pub":/home/test/.ssh/keys/id_rsa.pub:ro \ + -v "$sshKeyPub":/home/test/.ssh/keys/id_rsa.pub:ro \ "$imageName" "test::::testdir,dir with spaces" \ > "$redirect" 2>&1 @@ -284,7 +286,7 @@ function testBindmountDirScript() { docker run --name "$containerName" -d \ --privileged=true \ - -v "/tmp/atmoz_sftp_test_rsa.pub":/home/custom/.ssh/keys/id_rsa.pub:ro \ + -v "$sshKeyPub":/home/custom/.ssh/keys/id_rsa.pub:ro \ -v "$containerTmpDir/custom/bindmount":/custom \ -v "$containerTmpDir/mount.sh":/etc/sftp.d/mount.sh \ "$imageName" custom:123 \ @@ -303,6 +305,21 @@ function testBindmountDirScript() { assertTrue "directory exist" $? } +function testDuplicateSshKeys() { + docker run --name "$containerName" -d \ + -v "$sshKeyPub":/home/user/.ssh/keys/key1.pub:ro \ + -v "$sshKeyPub":/home/user/.ssh/keys/key2.pub:ro \ + "$imageName" "user:" \ + > "$redirect" 2>&1 + + waitForServer "$containerName" + assertTrue "waitForServer" $? + + lines="$(docker exec "$containerName" sh -c \ + "wc -l < /home/user/.ssh/authorized_keys")" + assertEquals "1" "$lines" +} + ############################################################################## ## Run ############################################################################## From 2770bec896d2eabdd955feee12580b7212edfdbd Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Sun, 12 Jul 2020 22:23:41 +0200 Subject: [PATCH 02/32] Upgrade to Debian Buster --- Dockerfile | 2 +- README.md | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index bb5637c..c4454e2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:stretch +FROM debian:buster MAINTAINER Adrian Dvergsdal [atmoz.net] # Steps done in one RUN layer: diff --git a/README.md b/README.md index a550dc1..563e096 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,7 @@ # Supported tags and respective `Dockerfile` links -- [`debian-stretch`, `debian`, `latest` (*Dockerfile*)](https://github.com/atmoz/sftp/blob/master/Dockerfile) [![](https://images.microbadger.com/badges/image/atmoz/sftp.svg)](http://microbadger.com/images/atmoz/sftp "Get your own image badge on microbadger.com") -- [`debian-jessie` (*Dockerfile*)](https://github.com/atmoz/sftp/blob/debian-jessie/Dockerfile) [![](https://images.microbadger.com/badges/image/atmoz/sftp:debian-jessie.svg)](http://microbadger.com/images/atmoz/sftp:debian-jessie "Get your own image badge on microbadger.com") +- [`debian`, `latest` (*Dockerfile*)](https://github.com/atmoz/sftp/blob/master/Dockerfile) [![](https://images.microbadger.com/badges/image/atmoz/sftp.svg)](http://microbadger.com/images/atmoz/sftp "Get your own image badge on microbadger.com") - [`alpine` (*Dockerfile*)](https://github.com/atmoz/sftp/blob/alpine/Dockerfile) [![](https://images.microbadger.com/badges/image/atmoz/sftp:alpine.svg)](http://microbadger.com/images/atmoz/sftp:alpine "Get your own image badge on microbadger.com") # Securely share your files From 79b46e3aec30c6fc51c4c512f3a86bf872c8fdf9 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Sun, 12 Jul 2020 22:55:42 +0200 Subject: [PATCH 03/32] Docker Hub no longer support automated builds See https://github.com/docker/hub-feedback/issues/1587 --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 563e096..2a0afbe 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ # Securely share your files Easy to use SFTP ([SSH File Transfer Protocol](https://en.wikipedia.org/wiki/SSH_File_Transfer_Protocol)) server with [OpenSSH](https://en.wikipedia.org/wiki/OpenSSH). -This is an automated build linked with the [debian](https://hub.docker.com/_/debian/) and [alpine](https://hub.docker.com/_/alpine/) repositories. # Usage From c6285fe34c64632c2015c9cd51aaedc51eeaa1f8 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Sun, 12 Jul 2020 23:21:00 +0200 Subject: [PATCH 04/32] Make host dir more clear See #224 --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 2a0afbe..cde07cc 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Let's mount a directory and set UID: ``` docker run \ - -v /host/upload:/home/foo/upload \ + -v /upload:/home/foo/upload \ -p 2222:22 -d atmoz/sftp \ foo:pass:1001 ``` @@ -58,7 +58,7 @@ docker run \ sftp: image: atmoz/sftp volumes: - - /host/upload:/home/foo/upload + - /upload:/home/foo/upload ports: - "2222:22" command: foo:pass:1001 @@ -72,12 +72,12 @@ The OpenSSH server runs by default on port 22, and in this example, we are forwa ``` docker run \ - -v /host/users.conf:/etc/sftp/users.conf:ro \ + -v /users.conf:/etc/sftp/users.conf:ro \ -v mySftpVolume:/home \ -p 2222:22 -d atmoz/sftp ``` -/host/users.conf: +/users.conf: ``` foo:123:1001:100 @@ -91,7 +91,7 @@ Add `:e` behind password to mark it as encrypted. Use single quotes if using ter ``` docker run \ - -v /host/share:/home/foo/share \ + -v /share:/home/foo/share \ -p 2222:22 -d atmoz/sftp \ 'foo:$1$0G2g0GSt$ewU0t6GXG15.0hWoOX8X9.:e:1001' ``` @@ -105,9 +105,9 @@ Mount public keys in the user's `.ssh/keys/` directory. All keys are automatical ``` docker run \ - -v /host/id_rsa.pub:/home/foo/.ssh/keys/id_rsa.pub:ro \ - -v /host/id_other.pub:/home/foo/.ssh/keys/id_other.pub:ro \ - -v /host/share:/home/foo/share \ + -v /id_rsa.pub:/home/foo/.ssh/keys/id_rsa.pub:ro \ + -v /id_other.pub:/home/foo/.ssh/keys/id_other.pub:ro \ + -v /share:/home/foo/share \ -p 2222:22 -d atmoz/sftp \ foo::1001 ``` @@ -118,9 +118,9 @@ This container will generate new SSH host keys at first run. To avoid that your ``` docker run \ - -v /host/ssh_host_ed25519_key:/etc/ssh/ssh_host_ed25519_key \ - -v /host/ssh_host_rsa_key:/etc/ssh/ssh_host_rsa_key \ - -v /host/share:/home/foo/share \ + -v /ssh_host_ed25519_key:/etc/ssh/ssh_host_ed25519_key \ + -v /ssh_host_rsa_key:/etc/ssh/ssh_host_rsa_key \ + -v /share:/home/foo/share \ -p 2222:22 -d atmoz/sftp \ foo::1001 ``` From 55900d43277a83e5a2d61b0ec4af3233f4131258 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Tue, 14 Jul 2020 22:28:13 +0200 Subject: [PATCH 05/32] Test posibility of using chroot in subdirs (#207) --- tests/run | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/run b/tests/run index dce27bd..b05c449 100755 --- a/tests/run +++ b/tests/run @@ -276,6 +276,41 @@ function testWriteAccessToAutocreatedDirs() { assertTrue "dir with spaces write access" $? } +function testWriteAccessToLimitedChroot() { + # Modified sshd_config with chrooted home subdir + tmpConfig="$(mktemp)" + sed 's/^ChrootDirectory.*/ChrootDirectory %h\/sftp/' \ + < "$testDir/../files/sshd_config" > "$tmpConfig" + + # Set correct permissions on chroot + tmpScript="$(mktemp)" + cat > "$tmpScript" < "$redirect" 2>&1 + + waitForServer "$containerName" + assertTrue "waitForServer" $? + + runSftpCommands "$containerName" "test" \ + "cd upload" \ + "mkdir test" \ + "exit" + assertTrue "runSftpCommands" $? + + docker exec "$containerName" test -d /home/test/sftp/upload/test + assertTrue "limited chroot write access" $? +} + function testBindmountDirScript() { mkdir -p "$containerTmpDir/custom/bindmount" echo "mkdir -p /home/custom/bindmount && \ From 5a074ec171bab59036f1d6b8c95bda48134f1d28 Mon Sep 17 00:00:00 2001 From: Adrian Dvergdal Date: Wed, 15 Jul 2020 20:40:39 +0200 Subject: [PATCH 06/32] Create GitHub Workflow --- .github/workflows/docker-image.yml | 78 ++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..1e86202 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,78 @@ +name: Docker + +on: + schedule: + - cron: "0 12 * * *" + push: + pull_request: + +env: + GPG_KEY_FINGERPRINT: "838460D0CBD26750AB26DF8FB9FB68F98F88BA47" + +jobs: + # Run tests. + # See also https://docs.docker.com/docker-hub/builds/automated-testing/ + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + # Only allow commits signed by owner + - name: Verify commit signature + run: | + if git verify-commit --raw HEAD 2>&1 | grep -e VALIDSIG | grep -q "$GPG_KEY_FINGERPRINT"; then + echo "Verified signature from $GPG_KEY_FINGERPRINT" + else + echo "Missing signature by $GPG_KEY_FINGERPRINT" + exit 1 + fi + + - name: Run tests + run: tests/run + + # Push image to GitHub Packages. + # See also https://docs.docker.com/docker-hub/builds/ + push: + # Ensure test job passes before pushing image. + needs: test + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Build and push Docker images + uses: docker/build-push-action@v1.1.0 + with: + # Username used to log in to a Docker registry. If not set then no login will occur + username: ${{ secrets.DOCKER_HUB_USERNAME }} + # Password or personal access token used to log in to a Docker registry. If not set then no login will occur + password: ${{ secrets.DOCKER_HUB_PASSWORD }} + # Server address of Docker registry. If not set then will default to Docker Hub + #registry: # optional + # Docker repository to tag the image with + repository: atmoz/sftp + # Comma-delimited list of tags. These will be added to the registry/repository to form the image's tags + #tags: # optional + # Automatically tags the built image with the git reference as per the readme + tag_with_ref: true # optional + # Automatically tags the built image with the git short SHA as per the readme + tag_with_sha: false # optional + # Path to the build context + #path: # optional, default is . + # Path to the Dockerfile (Default is '{path}/Dockerfile') + #dockerfile: # optional + # Sets the target stage to build + #target: # optional + # Always attempt to pull a newer version of the image + always_pull: true # optional + # Comma-delimited list of build-time variables + #build_args: # optional + # Comma-delimited list of images to consider as cache sources + #cache_froms: # optional + # Comma-delimited list of labels to add to the built image + #labels: # optional + # Adds labels with git repository information to the built image + add_git_labels: true # optional + # Whether to push the image + #push: # optional, default is true From fcf9c8f1ac4e753ca6b2a4e5c2350c8fcc760805 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Wed, 15 Jul 2020 22:24:28 +0200 Subject: [PATCH 07/32] Verify either commit or tag --- .github/workflows/docker-image.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 1e86202..269fd28 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -21,7 +21,12 @@ jobs: # Only allow commits signed by owner - name: Verify commit signature run: | - if git verify-commit --raw HEAD 2>&1 | grep -e VALIDSIG | grep -q "$GPG_KEY_FINGERPRINT"; then + verify_head() { + ( git verify-commit --raw HEAD || git verify-tag --raw $(git tag --points-at HEAD) ) 2>&1 \ + | grep -e VALIDSIG | grep -q "$1" + } + + if verify_head "$GPG_KEY_FINGERPRINT"; then echo "Verified signature from $GPG_KEY_FINGERPRINT" else echo "Missing signature by $GPG_KEY_FINGERPRINT" From bba0f3babec77bf0e0650da0a49e612db0b32d7f Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Wed, 15 Jul 2020 22:54:58 +0200 Subject: [PATCH 08/32] Fetch tags and checkout submodules --- .github/workflows/docker-image.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 269fd28..6be3164 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -17,6 +17,9 @@ jobs: steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 + submodules: true # Only allow commits signed by owner - name: Verify commit signature From 2198c24da39f5a90304b15c112e2de958422e11a Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Fri, 17 Jul 2020 00:51:46 +0200 Subject: [PATCH 09/32] Improved github actions --- .github/actions/git-verify-ref/Dockerfile | 13 ++ .github/actions/git-verify-ref/action.yml | 6 + .github/actions/git-verify-ref/entrypoint.sh | 60 ++++++ .../git-verify-ref/public-keys/atmoz.asc | 179 ++++++++++++++++++ .github/workflows/docker-image.yml | 28 +-- 5 files changed, 265 insertions(+), 21 deletions(-) create mode 100644 .github/actions/git-verify-ref/Dockerfile create mode 100644 .github/actions/git-verify-ref/action.yml create mode 100755 .github/actions/git-verify-ref/entrypoint.sh create mode 100644 .github/actions/git-verify-ref/public-keys/atmoz.asc diff --git a/.github/actions/git-verify-ref/Dockerfile b/.github/actions/git-verify-ref/Dockerfile new file mode 100644 index 0000000..f9f8c00 --- /dev/null +++ b/.github/actions/git-verify-ref/Dockerfile @@ -0,0 +1,13 @@ +FROM debian:buster-slim + +RUN apt update && \ + apt install -y git gnupg && \ + rm -rf /var/lib/apt/lists/* + +#RUN apk add --no-cache git gnupg + +COPY ./public-keys/atmoz.asc /tmp/atmoz.asc +RUN gpg --import /tmp/atmoz.asc + +COPY ./entrypoint.sh / +ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/actions/git-verify-ref/action.yml b/.github/actions/git-verify-ref/action.yml new file mode 100644 index 0000000..7b3d15e --- /dev/null +++ b/.github/actions/git-verify-ref/action.yml @@ -0,0 +1,6 @@ +name: 'Verify git ref' +description: 'Verify GPG signed commits or tags' + +runs: + using: 'docker' + image: 'Dockerfile' diff --git a/.github/actions/git-verify-ref/entrypoint.sh b/.github/actions/git-verify-ref/entrypoint.sh new file mode 100755 index 0000000..fa79f83 --- /dev/null +++ b/.github/actions/git-verify-ref/entrypoint.sh @@ -0,0 +1,60 @@ +#!/bin/bash +set -eo pipefail + +# Git reference +ref="${1:-HEAD}" + +# Number of required signatures +required="${2:-"1"}" + +# Options passed to git +git_options="${*:3}" + + +# GitHub Actions fix +if [ "$HOME" == "/github/home/" ]; then + cp -r /root/.gnupg /github/home/ +fi + +# Show imported public keys +gpg --list-keys --keyid LONG + +# Check signatures +raw_gpg_status=$( + # shellcheck disable=SC2086 + git $git_options verify-commit --raw "$ref" 2>&1 + tags="$(git tag --points-at "$ref")" + + if [ -n "$tags" ]; then + # shellcheck disable=SC2046,SC2086 + git $git_options verify-tag --raw $tags 2>&1 + fi +) + +goodsig=0 +readarray -t status_line <<<"$raw_gpg_status" +# read -r -a info <<<"$status" +for status in "${status_line[@]}"; do + #readarray -t -d" " info <<<"$status" + read -r -a info <<<"$status" + + case "${info[1]}" in + "GOODSIG") + echo "Verified signature from ${info[2]}" + ((goodsig++)) || true + ;; + + "NO_PUBKEY") + echo "WARNING: Missing public key for ${info[2]}" + ;; + esac +done + +echo "RESULT: Found $goodsig good signatures" + +if [ "$goodsig" -lt "$required" ]; then + echo "FAIL: Not enough signatures ($required was required)" + exit 1 +else + exit 0 +fi diff --git a/.github/actions/git-verify-ref/public-keys/atmoz.asc b/.github/actions/git-verify-ref/public-keys/atmoz.asc new file mode 100644 index 0000000..434781e --- /dev/null +++ b/.github/actions/git-verify-ref/public-keys/atmoz.asc @@ -0,0 +1,179 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBFnfpzIBEAC5rEA7zyYl8JdcXowGzFquerQBhFEJkH2fiJ544v/9pCkkaCIv +5tqSWDHAL2mbhh6Y5wVJtXuOGzPgJXd1zl8H88NlZpUInOyPtgLpy6Mr7H/0VzS6 +U6+SusR4u8Mwi+glNuVCFla7N0WsnWCK9sLo1hhvpFRoDY0cRPE8TnlhU5WO30b6 +g64yeZEqSIApgPftDolfDprtO4ah3br6bGLyfwOfOODPV4Aqn347WX8o0afP5gHp +ogG2xHdwk2beLXR9CSnS1RiMQw/zthXb6aP5w3BpwevN5MHWx3wfatceyfhTACst +LcliiOXLJvlvUiOL4W+vwkKp9v1N4aEDq4fPlEfE9Fh8YpN6/AHAafaxqfLaDLGn +Grm2GGWSKlWcyfqfKd3RyAIXVnBv3ceg5331vRGtW17bKKzoRgPRJwqRM+0QfSX/ +rqPDjoJTmmlI2NWfdtYmarbGn3ipGFdm4zCEG6tDAYHUMti+ynC3mXaoH9G7KH6r +7TI3Q4EETYbS9+QV+EfV4cEaJ/m9lHyPqAgcUHSd+MpdJVMqpRDSac8xD0Oixo8I +fIfWIOMbMTgrE4xmA5DHdET2Htj8LE8ayQQ7sr1XIMuEHmCTMdZ+zf/7Lfja/pwZ +/qc8lWOBYCC+kPUf3B3TLhdyWPO7yW0g9jGd+2Pqg3o4KRgekAyFT8HSKQARAQAB +tCJBZHJpYW4gRHZlcmdzZGFsIDxwcml2YXRAYXRtb3oubm8+iQI3BBMBCgAhBQJZ +4disAhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJELn7aPmPiLpHgggQAIgT +tp5ilnAR5pXUXmObN8gTf469jLphiClTlxr05dVp+yknZ8JhA5fEmbzf1OIzyc0K +P7hz91Lh2f7a4+OygSSyKHh8TwmCcYDM607NBd6oSF8H1XPOsZgJ+e0Wp5gGPB9M +7LsCB4oopUcdqk5Zp4tlUWNDqmfQ1ZEztxSFCXXY/bSHDaOTSJUZ8IMPpll/190q +pMSglwQrbKd+ifCl4CC8nPkdmkV/4anMQtKoHL+dATueRbMrZgKq/LRyRzi1g3Wl +x03HJLyWDp2zB+4Ls68fuCUNTcmOyLqKhFbTbPUeE+98adxHuMKYSmjVH4O2u14y +ForfUWZT6gk4PnSOnEV8/+hgmTnKwYHHZqIET2u644fcmDhDUsg61C8FBGFLFzxr +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 +dFqBkw== +=4DZp +-----END PGP PUBLIC KEY BLOCK----- diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 6be3164..e91168d 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -6,12 +6,7 @@ on: push: pull_request: -env: - GPG_KEY_FINGERPRINT: "838460D0CBD26750AB26DF8FB9FB68F98F88BA47" - jobs: - # Run tests. - # See also https://docs.docker.com/docker-hub/builds/automated-testing/ test: runs-on: ubuntu-latest @@ -21,28 +16,19 @@ jobs: fetch-depth: 0 submodules: true - # Only allow commits signed by owner - - name: Verify commit signature - run: | - verify_head() { - ( git verify-commit --raw HEAD || git verify-tag --raw $(git tag --points-at HEAD) ) 2>&1 \ - | grep -e VALIDSIG | grep -q "$1" - } + - name: Verify signature + uses: ./.github/actions/git-verify-ref - if verify_head "$GPG_KEY_FINGERPRINT"; then - echo "Verified signature from $GPG_KEY_FINGERPRINT" - else - echo "Missing signature by $GPG_KEY_FINGERPRINT" - exit 1 - fi + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@master + with: + ignore: tests/shunit2 - name: Run tests run: tests/run - # Push image to GitHub Packages. - # See also https://docs.docker.com/docker-hub/builds/ push: - # Ensure test job passes before pushing image. + if: ${{ github.event_name == "push" && github.ref == "master" }} needs: test runs-on: ubuntu-latest From b1a6c5245feb06fb0851b70eafd3b7bea2a3fe90 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Fri, 17 Jul 2020 01:04:42 +0200 Subject: [PATCH 10/32] Fix push condition --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index e91168d..9da214f 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -28,7 +28,7 @@ jobs: run: tests/run push: - if: ${{ github.event_name == "push" && github.ref == "master" }} + if: github.event_name == "push" && github.ref == "refs/heads/master" needs: test runs-on: ubuntu-latest From a41a712b61e7b6c019e326b80e5b1cfbbcdc23ab Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Fri, 17 Jul 2020 01:09:12 +0200 Subject: [PATCH 11/32] Must use single quotes https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#literals --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 9da214f..a2b2b7c 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -28,7 +28,7 @@ jobs: run: tests/run push: - if: github.event_name == "push" && github.ref == "refs/heads/master" + if: github.event_name == 'push' && github.ref == 'refs/heads/master' needs: test runs-on: ubuntu-latest From 84f158b5e6be36fd0fc50ede30955fe52dff10fe Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Fri, 17 Jul 2020 01:14:05 +0200 Subject: [PATCH 12/32] Don't rely on $HOME --- .github/actions/git-verify-ref/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/git-verify-ref/entrypoint.sh b/.github/actions/git-verify-ref/entrypoint.sh index fa79f83..c6eba43 100755 --- a/.github/actions/git-verify-ref/entrypoint.sh +++ b/.github/actions/git-verify-ref/entrypoint.sh @@ -12,7 +12,7 @@ git_options="${*:3}" # GitHub Actions fix -if [ "$HOME" == "/github/home/" ]; then +if [ -e "/github/home/" ]; then cp -r /root/.gnupg /github/home/ fi From a03ecdf4fcd4d3d334ae583989bdf45fcbfff579 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Fri, 17 Jul 2020 01:29:36 +0200 Subject: [PATCH 13/32] Fix submodule tests/shunit2 --- tests/run | 2 +- tests/shunit2 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 160000 tests/shunit2 diff --git a/tests/run b/tests/run index b05c449..b322917 100755 --- a/tests/run +++ b/tests/run @@ -24,7 +24,7 @@ fi if [ ! -f "$testDir/shunit2/shunit2" ]; then echo "Could not find shunit2 in $testDir/shunit2." - echo "Run 'git submodules init && git submodules update'" + echo "Run 'git submodule update --init'" exit 2 fi diff --git a/tests/shunit2 b/tests/shunit2 new file mode 160000 index 0000000..3f2bff2 --- /dev/null +++ b/tests/shunit2 @@ -0,0 +1 @@ +Subproject commit 3f2bff2a815097be557a5c0af77f967a87139409 From 6e68b54efa5ae16240c99eb7984632e4c4a6de80 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Fri, 17 Jul 2020 01:38:38 +0200 Subject: [PATCH 14/32] Ignore cleanup failure --- tests/run | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run b/tests/run index b322917..5412e72 100755 --- a/tests/run +++ b/tests/run @@ -74,7 +74,7 @@ function tearDown() { retireContainer "$containerName" if [ "$argCleanup" == "cleanup" ] && [ -d "$containerTmpDir" ]; then - rm -rf "$containerTmpDir" + rm -rf "$containerTmpDir" || true # Can fail on GitHub Actions fi } From 45920988a92f7738e29118e3e34be3d6d2bfa148 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Fri, 17 Jul 2020 02:11:57 +0200 Subject: [PATCH 15/32] Only verify signature on push to master branch --- .github/workflows/docker-image.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index a2b2b7c..89a2bb5 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -17,6 +17,7 @@ jobs: submodules: true - name: Verify signature + if: github.event_name == 'push' && github.ref == 'refs/heads/master' uses: ./.github/actions/git-verify-ref - name: Run ShellCheck From aa8e5a8b6d8a519f0c4a2851240ed70df9304330 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Fri, 17 Jul 2020 02:12:20 +0200 Subject: [PATCH 16/32] Set debian tag when on master branch --- .github/workflows/docker-image.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 89a2bb5..ce9714e 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -36,6 +36,16 @@ jobs: steps: - uses: actions/checkout@v2 + + - name: Define tags + id: define_tags + run: | + if [ "$GITHUB_REF" == "refs/heads/master" ]; then + echo "::set-output name=tags::debian" + else + echo "::set-output name=tags::" + fi + - name: Build and push Docker images uses: docker/build-push-action@v1.1.0 with: @@ -48,7 +58,7 @@ jobs: # Docker repository to tag the image with repository: atmoz/sftp # Comma-delimited list of tags. These will be added to the registry/repository to form the image's tags - #tags: # optional + tags: ${{ steps.define_tags.outputs.tags }} # optional # Automatically tags the built image with the git reference as per the readme tag_with_ref: true # optional # Automatically tags the built image with the git short SHA as per the readme From 7493f162dc4b77556c382ee0a0fd5efb1886cf69 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Fri, 17 Jul 2020 16:24:33 +0200 Subject: [PATCH 17/32] Rewrite build process and include alpine in master --- .github/workflows/build.yml | 78 ++++++++++++++++++++++++++++ .github/workflows/docker-image.yml | 83 ------------------------------ Dockerfile-alpine | 21 ++++++++ tests/run | 49 +++++++----------- 4 files changed, 117 insertions(+), 114 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/docker-image.yml create mode 100644 Dockerfile-alpine diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..2807006 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,78 @@ +name: build + +on: + schedule: + - cron: "0 12 * * *" + push: + pull_request: + +env: + IMAGE_NAME: atmoz/sftp + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # for proper signature verification + submodules: true # for shunit2 + + - name: Build debian image + run: | + docker build . \ + --pull=true \ + --file=Dockerfile \ + --tag="$IMAGE_NAME:debian" \ + --tag="$IMAGE_NAME:latest" \ + --label="org.opencontainers.image.source=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" \ + --label="org.opencontainers.image.revision=$GITHUB_SHA" \ + --label="org.opencontainers.image.created=$(date --rfc-3339=seconds)" + + - name: Test debian image + run: tests/run $IMAGE_NAME:debian + + - name: Build alpine image + run: | + docker build . \ + --pull=true \ + --file=Dockerfile-alpine \ + --tag="$IMAGE_NAME:alpine" \ + --label="org.opencontainers.image.source=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" \ + --label="org.opencontainers.image.revision=$GITHUB_SHA" \ + --label="org.opencontainers.image.created=$(date --rfc-3339=seconds)" + + - name: Test alpine image + run: tests/run $IMAGE_NAME:alpine + + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@master + with: + ignore: tests/shunit2 + + - name: Verify signature + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' + uses: ./.github/actions/git-verify-ref + + - name: Push images to Docker Hub registry + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' + run: | + echo "${{ secrets.DOCKER_HUB_PASSWORD }}" | docker login \ + -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin + + docker push $IMAGE_NAME # no tags specified to include all tags + + - name: Push images to GitHub registry + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' + run: | + echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com \ + -u ${{ github.actor }} --password-stdin + + TAG_DEBIAN=docker.pkg.github.com/$GITHUB_REPOSITORY/debian + TAG_ALPINE=docker.pkg.github.com/$GITHUB_REPOSITORY/alpine + docker tag $IMAGE_NAME:debian $TAG_DEBIAN + docker tag $IMAGE_NAME:alpine $TAG_ALPINE + docker push $TAG_DEBIAN + docker push $TAG_ALPINE + diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml deleted file mode 100644 index ce9714e..0000000 --- a/.github/workflows/docker-image.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Docker - -on: - schedule: - - cron: "0 12 * * *" - push: - pull_request: - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - submodules: true - - - name: Verify signature - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - uses: ./.github/actions/git-verify-ref - - - name: Run ShellCheck - uses: ludeeus/action-shellcheck@master - with: - ignore: tests/shunit2 - - - name: Run tests - run: tests/run - - push: - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - needs: test - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Define tags - id: define_tags - run: | - if [ "$GITHUB_REF" == "refs/heads/master" ]; then - echo "::set-output name=tags::debian" - else - echo "::set-output name=tags::" - fi - - - name: Build and push Docker images - uses: docker/build-push-action@v1.1.0 - with: - # Username used to log in to a Docker registry. If not set then no login will occur - username: ${{ secrets.DOCKER_HUB_USERNAME }} - # Password or personal access token used to log in to a Docker registry. If not set then no login will occur - password: ${{ secrets.DOCKER_HUB_PASSWORD }} - # Server address of Docker registry. If not set then will default to Docker Hub - #registry: # optional - # Docker repository to tag the image with - repository: atmoz/sftp - # Comma-delimited list of tags. These will be added to the registry/repository to form the image's tags - tags: ${{ steps.define_tags.outputs.tags }} # optional - # Automatically tags the built image with the git reference as per the readme - tag_with_ref: true # optional - # Automatically tags the built image with the git short SHA as per the readme - tag_with_sha: false # optional - # Path to the build context - #path: # optional, default is . - # Path to the Dockerfile (Default is '{path}/Dockerfile') - #dockerfile: # optional - # Sets the target stage to build - #target: # optional - # Always attempt to pull a newer version of the image - always_pull: true # optional - # Comma-delimited list of build-time variables - #build_args: # optional - # Comma-delimited list of images to consider as cache sources - #cache_froms: # optional - # Comma-delimited list of labels to add to the built image - #labels: # optional - # Adds labels with git repository information to the built image - add_git_labels: true # optional - # Whether to push the image - #push: # optional, default is true diff --git a/Dockerfile-alpine b/Dockerfile-alpine new file mode 100644 index 0000000..72a28c7 --- /dev/null +++ b/Dockerfile-alpine @@ -0,0 +1,21 @@ +FROM alpine:latest +MAINTAINER Adrian Dvergsdal [atmoz.net] + +# Steps done in one RUN layer: +# - Install packages +# - Fix default group (1000 does not exist) +# - OpenSSH needs /var/run/sshd to run +# - Remove generic host keys, entrypoint generates unique keys +RUN echo "@community http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories && \ + apk add --no-cache bash shadow@community openssh openssh-sftp-server && \ + sed -i 's/GROUP=1000/GROUP=100/' /etc/default/useradd && \ + mkdir -p /var/run/sshd && \ + rm -f /etc/ssh/ssh_host_*key* + +COPY files/sshd_config /etc/ssh/sshd_config +COPY files/create-sftp-user /usr/local/bin/ +COPY files/entrypoint / + +EXPOSE 22 + +ENTRYPOINT ["/entrypoint"] diff --git a/tests/run b/tests/run index 5412e72..406ae9a 100755 --- a/tests/run +++ b/tests/run @@ -1,20 +1,29 @@ #!/bin/bash # See: https://github.com/kward/shunit2 +argImage=$1 +argOutput=${2:-"quiet"} +argCleanup=${3:-"cleanup"} +testDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +imageName="$argImage" +sshKeyPri="/tmp/atmoz_sftp_test_rsa" +sshKeyPub="/tmp/atmoz_sftp_test_rsa.pub" + if [ $UID != 0 ] && ! groups | grep -qw docker; then echo "Run with sudo/root or add user $USER to group 'docker'" exit 1 fi -argBuild=${1:-"build"} -argOutput=${2:-"quiet"} -argCleanup=${3:-"cleanup"} -testDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -buildDir="$testDir/.." -imageName="atmoz/sftp_test" -buildOptions=(--tag "$imageName") -sshKeyPri="/tmp/atmoz_sftp_test_rsa" -sshKeyPub="/tmp/atmoz_sftp_test_rsa.pub" +if [ ! -f "$testDir/shunit2/shunit2" ]; then + echo "Could not find shunit2 in $testDir/shunit2." + echo "Run 'git submodule update --init'" + exit 2 +fi + +if [ -z "$argImage" ]; then + echo "Missing image name" + exit 3 +fi if [ "$argOutput" == "quiet" ]; then redirect="/dev/null" @@ -22,12 +31,6 @@ else redirect="/dev/stdout" fi -if [ ! -f "$testDir/shunit2/shunit2" ]; then - echo "Could not find shunit2 in $testDir/shunit2." - echo "Run 'git submodule update --init'" - exit 2 -fi - # clear argument list (or shunit2 will try to use them) set -- @@ -36,16 +39,6 @@ set -- ############################################################################## function oneTimeSetUp() { - if [ "$argBuild" == "build" ]; then - buildOptions+=("--no-cache" "--pull=true") - fi - - # Build image - if ! docker build "${buildOptions[@]}" "$buildDir"; then - echo "Build failed" - exit 1 - fi - # Generate temporary ssh keys for testing if [ ! -f "$sshKeyPri" ]; then ssh-keygen -t rsa -f "$sshKeyPri" -N '' > "$redirect" 2>&1 @@ -55,12 +48,6 @@ function oneTimeSetUp() { chmod go-rw "$sshKeyPri" } -function oneTimeTearDown() { - if [ "$argCleanup" == "cleanup" ]; then - docker image rm "$imageName" > "$redirect" 2>&1 - fi -} - function setUp() { # shellcheck disable=SC2154 containerName="atmoz_sftp_${_shunit_test_}" From 0c673aeda3c4b106912363c18341a1086e06b87b Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Fri, 17 Jul 2020 16:33:54 +0200 Subject: [PATCH 18/32] Fix indentation --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2807006..c38aab0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,7 +61,7 @@ jobs: echo "${{ secrets.DOCKER_HUB_PASSWORD }}" | docker login \ -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin - docker push $IMAGE_NAME # no tags specified to include all tags + docker push $IMAGE_NAME # no tags specified to include all tags - name: Push images to GitHub registry if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' From fcf2fd1b32773e855fcb9b687ec4d8d6b6dae52d Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Fri, 17 Jul 2020 18:09:07 +0200 Subject: [PATCH 19/32] Update badges and Dockerfile links in README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cde07cc..4aa551a 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # SFTP -![Docker Automated build](https://img.shields.io/docker/automated/atmoz/sftp.svg) ![Docker Build Status](https://img.shields.io/docker/build/atmoz/sftp.svg) ![Docker Stars](https://img.shields.io/docker/stars/atmoz/sftp.svg) ![Docker Pulls](https://img.shields.io/docker/pulls/atmoz/sftp.svg) +![GitHub Workflow Status](https://img.shields.io/github/workflow/status/atmoz/sftp/build?logo=github) ![GitHub stars](https://img.shields.io/github/stars/atmoz/sftp?logo=github) ![Docker Stars](https://img.shields.io/docker/stars/atmoz/sftp?label=stars&logo=docker) ![Docker Pulls](https://img.shields.io/docker/pulls/atmoz/sftp?label=pulls&logo=docker) ![OpenSSH logo](https://raw.githubusercontent.com/atmoz/sftp/master/openssh.png "Powered by OpenSSH") # Supported tags and respective `Dockerfile` links -- [`debian`, `latest` (*Dockerfile*)](https://github.com/atmoz/sftp/blob/master/Dockerfile) [![](https://images.microbadger.com/badges/image/atmoz/sftp.svg)](http://microbadger.com/images/atmoz/sftp "Get your own image badge on microbadger.com") -- [`alpine` (*Dockerfile*)](https://github.com/atmoz/sftp/blob/alpine/Dockerfile) [![](https://images.microbadger.com/badges/image/atmoz/sftp:alpine.svg)](http://microbadger.com/images/atmoz/sftp:alpine "Get your own image badge on microbadger.com") +- [`debian`, `latest` (*Dockerfile*)](https://github.com/atmoz/sftp/blob/master/Dockerfile) ![Docker Image Size (debian)](https://img.shields.io/docker/image-size/atmoz/sftp/debian?label=debian&logo=debian&style=plastic) +- [`alpine` (*Dockerfile*)](https://github.com/atmoz/sftp/blob/master/Dockerfile-alpine) ![Docker Image Size (alpine)](https://img.shields.io/docker/image-size/atmoz/sftp/alpine?label=alpine&logo=Alpine%20Linux&style=plastic) # Securely share your files From dd3bdc7a4cf1953c36a1be9baf4de73464c5c1d1 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Fri, 17 Jul 2020 18:21:44 +0200 Subject: [PATCH 20/32] Ignore files --- .github/workflows/build.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c38aab0..222e4d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,15 @@ on: schedule: - cron: "0 12 * * *" push: + paths-ignore: + - "*.md" + - "*.txt" + - "*.png" pull_request: + paths-ignore: + - "*.md" + - "*.txt" + - "*.png" env: IMAGE_NAME: atmoz/sftp From 9c1a44956173333db04a0757197691452e93126b Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Sat, 18 Jul 2020 15:39:06 +0200 Subject: [PATCH 21/32] Add git sha labels --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 222e4d6..b0a1079 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,8 +32,9 @@ jobs: docker build . \ --pull=true \ --file=Dockerfile \ - --tag="$IMAGE_NAME:debian" \ --tag="$IMAGE_NAME:latest" \ + --tag="$IMAGE_NAME:debian" \ + --tag="$IMAGE_NAME:debian-$GITHUB_SHA" \ --label="org.opencontainers.image.source=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" \ --label="org.opencontainers.image.revision=$GITHUB_SHA" \ --label="org.opencontainers.image.created=$(date --rfc-3339=seconds)" @@ -47,6 +48,7 @@ jobs: --pull=true \ --file=Dockerfile-alpine \ --tag="$IMAGE_NAME:alpine" \ + --tag="$IMAGE_NAME:alpine-$GITHUB_SHA" \ --label="org.opencontainers.image.source=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" \ --label="org.opencontainers.image.revision=$GITHUB_SHA" \ --label="org.opencontainers.image.created=$(date --rfc-3339=seconds)" From 2da6d177e64bc3f28b1fe76edd7680edc7512a7c Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Sat, 18 Jul 2020 15:44:46 +0200 Subject: [PATCH 22/32] Use atmoz/git-verify-ref --- .github/actions/git-verify-ref/Dockerfile | 13 ---- .github/actions/git-verify-ref/action.yml | 6 -- .github/actions/git-verify-ref/entrypoint.sh | 60 ------------------- .../git-verify-ref => }/public-keys/atmoz.asc | 0 .github/workflows/build.yml | 4 +- 5 files changed, 3 insertions(+), 80 deletions(-) delete mode 100644 .github/actions/git-verify-ref/Dockerfile delete mode 100644 .github/actions/git-verify-ref/action.yml delete mode 100755 .github/actions/git-verify-ref/entrypoint.sh rename .github/{actions/git-verify-ref => }/public-keys/atmoz.asc (100%) diff --git a/.github/actions/git-verify-ref/Dockerfile b/.github/actions/git-verify-ref/Dockerfile deleted file mode 100644 index f9f8c00..0000000 --- a/.github/actions/git-verify-ref/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM debian:buster-slim - -RUN apt update && \ - apt install -y git gnupg && \ - rm -rf /var/lib/apt/lists/* - -#RUN apk add --no-cache git gnupg - -COPY ./public-keys/atmoz.asc /tmp/atmoz.asc -RUN gpg --import /tmp/atmoz.asc - -COPY ./entrypoint.sh / -ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/actions/git-verify-ref/action.yml b/.github/actions/git-verify-ref/action.yml deleted file mode 100644 index 7b3d15e..0000000 --- a/.github/actions/git-verify-ref/action.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: 'Verify git ref' -description: 'Verify GPG signed commits or tags' - -runs: - using: 'docker' - image: 'Dockerfile' diff --git a/.github/actions/git-verify-ref/entrypoint.sh b/.github/actions/git-verify-ref/entrypoint.sh deleted file mode 100755 index c6eba43..0000000 --- a/.github/actions/git-verify-ref/entrypoint.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash -set -eo pipefail - -# Git reference -ref="${1:-HEAD}" - -# Number of required signatures -required="${2:-"1"}" - -# Options passed to git -git_options="${*:3}" - - -# GitHub Actions fix -if [ -e "/github/home/" ]; then - cp -r /root/.gnupg /github/home/ -fi - -# Show imported public keys -gpg --list-keys --keyid LONG - -# Check signatures -raw_gpg_status=$( - # shellcheck disable=SC2086 - git $git_options verify-commit --raw "$ref" 2>&1 - tags="$(git tag --points-at "$ref")" - - if [ -n "$tags" ]; then - # shellcheck disable=SC2046,SC2086 - git $git_options verify-tag --raw $tags 2>&1 - fi -) - -goodsig=0 -readarray -t status_line <<<"$raw_gpg_status" -# read -r -a info <<<"$status" -for status in "${status_line[@]}"; do - #readarray -t -d" " info <<<"$status" - read -r -a info <<<"$status" - - case "${info[1]}" in - "GOODSIG") - echo "Verified signature from ${info[2]}" - ((goodsig++)) || true - ;; - - "NO_PUBKEY") - echo "WARNING: Missing public key for ${info[2]}" - ;; - esac -done - -echo "RESULT: Found $goodsig good signatures" - -if [ "$goodsig" -lt "$required" ]; then - echo "FAIL: Not enough signatures ($required was required)" - exit 1 -else - exit 0 -fi diff --git a/.github/actions/git-verify-ref/public-keys/atmoz.asc b/.github/public-keys/atmoz.asc similarity index 100% rename from .github/actions/git-verify-ref/public-keys/atmoz.asc rename to .github/public-keys/atmoz.asc diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b0a1079..b5d2143 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -63,7 +63,9 @@ jobs: - name: Verify signature if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' - uses: ./.github/actions/git-verify-ref + uses: atmoz/git-verify-ref@master + with: + public_key_dir: ./github/public-keys - name: Push images to Docker Hub registry if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' From 885fcccacb4e9e131cae6cb6fde02f584d2ae113 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Sat, 18 Jul 2020 15:51:15 +0200 Subject: [PATCH 23/32] Fix typo --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b5d2143..4413c85 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,7 +65,7 @@ jobs: if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' uses: atmoz/git-verify-ref@master with: - public_key_dir: ./github/public-keys + public_key_dir: .github/public-keys - name: Push images to Docker Hub registry if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' From 56f2365606929ade95cdb406a0af5a8885764bca Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Sun, 19 Jul 2020 22:12:23 +0200 Subject: [PATCH 24/32] Only branch check is needed --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4413c85..7815e0b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,13 +62,13 @@ jobs: ignore: tests/shunit2 - name: Verify signature - if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/master' uses: atmoz/git-verify-ref@master with: public_key_dir: .github/public-keys - name: Push images to Docker Hub registry - if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/master' run: | echo "${{ secrets.DOCKER_HUB_PASSWORD }}" | docker login \ -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin @@ -76,7 +76,7 @@ jobs: docker push $IMAGE_NAME # no tags specified to include all tags - name: Push images to GitHub registry - if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/master' run: | echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com \ -u ${{ github.actor }} --password-stdin From e84f6a4378c8b8b8b2a52365d508cd68ea63bcc5 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Sun, 19 Jul 2020 22:13:03 +0200 Subject: [PATCH 25/32] Shellcheck before builds --- .github/workflows/build.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7815e0b..6cdd783 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,6 +27,11 @@ jobs: fetch-depth: 0 # for proper signature verification submodules: true # for shunit2 + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@master + with: + ignore: tests/shunit2 + - name: Build debian image run: | docker build . \ @@ -56,11 +61,6 @@ jobs: - name: Test alpine image run: tests/run $IMAGE_NAME:alpine - - name: Run ShellCheck - uses: ludeeus/action-shellcheck@master - with: - ignore: tests/shunit2 - - name: Verify signature if: github.ref == 'refs/heads/master' uses: atmoz/git-verify-ref@master From f406c062c0c1cda778c0301f8ded1732d122f5bf Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Sun, 19 Jul 2020 22:11:43 +0200 Subject: [PATCH 26/32] Use new GitHub user arg --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6cdd783..5886097 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,7 +65,7 @@ jobs: if: github.ref == 'refs/heads/master' uses: atmoz/git-verify-ref@master with: - public_key_dir: .github/public-keys + import-github-users: atmoz - name: Push images to Docker Hub registry if: github.ref == 'refs/heads/master' From e8a5d67e4327fad68ab9967ebb8b50aa80084897 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Sun, 19 Jul 2020 23:01:41 +0200 Subject: [PATCH 27/32] Using git sha tags does not make much sense If you pin to one sha, you won't get security updates after new commits are made --- .github/workflows/build.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5886097..df9ce19 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,7 +39,6 @@ jobs: --file=Dockerfile \ --tag="$IMAGE_NAME:latest" \ --tag="$IMAGE_NAME:debian" \ - --tag="$IMAGE_NAME:debian-$GITHUB_SHA" \ --label="org.opencontainers.image.source=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" \ --label="org.opencontainers.image.revision=$GITHUB_SHA" \ --label="org.opencontainers.image.created=$(date --rfc-3339=seconds)" @@ -53,7 +52,6 @@ jobs: --pull=true \ --file=Dockerfile-alpine \ --tag="$IMAGE_NAME:alpine" \ - --tag="$IMAGE_NAME:alpine-$GITHUB_SHA" \ --label="org.opencontainers.image.source=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY" \ --label="org.opencontainers.image.revision=$GITHUB_SHA" \ --label="org.opencontainers.image.created=$(date --rfc-3339=seconds)" From d6cb031f2766ea4ca8871a1146e87cdc97377158 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Sun, 19 Jul 2020 23:02:07 +0200 Subject: [PATCH 28/32] Logout from docker registry when done --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index df9ce19..cf5f148 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,6 +72,7 @@ jobs: -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin docker push $IMAGE_NAME # no tags specified to include all tags + docker logout - name: Push images to GitHub registry if: github.ref == 'refs/heads/master' @@ -85,4 +86,5 @@ jobs: docker tag $IMAGE_NAME:alpine $TAG_ALPINE docker push $TAG_DEBIAN docker push $TAG_ALPINE + docker logout docker.pkg.github.com From d38ae60a602f19e07ffe7954096eca301614b9eb Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Mon, 20 Jul 2020 01:36:51 +0200 Subject: [PATCH 29/32] Ignoring files in pull requests not neccessary --- .github/workflows/build.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cf5f148..15df422 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,10 +9,6 @@ on: - "*.txt" - "*.png" pull_request: - paths-ignore: - - "*.md" - - "*.txt" - - "*.png" env: IMAGE_NAME: atmoz/sftp From 4e4726aaa28fb4e5cf163be14ace94dcf0ac9769 Mon Sep 17 00:00:00 2001 From: "Alex D. Guerrieri" Date: Thu, 3 Oct 2019 12:14:12 +0200 Subject: [PATCH 30/32] Update entrypoint --- files/entrypoint | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/files/entrypoint b/files/entrypoint index 8730452..16d2270 100755 --- a/files/entrypoint +++ b/files/entrypoint @@ -67,9 +67,13 @@ if [ ! -f "$userConfFinalPath" ]; then # Generate unique ssh keys for this container, if needed if [ ! -f /etc/ssh/ssh_host_ed25519_key ]; then ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N '' + else + chmod 700 /etc/ssh/ssh_host_ed25519_key fi if [ ! -f /etc/ssh/ssh_host_rsa_key ]; then ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N '' + else + chmod 700 /etc/ssh/ssh_host_rsa_key fi fi From cd5d03295a84282224908f639c350928caa925a3 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Sat, 8 Aug 2020 16:00:09 +0200 Subject: [PATCH 31/32] Change host key permission to 600 and avoid error See https://github.com/atmoz/sftp/commit/4e4726aaa28fb4e5cf163be14ace94dcf0ac9769#commitcomment-41292709 --- files/entrypoint | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/files/entrypoint b/files/entrypoint index 16d2270..9c3d6e3 100755 --- a/files/entrypoint +++ b/files/entrypoint @@ -67,14 +67,14 @@ if [ ! -f "$userConfFinalPath" ]; then # Generate unique ssh keys for this container, if needed if [ ! -f /etc/ssh/ssh_host_ed25519_key ]; then ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N '' - else - chmod 700 /etc/ssh/ssh_host_ed25519_key fi if [ ! -f /etc/ssh/ssh_host_rsa_key ]; then ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N '' - else - chmod 700 /etc/ssh/ssh_host_rsa_key fi + + # Restrict access from other users + chmod 600 /etc/ssh/ssh_host_ed25519_key || true + chmod 600 /etc/ssh/ssh_host_rsa_key || true fi # Source custom scripts, if any From f735906a358a03bcb3f6906604a50226ff877e04 Mon Sep 17 00:00:00 2001 From: Adrian Dvergsdal Date: Wed, 19 Aug 2020 23:25:27 +0200 Subject: [PATCH 32/32] Generate and use host key in tests --- tests/run | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/tests/run b/tests/run index 406ae9a..480f0c0 100755 --- a/tests/run +++ b/tests/run @@ -6,8 +6,12 @@ argOutput=${2:-"quiet"} argCleanup=${3:-"cleanup"} testDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" imageName="$argImage" -sshKeyPri="/tmp/atmoz_sftp_test_rsa" -sshKeyPub="/tmp/atmoz_sftp_test_rsa.pub" +tmpDir="$(mktemp -d /tmp/atmoz_sftp_XXXX)" +sshKeyPri="$tmpDir/rsa" +sshKeyPub="$tmpDir/rsa.pub" +sshHostEd25519Key="$tmpDir/ssh_host_ed25519_key" +sshHostKeyMountArg="--volume=$sshHostEd25519Key:/etc/ssh/ssh_host_ed25519_key" +sshKnownHosts="$tmpDir/known_hosts" if [ $UID != 0 ] && ! groups | grep -qw docker; then echo "Run with sudo/root or add user $USER to group 'docker'" @@ -46,6 +50,9 @@ function oneTimeSetUp() { # Private key can not be read by others (sshd will complain) chmod go-rw "$sshKeyPri" + + # Generate host key + ssh-keygen -t ed25519 -f "$sshHostEd25519Key" < /dev/null } function setUp() { @@ -87,6 +94,8 @@ function runSftpCommands() { user="$2" shift 2 + echo "$ip $(cat "$sshHostEd25519Key.pub")" >> "$sshKnownHosts" + commands="" for cmd in "$@"; do commands="$commands$cmd"$'\n' @@ -94,8 +103,7 @@ function runSftpCommands() { echo "$commands" | sftp \ -i "$sshKeyPri" \ - -oStrictHostKeyChecking=no \ - -oUserKnownHostsFile=/dev/null \ + -oUserKnownHostsFile="$sshKnownHosts" \ -b - "$user@$ip" \ > "$redirect" 2>&1 @@ -127,7 +135,7 @@ function waitForServer() { ############################################################################## function testSmallestUserConfig() { - docker run --name "$containerName" \ + docker run --name "$containerName" "$sshHostKeyMountArg" \ --entrypoint="/bin/sh" \ "$imageName" \ -c "create-sftp-user u: && id u" \ @@ -136,7 +144,7 @@ function testSmallestUserConfig() { } function testCreateUserWithDot() { - docker run --name "$containerName" \ + docker run --name "$containerName" "$sshHostKeyMountArg" \ --entrypoint="/bin/sh" \ "$imageName" \ -c "create-sftp-user user.with.dot: && id user.with.dot" \ @@ -145,7 +153,7 @@ function testCreateUserWithDot() { } function testUserCustomUidAndGid() { - id="$(docker run --name "$containerName" \ + id="$(docker run --name "$containerName" "$sshHostKeyMountArg" \ --entrypoint="/bin/sh" \ "$imageName" \ -c "create-sftp-user u::1234:4321: > /dev/null && id u" )" @@ -161,14 +169,14 @@ function testUserCustomUidAndGid() { } function testCommandPassthrough() { - docker run --name "$containerName" \ + docker run --name "$containerName" "$sshHostKeyMountArg" \ "$imageName" test 1 -eq 1 \ > "$redirect" 2>&1 assertTrue "command passthrough" $? } function testUsersConf() { - docker run --name "$containerName" -d \ + docker run --name "$containerName" "$sshHostKeyMountArg" -d \ -v "$testDir/files/users.conf:/etc/sftp/users.conf:ro" \ "$imageName" \ > "$redirect" 2>&1 @@ -190,7 +198,7 @@ function testUsersConf() { } function testLegacyUsersConf() { - docker run --name "$containerName" -d \ + docker run --name "$containerName" "$sshHostKeyMountArg" -d \ -v "$testDir/files/users.conf:/etc/sftp-users.conf:ro" \ "$imageName" \ > "$redirect" 2>&1 @@ -203,7 +211,7 @@ function testLegacyUsersConf() { } function testCreateUsersUsingEnv() { - docker run --name "$containerName" -d \ + docker run --name "$containerName" "$sshHostKeyMountArg" -d \ -e "SFTP_USERS=user-from-env: user-from-env-2:" \ "$imageName" \ > "$redirect" 2>&1 @@ -219,7 +227,7 @@ function testCreateUsersUsingEnv() { } function testCreateUsersUsingCombo() { - docker run --name "$containerName" -d \ + docker run --name "$containerName" "$sshHostKeyMountArg" -d \ -v "$testDir/files/users.conf:/etc/sftp-users.conf:ro" \ -e "SFTP_USERS=user-from-env:" \ "$imageName" \ @@ -240,7 +248,7 @@ function testCreateUsersUsingCombo() { } function testWriteAccessToAutocreatedDirs() { - docker run --name "$containerName" -d \ + docker run --name "$containerName" "$sshHostKeyMountArg" -d \ -v "$sshKeyPub":/home/test/.ssh/keys/id_rsa.pub:ro \ "$imageName" "test::::testdir,dir with spaces" \ > "$redirect" 2>&1 @@ -278,7 +286,7 @@ chmod 755 /home/*/sftp EOF chmod +x "$tmpScript" - docker run --name "$containerName" -d \ + docker run --name "$containerName" "$sshHostKeyMountArg" -d \ -v "$sshKeyPub":/home/test/.ssh/keys/id_rsa.pub:ro \ -v "$tmpConfig:/etc/ssh/sshd_config" \ -v "$tmpScript:/etc/sftp.d/limited_home_dir" \ @@ -306,7 +314,7 @@ function testBindmountDirScript() { > "$containerTmpDir/mount.sh" chmod +x "$containerTmpDir/mount.sh" - docker run --name "$containerName" -d \ + docker run --name "$containerName" "$sshHostKeyMountArg" -d \ --privileged=true \ -v "$sshKeyPub":/home/custom/.ssh/keys/id_rsa.pub:ro \ -v "$containerTmpDir/custom/bindmount":/custom \ @@ -328,7 +336,7 @@ function testBindmountDirScript() { } function testDuplicateSshKeys() { - docker run --name "$containerName" -d \ + docker run --name "$containerName" "$sshHostKeyMountArg" -d \ -v "$sshKeyPub":/home/user/.ssh/keys/key1.pub:ro \ -v "$sshKeyPub":/home/user/.ssh/keys/key2.pub:ro \ "$imageName" "user:" \