Skip to content

Commit

Permalink
🎨 create efs guardian specific user (#5936)
Browse files Browse the repository at this point in the history
  • Loading branch information
matusdrobuliak66 authored Jul 3, 2024
1 parent 9f0cf48 commit ba9c6b3
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 35 deletions.
8 changes: 8 additions & 0 deletions .env-devel
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ DIRECTOR_PORT=8080
DIRECTOR_REGISTRY_CACHING_TTL=900
DIRECTOR_REGISTRY_CACHING=True

EFS_USER_ID=8006
EFS_USER_NAME=efs
EFS_GROUP_ID=8106
EFS_GROUP_NAME=efs-group
EFS_DNS_NAME=fs-xxx.efs.us-east-1.amazonaws.com
EFS_MOUNTED_PATH=/tmp/efs
EFS_PROJECT_SPECIFIC_DATA_DIRECTORY=project-specific-data
Expand Down Expand Up @@ -169,6 +173,10 @@ R_CLONE_OPTION_RETRIES=3
R_CLONE_OPTION_TRANSFERS=5
R_CLONE_PROVIDER=MINIO

# simcore-user used in docker images
SC_USER_ID=8004
SC_USER_NAME=scu

S3_ACCESS_KEY=12345678
S3_BUCKET_NAME=simcore
S3_ENDPOINT=http://172.17.0.1:9001
Expand Down
6 changes: 6 additions & 0 deletions services/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,12 @@ services:
RABBIT_PORT: ${RABBIT_PORT}
RABBIT_SECURE: ${RABBIT_SECURE}
RABBIT_USER: ${RABBIT_USER}
SC_USER_ID: ${SC_USER_ID}
SC_USER_NAME: ${SC_USER_NAME}
EFS_USER_ID: ${EFS_USER_ID}
EFS_USER_NAME: ${EFS_USER_NAME}
EFS_GROUP_ID: ${EFS_GROUP_ID}
EFS_GROUP_NAME: ${EFS_GROUP_NAME}
EFS_DNS_NAME: ${EFS_DNS_NAME}
EFS_MOUNTED_PATH: ${EFS_MOUNTED_PATH}
EFS_ONLY_ENABLED_FOR_USERIDS: ${EFS_ONLY_ENABLED_FOR_USERIDS}
Expand Down
38 changes: 28 additions & 10 deletions services/efs-guardian/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -45,26 +45,44 @@ RUN --mount=type=cache,target=/var/cache/apt,mode=0755,sharing=private \
&& gosu nobody true

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

RUN adduser \
--uid ${SC_USER_ID} \
--uid 8004 \
--disabled-password \
--gecos "" \
--shell /bin/sh \
--home /home/${SC_USER_NAME} \
${SC_USER_NAME}
--home /home/scu \
scu

RUN adduser \
--uid 8006 \
--disabled-password \
--gecos "" \
--shell /bin/sh \
--home /home/efs \
efs

# we create efs-group which is then used in efs guardian when he is creating a directory for user services.
RUN groupadd -g 8106 efs-group
RUN usermod -a -G efs-group efs
RUN usermod -a -G efs-group scu

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

# Turns off writing .pyc files; superfluous on an ephemeral container.
ENV PYTHONDONTWRITEBYTECODE=1 \
VIRTUAL_ENV=/home/scu/.venv
VIRTUAL_ENV=/home/efs/.venv

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

ENV PYTHONOPTIMIZE=TRUE

WORKDIR /home/scu
# ensure home folder is read/writable for user scu
RUN chown -R scu /home/scu
WORKDIR /home/efs
# ensure home folder is read/writable for user efs
RUN chown -R efs /home/efs

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

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


Expand Down Expand Up @@ -187,7 +205,7 @@ ENV SC_BUILD_TARGET=development \

WORKDIR /devel

RUN chown -R scu:scu "${VIRTUAL_ENV}"
RUN chown -R efs:efs "${VIRTUAL_ENV}"

ENTRYPOINT ["/bin/sh", "services/efs-guardian/docker/entrypoint.sh"]
CMD ["/bin/sh", "services/efs-guardian/docker/boot.sh"]
32 changes: 16 additions & 16 deletions services/efs-guardian/docker/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,30 +43,30 @@ if [ "${SC_BUILD_TARGET}" = "development" ]; then
HOST_GROUPID=$(stat --format=%g "${SC_DEVEL_MOUNT}")
CONT_GROUPNAME=$(getent group "${HOST_GROUPID}" | cut --delimiter=: --fields=1)
if [ "$HOST_USERID" -eq 0 ]; then
echo "$WARNING" "Folder mounted owned by root user... adding $SC_USER_NAME to root..."
adduser "$SC_USER_NAME" root
echo "$WARNING" "Folder mounted owned by root user... adding $EFS_USER_NAME to root..."
adduser "$EFS_USER_NAME" root
else
echo "$INFO" "Folder mounted owned by user $HOST_USERID:$HOST_GROUPID-'$CONT_GROUPNAME'..."
# take host's credentials in $SC_USER_NAME
# take host's credentials in $EFS_USER_NAME
if [ -z "$CONT_GROUPNAME" ]; then
echo "$WARNING" "Creating new group grp$SC_USER_NAME"
CONT_GROUPNAME=grp$SC_USER_NAME
echo "$WARNING" "Creating new group grp$EFS_USER_NAME"
CONT_GROUPNAME=grp$EFS_USER_NAME
addgroup --gid "$HOST_GROUPID" "$CONT_GROUPNAME"
else
echo "$INFO" "group already exists"
fi
echo "$INFO" "Adding $SC_USER_NAME to group $CONT_GROUPNAME..."
adduser "$SC_USER_NAME" "$CONT_GROUPNAME"
echo "$INFO" "Adding $EFS_USER_NAME to group $CONT_GROUPNAME..."
adduser "$EFS_USER_NAME" "$CONT_GROUPNAME"

echo "$WARNING" "Changing ownership [this could take some time]"
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)"
usermod --uid "$HOST_USERID" --gid "$HOST_GROUPID" "$SC_USER_NAME"
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)"
usermod --uid "$HOST_USERID" --gid "$HOST_GROUPID" "$EFS_USER_NAME"

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

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

echo "$INFO Starting $* ..."
echo " $SC_USER_NAME rights : $(id "$SC_USER_NAME")"
echo " $EFS_USER_NAME rights : $(id "$EFS_USER_NAME")"
echo " local dir : $(ls -al)"

exec gosu "$SC_USER_NAME" "$@"
exec gosu "$EFS_USER_NAME" "$@"
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,21 @@ class ApplicationSettings(BaseCustomSettings, MixinLoggingSettings):
"then the check is considered to have failed."
"It takes retries consecutive failures of the health check for the container to be considered unhealthy.",
)
SC_USER_ID: int | None = None
SC_USER_NAME: str | None = None
SC_USER_ID: int
SC_USER_NAME: str

EFS_USER_ID: int = Field(
description="Linux user ID that the Guardian service will run with"
)
EFS_USER_NAME: str = Field(
description="Linux user name that the Guardian service will run with"
)
EFS_GROUP_ID: int = Field(
description="Linux group ID that the EFS and Simcore linux users are part of"
)
EFS_GROUP_NAME: str = Field(
description="Linux group name that the EFS and Simcore linux users are part of"
)

# RUNTIME -----------------------------------------------------------
EFS_GUARDIAN_DEBUG: bool = Field(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import os
from dataclasses import dataclass
from pathlib import Path

from fastapi import FastAPI
from models_library.projects import ProjectID
from models_library.projects_nodes_io import NodeID

from ..core.settings import ApplicationSettings, get_application_settings


@dataclass(frozen=True)
class EfsManager:
app: FastAPI

_efs_mounted_path: Path
_project_specific_data_base_directory: str
_settings: ApplicationSettings

@classmethod
async def create(
Expand All @@ -20,7 +24,10 @@ async def create(
efs_mounted_path: Path,
project_specific_data_base_directory: str,
):
return cls(app, efs_mounted_path, project_specific_data_base_directory)
settings = get_application_settings(app)
return cls(
app, efs_mounted_path, project_specific_data_base_directory, settings
)

async def initialize_directories(self):
_dir_path = self._efs_mounted_path / self._project_specific_data_base_directory
Expand All @@ -36,5 +43,12 @@ async def create_project_specific_data_dir(
/ f"{node_id}"
/ f"{storage_directory_name}"
)
# Ensure the directory exists with the right parents
Path.mkdir(_dir_path, parents=True, exist_ok=True)
# Change the owner to user id 8006(efs) and group id 8106(efs-group)
os.chown(_dir_path, self._settings.EFS_USER_ID, self._settings.EFS_GROUP_ID)
# Set directory permissions to allow group write access
Path.chmod(
_dir_path, 0o770
) # This gives rwx permissions to user and group, and nothing to others
return _dir_path
18 changes: 12 additions & 6 deletions services/efs-guardian/tests/unit/test_efs_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# pylint: disable=unused-variable

from pathlib import Path
from unittest.mock import patch

import pytest
from faker import Faker
Expand Down Expand Up @@ -45,12 +46,17 @@ async def test_rpc_create_project_specific_data_dir(
_node_id = faker.uuid4()
_storage_directory_name = faker.name()

result = await efs_manager.create_project_specific_data_dir(
rpc_client,
project_id=_project_id,
node_id=_node_id,
storage_directory_name=_storage_directory_name,
)
with patch(
"simcore_service_efs_guardian.services.efs_manager.os.chown"
) as mocked_chown:
result = await efs_manager.create_project_specific_data_dir(
rpc_client,
project_id=_project_id,
node_id=_node_id,
storage_directory_name=_storage_directory_name,
)
mocked_chown.assert_called_once()

assert isinstance(result, Path)
_expected_path = (
aws_efs_settings.EFS_MOUNTED_PATH
Expand Down

0 comments on commit ba9c6b3

Please sign in to comment.