diff --git a/README.md b/README.md index fd0f384..4f7f3f9 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ Easy to use SFTP ([SSH File Transfer Protocol](https://en.wikipedia.org/wiki/SSH Usage ----- -- Define users as last arguments to `docker run`, one user per argument - (syntax: `user:pass[:e][:[uid][:gid]]`). +- Define users as command arguments, STDIN or mounted in /etc/sftp-users.conf + (syntax: `user:pass[:e][:uid[:gid]]...`). - You must set custom UID for your users if you want them to make changes to your mounted volumes with permissions matching your host filesystem. - Mount volumes in user's home folder. @@ -18,7 +18,7 @@ Usage Examples -------- -### Single user and volume +### Simple example ``` docker run \ @@ -45,16 +45,22 @@ The OpenSSH server runs by default on port 22, and in this example, we are forwarding the container's port 22 to the host's port 2222. To log in with an OpenSSH client, run: `sftp -P 2222 foo@` -### Multiple users and volumes +### Store users in config + +/host/users.conf: + +``` +foo:123:1001 +bar:abc:1002 +``` ``` docker run \ + -v /host/users.conf:/etc/sftp-users.conf:ro \ -v /host/share:/home/foo/share \ -v /host/documents:/home/foo/documents \ -v /host/http:/home/bar/http \ - -p 2222:22 -d atmoz/sftp \ - foo:123:1001 \ - bar:abc:1002 + -p 2222:22 -d atmoz/sftp ``` ### Encrypted password @@ -68,8 +74,8 @@ docker run \ 'foo:$1$0G2g0GSt$ewU0t6GXG15.0hWoOX8X9.:e:1001' ``` -Tip: you can use makepasswd to generate encrypted passwords: -`echo -n 123 | makepasswd --crypt-md5 --clearfrom -` +Tip: you can use makepasswd to generate encrypted passwords: +`echo -n "password" | makepasswd --crypt-md5 --clearfrom -` ### Using SSH key (without password) diff --git a/entrypoint b/entrypoint index 6c02243..53438b7 100755 --- a/entrypoint +++ b/entrypoint @@ -1,12 +1,17 @@ #!/bin/bash +userConfPath="/etc/sftp-users.conf" +userConfFinalPath="/var/run/sftp-users.conf" + function printHelp() { - echo "Syntax: user:pass[:e][:[uid][:gid]]..." - echo "Use --readme for information and examples." + echo "Add users as command arguments, STDIN or mounted in $userConfPath" + echo "Syntax: user:pass[:e][:uid[:gid]]..." + echo "Use --readme for more information and examples." } function printReadme() { cat /README.md + echo "TIP: Read this in HTML format here: https://github.com/atmoz/sftp" } function createUser() { @@ -14,8 +19,8 @@ function createUser() { user="${param[0]}" pass="${param[1]}" - if [ -z "$user" -o -z "$pass" ]; then - echo "You must at least provide a username and a password." + if [ -z "$user" ]; then + echo "You must at least provide a username." printHelp exit 1 fi @@ -29,7 +34,7 @@ function createUser() { gid="${param[3]}" fi - useraddOptions="--create-home --no-user-group" + useraddOptions="--no-user-group" if [ -n "$uid" ]; then useraddOptions="$useraddOptions --non-unique --uid $uid" @@ -37,7 +42,10 @@ function createUser() { if [ -n "$gid" ]; then useraddOptions="$useraddOptions --gid $gid" - groupadd --gid $gid $gid + + if [ "$(cat /etc/group | cut -d : -f3 | grep -q "$gid")" ]; then + groupadd --gid $gid $gid + fi fi useradd $useraddOptions $user @@ -56,7 +64,7 @@ function createUser() { chmod 600 /home/$user/.ssh/authorized_keys } -if [[ -z $1 || $1 =~ ^--help$|^-h$ ]]; then +if [[ $1 =~ ^--help$|^-h$ ]]; then printHelp exit 0 fi @@ -66,8 +74,30 @@ if [ "$1" == "--readme" ]; then exit 0 fi +# Append mounted config to final config +if [ -f "$userConfPath" ]; then + cat "$userConfPath" >> "$userConfFinalPath" +fi + +# Append users from arguments to final config for user in "$@"; do - createUser $user + echo "$user" >> "$userConfFinalPath" done +# Append users from STDIN to final config +while IFS= read -r user || [[ -n "$user" ]]; do + echo "$user" >> "$userConfFinalPath" +done + +if [ ! -f "$userConfFinalPath" ]; then + echo "ERROR: Missing users!" + printHelp + exit 1 +fi + +# Import users from final conf file +while IFS= read -r user || [[ -n "$user" ]]; do + createUser "$user"; +done < "$userConfFinalPath" + exec /usr/sbin/sshd -D diff --git a/tests/run b/tests/run index 6b76a7b..1679b11 100755 --- a/tests/run +++ b/tests/run @@ -6,9 +6,19 @@ buildDir="$scriptDir/.." tmpDir="/tmp/atmoz_sftp_test" build=${1:-"build"} +output=${2:-"quiet"} +cleanup=${3:-"cleanup"} sftpImageName="atmoz/sftp_test" sftpContainerName="atmoz_sftp_test" +if [ "$output" == "quiet" ]; then + redirect="/dev/null" +else + redirect=$'&1' +fi + +############################################################################## + function beforeTest() { if [ "$build" == "build" ]; then docker build --pull=true --tag "$sftpImageName" "$buildDir" @@ -20,20 +30,27 @@ function beforeTest() { rm -rf "$tmpDir" # clean state mkdir "$tmpDir" + echo "test::$(id -u):$(id -g)" >> "$tmpDir/users" docker run \ + -v "$tmpDir/users:/etc/sftp-users.conf:ro" \ -v "$scriptDir/id_rsa.pub":/home/test/.ssh/keys/id_rsa.pub:ro \ -v "$tmpDir":/home/test/share \ --name "$sftpContainerName" \ --expose 22 \ -d "$sftpImageName" \ - test:123:$(id -u):$(id -g) \ - > /dev/null + > "$redirect" sleep 1 # wait for sftp server to get ready } function afterTest() { - docker rm -fv "$sftpContainerName" > /dev/null - rm -rf "$tmpDir" + if [ "$output" != "quiet" ]; then + docker logs "$sftpContainerName" + fi + + if [ "$cleanup" == "cleanup" ]; then + docker rm -fv "$sftpContainerName" > "$redirect" + rm -rf "$tmpDir" + fi } function getSftpIp() { @@ -42,14 +59,22 @@ function getSftpIp() { function runSftpCommands() { ip="$(getSftpIp)" - echo "$@" | sftp \ + + commands="" + for cmd in "$@"; do + commands="$commands$cmd"$'\n' + done + + echo "$commands" | sftp \ -i "$scriptDir/id_rsa" \ -oStrictHostKeyChecking=no \ -oUserKnownHostsFile=/dev/null \ -b - test@$ip \ - > /dev/null 2>&1 + > "$redirect" 2>&1 } +############################################################################## + function testContainerIsRunning() { ps="$(docker ps -q -f name="$sftpContainerName")" assertNotEqual "$ps" "" @@ -61,11 +86,13 @@ function testLoginUsingSshKey() { } function testWritePermission() { - runSftpCommands $'cd share\nmkdir test' + runSftpCommands "cd share" "mkdir test" "exit" test -d "$tmpDir/test" assertReturn $? 0 } +############################################################################## + # Run tests source "$scriptDir/bashunit.bash" # Nothing happens after this