parent
fef1685786
commit
b4264bccfa
@ -0,0 +1,15 @@ |
|||||||
|
FROM alpine:latest |
||||||
|
|
||||||
|
# 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 apk add --no-cache 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 root/ / |
||||||
|
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"] |
@ -0,0 +1,22 @@ |
|||||||
|
# Secure defaults |
||||||
|
# See: https://stribika.github.io/2015/01/04/secure-secure-shell.html |
||||||
|
Protocol 2 |
||||||
|
HostKey /etc/ssh/ssh_host_ed25519_key |
||||||
|
HostKey /etc/ssh/ssh_host_rsa_key |
||||||
|
|
||||||
|
# Faster connection |
||||||
|
# See: https://github.com/atmoz/sftp/issues/11 |
||||||
|
UseDNS no |
||||||
|
|
||||||
|
# Limited access |
||||||
|
PermitRootLogin no |
||||||
|
X11Forwarding no |
||||||
|
AllowTcpForwarding no |
||||||
|
|
||||||
|
# Force sftp and chroot jail |
||||||
|
Subsystem sftp internal-sftp |
||||||
|
ForceCommand internal-sftp |
||||||
|
ChrootDirectory %h |
||||||
|
|
||||||
|
# Enable this for more logs |
||||||
|
#LogLevel VERBOSE |
@ -0,0 +1,97 @@ |
|||||||
|
#!/bin/bash |
||||||
|
set -Eeo pipefail |
||||||
|
|
||||||
|
if [ ! -e /etc/openvpn/sshd_config ]; then |
||||||
|
log "Configuration file sshd_config not found. Initializing..." |
||||||
|
cp /config/sshd_config /etc/ssh/sshd_config |
||||||
|
fi |
||||||
|
|
||||||
|
# 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" |
||||||
|
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 |
||||||
|
|
||||||
|
# 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.sh "$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 |
||||||
|
|
||||||
|
# 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 |
||||||
|
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 |
@ -0,0 +1,113 @@ |
|||||||
|
#!/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" || exit 1 |
||||||
|
pass="${args[1]}"; validateArg "password" "$pass" "$rePass" || exit 1 |
||||||
|
|
||||||
|
if [ "${args[2]}" == "e" ]; then |
||||||
|
chpasswdOptions="-e" |
||||||
|
skipIndex=1 |
||||||
|
fi |
||||||
|
|
||||||
|
uid="${args[$((skipIndex+2))]}"; validateArg "UID" "$uid" "$reUid" || exit 1 |
||||||
|
gid="${args[$((skipIndex+3))]}"; validateArg "GID" "$gid" "$reGid" || exit 1 |
||||||
|
dir="${args[$((skipIndex+4))]}"; validateArg "dirs" "$dir" "$reDir" || exit 1 |
||||||
|
|
||||||
|
if getent passwd "$user" > /dev/null; then |
||||||
|
log "WARNING: User \"$user\" already exists. Skipping." |
||||||
|
exit 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 |
||||||
|
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 |
||||||
|
|
||||||
|
# Remove duplicate keys |
||||||
|
sort < "$userKeysAllowedFileTmp" | uniq > "$userKeysAllowedFile" |
||||||
|
|
||||||
|
chown "$uid" "$userKeysAllowedFile" |
||||||
|
chmod 600 "$userKeysAllowedFile" |
||||||
|
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 |
Loading…
Reference in new issue