Skip to content

Commit ba9c6b3

Browse files
🎨 create efs guardian specific user (#5936)
1 parent 9f0cf48 commit ba9c6b3

File tree

7 files changed

+100
-35
lines changed

7 files changed

+100
-35
lines changed

.env-devel

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ DIRECTOR_PORT=8080
6565
DIRECTOR_REGISTRY_CACHING_TTL=900
6666
DIRECTOR_REGISTRY_CACHING=True
6767

68+
EFS_USER_ID=8006
69+
EFS_USER_NAME=efs
70+
EFS_GROUP_ID=8106
71+
EFS_GROUP_NAME=efs-group
6872
EFS_DNS_NAME=fs-xxx.efs.us-east-1.amazonaws.com
6973
EFS_MOUNTED_PATH=/tmp/efs
7074
EFS_PROJECT_SPECIFIC_DATA_DIRECTORY=project-specific-data
@@ -169,6 +173,10 @@ R_CLONE_OPTION_RETRIES=3
169173
R_CLONE_OPTION_TRANSFERS=5
170174
R_CLONE_PROVIDER=MINIO
171175

176+
# simcore-user used in docker images
177+
SC_USER_ID=8004
178+
SC_USER_NAME=scu
179+
172180
S3_ACCESS_KEY=12345678
173181
S3_BUCKET_NAME=simcore
174182
S3_ENDPOINT=http://172.17.0.1:9001

services/docker-compose.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,12 @@ services:
365365
RABBIT_PORT: ${RABBIT_PORT}
366366
RABBIT_SECURE: ${RABBIT_SECURE}
367367
RABBIT_USER: ${RABBIT_USER}
368+
SC_USER_ID: ${SC_USER_ID}
369+
SC_USER_NAME: ${SC_USER_NAME}
370+
EFS_USER_ID: ${EFS_USER_ID}
371+
EFS_USER_NAME: ${EFS_USER_NAME}
372+
EFS_GROUP_ID: ${EFS_GROUP_ID}
373+
EFS_GROUP_NAME: ${EFS_GROUP_NAME}
368374
EFS_DNS_NAME: ${EFS_DNS_NAME}
369375
EFS_MOUNTED_PATH: ${EFS_MOUNTED_PATH}
370376
EFS_ONLY_ENABLED_FOR_USERIDS: ${EFS_ONLY_ENABLED_FOR_USERIDS}

services/efs-guardian/Dockerfile

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,26 +45,44 @@ RUN --mount=type=cache,target=/var/cache/apt,mode=0755,sharing=private \
4545
&& gosu nobody true
4646

4747
# simcore-user uid=8004(scu) gid=8004(scu) groups=8004(scu)
48+
# simcore-efs-user uid=8006(efs) gid=8006(efs) groups=8006(efs)
49+
# Currently all simcore services run as user 8004. The Guardian needs to run as a different user 8006.
50+
# The Guardian has access to the root directory of the EFS distributed file system
51+
# and can change the file permissions of user 8004 if needed.
4852
ENV SC_USER_ID=8004 \
4953
SC_USER_NAME=scu \
54+
EFS_USER_ID=8006 \
55+
EFS_USER_NAME=efs \
5056
SC_BUILD_TARGET=base \
5157
SC_BOOT_MODE=default
5258

5359
RUN adduser \
54-
--uid ${SC_USER_ID} \
60+
--uid 8004 \
5561
--disabled-password \
5662
--gecos "" \
5763
--shell /bin/sh \
58-
--home /home/${SC_USER_NAME} \
59-
${SC_USER_NAME}
64+
--home /home/scu \
65+
scu
6066

67+
RUN adduser \
68+
--uid 8006 \
69+
--disabled-password \
70+
--gecos "" \
71+
--shell /bin/sh \
72+
--home /home/efs \
73+
efs
74+
75+
# we create efs-group which is then used in efs guardian when he is creating a directory for user services.
76+
RUN groupadd -g 8106 efs-group
77+
RUN usermod -a -G efs-group efs
78+
RUN usermod -a -G efs-group scu
6179

6280
# Sets utf-8 encoding for Python et al
6381
ENV LANG=C.UTF-8
6482

6583
# Turns off writing .pyc files; superfluous on an ephemeral container.
6684
ENV PYTHONDONTWRITEBYTECODE=1 \
67-
VIRTUAL_ENV=/home/scu/.venv
85+
VIRTUAL_ENV=/home/efs/.venv
6886

6987
# Ensures that the python and pip executables used in the image will be
7088
# those from our virtualenv.
@@ -149,15 +167,15 @@ ENV SC_BUILD_TARGET=production \
149167

150168
ENV PYTHONOPTIMIZE=TRUE
151169

152-
WORKDIR /home/scu
153-
# ensure home folder is read/writable for user scu
154-
RUN chown -R scu /home/scu
170+
WORKDIR /home/efs
171+
# ensure home folder is read/writable for user efs
172+
RUN chown -R efs /home/efs
155173

156174
# Starting from clean base image, copies pre-installed virtualenv from prod-only-deps
157-
COPY --chown=scu:scu --from=prod-only-deps ${VIRTUAL_ENV} ${VIRTUAL_ENV}
175+
COPY --chown=efs:efs --from=prod-only-deps ${VIRTUAL_ENV} ${VIRTUAL_ENV}
158176

159177
# Copies booting scripts
160-
COPY --chown=scu:scu services/efs-guardian/docker services/efs-guardian/docker
178+
COPY --chown=efs:efs services/efs-guardian/docker services/efs-guardian/docker
161179
RUN chmod +x services/efs-guardian/docker/*.sh
162180

163181

@@ -187,7 +205,7 @@ ENV SC_BUILD_TARGET=development \
187205

188206
WORKDIR /devel
189207

190-
RUN chown -R scu:scu "${VIRTUAL_ENV}"
208+
RUN chown -R efs:efs "${VIRTUAL_ENV}"
191209

192210
ENTRYPOINT ["/bin/sh", "services/efs-guardian/docker/entrypoint.sh"]
193211
CMD ["/bin/sh", "services/efs-guardian/docker/boot.sh"]

services/efs-guardian/docker/entrypoint.sh

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,30 +43,30 @@ if [ "${SC_BUILD_TARGET}" = "development" ]; then
4343
HOST_GROUPID=$(stat --format=%g "${SC_DEVEL_MOUNT}")
4444
CONT_GROUPNAME=$(getent group "${HOST_GROUPID}" | cut --delimiter=: --fields=1)
4545
if [ "$HOST_USERID" -eq 0 ]; then
46-
echo "$WARNING" "Folder mounted owned by root user... adding $SC_USER_NAME to root..."
47-
adduser "$SC_USER_NAME" root
46+
echo "$WARNING" "Folder mounted owned by root user... adding $EFS_USER_NAME to root..."
47+
adduser "$EFS_USER_NAME" root
4848
else
4949
echo "$INFO" "Folder mounted owned by user $HOST_USERID:$HOST_GROUPID-'$CONT_GROUPNAME'..."
50-
# take host's credentials in $SC_USER_NAME
50+
# take host's credentials in $EFS_USER_NAME
5151
if [ -z "$CONT_GROUPNAME" ]; then
52-
echo "$WARNING" "Creating new group grp$SC_USER_NAME"
53-
CONT_GROUPNAME=grp$SC_USER_NAME
52+
echo "$WARNING" "Creating new group grp$EFS_USER_NAME"
53+
CONT_GROUPNAME=grp$EFS_USER_NAME
5454
addgroup --gid "$HOST_GROUPID" "$CONT_GROUPNAME"
5555
else
5656
echo "$INFO" "group already exists"
5757
fi
58-
echo "$INFO" "Adding $SC_USER_NAME to group $CONT_GROUPNAME..."
59-
adduser "$SC_USER_NAME" "$CONT_GROUPNAME"
58+
echo "$INFO" "Adding $EFS_USER_NAME to group $CONT_GROUPNAME..."
59+
adduser "$EFS_USER_NAME" "$CONT_GROUPNAME"
6060

6161
echo "$WARNING" "Changing ownership [this could take some time]"
62-
echo "$INFO" "Changing $SC_USER_NAME:$SC_USER_NAME ($SC_USER_ID:$SC_USER_ID) to $SC_USER_NAME:$CONT_GROUPNAME ($HOST_USERID:$HOST_GROUPID)"
63-
usermod --uid "$HOST_USERID" --gid "$HOST_GROUPID" "$SC_USER_NAME"
62+
echo "$INFO" "Changing $EFS_USER_NAME:$EFS_USER_NAME ($EFS_USER_ID:$EFS_USER_ID) to $EFS_USER_NAME:$CONT_GROUPNAME ($HOST_USERID:$HOST_GROUPID)"
63+
usermod --uid "$HOST_USERID" --gid "$HOST_GROUPID" "$EFS_USER_NAME"
6464

65-
echo "$INFO" "Changing group properties of files around from $SC_USER_ID to group $CONT_GROUPNAME"
66-
find / -path /proc -prune -o -group "$SC_USER_ID" -exec chgrp --no-dereference "$CONT_GROUPNAME" {} \;
65+
echo "$INFO" "Changing group properties of files around from $EFS_USER_ID to group $CONT_GROUPNAME"
66+
find / -path /proc -prune -o -group "$EFS_USER_ID" -exec chgrp --no-dereference "$CONT_GROUPNAME" {} \;
6767
# change user property of files already around
68-
echo "$INFO" "Changing ownership properties of files around from $SC_USER_ID to group $CONT_GROUPNAME"
69-
find / -path /proc -prune -o -user "$SC_USER_ID" -exec chown --no-dereference "$SC_USER_NAME" {} \;
68+
echo "$INFO" "Changing ownership properties of files around from $EFS_USER_ID to group $CONT_GROUPNAME"
69+
find / -path /proc -prune -o -user "$EFS_USER_ID" -exec chown --no-dereference "$EFS_USER_NAME" {} \;
7070
fi
7171
fi
7272

@@ -84,11 +84,11 @@ if stat $DOCKER_MOUNT >/dev/null 2>&1; then
8484
GROUPNAME=$(getent group "${GROUPID}" | cut --delimiter=: --fields=1)
8585
echo "$WARNING docker group with $GROUPID has name $GROUPNAME"
8686
fi
87-
adduser "$SC_USER_NAME" "$GROUPNAME"
87+
adduser "$EFS_USER_NAME" "$GROUPNAME"
8888
fi
8989

9090
echo "$INFO Starting $* ..."
91-
echo " $SC_USER_NAME rights : $(id "$SC_USER_NAME")"
91+
echo " $EFS_USER_NAME rights : $(id "$EFS_USER_NAME")"
9292
echo " local dir : $(ls -al)"
9393

94-
exec gosu "$SC_USER_NAME" "$@"
94+
exec gosu "$EFS_USER_NAME" "$@"

services/efs-guardian/src/simcore_service_efs_guardian/core/settings.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,21 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings):
4141
"then the check is considered to have failed."
4242
"It takes retries consecutive failures of the health check for the container to be considered unhealthy.",
4343
)
44-
SC_USER_ID: int | None = None
45-
SC_USER_NAME: str | None = None
44+
SC_USER_ID: int
45+
SC_USER_NAME: str
46+
47+
EFS_USER_ID: int = Field(
48+
description="Linux user ID that the Guardian service will run with"
49+
)
50+
EFS_USER_NAME: str = Field(
51+
description="Linux user name that the Guardian service will run with"
52+
)
53+
EFS_GROUP_ID: int = Field(
54+
description="Linux group ID that the EFS and Simcore linux users are part of"
55+
)
56+
EFS_GROUP_NAME: str = Field(
57+
description="Linux group name that the EFS and Simcore linux users are part of"
58+
)
4659

4760
# RUNTIME -----------------------------------------------------------
4861
EFS_GUARDIAN_DEBUG: bool = Field(

services/efs-guardian/src/simcore_service_efs_guardian/services/efs_manager.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1+
import os
12
from dataclasses import dataclass
23
from pathlib import Path
34

45
from fastapi import FastAPI
56
from models_library.projects import ProjectID
67
from models_library.projects_nodes_io import NodeID
78

9+
from ..core.settings import ApplicationSettings, get_application_settings
10+
811

912
@dataclass(frozen=True)
1013
class EfsManager:
1114
app: FastAPI
1215

1316
_efs_mounted_path: Path
1417
_project_specific_data_base_directory: str
18+
_settings: ApplicationSettings
1519

1620
@classmethod
1721
async def create(
@@ -20,7 +24,10 @@ async def create(
2024
efs_mounted_path: Path,
2125
project_specific_data_base_directory: str,
2226
):
23-
return cls(app, efs_mounted_path, project_specific_data_base_directory)
27+
settings = get_application_settings(app)
28+
return cls(
29+
app, efs_mounted_path, project_specific_data_base_directory, settings
30+
)
2431

2532
async def initialize_directories(self):
2633
_dir_path = self._efs_mounted_path / self._project_specific_data_base_directory
@@ -36,5 +43,12 @@ async def create_project_specific_data_dir(
3643
/ f"{node_id}"
3744
/ f"{storage_directory_name}"
3845
)
46+
# Ensure the directory exists with the right parents
3947
Path.mkdir(_dir_path, parents=True, exist_ok=True)
48+
# Change the owner to user id 8006(efs) and group id 8106(efs-group)
49+
os.chown(_dir_path, self._settings.EFS_USER_ID, self._settings.EFS_GROUP_ID)
50+
# Set directory permissions to allow group write access
51+
Path.chmod(
52+
_dir_path, 0o770
53+
) # This gives rwx permissions to user and group, and nothing to others
4054
return _dir_path

services/efs-guardian/tests/unit/test_efs_manager.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# pylint: disable=unused-variable
66

77
from pathlib import Path
8+
from unittest.mock import patch
89

910
import pytest
1011
from faker import Faker
@@ -45,12 +46,17 @@ async def test_rpc_create_project_specific_data_dir(
4546
_node_id = faker.uuid4()
4647
_storage_directory_name = faker.name()
4748

48-
result = await efs_manager.create_project_specific_data_dir(
49-
rpc_client,
50-
project_id=_project_id,
51-
node_id=_node_id,
52-
storage_directory_name=_storage_directory_name,
53-
)
49+
with patch(
50+
"simcore_service_efs_guardian.services.efs_manager.os.chown"
51+
) as mocked_chown:
52+
result = await efs_manager.create_project_specific_data_dir(
53+
rpc_client,
54+
project_id=_project_id,
55+
node_id=_node_id,
56+
storage_directory_name=_storage_directory_name,
57+
)
58+
mocked_chown.assert_called_once()
59+
5460
assert isinstance(result, Path)
5561
_expected_path = (
5662
aws_efs_settings.EFS_MOUNTED_PATH

0 commit comments

Comments
 (0)