Skip to content

Commit d0a1639

Browse files
committed
big refactor to permissions
1 parent 0aec718 commit d0a1639

File tree

7 files changed

+273
-59
lines changed

7 files changed

+273
-59
lines changed

examples/dock-run.sh

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
# run this from the root project
44

55
# docker build -t yakworks/sftp .
6+
docker stop sftp || true && docker rm sftp || true
67

7-
docker run --name sftp --rm --cap-add=SYS_ADMIN -p 30022:22 \
8+
docker run --name sftp --cap-add=SYS_ADMIN \
9+
-p 30022:22 \
10+
-e DATA_MOUNT_NAME=ninebox \
811
-v $(pwd)/examples/users.conf:/etc/sftp/users.conf \
9-
-v $(pwd)/examples/sftp-data:/sftp-data \
10-
yakworks/sftp
12+
-v $(pwd)/examples/sftp-data:/data \
13+
-d yakworks/sftp

examples/sftp-data/test.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

examples/users.conf

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
# user:pass:uid:gid - if gid is 27 is sudo/admin, 100 is user.
2-
# admin will get data mounted and user with get data/user/%u mounted
3-
foo:FuB4r:1001:27
4-
bar:FuB4r:1002:27
5-
cust:FuB4r:1003:100
6-
cust2:FuB4r:1004:100
1+
# user:pass:uid:gid
2+
# if no uid will be auto assigned in normal linux fashion
3+
# if no gid will default to users(100)
4+
# admin,sudo or 27 will be able to see all data
5+
# users will only be able to see whats in their dir
6+
# TODO read only group
7+
admin:FuB4r::admin
8+
superu:FuB4r::sudo
9+
superu:FuB4r::27
10+
user1:FuB4r::users
11+
user2:FuB4r::100
12+
user3:FuB4r

files/create-sftp-user

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function validateArg() {
2929
fi
3030
}
3131

32-
log "Parsing user data: \"$1\""
32+
# log "Parsing user data: \"$1\""
3333
IFS=':' read -ra args <<< "$1"
3434

3535
skipIndex=0
@@ -45,7 +45,7 @@ if [ "${args[2]}" == "e" ]; then
4545
fi
4646

4747
uid="${args[$((skipIndex+2))]}"; validateArg "UID" "$uid" "$reUid" || exit 1
48-
gid="${args[$((skipIndex+3))]}"; validateArg "GID" "$gid" "$reGid" || exit 1
48+
gid="${args[$((skipIndex+3))]}"; # validateArg "GID" "$gid" "$reGid" || exit 1
4949
dir="${args[$((skipIndex+4))]}"; validateArg "dirs" "$dir" "$reDir" || exit 1
5050

5151
if getent passwd "$user" > /dev/null; then
@@ -58,22 +58,44 @@ if [ -n "$uid" ]; then
5858
fi
5959

6060
if [ -n "$gid" ]; then
61+
#if gid = admin then its old ubuntu lingo and new group should be sudo, change it
62+
[ "$gid" = "admin" ] && gid="sudo" && log "'admin' group reclassed as 'sudo'. use numeric gid to force admin"
63+
# check if it exists
6164
if ! getent group "$gid" > /dev/null; then
62-
groupadd --gid "$gid" "group_$gid"
65+
log "group $gid does not exists. Creating it"
66+
if [[ "$gid" =~ ^[0-9]+$ ]]; then
67+
# its a digit
68+
groupadd --gid "$gid" "group_$gid"
69+
else
70+
groupadd "$gid"
71+
#gid="$(cut -d: -f3 < <(getent group $gid))"
72+
fi
6373
fi
74+
#ensure we are workign with the id
75+
grp_id="$(cut -d: -f3 < <(getent group $gid))"
76+
gid=$grp_id
77+
#log "group $gid has grp_id $grp_id"
78+
# useraddOptions+=(--gid "$gid")
6479

65-
useraddOptions+=(--gid "$gid")
80+
else
81+
log "no group specified, defaulting to 100:users"
82+
gid=100
6683
fi
6784

85+
useraddOptions+=(--gid "$gid")
86+
6887
log "useradd ${useraddOptions[@]} $user"
6988
useradd "${useraddOptions[@]}" "$user"
89+
90+
# locked down chroot setup where user's home is owned by root
7091
mkdir -p "/home/$user"
7192
chown root:root "/home/$user"
7293
chmod 755 "/home/$user"
7394

7495
# Retrieving user id to use it in chown commands instead of the user name
7596
# to avoid problems on alpine when the user name contains a '.'
7697
uid="$(id -u "$user")"
98+
log "uid is $uid"
7799

78100
if [ -n "$pass" ]; then
79101
echo "$user:$pass" | chpasswd $chpasswdOptions
@@ -106,10 +128,14 @@ if [ -n "$dir" ]; then
106128
fi
107129

108130
###### MODS for bind mounts #####
131+
132+
# if DATA_DIR_NAME env is not set then make data the default
133+
: ${DATA_MOUNT_NAME:=data}
109134
# mount user dir
110-
dataPath="/sftp-data"
111-
userDataDir="$dataPath/users/$user"
112-
homeDataDir="/home/$user/data"
135+
dataPath="/data"
136+
usersDir="$dataPath/users"
137+
userDataDir="$usersDir/$user"
138+
homeDataDir="/home/$user/$DATA_MOUNT_NAME"
113139

114140
# always create a data dir by default in the users home.
115141
if [ ! -d "$homeDataDir" ]; then
@@ -118,31 +144,39 @@ if [ ! -d "$homeDataDir" ]; then
118144
chown -R "$uid:$gid" "$homeDataDir"
119145
fi
120146
#mod user so the data dir is their home
121-
usermod -d /data "$user"
147+
usermod -d "$homeDataDir" "$user"
122148

149+
#if the data path has been mapped in as volume then it will exists and do our special logic
123150
if [ -d "$dataPath" ]; then
124-
log "- has $dataPath"
125-
# for users mount the data/users/%u directory
126-
if [ "$gid" = "100" ]; then
127-
if [ ! -d "$userDataDir" ]; then
128-
log "- mkdir -p $userDataDir"
129-
mkdir -p "$userDataDir"
130-
fi
151+
log "data path has been mapped in as a volume"
152+
# create users dir
153+
if [ ! -d "$usersDir" ]; then
154+
log "- no $usersDir so exec: mkdir -p $usersDir"
155+
mkdir -p "$usersDir"
156+
chown :sudo "$usersDir"
157+
chmod 775 "$usersDir"
158+
fi
159+
160+
# create users data dir
161+
if [ ! -d "$userDataDir" ]; then
162+
log "- mkdir -p $userDataDir"
163+
mkdir -p "$userDataDir"
164+
chown $user:users "$userDataDir"
165+
chmod 775 "$userDataDir"
166+
fi
167+
168+
# for users mount the ($userDataDir) directory
169+
if [ "$gid" = "100" ] || [ "$gid" = "users" ]; then
131170
log "- mount --bind $userDataDir $homeDataDir"
132-
# Remember permissions, you may have to fix them:
133-
# chown -R :100 "$userDataDir"
134171
mount --bind "$userDataDir" "$homeDataDir"
135-
#make sure permissions are good on users dir
136-
chown -R :100 "$dataPath"
137172
fi
138-
# for sudo (27) admins mount the data directory
139-
if [ "$gid" = "27" ] ; then
173+
# for (27) sudo/admins mount the data directory
174+
if [ "$gid" = "27" ] || [ "$gid" = "sudo" ]; then
140175
# chown -R :100 "$userDataDir"
141176
mount --bind "$dataPath" "$homeDataDir"
142-
#also make sure that they are assigned to the user group
143-
usermod -g 100 "$user"
177+
#also make sure that sudo/admins are assigned to the users group
178+
usermod -g users "$user"
144179
usermod -a -G 27 "$user"
145180
fi
146-
chown -R :100 "$dataPath"
147-
chmod -R 775 "$dataPath"
148181
fi
182+

files/create-sftp-user-new

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
#!/bin/bash
2+
set -Eeo pipefail
3+
4+
# shellcheck disable=2154
5+
trap 's=$?; echo "$0: Error on line "$LINENO": $BASH_COMMAND"; exit $s' ERR
6+
7+
# Extended regular expression (ERE) for arguments
8+
reUser='[A-Za-z0-9._][A-Za-z0-9._-]{0,31}' # POSIX.1-2008
9+
rePass='[^:]{0,255}'
10+
reUid='[[:digit:]]*'
11+
reGid='[[:digit:]]*'
12+
reDir='[^:]*'
13+
#reArgs="^($reUser)(:$rePass)(:e)?(:$reUid)?(:$reGid)?(:$reDir)?$"
14+
15+
function log() {
16+
echo "[$0] $*"
17+
}
18+
19+
function validateArg() {
20+
name="$1"
21+
val="$2"
22+
re="$3"
23+
24+
if [[ "$val" =~ ^$re$ ]]; then
25+
return 0
26+
else
27+
log "ERROR: Invalid $name \"$val\", do not match required regex pattern: $re"
28+
return 1
29+
fi
30+
}
31+
32+
log "Parsing user data: \"$1\""
33+
IFS=':' read -ra args <<< "$1"
34+
35+
skipIndex=0
36+
chpasswdOptions=""
37+
useraddOptions=(--no-user-group)
38+
39+
user="${args[0]}"; validateArg "username" "$user" "$reUser" || exit 1
40+
pass="${args[1]}"; validateArg "password" "$pass" "$rePass" || exit 1
41+
42+
if [ "${args[2]}" == "e" ]; then
43+
chpasswdOptions="-e"
44+
skipIndex=1
45+
fi
46+
47+
uid="${args[$((skipIndex+2))]}"; validateArg "UID" "$uid" "$reUid" || exit 1
48+
gid="${args[$((skipIndex+3))]}"; validateArg "GID" "$gid" "$reGid" || exit 1
49+
dir="${args[$((skipIndex+4))]}"; validateArg "dirs" "$dir" "$reDir" || exit 1
50+
51+
if getent passwd "$user" > /dev/null; then
52+
log "WARNING: User \"$user\" already exists. Skipping."
53+
exit 0
54+
fi
55+
56+
if [ -n "$uid" ]; then
57+
useraddOptions+=(--non-unique --uid "$uid")
58+
fi
59+
60+
if [ -n "$gid" ]; then
61+
if ! getent group "$gid" > /dev/null; then
62+
groupadd --gid "$gid" "group_$gid"
63+
fi
64+
65+
useraddOptions+=(--gid "$gid")
66+
fi
67+
68+
log "useradd ${useraddOptions[@]} $user"
69+
useradd "${useraddOptions[@]}" "$user"
70+
mkdir -p "/home/$user"
71+
chown root:root "/home/$user"
72+
chmod 755 "/home/$user"
73+
74+
# Retrieving user id to use it in chown commands instead of the user name
75+
# to avoid problems on alpine when the user name contains a '.'
76+
uid="$(id -u "$user")"
77+
78+
if [ -n "$pass" ]; then
79+
echo "$user:$pass" | chpasswd $chpasswdOptions
80+
else
81+
usermod -p "*" "$user" # disabled password
82+
fi
83+
84+
# Add SSH keys to authorized_keys with valid permissions
85+
if [ -d "/home/$user/.ssh/keys" ]; then
86+
for publickey in "/home/$user/.ssh/keys"/*; do
87+
(cat "${publickey}"; echo) >> "/home/$user/.ssh/authorized_keys"
88+
done
89+
chown "$uid" "/home/$user/.ssh/authorized_keys"
90+
chmod 600 "/home/$user/.ssh/authorized_keys"
91+
fi
92+
93+
# Make sure dirs exists
94+
if [ -n "$dir" ]; then
95+
IFS=',' read -ra dirArgs <<< "$dir"
96+
for dirPath in "${dirArgs[@]}"; do
97+
dirPath="/home/$user/$dirPath"
98+
if [ ! -d "$dirPath" ]; then
99+
log "Creating directory: $dirPath"
100+
mkdir -p "$dirPath"
101+
chown -R "$uid:$gid" "$dirPath"
102+
else
103+
log "Directory already exists: $dirPath"
104+
fi
105+
done
106+
# if dirs are not specified then create a default data one
107+
else
108+
109+
fi
110+
111+
###### MODS for shared sftp-data bind mounts #####
112+
# mount user dir
113+
sftpDataPath="/sftp-data"
114+
userDataDir="$dataPath/users/$user"
115+
homeDataMount="/home/$user/data"
116+
117+
# always create a data dir by default in the users home.
118+
if [ ! -d "$homeDataDir" ]; then
119+
log "- mkdir -p $homeDataDir"
120+
mkdir -p "$homeDataDir"
121+
chown -R "$uid:$gid" "$homeDataDir"
122+
fi
123+
#mod user so the data dir is their home
124+
usermod -d /data "$user"
125+
126+
#if the data path has been mapped in as volume then it will exists and do our special logic
127+
if [ -d "$dataPath" ]; then
128+
log "- has $dataPath"
129+
#ensure main dataPath is owned by users (100) group and has good perimssions
130+
#chown :users "$dataPath"
131+
#chmod 775 "$dataPath"
132+
133+
if [ ! -d "$userDataDir" ]; then
134+
log "- mkdir -p $userDataDir"
135+
mkdir -p "$userDataDir"
136+
fi
137+
138+
log "- mount --bind $userDataDir $homeDataDir"
139+
# Remember permissions, you may have to fix them:
140+
# chown -R :100 "$userDataDir"
141+
mount --bind "$userDataDir" "$homeDataDir"
142+
#make sure permissions are good on users dir
143+
chown -R :users "$userDataDir"
144+
145+
# for users mount the data/users/%u directory
146+
if [ "$gid" = "100" ]; then
147+
if [ ! -d "$userDataDir" ]; then
148+
log "- mkdir -p $userDataDir"
149+
mkdir -p "$userDataDir"
150+
fi
151+
log "- mount --bind $userDataDir $homeDataDir"
152+
# Remember permissions, you may have to fix them:
153+
# chown -R :100 "$userDataDir"
154+
mount --bind "$userDataDir" "$homeDataDir"
155+
#make sure permissions are good on users dir
156+
chown -R :users "$userDataDir"
157+
fi
158+
# for sudo (27) admins mount the data directory
159+
if [ "$gid" = "27" ] ; then
160+
# chown -R :100 "$userDataDir"
161+
mount --bind "$dataPath" "$homeDataDir"
162+
#also make sure that sudo admins are assigned to the user group
163+
usermod -g users "$user"
164+
usermod -a -G 27 "$user"
165+
fi
166+
#chown -R :100 "$dataPath"
167+
#chmod -R 775 "$dataPath"
168+
fi
169+
170+
# Source custom scripts, if any
171+
if [ -x /etc/sftp/user-setup.sh ]; then
172+
log "Running user-setup.sh ..."
173+
174+
/etc/sftp/user-setup.sh "$user" "$uid" "$gid"
175+
176+
fi

0 commit comments

Comments
 (0)