mirror of
https://github.com/atmoz/sftp.git
synced 2024-12-01 13:00:56 -05:00
Merge branch 'master' into alpine
This commit is contained in:
commit
74df8c4b0d
12 changed files with 438 additions and 622 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "tests/shunit2"]
|
||||||
|
path = tests/shunit2
|
||||||
|
url = https://github.com/kward/shunit2.git
|
|
@ -12,8 +12,9 @@ RUN echo "@community http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /et
|
||||||
mkdir -p /var/run/sshd && \
|
mkdir -p /var/run/sshd && \
|
||||||
rm -f /etc/ssh/ssh_host_*key*
|
rm -f /etc/ssh/ssh_host_*key*
|
||||||
|
|
||||||
COPY sshd_config /etc/ssh/sshd_config
|
COPY files/sshd_config /etc/ssh/sshd_config
|
||||||
COPY entrypoint /
|
COPY files/create-sftp-user /usr/local/bin/
|
||||||
|
COPY files/entrypoint /
|
||||||
|
|
||||||
EXPOSE 22
|
EXPOSE 22
|
||||||
|
|
||||||
|
|
192
entrypoint
192
entrypoint
|
@ -1,192 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
set -Eeo pipefail
|
|
||||||
|
|
||||||
# shellcheck disable=2154
|
|
||||||
trap 's=$?; echo "$0: Error on line "$LINENO": $BASH_COMMAND"; exit $s' ERR
|
|
||||||
|
|
||||||
# Paths
|
|
||||||
userConfPath="/etc/sftp/users.conf"
|
|
||||||
userConfPathLegacy="/etc/sftp-users.conf"
|
|
||||||
userConfFinalPath="/var/run/sftp/users.conf"
|
|
||||||
|
|
||||||
# Extended regular expression (ERE) for arguments
|
|
||||||
reUser='[A-Za-z0-9._][A-Za-z0-9._-]{0,31}' # POSIX.1-2008
|
|
||||||
rePass='[^:]{0,255}'
|
|
||||||
reUid='[[:digit:]]*'
|
|
||||||
reGid='[[:digit:]]*'
|
|
||||||
reDir='[^:]*'
|
|
||||||
#reArgs="^($reUser)(:$rePass)(:e)?(:$reUid)?(:$reGid)?(:$reDir)?$"
|
|
||||||
reArgsMaybe="^[^:[:space:]]+:.*$" # Smallest indication of attempt to use argument
|
|
||||||
reArgSkip='^([[:blank:]]*#.*|[[:blank:]]*)$' # comment or empty line
|
|
||||||
|
|
||||||
function log() {
|
|
||||||
echo "[entrypoint] $*"
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateArg() {
|
|
||||||
name="$1"
|
|
||||||
val="$2"
|
|
||||||
re="$3"
|
|
||||||
|
|
||||||
if [[ "$val" =~ ^$re$ ]]; then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
log "ERROR: Invalid $name \"$val\", do not match required regex pattern: $re"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function createUser() {
|
|
||||||
log "Parsing user data: \"$1\""
|
|
||||||
IFS=':' read -ra args <<< "$1"
|
|
||||||
|
|
||||||
skipIndex=0
|
|
||||||
chpasswdOptions=""
|
|
||||||
useraddOptions=(--no-user-group)
|
|
||||||
|
|
||||||
user="${args[0]}"; validateArg "username" "$user" "$reUser" || return 1
|
|
||||||
pass="${args[1]}"; validateArg "password" "$pass" "$rePass" || return 1
|
|
||||||
|
|
||||||
if [ "${args[2]}" == "e" ]; then
|
|
||||||
chpasswdOptions="-e"
|
|
||||||
skipIndex=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
uid="${args[$((skipIndex+2))]}"; validateArg "UID" "$uid" "$reUid" || return 1
|
|
||||||
gid="${args[$((skipIndex+3))]}"; validateArg "GID" "$gid" "$reGid" || return 1
|
|
||||||
dir="${args[$((skipIndex+4))]}"; validateArg "dirs" "$dir" "$reDir" || return 1
|
|
||||||
|
|
||||||
if getent passwd "$user" > /dev/null; then
|
|
||||||
log "WARNING: User \"$user\" already exists. Skipping."
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$uid" ]; then
|
|
||||||
useraddOptions+=(--non-unique --uid "$uid")
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$gid" ]; then
|
|
||||||
if ! getent group "$gid" > /dev/null; then
|
|
||||||
groupadd --gid "$gid" "group_$gid"
|
|
||||||
fi
|
|
||||||
|
|
||||||
useraddOptions+=(--gid "$gid")
|
|
||||||
fi
|
|
||||||
|
|
||||||
useradd "${useraddOptions[@]}" "$user"
|
|
||||||
mkdir -p "/home/$user"
|
|
||||||
chown root:root "/home/$user"
|
|
||||||
chmod 755 "/home/$user"
|
|
||||||
|
|
||||||
# Retrieving user id to use it in chown commands instead of the user name
|
|
||||||
# to avoid problems on alpine when the user name contains a '.'
|
|
||||||
uid="$(id -u "$user")"
|
|
||||||
|
|
||||||
if [ -n "$pass" ]; then
|
|
||||||
echo "$user:$pass" | chpasswd $chpasswdOptions
|
|
||||||
else
|
|
||||||
usermod -p "*" "$user" # disabled password
|
|
||||||
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"
|
|
||||||
done
|
|
||||||
chown "$uid" "/home/$user/.ssh/authorized_keys"
|
|
||||||
chmod 600 "/home/$user/.ssh/authorized_keys"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Make sure dirs exists
|
|
||||||
if [ -n "$dir" ]; then
|
|
||||||
IFS=',' read -ra dirArgs <<< "$dir"
|
|
||||||
for dirPath in "${dirArgs[@]}"; do
|
|
||||||
dirPath="/home/$user/$dirPath"
|
|
||||||
if [ ! -d "$dirPath" ]; then
|
|
||||||
log "Creating directory: $dirPath"
|
|
||||||
mkdir -p "$dirPath"
|
|
||||||
chown -R "$uid:users" "$dirPath"
|
|
||||||
else
|
|
||||||
log "Directory already exists: $dirPath"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Allow running other programs, e.g. bash
|
|
||||||
if [[ -z "$1" || "$1" =~ $reArgsMaybe ]]; then
|
|
||||||
startSshd=true
|
|
||||||
else
|
|
||||||
startSshd=false
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Backward compatibility with legacy config path
|
|
||||||
if [ ! -f "$userConfPath" ] && [ -f "$userConfPathLegacy" ]; then
|
|
||||||
mkdir -p "$(dirname $userConfPath)"
|
|
||||||
ln -s "$userConfPathLegacy" "$userConfPath"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create users only on first run
|
|
||||||
if [ ! -f "$userConfFinalPath" ]; then
|
|
||||||
mkdir -p "$(dirname $userConfFinalPath)"
|
|
||||||
|
|
||||||
# Append mounted config to final config
|
|
||||||
if [ -f "$userConfPath" ]; then
|
|
||||||
grep -v -E "$reArgSkip" < "$userConfPath" > "$userConfFinalPath"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if $startSshd; then
|
|
||||||
# Append users from arguments to final config
|
|
||||||
for user in "$@"; do
|
|
||||||
echo "$user" >> "$userConfFinalPath"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$SFTP_USERS" ]; then
|
|
||||||
# Append users from environment variable to final config
|
|
||||||
IFS=" " read -r -a usersFromEnv <<< "$SFTP_USERS"
|
|
||||||
for user in "${usersFromEnv[@]}"; do
|
|
||||||
echo "$user" >> "$userConfFinalPath"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check that we have users in config
|
|
||||||
if [ -f "$userConfFinalPath" ] && [ "$(wc -l < "$userConfFinalPath")" -gt 0 ]; then
|
|
||||||
# Import users from final conf file
|
|
||||||
while IFS= read -r user || [[ -n "$user" ]]; do
|
|
||||||
createUser "$user"
|
|
||||||
done < "$userConfFinalPath"
|
|
||||||
elif $startSshd; then
|
|
||||||
log "FATAL: No users provided!"
|
|
||||||
exit 3
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 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 ''
|
|
||||||
fi
|
|
||||||
if [ ! -f /etc/ssh/ssh_host_rsa_key ]; then
|
|
||||||
ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ''
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Source custom scripts, if any
|
|
||||||
if [ -d /etc/sftp.d ]; then
|
|
||||||
for f in /etc/sftp.d/*; do
|
|
||||||
if [ -x "$f" ]; then
|
|
||||||
log "Running $f ..."
|
|
||||||
$f
|
|
||||||
else
|
|
||||||
log "Could not run $f, because it's missing execute permission (+x)."
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
unset f
|
|
||||||
fi
|
|
||||||
|
|
||||||
if $startSshd; then
|
|
||||||
log "Executing sshd"
|
|
||||||
exec /usr/sbin/sshd -D -e
|
|
||||||
else
|
|
||||||
log "Executing $*"
|
|
||||||
exec "$@"
|
|
||||||
fi
|
|
105
files/create-sftp-user
Executable file
105
files/create-sftp-user
Executable file
|
@ -0,0 +1,105 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -Eeo pipefail
|
||||||
|
|
||||||
|
# shellcheck disable=2154
|
||||||
|
trap 's=$?; echo "$0: Error on line "$LINENO": $BASH_COMMAND"; exit $s' ERR
|
||||||
|
|
||||||
|
# Extended regular expression (ERE) for arguments
|
||||||
|
reUser='[A-Za-z0-9._][A-Za-z0-9._-]{0,31}' # POSIX.1-2008
|
||||||
|
rePass='[^:]{0,255}'
|
||||||
|
reUid='[[:digit:]]*'
|
||||||
|
reGid='[[:digit:]]*'
|
||||||
|
reDir='[^:]*'
|
||||||
|
#reArgs="^($reUser)(:$rePass)(:e)?(:$reUid)?(:$reGid)?(:$reDir)?$"
|
||||||
|
|
||||||
|
function log() {
|
||||||
|
echo "[$0] $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateArg() {
|
||||||
|
name="$1"
|
||||||
|
val="$2"
|
||||||
|
re="$3"
|
||||||
|
|
||||||
|
if [[ "$val" =~ ^$re$ ]]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log "ERROR: Invalid $name \"$val\", do not match required regex pattern: $re"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
log "Parsing user data: \"$1\""
|
||||||
|
IFS=':' read -ra args <<< "$1"
|
||||||
|
|
||||||
|
skipIndex=0
|
||||||
|
chpasswdOptions=""
|
||||||
|
useraddOptions=(--no-user-group)
|
||||||
|
|
||||||
|
user="${args[0]}"; validateArg "username" "$user" "$reUser" || return 1
|
||||||
|
pass="${args[1]}"; validateArg "password" "$pass" "$rePass" || return 1
|
||||||
|
|
||||||
|
if [ "${args[2]}" == "e" ]; then
|
||||||
|
chpasswdOptions="-e"
|
||||||
|
skipIndex=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
uid="${args[$((skipIndex+2))]}"; validateArg "UID" "$uid" "$reUid" || return 1
|
||||||
|
gid="${args[$((skipIndex+3))]}"; validateArg "GID" "$gid" "$reGid" || return 1
|
||||||
|
dir="${args[$((skipIndex+4))]}"; validateArg "dirs" "$dir" "$reDir" || return 1
|
||||||
|
|
||||||
|
if getent passwd "$user" > /dev/null; then
|
||||||
|
log "WARNING: User \"$user\" already exists. Skipping."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$uid" ]; then
|
||||||
|
useraddOptions+=(--non-unique --uid "$uid")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$gid" ]; then
|
||||||
|
if ! getent group "$gid" > /dev/null; then
|
||||||
|
groupadd --gid "$gid" "group_$gid"
|
||||||
|
fi
|
||||||
|
|
||||||
|
useraddOptions+=(--gid "$gid")
|
||||||
|
fi
|
||||||
|
|
||||||
|
useradd "${useraddOptions[@]}" "$user"
|
||||||
|
mkdir -p "/home/$user"
|
||||||
|
chown root:root "/home/$user"
|
||||||
|
chmod 755 "/home/$user"
|
||||||
|
|
||||||
|
# Retrieving user id to use it in chown commands instead of the user name
|
||||||
|
# to avoid problems on alpine when the user name contains a '.'
|
||||||
|
uid="$(id -u "$user")"
|
||||||
|
|
||||||
|
if [ -n "$pass" ]; then
|
||||||
|
echo "$user:$pass" | chpasswd $chpasswdOptions
|
||||||
|
else
|
||||||
|
usermod -p "*" "$user" # disabled password
|
||||||
|
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"
|
||||||
|
done
|
||||||
|
chown "$uid" "/home/$user/.ssh/authorized_keys"
|
||||||
|
chmod 600 "/home/$user/.ssh/authorized_keys"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make sure dirs exists
|
||||||
|
if [ -n "$dir" ]; then
|
||||||
|
IFS=',' read -ra dirArgs <<< "$dir"
|
||||||
|
for dirPath in "${dirArgs[@]}"; do
|
||||||
|
dirPath="/home/$user/$dirPath"
|
||||||
|
if [ ! -d "$dirPath" ]; then
|
||||||
|
log "Creating directory: $dirPath"
|
||||||
|
mkdir -p "$dirPath"
|
||||||
|
chown -R "$uid:users" "$dirPath"
|
||||||
|
else
|
||||||
|
log "Directory already exists: $dirPath"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
95
files/entrypoint
Executable file
95
files/entrypoint
Executable file
|
@ -0,0 +1,95 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -Eeo pipefail
|
||||||
|
|
||||||
|
# shellcheck disable=2154
|
||||||
|
trap 's=$?; echo "$0: Error on line "$LINENO": $BASH_COMMAND"; exit $s' ERR
|
||||||
|
|
||||||
|
reArgsMaybe="^[^:[:space:]]+:.*$" # Smallest indication of attempt to use argument
|
||||||
|
reArgSkip='^([[:blank:]]*#.*|[[:blank:]]*)$' # comment or empty line
|
||||||
|
|
||||||
|
# Paths
|
||||||
|
userConfPath="/etc/sftp/users.conf"
|
||||||
|
userConfPathLegacy="/etc/sftp-users.conf"
|
||||||
|
userConfFinalPath="/var/run/sftp/users.conf"
|
||||||
|
|
||||||
|
function log() {
|
||||||
|
echo "[$0] $*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Allow running other programs, e.g. bash
|
||||||
|
if [[ -z "$1" || "$1" =~ $reArgsMaybe ]]; then
|
||||||
|
startSshd=true
|
||||||
|
else
|
||||||
|
startSshd=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Backward compatibility with legacy config path
|
||||||
|
if [ ! -f "$userConfPath" ] && [ -f "$userConfPathLegacy" ]; then
|
||||||
|
mkdir -p "$(dirname $userConfPath)"
|
||||||
|
ln -s "$userConfPathLegacy" "$userConfPath"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create users only on first run
|
||||||
|
if [ ! -f "$userConfFinalPath" ]; then
|
||||||
|
mkdir -p "$(dirname $userConfFinalPath)"
|
||||||
|
|
||||||
|
if [ -f "$userConfPath" ]; then
|
||||||
|
# Append mounted config to final config
|
||||||
|
grep -v -E "$reArgSkip" < "$userConfPath" > "$userConfFinalPath"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $startSshd; then
|
||||||
|
# Append users from arguments to final config
|
||||||
|
for user in "$@"; do
|
||||||
|
echo "$user" >> "$userConfFinalPath"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$SFTP_USERS" ]; then
|
||||||
|
# Append users from environment variable to final config
|
||||||
|
IFS=" " read -r -a usersFromEnv <<< "$SFTP_USERS"
|
||||||
|
for user in "${usersFromEnv[@]}"; do
|
||||||
|
echo "$user" >> "$userConfFinalPath"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check that we have users in config
|
||||||
|
if [ -f "$userConfFinalPath" ] && [ "$(wc -l < "$userConfFinalPath")" -gt 0 ]; then
|
||||||
|
# Import users from final conf file
|
||||||
|
while IFS= read -r user || [[ -n "$user" ]]; do
|
||||||
|
create-sftp-user "$user"
|
||||||
|
done < "$userConfFinalPath"
|
||||||
|
elif $startSshd; then
|
||||||
|
log "FATAL: No users provided!"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 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 ''
|
||||||
|
fi
|
||||||
|
if [ ! -f /etc/ssh/ssh_host_rsa_key ]; then
|
||||||
|
ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ''
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Source custom scripts, if any
|
||||||
|
if [ -d /etc/sftp.d ]; then
|
||||||
|
for f in /etc/sftp.d/*; do
|
||||||
|
if [ -x "$f" ]; then
|
||||||
|
log "Running $f ..."
|
||||||
|
$f
|
||||||
|
else
|
||||||
|
log "Could not run $f, because it's missing execute permission (+x)."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
unset f
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $startSshd; then
|
||||||
|
log "Executing sshd"
|
||||||
|
exec /usr/sbin/sshd -D -e
|
||||||
|
else
|
||||||
|
log "Executing $*"
|
||||||
|
exec "$@"
|
||||||
|
fi
|
|
@ -1,198 +0,0 @@
|
||||||
|
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
########################################################################
|
|
||||||
# GLOBALS
|
|
||||||
########################################################################
|
|
||||||
|
|
||||||
verbose=2
|
|
||||||
|
|
||||||
bashunit_passed=0
|
|
||||||
bashunit_failed=0
|
|
||||||
bashunit_skipped=0
|
|
||||||
|
|
||||||
########################################################################
|
|
||||||
# ASSERT FUNCTIONS
|
|
||||||
########################################################################
|
|
||||||
|
|
||||||
# Assert that a given expression evaluates to true.
|
|
||||||
#
|
|
||||||
# $1: Expression
|
|
||||||
assert() {
|
|
||||||
if test $* ; then _passed ; else _failed "$*" true ; fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert that a given output string is equal to an expected string.
|
|
||||||
#
|
|
||||||
# $1: Output
|
|
||||||
# $2: Expected
|
|
||||||
assertEqual() {
|
|
||||||
echo $1 | grep -E "^$2$" > /dev/null
|
|
||||||
if [ $? -eq 0 ] ; then _passed ; else _failed "$1" "$2" ; fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert that a given output string is not equal to an expected string.
|
|
||||||
#
|
|
||||||
# $1: Output
|
|
||||||
# $2: Expected
|
|
||||||
assertNotEqual() {
|
|
||||||
echo $1 | grep -E "^$2$" > /dev/null
|
|
||||||
if [ $? -ne 0 ] ; then _passed ; else _failed "$1" "$2" ; fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert that a given output string starts with an expected string.
|
|
||||||
#
|
|
||||||
# $1: Output
|
|
||||||
# $2: Expected
|
|
||||||
assertStartsWith() {
|
|
||||||
echo $1 | grep -E "^$2" > /dev/null
|
|
||||||
if [ $? -eq 0 ] ; then _passed ; else _failed "$1" "$2" ; fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert that the last command's return code is equal to an expected integer.
|
|
||||||
#
|
|
||||||
# $1: Output
|
|
||||||
# $2: Expected
|
|
||||||
# $?: Provided
|
|
||||||
assertReturn() {
|
|
||||||
local code=$?
|
|
||||||
if [ $code -eq $2 ] ; then _passed ; else _failed "$code" "$2" ; fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert that the last command's return code is not equal to an expected integer.
|
|
||||||
#
|
|
||||||
# $1: Output
|
|
||||||
# $2: Expected
|
|
||||||
# $?: Provided
|
|
||||||
assertNotReturn() {
|
|
||||||
local code=$?
|
|
||||||
if [ $code -ne $2 ] ; then _passed ; else _failed "$code" "$2" ; fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert that a given integer is greater than an expected integer.
|
|
||||||
#
|
|
||||||
# $1: Output
|
|
||||||
# $2: Expected
|
|
||||||
assertGreaterThan() {
|
|
||||||
if [ $1 -gt $2 ] ; then _passed ; else _failed "$1" "$2" ; fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert that a given integer is greater than or equal to an expected integer.
|
|
||||||
#
|
|
||||||
# $1: Output
|
|
||||||
# $2: Expected
|
|
||||||
assertAtLeast() {
|
|
||||||
if [ $1 -ge $2 ] ; then _passed ; else _failed "$1" "$2" ; fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert that a given integer is less than an expected integer.
|
|
||||||
#
|
|
||||||
# $1: Output
|
|
||||||
# $2: Expected
|
|
||||||
assertLessThan() {
|
|
||||||
if [ $1 -lt $2 ] ; then _passed ; else _failed "$1" "$2" ; fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Assert that a given integer is less than or equal to an expected integer.
|
|
||||||
#
|
|
||||||
# $1: Output
|
|
||||||
# $2: Expected
|
|
||||||
assertAtMost() {
|
|
||||||
if [ $1 -le $2 ] ; then _passed ; else _failed "$1" "$2" ; fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Skip the current test case.
|
|
||||||
#
|
|
||||||
skip() {
|
|
||||||
_skipped
|
|
||||||
}
|
|
||||||
|
|
||||||
_failed() {
|
|
||||||
bashunit_failed=$((bashunit_failed+1))
|
|
||||||
|
|
||||||
local tc=${FUNCNAME[2]}
|
|
||||||
local line=${BASH_LINENO[1]}
|
|
||||||
if [ $verbose -ge 2 ] ; then
|
|
||||||
echo -e "\033[37;1m$tc\033[0m:$line:\033[31mFailed\033[0m"
|
|
||||||
fi
|
|
||||||
if [ $verbose -eq 3 ] ; then
|
|
||||||
echo -e "\033[31mExpected\033[0m: $2"
|
|
||||||
echo -e "\033[31mProvided\033[0m: $1"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_passed() {
|
|
||||||
bashunit_passed=$((bashunit_passed+1))
|
|
||||||
|
|
||||||
local tc=${FUNCNAME[2]}
|
|
||||||
local line=${BASH_LINENO[1]}
|
|
||||||
if [ $verbose -ge 2 ] ; then
|
|
||||||
echo -e "\033[37;1m$tc\033[0m:$line:\033[32mPassed\033[0m"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_skipped() {
|
|
||||||
bashunit_skipped=$((bashunit_skipped+1))
|
|
||||||
|
|
||||||
local tc=${FUNCNAME[2]}
|
|
||||||
local line=${BASH_LINENO[1]}
|
|
||||||
if [ $verbose -ge 2 ] ; then
|
|
||||||
echo -e "\033[37;1m$tc\033[0m:$line:\033[33mSkipped\033[0m"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
########################################################################
|
|
||||||
# RUN
|
|
||||||
########################################################################
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
echo "Usage: <testscript> [options...]"
|
|
||||||
echo
|
|
||||||
echo "Options:"
|
|
||||||
echo " -v, --verbose Print exptected and provided values"
|
|
||||||
echo " -s, --summary Only print summary omitting individual test results"
|
|
||||||
echo " -q, --quiet Do not print anything to standard output"
|
|
||||||
echo " -h, --help Show usage screen"
|
|
||||||
}
|
|
||||||
|
|
||||||
runTests() {
|
|
||||||
local test_pattern="test[a-zA-Z0-9_]\+"
|
|
||||||
local testcases=$(grep "^ *\(function \)*$test_pattern *\\(\\)" $0 | \
|
|
||||||
grep -o $test_pattern)
|
|
||||||
|
|
||||||
if [ ! "${testcases[*]}" ] ; then
|
|
||||||
usage
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$(type -t "beforeTest")" == "function" ]; then
|
|
||||||
beforeTest
|
|
||||||
fi
|
|
||||||
|
|
||||||
for tc in $testcases ; do $tc ; done
|
|
||||||
|
|
||||||
if [ "$(type -t "afterTest")" == "function" ]; then
|
|
||||||
afterTest
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $verbose -ge 1 ] ; then
|
|
||||||
echo "Done. $bashunit_passed passed." \
|
|
||||||
"$bashunit_failed failed." \
|
|
||||||
"$bashunit_skipped skipped."
|
|
||||||
fi
|
|
||||||
exit $bashunit_failed
|
|
||||||
}
|
|
||||||
|
|
||||||
# Arguments
|
|
||||||
while [ $# -gt 0 ]; do
|
|
||||||
arg=$1; shift
|
|
||||||
case $arg in
|
|
||||||
"-v"|"--verbose") verbose=3;;
|
|
||||||
"-s"|"--summary") verbose=1;;
|
|
||||||
"-q"|"--quiet") verbose=0;;
|
|
||||||
"-h"|"--help") usage; exit 0;;
|
|
||||||
*) shift;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
runTests
|
|
14
tests/files/users.conf
Normal file
14
tests/files/users.conf
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# comment
|
||||||
|
# whitespace before and after comment
|
||||||
|
|
||||||
|
# empty lines
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
user-from-conf:
|
||||||
|
test::::dir1,dir2
|
||||||
|
|
||||||
|
# next line has only whitespace (spaces and tabs)
|
||||||
|
|
||||||
|
|
||||||
|
user.with.dot
|
27
tests/id_rsa
27
tests/id_rsa
|
@ -1,27 +0,0 @@
|
||||||
-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIIEpAIBAAKCAQEAxww0MPWvZohlgxFyxvZjEezrld7n5pIJuYbZ7bmZ8UcNbFH8
|
|
||||||
kpoHmmOWQ7x8ObhDLpcWEv8ifoS8RYLI97VMCJcEc/ckcq82ZvoAROF4bG/jYXWj
|
|
||||||
EHRZzEp93FMPFBzvBZkXeBS4ksd9sfqRfNyZzUeu/t380oNsCpr8+TnaEEhC5+E6
|
|
||||||
UbjreH0CPttkD0aRjrszer1hkGs2Py80VZSnidZGrFRNafxigjcLRNp4ZJNweFc7
|
|
||||||
mcuLIfBapJXIJGuj2XAxrt86pBQoESfaETg0m3DWl4Z1w6wZZ26WnIiVOHdiXcj3
|
|
||||||
U0tdZwCixPihH5yYYyy6fE11kMw9sQdhx+4BHQIDAQABAoIBAFGxrpIRpCW/AXrj
|
|
||||||
5GnIoiyvQpnGXQODGL6unC83p/khIl883x8EXO5+xSOT7qB6AgjTNdoiIPQwYl1d
|
|
||||||
KkKQhF5aLRezbaAsTXXCUe3zZEuNOJO9hmmwd1KjmDifVmb44Rk5FirQxlhnzC0K
|
|
||||||
HEBVAkMAktBEKAn2qpdHuWBI4Dkh1hWIpChtkq4h1brsgaYurkfRnJOv1i/8kIWa
|
|
||||||
QlpzxZk2m5i1TpyfRsxuqt3migcrUSJsCmMwkFylDNKYQVS4HRZAXZndq3o51ZF5
|
|
||||||
AzwgMjCty9G1eQGFx4k8CzDvahhTKHds73RHFTEGqWZiJNayuzb0Yz0wvaFlHnWl
|
|
||||||
35E11bkCgYEA5ALPklJ36jLNJiCzReH7SlAczIN9Rh1kRReHDD4St5cxA/nCynhI
|
|
||||||
HET2g8QBnonup1V3WPsDmLTga0hPhXhrI3oSm7jhBc2HOqNaJ/z6jYgGbE6pP05u
|
|
||||||
PgCi8gubX59733FJINmy5XOMzRaVTCfO2lh3zB4Ioj2t4EQyWTaQSw8CgYEA33s6
|
|
||||||
aa+OtrwFmB42KQBPv0E1Yg++jpyrEysrvr7+hSI/8wFJvMqz6xCjsEolgU5/BIIr
|
|
||||||
xQJhqGgtBn2/HRGYqwa00vJJwxYwzPZHK2CiGL5n9HamVBEXeQAp16V6ftPE7lAi
|
|
||||||
MmbhEpbZZpVwCRsi8XofJNS9+YHhUk0si1O8oRMCgYEAn4uf90Ehi40Uo9NJ3mJs
|
|
||||||
VemM3UY8yG0UlowKAXUF39U5hRClTsuvmahf3n+uqmLVzd0t7+Nk9tvKFQe6LSi/
|
|
||||||
v0lR8AkD2+2e7FcVZNnN8G74H51DLHsTBOupGTkp9VVBdm5sv0HVvlyGb5OX0Hwi
|
|
||||||
cAJrgTaaz/vcyQqvOGHHwd0CgYEAo679FKVqEPtb2ZPfNV6uCjX3pJBFkOy8/Hg1
|
|
||||||
PStk/hwc3J6H5IhPCQ+R5LAaEkBtFd9FsbFR1+gdelClpuPZfwKVdJ/TWNkq+yQy
|
|
||||||
8ll/wEHNoCc7If22xIBTJUhllPkEl0wIEAR8O4JTTyiK+5BtopJAt1g+oL35S6+M
|
|
||||||
vauiUBMCgYAHum82lFQDVvLGEj79dpuj0cFfAwWZaPhxjjpQKp4pyCR6mU2O0uO8
|
|
||||||
FtGG1swVG8H/sW7mcFeamZqjCHFSwcKqp5Ij6Wr2xrBU7R2VqIPAsLKHWZzM0G11
|
|
||||||
X391kZk9mXwucy8D0eM8lE/suWmdFK4vdtP+y62q4AFru0HslSCAnA==
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
|
@ -1 +0,0 @@
|
||||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHDDQw9a9miGWDEXLG9mMR7OuV3ufmkgm5htntuZnxRw1sUfySmgeaY5ZDvHw5uEMulxYS/yJ+hLxFgsj3tUwIlwRz9yRyrzZm+gBE4Xhsb+NhdaMQdFnMSn3cUw8UHO8FmRd4FLiSx32x+pF83JnNR67+3fzSg2wKmvz5OdoQSELn4TpRuOt4fQI+22QPRpGOuzN6vWGQazY/LzRVlKeJ1kasVE1p/GKCNwtE2nhkk3B4VzuZy4sh8Fqklcgka6PZcDGu3zqkFCgRJ9oRODSbcNaXhnXDrBlnbpaciJU4d2JdyPdTS11nAKLE+KEfnJhjLLp8TXWQzD2xB2HH7gEd test
|
|
420
tests/run
420
tests/run
|
@ -1,87 +1,100 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# See: https://github.com/djui/bashunit
|
# See: https://github.com/kward/shunit2
|
||||||
|
|
||||||
skipAllTests=false
|
if [ $UID != 0 ] && ! groups | grep -qw docker; then
|
||||||
|
echo "Run with sudo/root or add user $USER to group 'docker'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
scriptDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
argBuild=${1:-"build"}
|
||||||
buildDir="$scriptDir/.."
|
argOutput=${2:-"quiet"}
|
||||||
tmpDir="/tmp/atmoz_sftp_test"
|
argCleanup=${3:-"cleanup"}
|
||||||
|
testDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
buildDir="$testDir/.."
|
||||||
|
imageName="atmoz/sftp_test"
|
||||||
|
buildOptions=(--tag "$imageName")
|
||||||
|
|
||||||
sudo="sudo"
|
if [ "$argOutput" == "quiet" ]; then
|
||||||
cache="--no-cache"
|
|
||||||
|
|
||||||
build=${1:-"build"}
|
|
||||||
output=${2:-"quiet"}
|
|
||||||
cleanup=${3:-"cleanup"}
|
|
||||||
sftpImageName="atmoz/sftp_test"
|
|
||||||
sftpContainerName="atmoz_sftp_test"
|
|
||||||
|
|
||||||
if [ "$output" == "quiet" ]; then
|
|
||||||
redirect="/dev/null"
|
redirect="/dev/null"
|
||||||
else
|
else
|
||||||
redirect="/dev/stdout"
|
redirect="/dev/stdout"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
buildOptions="--tag $sftpImageName"
|
if [ ! -f "$testDir/shunit2/shunit2" ]; then
|
||||||
|
echo "Could not find shunit2 in $testDir/shunit2."
|
||||||
|
echo "Run 'git submodules init && git submodules update'"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# clear argument list (or shunit2 will try to use them)
|
||||||
|
set --
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
## Helper functions
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
function beforeTest() {
|
function oneTimeSetUp() {
|
||||||
if [ "$build" == "build" ]; then
|
if [ "$argBuild" == "build" ]; then
|
||||||
buildOptions="$buildOptions $cache --pull=true"
|
buildOptions+=("--no-cache" "--pull=true")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
$sudo docker build $buildOptions "$buildDir"
|
# Build image
|
||||||
if [ $? -gt 0 ]; then
|
if ! docker build "${buildOptions[@]}" "$buildDir"; then
|
||||||
echo "Build failed"
|
echo "Build failed"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Private key can not be read by others
|
# Generate temporary ssh keys for testing
|
||||||
chmod go-rw "$scriptDir/id_rsa"
|
if [ ! -f "/tmp/atmoz_sftp_test_rsa" ]; then
|
||||||
|
ssh-keygen -t rsa -f "/tmp/atmoz_sftp_test_rsa" -N '' > "$redirect" 2>&1
|
||||||
rm -rf "$tmpDir" # clean state
|
|
||||||
mkdir "$tmpDir"
|
|
||||||
|
|
||||||
echo "test::$(id -u):$(id -g):dir1,dir2" >> "$tmpDir/users"
|
|
||||||
echo "" >> "$tmpDir/users" # empty line
|
|
||||||
echo "# comments are allowed" >> "$tmpDir/users"
|
|
||||||
echo " " >> "$tmpDir/users" # only whitespace
|
|
||||||
echo " # with whitespace in front" >> "$tmpDir/users"
|
|
||||||
echo "user.with.dot::$(id -u):$(id -g)" >> "$tmpDir/users"
|
|
||||||
$sudo docker run \
|
|
||||||
-v "$tmpDir/users:/etc/sftp/users.conf:ro" \
|
|
||||||
-v "$scriptDir/id_rsa.pub":/home/test/.ssh/keys/id_rsa.pub:ro \
|
|
||||||
-v "$scriptDir/id_rsa.pub":/home/user-from-env/.ssh/keys/id_rsa.pub:ro \
|
|
||||||
-v "$scriptDir/id_rsa.pub":/home/user.with.dot/.ssh/keys/id_rsa.pub:ro \
|
|
||||||
-v "$tmpDir":/home/test/share \
|
|
||||||
--name "$sftpContainerName" \
|
|
||||||
--expose 22 \
|
|
||||||
-e "SFTP_USERS=user-from-env::$(id -u):$(id -g) user-from-env-2::$(id -u):$(id -g)" \
|
|
||||||
-d "$sftpImageName" \
|
|
||||||
> "$redirect"
|
|
||||||
|
|
||||||
waitForServer $sftpContainerName
|
|
||||||
}
|
|
||||||
|
|
||||||
function afterTest() {
|
|
||||||
if [ "$output" != "quiet" ]; then
|
|
||||||
echo "Docker logs:"
|
|
||||||
$sudo docker logs "$sftpContainerName"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$cleanup" == "cleanup" ]; then
|
# Private key can not be read by others (sshd will complain)
|
||||||
$sudo docker rm -fv "$sftpContainerName" > "$redirect"
|
chmod go-rw "/tmp/atmoz_sftp_test_rsa"
|
||||||
rm -rf "$tmpDir"
|
}
|
||||||
|
|
||||||
|
function oneTimeTearDown() {
|
||||||
|
if [ "$argCleanup" == "cleanup" ]; then
|
||||||
|
docker image rm "$imageName" > "$redirect" 2>&1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUp() {
|
||||||
|
# shellcheck disable=SC2154
|
||||||
|
containerName="atmoz_sftp_${_shunit_test_}"
|
||||||
|
containerTmpDir="$(mktemp -d "/tmp/${containerName}_XXXX")"
|
||||||
|
export containerName containerTmpDir
|
||||||
|
|
||||||
|
retireContainer "$containerName" # clean up leftover container
|
||||||
|
}
|
||||||
|
|
||||||
|
function tearDown() {
|
||||||
|
retireContainer "$containerName"
|
||||||
|
|
||||||
|
if [ "$argCleanup" == "cleanup" ] && [ -d "$containerTmpDir" ]; then
|
||||||
|
rm -rf "$containerTmpDir"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function retireContainer() {
|
||||||
|
if [ "$(docker ps -qaf name="$1")" ]; then
|
||||||
|
if [ "$argOutput" != "quiet" ]; then
|
||||||
|
echo "Docker log for $1:"
|
||||||
|
docker logs "$1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$argCleanup" == "cleanup" ]; then
|
||||||
|
docker rm -fv "$1" > "$redirect" 2>&1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSftpIp() {
|
function getSftpIp() {
|
||||||
$sudo docker inspect -f {{.NetworkSettings.IPAddress}} "$1"
|
docker inspect -f "{{.NetworkSettings.IPAddress}}" "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
function runSftpCommands() {
|
function runSftpCommands() {
|
||||||
ip="$(getSftpIp $1)"
|
ip="$(getSftpIp "$1")"
|
||||||
user="$2"
|
user="$2"
|
||||||
shift 2
|
shift 2
|
||||||
|
|
||||||
|
@ -91,10 +104,10 @@ function runSftpCommands() {
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "$commands" | sftp \
|
echo "$commands" | sftp \
|
||||||
-i "$scriptDir/id_rsa" \
|
-i "/tmp/atmoz_sftp_test_rsa" \
|
||||||
-oStrictHostKeyChecking=no \
|
-oStrictHostKeyChecking=no \
|
||||||
-oUserKnownHostsFile=/dev/null \
|
-oUserKnownHostsFile=/dev/null \
|
||||||
-b - $user@$ip \
|
-b - "$user@$ip" \
|
||||||
> "$redirect" 2>&1
|
> "$redirect" 2>&1
|
||||||
|
|
||||||
status=$?
|
status=$?
|
||||||
|
@ -106,11 +119,11 @@ function waitForServer() {
|
||||||
containerName="$1"
|
containerName="$1"
|
||||||
echo -n "Waiting for $containerName to open port 22 ..."
|
echo -n "Waiting for $containerName to open port 22 ..."
|
||||||
|
|
||||||
for i in {1..30}; do
|
for _ in {1..30}; do
|
||||||
sleep 1
|
sleep 1
|
||||||
ip="$(getSftpIp $containerName)"
|
ip="$(getSftpIp "$containerName")"
|
||||||
echo -n "."
|
echo -n "."
|
||||||
if [ -n "$ip" ] && nc -z $ip 22; then
|
if [ -n "$ip" ] && nc -z "$ip" 22; then
|
||||||
echo " OPEN"
|
echo " OPEN"
|
||||||
return 0;
|
return 0;
|
||||||
fi
|
fi
|
||||||
|
@ -120,174 +133,177 @@ function waitForServer() {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
## Tests
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
function testContainerIsRunning() {
|
function testSmallestUserConfig() {
|
||||||
$skipAllTests && skip && return 0
|
docker run --name "$containerName" \
|
||||||
|
--entrypoint="/bin/sh" \
|
||||||
ps="$($sudo docker ps -q -f name="$sftpContainerName")"
|
"$imageName" \
|
||||||
assertNotEqual "$ps" ""
|
-c "create-sftp-user u: && id u" \
|
||||||
|
> "$redirect" 2>&1
|
||||||
if [ -z "$ps" ]; then
|
assertTrue "user created" $?
|
||||||
skipAllTests=true
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function testLoginUsingSshKey() {
|
function testCreateUserWithDot() {
|
||||||
$skipAllTests && skip && return 0
|
docker run --name "$containerName" \
|
||||||
|
--entrypoint="/bin/sh" \
|
||||||
runSftpCommands "$sftpContainerName" "test" "exit"
|
"$imageName" \
|
||||||
assertReturn $? 0
|
-c "create-sftp-user user.with.dot: && id user.with.dot" \
|
||||||
|
> "$redirect" 2>&1
|
||||||
|
assertTrue "user created" $?
|
||||||
}
|
}
|
||||||
|
|
||||||
function testUserWithDotLogin() {
|
function testUserCustomUidAndGid() {
|
||||||
$skipAllTests && skip && return 0
|
id="$(docker run --name "$containerName" \
|
||||||
|
--entrypoint="/bin/sh" \
|
||||||
|
"$imageName" \
|
||||||
|
-c "create-sftp-user u::1234:4321: > /dev/null && id u" )"
|
||||||
|
|
||||||
runSftpCommands "$sftpContainerName" "user.with.dot" "exit"
|
echo "$id" | grep -q 'uid=1234('
|
||||||
assertReturn $? 0
|
assertTrue "custom UID" $?
|
||||||
|
|
||||||
|
echo "$id" | grep -q 'gid=4321('
|
||||||
|
assertTrue "custom GID" $?
|
||||||
|
|
||||||
|
# Here we also check group name
|
||||||
|
assertEquals "uid=1234(u) gid=4321(group_4321) groups=4321(group_4321)" "$id"
|
||||||
}
|
}
|
||||||
|
|
||||||
function testLoginUsingUserFromEnv() {
|
function testCommandPassthrough() {
|
||||||
$skipAllTests && skip && return 0
|
docker run --name "$containerName" \
|
||||||
|
"$imageName" test 1 -eq 1 \
|
||||||
runSftpCommands "$sftpContainerName" "user-from-env" "exit"
|
> "$redirect" 2>&1
|
||||||
assertReturn $? 0
|
assertTrue "command passthrough" $?
|
||||||
}
|
}
|
||||||
|
|
||||||
function testWritePermission() {
|
function testUsersConf() {
|
||||||
$skipAllTests && skip && return 0
|
docker run --name "$containerName" -d \
|
||||||
|
-v "$testDir/files/users.conf:/etc/sftp/users.conf:ro" \
|
||||||
|
"$imageName" \
|
||||||
|
> "$redirect" 2>&1
|
||||||
|
|
||||||
runSftpCommands "$sftpContainerName" "test" \
|
waitForServer "$containerName"
|
||||||
"cd share" \
|
assertTrue "waitForServer" $?
|
||||||
|
|
||||||
|
docker exec "$containerName" id user-from-conf > /dev/null
|
||||||
|
assertTrue "user-from-conf" $?
|
||||||
|
|
||||||
|
docker exec "$containerName" id test > /dev/null
|
||||||
|
assertTrue "test" $?
|
||||||
|
|
||||||
|
docker exec "$containerName" id user.with.dot > /dev/null
|
||||||
|
assertTrue "user.with.dot" $?
|
||||||
|
|
||||||
|
docker exec "$containerName" test -d /home/test/dir1 -a -d /home/test/dir2
|
||||||
|
assertTrue "dirs exists" $?
|
||||||
|
}
|
||||||
|
|
||||||
|
function testLegacyUsersConf() {
|
||||||
|
docker run --name "$containerName" -d \
|
||||||
|
-v "$testDir/files/users.conf:/etc/sftp-users.conf:ro" \
|
||||||
|
"$imageName" \
|
||||||
|
> "$redirect" 2>&1
|
||||||
|
|
||||||
|
waitForServer "$containerName"
|
||||||
|
assertTrue "waitForServer" $?
|
||||||
|
|
||||||
|
docker exec "$containerName" id user-from-conf > /dev/null
|
||||||
|
assertTrue "user-from-conf" $?
|
||||||
|
}
|
||||||
|
|
||||||
|
function testCreateUsersUsingEnv() {
|
||||||
|
docker run --name "$containerName" -d \
|
||||||
|
-e "SFTP_USERS=user-from-env: user-from-env-2:" \
|
||||||
|
"$imageName" \
|
||||||
|
> "$redirect" 2>&1
|
||||||
|
|
||||||
|
waitForServer "$containerName"
|
||||||
|
assertTrue "waitForServer" $?
|
||||||
|
|
||||||
|
docker exec "$containerName" id user-from-env > /dev/null
|
||||||
|
assertTrue "user-from-env" $?
|
||||||
|
|
||||||
|
docker exec "$containerName" id user-from-env-2 > /dev/null
|
||||||
|
assertTrue "user-from-env-2" $?
|
||||||
|
}
|
||||||
|
|
||||||
|
function testCreateUsersUsingCombo() {
|
||||||
|
docker run --name "$containerName" -d \
|
||||||
|
-v "$testDir/files/users.conf:/etc/sftp-users.conf:ro" \
|
||||||
|
-e "SFTP_USERS=user-from-env:" \
|
||||||
|
"$imageName" \
|
||||||
|
user-from-cmd: \
|
||||||
|
> "$redirect" 2>&1
|
||||||
|
|
||||||
|
waitForServer "$containerName"
|
||||||
|
assertTrue "waitForServer" $?
|
||||||
|
|
||||||
|
docker exec "$containerName" id user-from-conf > /dev/null
|
||||||
|
assertTrue "user-from-conf" $?
|
||||||
|
|
||||||
|
docker exec "$containerName" id user-from-env > /dev/null
|
||||||
|
assertTrue "user-from-env" $?
|
||||||
|
|
||||||
|
docker exec "$containerName" id user-from-cmd > /dev/null
|
||||||
|
assertTrue "user-from-cmd" $?
|
||||||
|
}
|
||||||
|
|
||||||
|
function testWriteAccessToAutocreatedDirs() {
|
||||||
|
docker run --name "$containerName" -d \
|
||||||
|
-v "/tmp/atmoz_sftp_test_rsa.pub":/home/test/.ssh/keys/id_rsa.pub:ro \
|
||||||
|
"$imageName" test::::dir1,dir2 \
|
||||||
|
> "$redirect" 2>&1
|
||||||
|
|
||||||
|
waitForServer "$containerName"
|
||||||
|
assertTrue "waitForServer" $?
|
||||||
|
|
||||||
|
runSftpCommands "$containerName" "test" \
|
||||||
|
"cd dir1" \
|
||||||
|
"mkdir test" \
|
||||||
|
"cd ../dir2" \
|
||||||
"mkdir test" \
|
"mkdir test" \
|
||||||
"exit"
|
"exit"
|
||||||
test -d "$tmpDir/test"
|
assertTrue "runSftpCommands" $?
|
||||||
assertReturn $? 0
|
|
||||||
|
docker exec "$containerName" test -d /home/test/dir1/test -a -d /home/test/dir2/test
|
||||||
|
assertTrue "dirs exists" $?
|
||||||
}
|
}
|
||||||
|
|
||||||
function testDir() {
|
function testBindmountDirScript() {
|
||||||
$skipAllTests && skip && return 0
|
mkdir -p "$containerTmpDir/custom/bindmount"
|
||||||
|
|
||||||
runSftpCommands "$sftpContainerName" "test" \
|
|
||||||
"cd dir1" \
|
|
||||||
"mkdir test-dir1" \
|
|
||||||
"get -rf test-dir1 $tmpDir/" \
|
|
||||||
"cd ../dir2" \
|
|
||||||
"mkdir test-dir2" \
|
|
||||||
"get -rf test-dir2 $tmpDir/" \
|
|
||||||
"exit"
|
|
||||||
test -d "$tmpDir/test-dir1"
|
|
||||||
assertReturn $? 0
|
|
||||||
test -d "$tmpDir/test-dir2"
|
|
||||||
assertReturn $? 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# Smallest user config possible
|
|
||||||
function testMinimalContainerStart() {
|
|
||||||
$skipAllTests && skip && return 0
|
|
||||||
|
|
||||||
tmpContainerName="$sftpContainerName""_minimal"
|
|
||||||
|
|
||||||
$sudo docker run \
|
|
||||||
--name "$tmpContainerName" \
|
|
||||||
-d "$sftpImageName" \
|
|
||||||
m: \
|
|
||||||
> "$redirect"
|
|
||||||
|
|
||||||
waitForServer $tmpContainerName
|
|
||||||
|
|
||||||
ps="$($sudo docker ps -q -f name="$tmpContainerName")"
|
|
||||||
assertNotEqual "$ps" ""
|
|
||||||
|
|
||||||
if [ -z "$ps" ]; then
|
|
||||||
skipAllTests=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$output" != "quiet" ]; then
|
|
||||||
$sudo docker logs "$tmpContainerName"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$cleanup" == "cleanup" ]; then
|
|
||||||
$sudo docker rm -fv "$tmpContainerName" > "$redirect"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function testLegacyConfigPath() {
|
|
||||||
$skipAllTests && skip && return 0
|
|
||||||
|
|
||||||
tmpContainerName="$sftpContainerName""_legacy"
|
|
||||||
|
|
||||||
echo "test::$(id -u):$(id -g)" >> "$tmpDir/legacy_users"
|
|
||||||
$sudo docker run \
|
|
||||||
-v "$tmpDir/legacy_users:/etc/sftp-users.conf:ro" \
|
|
||||||
--name "$tmpContainerName" \
|
|
||||||
--expose 22 \
|
|
||||||
-d "$sftpImageName" \
|
|
||||||
> "$redirect"
|
|
||||||
|
|
||||||
waitForServer $tmpContainerName
|
|
||||||
|
|
||||||
ps="$($sudo docker ps -q -f name="$tmpContainerName")"
|
|
||||||
assertNotEqual "$ps" ""
|
|
||||||
|
|
||||||
if [ "$output" != "quiet" ]; then
|
|
||||||
$sudo docker logs "$tmpContainerName"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$cleanup" == "cleanup" ]; then
|
|
||||||
$sudo docker rm -fv "$tmpContainerName" > "$redirect"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Bind-mount folder using script in /etc/sftp.d/
|
|
||||||
function testCustomContainerStart() {
|
|
||||||
$skipAllTests && skip && return 0
|
|
||||||
|
|
||||||
tmpContainerName="$sftpContainerName""_custom"
|
|
||||||
|
|
||||||
mkdir -p "$tmpDir/custom/bindmount"
|
|
||||||
echo "mkdir -p /home/custom/bindmount && \
|
echo "mkdir -p /home/custom/bindmount && \
|
||||||
chown custom /home/custom/bindmount && \
|
chown custom /custom /home/custom/bindmount && \
|
||||||
mount --bind /custom /home/custom/bindmount" \
|
mount --bind /custom /home/custom/bindmount" \
|
||||||
> "$tmpDir/mount.sh"
|
> "$containerTmpDir/mount.sh"
|
||||||
chmod +x "$tmpDir/mount.sh"
|
chmod +x "$containerTmpDir/mount.sh"
|
||||||
|
|
||||||
$sudo docker run \
|
docker run --name "$containerName" -d \
|
||||||
--privileged=true \
|
--privileged=true \
|
||||||
--name "$tmpContainerName" \
|
-v "/tmp/atmoz_sftp_test_rsa.pub":/home/custom/.ssh/keys/id_rsa.pub:ro \
|
||||||
-v "$scriptDir/id_rsa.pub":/home/custom/.ssh/keys/id_rsa.pub:ro \
|
-v "$containerTmpDir/custom/bindmount":/custom \
|
||||||
-v "$tmpDir/custom/bindmount":/custom \
|
-v "$containerTmpDir/mount.sh":/etc/sftp.d/mount.sh \
|
||||||
-v "$tmpDir/mount.sh":/etc/sftp.d/mount.sh \
|
"$imageName" custom:123 \
|
||||||
--expose 22 \
|
> "$redirect" 2>&1
|
||||||
-d "$sftpImageName" \
|
|
||||||
custom:123 \
|
|
||||||
> "$redirect"
|
|
||||||
|
|
||||||
waitForServer $tmpContainerName
|
waitForServer "$containerName"
|
||||||
|
assertTrue "waitForServer" $?
|
||||||
|
|
||||||
ps="$($sudo docker ps -q -f name="$tmpContainerName")"
|
runSftpCommands "$containerName" "custom" \
|
||||||
assertNotEqual "$ps" ""
|
|
||||||
|
|
||||||
runSftpCommands "$tmpContainerName" "custom" \
|
|
||||||
"cd bindmount" \
|
"cd bindmount" \
|
||||||
"mkdir test" \
|
"mkdir test" \
|
||||||
"exit"
|
"exit"
|
||||||
|
assertTrue "runSftpCommands" $?
|
||||||
|
|
||||||
test -d "$tmpDir/custom/bindmount/test"
|
docker exec "$containerName" test -d /home/custom/bindmount/test
|
||||||
assertReturn $? 0
|
assertTrue "directory exist" $?
|
||||||
|
|
||||||
if [ "$output" != "quiet" ]; then
|
|
||||||
$sudo docker logs "$tmpContainerName"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$cleanup" == "cleanup" ]; then
|
|
||||||
$sudo docker rm -fv "$tmpContainerName" > "$redirect"
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
## Run
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Run tests
|
# shellcheck disable=SC1090
|
||||||
source "$scriptDir/bashunit.bash"
|
source "$testDir/shunit2/shunit2"
|
||||||
# Nothing happens after this
|
# Nothing happens after this
|
||||||
|
|
Loading…
Reference in a new issue