6
0
mirror of https://github.com/ChristianLight/tutor.git synced 2024-12-12 22:27:47 +00:00

feat: run all services as unprivileged containers

With this change, containers are no longer run as "root" but as unprivileged
users. This is necessary in some environments, notably some Kubernetes
clusters.

To make this possible, we need to manually fix bind-mounted volumes in
docker-compose. This is pretty much equivalent to the behaviour in Kubernetes,
where permissions are fixed at runtime if the volume owner is incorrect. Thus,
we have a consistent behaviour between docker-compose and Kubernetes.

We achieve this by bind-mounting some repos inside "*-permissions" services.
These services run as root user on docker-compose and will fix the required
permissions, as per build/permissions/setowner.sh These services simply do not
run on Kubernetes, where we don't rely on bind-mounted volumes. There, we make
use of Kubernete's built-in volume ownership feature.

With this change, we get rid of the "openedx-dev" Docker image, in the sense
that it no longer has its own Dockerfile. Instead, the dev image is now simply
a different target in the multi-layer openedx Docker image. This makes it much
faster to build the openedx-dev image.

Because we declare the APP_USER_ID in the dev/docker-compose.yml file, we need
to pass the user ID from the host there. The only way to achieve that is with a
tutor config variable. The downside of this approach is that the
dev/docker-compose.yml file is no longer portable from one machine to the next.
We consider that this is not such a big issue, as it affects the development
environment only.

We take this opportunity to replace the base image of the "forum" image. There
is now no need to re-install ruby inside the image. The total image size is
only decreased by 10%, but re-building the image is faster.

In order to run the smtp service as non-root, we switch from namshi/smtp to
devture/exim-relay. This change should be backward-compatible.

Note that the nginx container remains privileged. We could switch to
nginxinc/nginx-unprivileged, but it's probably not worth the effort, as we are
considering to get rid of the nginx container altogether.

Close #323.
This commit is contained in:
Régis Behmo 2021-09-23 12:04:19 +02:00 committed by Régis Behmo
parent 231bbbfe99
commit 01b58d9d75
20 changed files with 230 additions and 132 deletions

View File

@ -2,6 +2,9 @@
Note: Breaking changes between versions are indicated by "💥". Note: Breaking changes between versions are indicated by "💥".
- 💥[Improvement] Run all services as unprivileged containers, for better security. This has multiple consequences:
- The "openedx-dev" image is now built with `tutor dev dc build lms`.
- The "smtp" service now runs the "devture/exim-relay" Docker image, which is unprivileged. Also, the default SMTP port is now 8025.
- 💥[Feature] Get rid of the nginx container and service, which is now replaced by Caddy. this has the following consequences: - 💥[Feature] Get rid of the nginx container and service, which is now replaced by Caddy. this has the following consequences:
- Patches "nginx-cms", "nginx-lms", "nginx-extra", "local-docker-compose-nginx-aliases" are replaced by "caddyfile-cms", "caddyfile-lms", "caddyfile", " local-docker-compose-caddy-aliases". - Patches "nginx-cms", "nginx-lms", "nginx-extra", "local-docker-compose-nginx-aliases" are replaced by "caddyfile-cms", "caddyfile-lms", "caddyfile", " local-docker-compose-caddy-aliases".
- Patches "k8s-deployments-nginx-volume-mounts", "k8s-deployments-nginx-volumes" were obsolete and are removed. - Patches "k8s-deployments-nginx-volume-mounts", "k8s-deployments-nginx-volumes" were obsolete and are removed.

View File

@ -352,7 +352,7 @@ And djangojs.po::
Then you will have to re-build the openedx Docker image:: Then you will have to re-build the openedx Docker image::
tutor images build openedx openedx-dev tutor images build openedx
Beware that this will take a long time! Unfortunately it's difficult to accelerate this process, as translation files need to be compiled prior to collecting the assets. In development it's possible to accelerate the iteration loop -- but that exercise is left to the reader. Beware that this will take a long time! Unfortunately it's difficult to accelerate this process, as translation files need to be compiled prior to collecting the assets. In development it's possible to accelerate the iteration loop -- but that exercise is left to the reader.

View File

@ -19,7 +19,7 @@ Once the local platform has been configured, you should stop it so that it does
Finally, you should build the ``openedx-dev`` docker image:: Finally, you should build the ``openedx-dev`` docker image::
tutor images build openedx-dev tutor dev dc build lms
This ``openedx-dev`` development image differs from the ``openedx`` production image: This ``openedx-dev`` development image differs from the ``openedx`` production image:

View File

@ -50,10 +50,6 @@ The LMS can then be accessed at http://local.overhang.io:8000. You will then hav
tutor dev settheme mythemename tutor dev settheme mythemename
Re-build development docker image (and compile assets)::
tutor images build openedx-dev
Watch the themes folders for changes (in a different terminal):: Watch the themes folders for changes (in a different terminal)::
tutor dev run watchthemes tutor dev run watchthemes

View File

@ -34,6 +34,7 @@ chown -R {user_id} {volumes_path}/{volume_name}""".format(
"run", "run",
"--rm", "--rm",
"--no-deps", "--no-deps",
"--user=0",
"--volume", "--volume",
"{}:{}".format(volumes_root_path, container_volumes_root_path), "{}:{}".format(volumes_root_path, container_volumes_root_path),
service, service,

View File

@ -8,11 +8,9 @@ from .. import exceptions
from .. import images from .. import images
from .. import plugins from .. import plugins
from ..types import Config from ..types import Config
from .. import utils
from .context import Context from .context import Context
BASE_IMAGE_NAMES = ["openedx", "forum"] BASE_IMAGE_NAMES = ["openedx", "forum", "permissions"]
DEV_IMAGE_NAMES = ["openedx-dev"]
VENDOR_IMAGES = [ VENDOR_IMAGES = [
"caddy", "caddy",
"elasticsearch", "elasticsearch",
@ -136,13 +134,6 @@ def build_image(root: str, config: Config, image: str, *args: str) -> None:
(tutor_env.pathjoin(root, "plugins", plugin, "build", img), tag, args) (tutor_env.pathjoin(root, "plugins", plugin, "build", img), tag, args)
) )
# Build dev images with user id argument
dev_build_arg = ("--build-arg", "USERID={}".format(utils.get_user_id()))
for img, tag in iter_images(config, image, DEV_IMAGE_NAMES):
to_build.append(
(tutor_env.pathjoin(root, "build", img), tag, dev_build_arg + args)
)
if not to_build: if not to_build:
raise ImageNotFoundError(image) raise ImageNotFoundError(image)

View File

@ -59,6 +59,7 @@ class Renderer:
environment.globals["rsa_import_key"] = utils.rsa_import_key environment.globals["rsa_import_key"] = utils.rsa_import_key
environment.filters["rsa_private_key"] = utils.rsa_private_key environment.filters["rsa_private_key"] = utils.rsa_private_key
environment.filters["walk_templates"] = self.walk_templates environment.filters["walk_templates"] = self.walk_templates
environment.globals["HOST_USER_ID"] = utils.get_user_id()
environment.globals["TUTOR_APP"] = __app__.replace("-", "_") environment.globals["TUTOR_APP"] = __app__.replace("-", "_")
environment.globals["TUTOR_VERSION"] = __version__ environment.globals["TUTOR_VERSION"] = __version__
self.environment = environment self.environment = environment

View File

@ -50,6 +50,7 @@ class BaseComposeJobRunner(BaseJobRunner):
def initialise(runner: BaseJobRunner, limit_to: Optional[str] = None) -> None: def initialise(runner: BaseJobRunner, limit_to: Optional[str] = None) -> None:
fmt.echo_info("Initialising all services...") fmt.echo_info("Initialising all services...")
if limit_to is None or limit_to == "mysql": if limit_to is None or limit_to == "mysql":
fmt.echo_info("Initialising mysql...")
runner.run_job_from_template("mysql", "hooks", "mysql", "init") runner.run_job_from_template("mysql", "hooks", "mysql", "init")
for plugin_name, hook in runner.iter_plugin_hooks("pre-init"): for plugin_name, hook in runner.iter_plugin_hooks("pre-init"):
if limit_to is None or limit_to == plugin_name: if limit_to is None or limit_to == plugin_name:

View File

@ -1,4 +1,4 @@
FROM docker.io/ubuntu:20.04 FROM docker.io/ruby:2.5.7-slim-stretch
MAINTAINER Overhang.io <contact@overhang.io> MAINTAINER Overhang.io <contact@overhang.io>
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
@ -12,32 +12,27 @@ RUN wget -O /tmp/dockerize.tar.gz https://github.com/jwilder/dockerize/releases/
&& tar -C /usr/local/bin -xzvf /tmp/dockerize.tar.gz \ && tar -C /usr/local/bin -xzvf /tmp/dockerize.tar.gz \
&& rm /tmp/dockerize.tar.gz && rm /tmp/dockerize.tar.gz
RUN mkdir /openedx # Create unprivileged "app" user
RUN useradd --home-dir /app --create-home --shell /bin/bash --uid 1000 app
# Install ruby-build for building specific version of ruby # Copy custom scripts
# The ruby-build version should be periodically updated to reflect the latest release COPY ./bin /app/bin
ARG RUBY_BUILD_VERSION=v20200401 RUN chmod a+x /app/bin/*
RUN git clone https://github.com/rbenv/ruby-build.git --branch $RUBY_BUILD_VERSION /openedx/ruby-build ENV PATH :${PATH}
WORKDIR /openedx/ruby-build
RUN PREFIX=/usr/local ./install.sh
# Install ruby and some specific dependencies # From then on, run as unprivileged app user
ARG RUBY_VERSION=2.5.7 USER app
ARG BUNDLER_VERSION=1.17.3
ARG RAKE_VERSION=13.0.1 # Install rake and bundler
RUN ruby-build $RUBY_VERSION /openedx/ruby ENV PATH "/app/bin:/app/.gem/ruby/2.5.0/bin:$PATH"
ENV PATH "/openedx/ruby/bin:$PATH" RUN gem install --user-install bundler --version 1.17.3
RUN gem install bundler -v $BUNDLER_VERSION RUN gem install --user-install rake --version 13.0.1
RUN gem install rake -v $RAKE_VERSION
# Install forum # Install forum
RUN git clone https://github.com/edx/cs_comments_service.git --branch {{ OPENEDX_COMMON_VERSION }} --depth 1 /openedx/cs_comments_service RUN git clone https://github.com/edx/cs_comments_service.git --branch {{ OPENEDX_COMMON_VERSION }} --depth 1 /app/cs_comments_service
WORKDIR /openedx/cs_comments_service WORKDIR /app/cs_comments_service
RUN bundle install --deployment RUN bundle install --deployment
COPY ./bin /openedx/bin
RUN chmod a+x /openedx/bin/*
ENV PATH /openedx/bin:${PATH}
ENTRYPOINT ["docker-entrypoint.sh"] ENTRYPOINT ["docker-entrypoint.sh"]
ENV SINATRA_ENV staging ENV SINATRA_ENV staging

View File

@ -1,34 +0,0 @@
FROM {{ DOCKER_IMAGE_OPENEDX }} as base
MAINTAINER Overhang.io <contact@overhang.io>
# Install useful system requirements
RUN apt update && \
apt install -y vim iputils-ping dnsutils telnet \
&& rm -rf /var/lib/apt/lists/*
# Install dev python requirements
RUN pip install -r requirements/edx/development.txt
RUN pip install ipdb==0.13.4 ipython==7.27.0
{{ patch("openedx-dev-dockerfile-post-python-requirements") }}
# Recompile static assets: in development mode all static assets are stored in edx-platform,
# and the location of these files is stored in webpack-stats.json. If we don't recompile
# static assets, then production assets will be served instead.
RUN rm -r /openedx/staticfiles && \
mkdir /openedx/staticfiles && \
openedx-assets webpack --env=dev
# Copy new entrypoint (to take care of permission issues at runtime)
COPY ./bin /openedx/bin
RUN chmod a+x /openedx/bin/*
# Configure new user
ARG USERID=1000
RUN create-user.sh $USERID
######## Development image
FROM base as dev
# Default django settings
ENV SETTINGS tutor.development

View File

@ -1,11 +0,0 @@
#! /bin/sh -e
USERID=$1
if [ "$USERID" != "" ] && [ "$USERID" != "0" ]
then
echo "Creating 'openedx' user with id $USERID"
useradd --home-dir /openedx --uid $USERID openedx
chown -R openedx:openedx /openedx
else
echo "Running as root"
fi

View File

@ -1,19 +0,0 @@
#!/bin/sh -e
export DJANGO_SETTINGS_MODULE=$SERVICE_VARIANT.envs.$SETTINGS
if id -u openedx > /dev/null 2>&1; then
# Change owners of mounted volumes
echo "Setting file permissions for user openedx..."
find /openedx \
-not -path "/openedx/edx-platform/*" \
-not -user openedx \
-writable \
-exec chown openedx:openedx {} \+
echo "File permissions set."
# Run CMD as user openedx
exec chroot --userspec="openedx:openedx" --skip-chdir / env HOME=/openedx "$@"
else
echo "Running openedx-dev as root user"
exec "$@"
fi

View File

@ -127,14 +127,19 @@ RUN apt update && \
apt install -y gettext gfortran graphviz graphviz-dev libffi-dev libfreetype6-dev libgeos-dev libjpeg8-dev liblapack-dev libmysqlclient-dev libpng-dev libsqlite3-dev libxmlsec1-dev lynx ntp pkg-config rdfind && \ apt install -y gettext gfortran graphviz graphviz-dev libffi-dev libfreetype6-dev libgeos-dev libjpeg8-dev liblapack-dev libmysqlclient-dev libpng-dev libsqlite3-dev libxmlsec1-dev lynx ntp pkg-config rdfind && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
COPY --from=dockerize /usr/local/bin/dockerize /usr/local/bin/dockerize # From then on, run as unprivileged "app" user
COPY --from=code /openedx/edx-platform /openedx/edx-platform ARG APP_USER_ID=1000
COPY --from=locales /openedx/locale/contrib/locale /openedx/locale/contrib/locale RUN useradd --home-dir /openedx --create-home --shell /bin/bash --uid ${APP_USER_ID} app
COPY --from=python /opt/pyenv /opt/pyenv USER ${APP_USER_ID}
COPY --from=python-requirements /openedx/venv /openedx/venv
COPY --from=python-requirements /openedx/requirements /openedx/requirements COPY --chown=app:app --from=dockerize /usr/local/bin/dockerize /usr/local/bin/dockerize
COPY --from=nodejs-requirements /openedx/nodeenv /openedx/nodeenv COPY --chown=app:app --from=code /openedx/edx-platform /openedx/edx-platform
COPY --from=nodejs-requirements /openedx/edx-platform/node_modules /openedx/edx-platform/node_modules COPY --chown=app:app --from=locales /openedx/locale /openedx/locale
COPY --chown=app:app --from=python /opt/pyenv /opt/pyenv
COPY --chown=app:app --from=python-requirements /openedx/venv /openedx/venv
COPY --chown=app:app --from=python-requirements /openedx/requirements /openedx/requirements
COPY --chown=app:app --from=nodejs-requirements /openedx/nodeenv /openedx/nodeenv
COPY --chown=app:app --from=nodejs-requirements /openedx/edx-platform/node_modules /openedx/edx-platform/node_modules
ENV PATH /openedx/venv/bin:./node_modules/.bin:/openedx/nodeenv/bin:${PATH} ENV PATH /openedx/venv/bin:./node_modules/.bin:/openedx/nodeenv/bin:${PATH}
ENV VIRTUAL_ENV /openedx/venv/ ENV VIRTUAL_ENV /openedx/venv/
@ -146,16 +151,16 @@ RUN pip install -r requirements/edx/local.in
# Create folder that will store lms/cms.env.json files, as well as # Create folder that will store lms/cms.env.json files, as well as
# the tutor-specific settings files. # the tutor-specific settings files.
RUN mkdir -p /openedx/config ./lms/envs/tutor ./cms/envs/tutor RUN mkdir -p /openedx/config ./lms/envs/tutor ./cms/envs/tutor
COPY revisions.yml /openedx/config/ COPY --chown=app:app revisions.yml /openedx/config/
ENV LMS_CFG /openedx/config/lms.env.json ENV LMS_CFG /openedx/config/lms.env.json
ENV STUDIO_CFG /openedx/config/cms.env.json ENV STUDIO_CFG /openedx/config/cms.env.json
ENV REVISION_CFG /openedx/config/revisions.yml ENV REVISION_CFG /openedx/config/revisions.yml
COPY settings/lms/*.py ./lms/envs/tutor/ COPY --chown=app:app settings/lms/*.py ./lms/envs/tutor/
COPY settings/cms/*.py ./cms/envs/tutor/ COPY --chown=app:app settings/cms/*.py ./cms/envs/tutor/
# Copy user-specific locales to /openedx/locale/user/locale and compile them # Copy user-specific locales to /openedx/locale/user/locale and compile them
RUN mkdir -p /openedx/locale/user RUN mkdir /openedx/locale/user
COPY ./locale/ /openedx/locale/user/locale/ COPY --chown=app:app ./locale/ /openedx/locale/user/locale/
RUN cd /openedx/locale/user && \ RUN cd /openedx/locale/user && \
django-admin.py compilemessages -v1 django-admin.py compilemessages -v1
@ -166,7 +171,7 @@ RUN ./manage.py lms --settings=tutor.i18n compilejsi18n
RUN ./manage.py cms --settings=tutor.i18n compilejsi18n RUN ./manage.py cms --settings=tutor.i18n compilejsi18n
# Copy scripts # Copy scripts
COPY ./bin /openedx/bin COPY --chown=app:app ./bin /openedx/bin
RUN chmod a+x /openedx/bin/* RUN chmod a+x /openedx/bin/*
ENV PATH /openedx/bin:${PATH} ENV PATH /openedx/bin:${PATH}
@ -188,7 +193,7 @@ RUN openedx-assets xmodule \
&& openedx-assets npm \ && openedx-assets npm \
&& openedx-assets webpack --env=prod \ && openedx-assets webpack --env=prod \
&& openedx-assets common && openedx-assets common
COPY ./themes/ /openedx/themes/ COPY --chown=app:app ./themes/ /openedx/themes/
RUN openedx-assets themes \ RUN openedx-assets themes \
&& openedx-assets collect --settings=tutor.assets \ && openedx-assets collect --settings=tutor.assets \
# De-duplicate static assets with symlinks # De-duplicate static assets with symlinks
@ -205,9 +210,40 @@ ENV SETTINGS tutor.production
# Entrypoint will set right environment variables # Entrypoint will set right environment variables
ENTRYPOINT ["docker-entrypoint.sh"] ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 8000
###### Intermediate image with dev/test dependencies
FROM production as development
# Install useful system requirements (as root)
USER root
RUN apt update && \
apt install -y vim iputils-ping dnsutils telnet \
&& rm -rf /var/lib/apt/lists/*
USER app
# Install dev python requirements
RUN pip install -r requirements/edx/development.txt
RUN pip install ipdb==0.13.4 ipython==7.27.0
# Recompile static assets: in development mode all static assets are stored in edx-platform,
# and the location of these files is stored in webpack-stats.json. If we don't recompile
# static assets, then production assets will be served instead.
RUN rm -r /openedx/staticfiles && \
mkdir /openedx/staticfiles && \
openedx-assets webpack --env=dev
{{ patch("openedx-dev-dockerfile-post-python-requirements") }}
# Default django settings
ENV SETTINGS tutor.development
CMD ./manage.py $SERVICE_VARIANT runserver 0.0.0.0:8000
###### Final image with production cmd
FROM production as final
# Run server # Run server
EXPOSE 8000
CMD uwsgi \ CMD uwsgi \
--static-map /static=/openedx/staticfiles/ \ --static-map /static=/openedx/staticfiles/ \
--static-map /media=/openedx/media/ \ --static-map /media=/openedx/media/ \

View File

@ -0,0 +1,7 @@
from docker.io/alpine:3.13.6
MAINTAINER Overhang.io <contact@overhang.io>
COPY ./setowner.sh /usr/local/bin/setowner
RUN chmod a+x /usr/local/bin/setowner
ENTRYPOINT ["setowner"]

View File

@ -0,0 +1,14 @@
#! /bin/sh
set -e
user_id="$1"
shift
for path in $@; do
path_user_id="$(stat -c '%u' $path)"
if [ "$path_user_id" != "$user_id" ]
then
echo "$path changing UID from $path_user_id to $user_id..."
chown --recursive $user_id $path
else
echo "$path already owned by $user_id"
fi
done

View File

@ -26,7 +26,7 @@ OPENEDX_AWS_SECRET_ACCESS_KEY: ""
DEV_PROJECT_NAME: "tutor_dev" DEV_PROJECT_NAME: "tutor_dev"
DOCKER_REGISTRY: "docker.io/" DOCKER_REGISTRY: "docker.io/"
DOCKER_IMAGE_OPENEDX: "{{ DOCKER_REGISTRY }}overhangio/openedx:{{ TUTOR_VERSION }}" DOCKER_IMAGE_OPENEDX: "{{ DOCKER_REGISTRY }}overhangio/openedx:{{ TUTOR_VERSION }}"
DOCKER_IMAGE_OPENEDX_DEV: "{{ DOCKER_REGISTRY }}overhangio/openedx-dev:{{ TUTOR_VERSION }}" DOCKER_IMAGE_OPENEDX_DEV: "openedx-dev"
DOCKER_IMAGE_CADDY: "{{ DOCKER_REGISTRY }}caddy:2.3.0" DOCKER_IMAGE_CADDY: "{{ DOCKER_REGISTRY }}caddy:2.3.0"
DOCKER_IMAGE_ELASTICSEARCH: "{{ DOCKER_REGISTRY }}elasticsearch:7.10.1" DOCKER_IMAGE_ELASTICSEARCH: "{{ DOCKER_REGISTRY }}elasticsearch:7.10.1"
DOCKER_IMAGE_FORUM: "{{ DOCKER_REGISTRY }}overhangio/openedx-forum:{{ TUTOR_VERSION }}" DOCKER_IMAGE_FORUM: "{{ DOCKER_REGISTRY }}overhangio/openedx-forum:{{ TUTOR_VERSION }}"
@ -34,8 +34,9 @@ DOCKER_IMAGE_MONGODB: "{{ DOCKER_REGISTRY }}mongo:4.2.17"
DOCKER_IMAGE_MYSQL: "{{ DOCKER_REGISTRY }}mysql:5.7.35" DOCKER_IMAGE_MYSQL: "{{ DOCKER_REGISTRY }}mysql:5.7.35"
DOCKER_IMAGE_ELASTICSEARCH: "{{ DOCKER_REGISTRY }}elasticsearch:7.10.1" DOCKER_IMAGE_ELASTICSEARCH: "{{ DOCKER_REGISTRY }}elasticsearch:7.10.1"
DOCKER_IMAGE_NGINX: "{{ DOCKER_REGISTRY }}nginx:1.21.1" DOCKER_IMAGE_NGINX: "{{ DOCKER_REGISTRY }}nginx:1.21.1"
DOCKER_IMAGE_PERMISSIONS: "{{ DOCKER_REGISTRY }}alpine:3.13.6"
DOCKER_IMAGE_REDIS: "{{ DOCKER_REGISTRY }}redis:6.2.6" DOCKER_IMAGE_REDIS: "{{ DOCKER_REGISTRY }}redis:6.2.6"
DOCKER_IMAGE_SMTP: "{{ DOCKER_REGISTRY }}namshi/smtp:latest" DOCKER_IMAGE_SMTP: "{{ DOCKER_REGISTRY }}devture/exim-relay:4.94.2-r0-4"
LOCAL_PROJECT_NAME: "{{ TUTOR_APP }}_local" LOCAL_PROJECT_NAME: "{{ TUTOR_APP }}_local"
ELASTICSEARCH_HOST: "elasticsearch" ELASTICSEARCH_HOST: "elasticsearch"
ELASTICSEARCH_PORT: 9200 ELASTICSEARCH_PORT: 9200
@ -77,7 +78,7 @@ REDIS_PORT: 6379
REDIS_USERNAME: "" REDIS_USERNAME: ""
REDIS_PASSWORD: "" REDIS_PASSWORD: ""
SMTP_HOST: "smtp" SMTP_HOST: "smtp"
SMTP_PORT: 25 SMTP_PORT: 8025
SMTP_USERNAME: "" SMTP_USERNAME: ""
SMTP_PASSWORD: "" SMTP_PASSWORD: ""
SMTP_USE_TLS: false SMTP_USE_TLS: false

View File

@ -3,6 +3,11 @@ version: "3.7"
x-openedx-service: x-openedx-service:
&openedx-service &openedx-service
image: {{ DOCKER_IMAGE_OPENEDX_DEV }} image: {{ DOCKER_IMAGE_OPENEDX_DEV }}
build:
context: ../build/openedx/
target: development
args:
APP_USER_ID: "{{ HOST_USER_ID }}"
environment: environment:
SETTINGS: ${TUTOR_EDX_PLATFORM_SETTINGS:-tutor.development} SETTINGS: ${TUTOR_EDX_PLATFORM_SETTINGS:-tutor.development}
volumes: volumes:
@ -16,6 +21,12 @@ x-openedx-service:
- ../build/openedx/requirements:/openedx/requirements - ../build/openedx/requirements:/openedx/requirements
services: services:
lms-permissions:
command: ["{{ HOST_USER_ID }}", "/openedx/data", "/openedx/media"]
cms-permissions:
command: ["{{ HOST_USER_ID }}", "/openedx/data", "/openedx/media"]
lms: lms:
<<: *openedx-service <<: *openedx-service
command: ./manage.py lms runserver 0.0.0.0:8000 command: ./manage.py lms runserver 0.0.0.0:8000

View File

@ -51,6 +51,9 @@ spec:
labels: labels:
app.kubernetes.io/name: cms app.kubernetes.io/name: cms
spec: spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
containers: containers:
- name: cms - name: cms
image: {{ DOCKER_IMAGE_OPENEDX }} image: {{ DOCKER_IMAGE_OPENEDX }}
@ -69,6 +72,8 @@ spec:
resources: resources:
requests: requests:
memory: 2Gi memory: 2Gi
securityContext:
allowPrivilegeEscalation: false
volumes: volumes:
- name: settings-lms - name: settings-lms
configMap: configMap:
@ -95,6 +100,9 @@ spec:
labels: labels:
app.kubernetes.io/name: cms-worker app.kubernetes.io/name: cms-worker
spec: spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
containers: containers:
- name: cms-worker - name: cms-worker
image: {{ DOCKER_IMAGE_OPENEDX }} image: {{ DOCKER_IMAGE_OPENEDX }}
@ -102,8 +110,6 @@ spec:
env: env:
- name: SERVICE_VARIANT - name: SERVICE_VARIANT
value: cms value: cms
- name: C_FORCE_ROOT
value: "1"
volumeMounts: volumeMounts:
- mountPath: /openedx/edx-platform/lms/envs/tutor/ - mountPath: /openedx/edx-platform/lms/envs/tutor/
name: settings-lms name: settings-lms
@ -111,6 +117,8 @@ spec:
name: settings-cms name: settings-cms
- mountPath: /openedx/config - mountPath: /openedx/config
name: config name: config
securityContext:
allowPrivilegeEscalation: false
volumes: volumes:
- name: settings-lms - name: settings-lms
configMap: configMap:
@ -139,6 +147,9 @@ spec:
labels: labels:
app.kubernetes.io/name: forum app.kubernetes.io/name: forum
spec: spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
containers: containers:
- name: forum - name: forum
image: {{ DOCKER_IMAGE_FORUM }} image: {{ DOCKER_IMAGE_FORUM }}
@ -155,6 +166,8 @@ spec:
value: "{{ MONGODB_PORT }}" value: "{{ MONGODB_PORT }}"
- name: MONGODB_DATABASE - name: MONGODB_DATABASE
value: "{{ FORUM_MONGODB_DATABASE }}" value: "{{ FORUM_MONGODB_DATABASE }}"
securityContext:
allowPrivilegeEscalation: false
{% endif %} {% endif %}
{% if RUN_LMS %} {% if RUN_LMS %}
--- ---
@ -173,6 +186,9 @@ spec:
labels: labels:
app.kubernetes.io/name: lms app.kubernetes.io/name: lms
spec: spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
containers: containers:
- name: lms - name: lms
image: {{ DOCKER_IMAGE_OPENEDX }} image: {{ DOCKER_IMAGE_OPENEDX }}
@ -188,6 +204,8 @@ spec:
resources: resources:
requests: requests:
memory: 2Gi memory: 2Gi
securityContext:
allowPrivilegeEscalation: false
volumes: volumes:
- name: settings-lms - name: settings-lms
configMap: configMap:
@ -214,6 +232,9 @@ spec:
labels: labels:
app.kubernetes.io/name: lms-worker app.kubernetes.io/name: lms-worker
spec: spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
containers: containers:
- name: lms-worker - name: lms-worker
image: {{ DOCKER_IMAGE_OPENEDX }} image: {{ DOCKER_IMAGE_OPENEDX }}
@ -221,8 +242,6 @@ spec:
env: env:
- name: SERVICE_VARIANT - name: SERVICE_VARIANT
value: lms value: lms
- name: C_FORCE_ROOT
value: "1"
volumeMounts: volumeMounts:
- mountPath: /openedx/edx-platform/lms/envs/tutor/ - mountPath: /openedx/edx-platform/lms/envs/tutor/
name: settings-lms name: settings-lms
@ -230,6 +249,8 @@ spec:
name: settings-cms name: settings-cms
- mountPath: /openedx/config - mountPath: /openedx/config
name: config name: config
securityContext:
allowPrivilegeEscalation: false
volumes: volumes:
- name: settings-lms - name: settings-lms
configMap: configMap:
@ -260,6 +281,11 @@ spec:
labels: labels:
app.kubernetes.io/name: elasticsearch app.kubernetes.io/name: elasticsearch
spec: spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
fsGroupChangePolicy: "OnRootMismatch"
containers: containers:
- name: elasticsearch - name: elasticsearch
image: {{ DOCKER_IMAGE_ELASTICSEARCH }} image: {{ DOCKER_IMAGE_ELASTICSEARCH }}
@ -276,6 +302,8 @@ spec:
value: "1" value: "1"
ports: ports:
- containerPort: 9200 - containerPort: 9200
securityContext:
allowPrivilegeEscalation: false
volumeMounts: volumeMounts:
- mountPath: /usr/share/elasticsearch/data - mountPath: /usr/share/elasticsearch/data
name: data name: data
@ -303,6 +331,11 @@ spec:
labels: labels:
app.kubernetes.io/name: mongodb app.kubernetes.io/name: mongodb
spec: spec:
securityContext:
runAsUser: 999
runAsGroup: 999
fsGroup: 999
fsGroupChangePolicy: "OnRootMismatch"
containers: containers:
- name: mongodb - name: mongodb
image: {{ DOCKER_IMAGE_MONGODB }} image: {{ DOCKER_IMAGE_MONGODB }}
@ -312,7 +345,8 @@ spec:
volumeMounts: volumeMounts:
- mountPath: /data/db - mountPath: /data/db
name: data name: data
securityContext:
allowPrivilegeEscalation: false
volumes: volumes:
- name: data - name: data
persistentVolumeClaim: persistentVolumeClaim:
@ -337,6 +371,11 @@ spec:
labels: labels:
app.kubernetes.io/name: mysql app.kubernetes.io/name: mysql
spec: spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
fsGroupChangePolicy: "OnRootMismatch"
containers: containers:
- name: mysql - name: mysql
image: {{ DOCKER_IMAGE_MYSQL }} image: {{ DOCKER_IMAGE_MYSQL }}
@ -351,6 +390,8 @@ spec:
volumeMounts: volumeMounts:
- mountPath: /var/lib/mysql - mountPath: /var/lib/mysql
name: data name: data
securityContext:
allowPrivilegeEscalation: false
volumes: volumes:
- name: data - name: data
persistentVolumeClaim: persistentVolumeClaim:
@ -373,11 +414,14 @@ spec:
labels: labels:
app.kubernetes.io/name: smtp app.kubernetes.io/name: smtp
spec: spec:
securityContext:
runAsUser: 100
runAsGroup: 101
containers: containers:
- name: smtp - name: smtp
image: {{ DOCKER_IMAGE_SMTP }} image: {{ DOCKER_IMAGE_SMTP }}
ports: ports:
- containerPort: 25 - containerPort: 8025
{% endif %} {% endif %}
{% if RUN_REDIS %} {% if RUN_REDIS %}
--- ---
@ -398,6 +442,11 @@ spec:
labels: labels:
app.kubernetes.io/name: redis app.kubernetes.io/name: redis
spec: spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
fsGroupChangePolicy: "OnRootMismatch"
containers: containers:
- name: redis - name: redis
image: {{ DOCKER_IMAGE_REDIS }} image: {{ DOCKER_IMAGE_REDIS }}
@ -410,6 +459,8 @@ spec:
name: config name: config
- mountPath: /openedx/redis/data - mountPath: /openedx/redis/data
name: data name: data
securityContext:
allowPrivilegeEscalation: false
volumes: volumes:
- name: config - name: config
configMap: configMap:

View File

@ -121,7 +121,7 @@ metadata:
spec: spec:
type: NodePort type: NodePort
ports: ports:
- port: 25 - port: 8025
protocol: TCP protocol: TCP
selector: selector:
app.kubernetes.io/name: smtp app.kubernetes.io/name: smtp

View File

@ -9,6 +9,16 @@ services:
# Use WiredTiger in all environments, just like at edx.org # Use WiredTiger in all environments, just like at edx.org
command: mongod --nojournal --storageEngine wiredTiger command: mongod --nojournal --storageEngine wiredTiger
restart: unless-stopped restart: unless-stopped
user: "999:999"
privileged: false
volumes:
- ../../data/mongodb:/data/db
depends_on:
- mongodb-permissions
mongodb-permissions:
image: {{ DOCKER_IMAGE_PERMISSIONS }}
command: ["999", "/data/db"]
restart: on-failure
volumes: volumes:
- ../../data/mongodb:/data/db - ../../data/mongodb:/data/db
{% endif %} {% endif %}
@ -18,10 +28,18 @@ services:
image: {{ DOCKER_IMAGE_MYSQL }} image: {{ DOCKER_IMAGE_MYSQL }}
command: mysqld --character-set-server=utf8 --collation-server=utf8_general_ci command: mysqld --character-set-server=utf8 --collation-server=utf8_general_ci
restart: unless-stopped restart: unless-stopped
user: "1000:1000"
privileged: false
volumes: volumes:
- ../../data/mysql:/var/lib/mysql - ../../data/mysql:/var/lib/mysql
environment: environment:
MYSQL_ROOT_PASSWORD: "{{ MYSQL_ROOT_PASSWORD }}" MYSQL_ROOT_PASSWORD: "{{ MYSQL_ROOT_PASSWORD }}"
mysql-permissions:
image: {{ DOCKER_IMAGE_PERMISSIONS }}
command: ["1000", "/var/lib/mysql"]
restart: on-failure
volumes:
- ../../data/mysql:/var/lib/mysql
{% endif %} {% endif %}
{% if RUN_ELASTICSEARCH %} {% if RUN_ELASTICSEARCH %}
@ -32,12 +50,20 @@ services:
- bootstrap.memory_lock=true - bootstrap.memory_lock=true
- discovery.type=single-node - discovery.type=single-node
- "ES_JAVA_OPTS=-Xms{{ ELASTICSEARCH_HEAP_SIZE }} -Xmx{{ ELASTICSEARCH_HEAP_SIZE }}" - "ES_JAVA_OPTS=-Xms{{ ELASTICSEARCH_HEAP_SIZE }} -Xmx{{ ELASTICSEARCH_HEAP_SIZE }}"
- TAKE_FILE_OWNERSHIP=1
ulimits: ulimits:
memlock: memlock:
soft: -1 soft: -1
hard: -1 hard: -1
restart: unless-stopped restart: unless-stopped
user: "1000:1000"
volumes:
- ../../data/elasticsearch:/usr/share/elasticsearch/data
depends_on:
- elasticsearch-permissions
elasticsearch-permissions:
image: {{ DOCKER_IMAGE_PERMISSIONS }}
command: ["1000", "/usr/share/elasticsearch/data"]
restart: on-failure
volumes: volumes:
- ../../data/elasticsearch:/usr/share/elasticsearch/data - ../../data/elasticsearch:/usr/share/elasticsearch/data
{% endif %} {% endif %}
@ -46,17 +72,29 @@ services:
redis: redis:
image: {{ DOCKER_IMAGE_REDIS }} image: {{ DOCKER_IMAGE_REDIS }}
working_dir: /openedx/redis/data working_dir: /openedx/redis/data
user: "1000:1000"
volumes: volumes:
- ../apps/redis/redis.conf:/openedx/redis/config/redis.conf:ro - ../apps/redis/redis.conf:/openedx/redis/config/redis.conf:ro
- ../../data/redis:/openedx/redis/data - ../../data/redis:/openedx/redis/data
command: redis-server /openedx/redis/config/redis.conf command: redis-server /openedx/redis/config/redis.conf
restart: unless-stopped restart: unless-stopped
depends_on:
- redis-permissions
redis-permissions:
image: {{ DOCKER_IMAGE_PERMISSIONS }}
command: ["1000", "/openedx/redis/data"]
restart: on-failure
volumes:
- ../../data/redis:/openedx/redis/data
{% endif %} {% endif %}
{% if RUN_SMTP %} {% if RUN_SMTP %}
smtp: smtp:
image: {{ DOCKER_IMAGE_SMTP }} image: {{ DOCKER_IMAGE_SMTP }}
restart: unless-stopped restart: unless-stopped
user: "100:101"
environment:
HOSTNAME: "{{ LMS_HOST }}"
{% endif %} {% endif %}
############# Forum ############# Forum
@ -91,6 +129,7 @@ services:
- ../../data/lms:/openedx/data - ../../data/lms:/openedx/data
- ../../data/openedx-media:/openedx/media - ../../data/openedx-media:/openedx/media
depends_on: depends_on:
- lms-permissions
{% if RUN_MYSQL %}- mysql{% endif %} {% if RUN_MYSQL %}- mysql{% endif %}
{% if RUN_ELASTICSEARCH %}- elasticsearch{% endif %} {% if RUN_ELASTICSEARCH %}- elasticsearch{% endif %}
{% if RUN_FORUM %}- forum{% endif %} {% if RUN_FORUM %}- forum{% endif %}
@ -98,6 +137,14 @@ services:
{% if RUN_REDIS %}- redis{% endif %} {% if RUN_REDIS %}- redis{% endif %}
{% if RUN_SMTP %}- smtp{% endif %} {% if RUN_SMTP %}- smtp{% endif %}
{{ patch("local-docker-compose-lms-dependencies")|indent(6) }} {{ patch("local-docker-compose-lms-dependencies")|indent(6) }}
lms-permissions:
image: {{ DOCKER_IMAGE_PERMISSIONS }}
command: ["1000", "/openedx/data", "/openedx/media"]
restart: on-failure
volumes:
- ../../data/redis:/openedx/redis/data
- ../../data/lms:/openedx/data
- ../../data/openedx-media:/openedx/media
{% endif %} {% endif %}
{% if RUN_CMS %} {% if RUN_CMS %}
@ -115,6 +162,7 @@ services:
- ../../data/cms:/openedx/data - ../../data/cms:/openedx/data
- ../../data/openedx-media:/openedx/media - ../../data/openedx-media:/openedx/media
depends_on: depends_on:
- cms-permissions
{% if RUN_MYSQL %}- mysql{% endif %} {% if RUN_MYSQL %}- mysql{% endif %}
{% if RUN_ELASTICSEARCH %}- elasticsearch{% endif %} {% if RUN_ELASTICSEARCH %}- elasticsearch{% endif %}
{% if RUN_MONGODB %}- mongodb{% endif %} {% if RUN_MONGODB %}- mongodb{% endif %}
@ -122,6 +170,14 @@ services:
{% if RUN_SMTP %}- smtp{% endif %} {% if RUN_SMTP %}- smtp{% endif %}
{% if RUN_LMS %}- lms{% endif %} {% if RUN_LMS %}- lms{% endif %}
{{ patch("local-docker-compose-cms-dependencies")|indent(6) }} {{ patch("local-docker-compose-cms-dependencies")|indent(6) }}
cms-permissions:
image: {{ DOCKER_IMAGE_PERMISSIONS }}
command: ["1000", "/openedx/data", "/openedx/media"]
restart: on-failure
volumes:
- ../../data/redis:/openedx/redis/data
- ../../data/cms:/openedx/data
- ../../data/openedx-media:/openedx/media
{% endif %} {% endif %}
############# LMS and CMS workers ############# LMS and CMS workers
@ -132,7 +188,6 @@ services:
environment: environment:
SERVICE_VARIANT: lms SERVICE_VARIANT: lms
SETTINGS: ${TUTOR_EDX_PLATFORM_SETTINGS:-tutor.production} SETTINGS: ${TUTOR_EDX_PLATFORM_SETTINGS:-tutor.production}
C_FORCE_ROOT: "1" # run celery tasks as root #nofear
command: celery worker --app=lms.celery --loglevel=info --hostname=edx.lms.core.default.%%h --maxtasksperchild=100 --exclude-queues=edx.cms.core.default command: celery worker --app=lms.celery --loglevel=info --hostname=edx.lms.core.default.%%h --maxtasksperchild=100 --exclude-queues=edx.cms.core.default
restart: unless-stopped restart: unless-stopped
volumes: volumes:
@ -151,7 +206,6 @@ services:
environment: environment:
SERVICE_VARIANT: cms SERVICE_VARIANT: cms
SETTINGS: ${TUTOR_EDX_PLATFORM_SETTINGS:-tutor.production} SETTINGS: ${TUTOR_EDX_PLATFORM_SETTINGS:-tutor.production}
C_FORCE_ROOT: "1" # run celery tasks as root #nofear
command: celery worker --app=cms.celery --loglevel=info --hostname=edx.cms.core.default.%%h --maxtasksperchild 100 --exclude-queues=edx.lms.core.default command: celery worker --app=cms.celery --loglevel=info --hostname=edx.cms.core.default.%%h --maxtasksperchild 100 --exclude-queues=edx.lms.core.default
restart: unless-stopped restart: unless-stopped
volumes: volumes: