diff --git a/workbench/template/Containerfile.ubuntu2204.jinja2 b/workbench/template/Containerfile.ubuntu2204.jinja2 index 1cfad95..6b812eb 100644 --- a/workbench/template/Containerfile.ubuntu2204.jinja2 +++ b/workbench/template/Containerfile.ubuntu2204.jinja2 @@ -34,6 +34,25 @@ ENV PWB_DIAGNOSTIC_DIR=/var/log/rstudio ENV PWB_DIAGNOSTIC_ENABLE=false ENV PWB_EXIT_AFTER_VERIFY=false +{% if Image.IsDevelopmentVersion %} +### Pre-create rstudio-server user so deb postinst finds it and UID/GID stays 999 across image rebuilds ### +# Idempotent so a cached layer (or the deb postinst) that already created the +# account does not fail the build. +RUN if ! getent group rstudio-server >/dev/null; then \ + groupadd --system --gid 999 rstudio-server; \ + fi \ + && if ! getent passwd rstudio-server >/dev/null; then \ + useradd --system --uid 999 --gid rstudio-server \ + --no-create-home --home-dir /var/lib/rstudio-server \ + --shell /usr/sbin/nologin \ + rstudio-server; \ + fi \ + && if [ "$(id -u rstudio-server)" != 999 ] || [ "$(id -g rstudio-server)" != 999 ]; then \ + echo "ERROR: rstudio-server must be uid/gid 999, got uid=$(id -u rstudio-server) gid=$(id -g rstudio-server)" >&2; \ + exit 1; \ + fi + +{% endif %} ### Setup environment ### {{ apt.run_setup() }} @@ -94,7 +113,23 @@ COPY "{{ Path.Version }}/conf/jupyter/*" "{{ Path.Version }}/conf/launcher/*" "{ ### Configure Workbench ### RUN mkdir -p /var/lib/rstudio-server/monitor/log \ +{% if Image.IsDevelopmentVersion %} + /var/lib/rstudio-server/conf \ + /var/lib/rstudio-server/body \ + /var/lib/rstudio-server/proxy \ + /var/lib/rstudio-launcher \ + /var/log/rstudio \ + /var/run/supervisor \ + && chown -R rstudio-server:rstudio-server \ + /var/lib/rstudio-server \ + /var/lib/rstudio-launcher \ + /var/log/rstudio \ + /var/run/supervisor \ + && chmod -R g+w /var/lib/rstudio-server /var/lib/rstudio-launcher /var/log/rstudio /var/run/supervisor \ + && find /var/lib/rstudio-server /var/lib/rstudio-launcher /var/log/rstudio /var/run/supervisor -type d -exec chmod g+s {} + \ +{% else %} && chown -R rstudio-server:rstudio-server /var/lib/rstudio-server/monitor \ +{% endif %} && mkdir -p /startup/custom/ \ && mkdir -p /startup/user-provisioning/ \ && printf '\n# allow home directory creation\nsession required pam_mkhomedir.so skel=/etc/skel umask=0077\n' >> /etc/pam.d/common-session \ diff --git a/workbench/template/Containerfile.ubuntu2404.jinja2 b/workbench/template/Containerfile.ubuntu2404.jinja2 index eaa3bb7..c205cee 100644 --- a/workbench/template/Containerfile.ubuntu2404.jinja2 +++ b/workbench/template/Containerfile.ubuntu2404.jinja2 @@ -34,6 +34,25 @@ ENV PWB_DIAGNOSTIC_DIR=/var/log/rstudio ENV PWB_DIAGNOSTIC_ENABLE=false ENV PWB_EXIT_AFTER_VERIFY=false +{% if Image.IsDevelopmentVersion %} +### Pre-create rstudio-server user so deb postinst finds it and UID/GID stays 999 across image rebuilds ### +# Idempotent so a cached layer (or the deb postinst) that already created the +# account does not fail the build. +RUN if ! getent group rstudio-server >/dev/null; then \ + groupadd --system --gid 999 rstudio-server; \ + fi \ + && if ! getent passwd rstudio-server >/dev/null; then \ + useradd --system --uid 999 --gid rstudio-server \ + --no-create-home --home-dir /var/lib/rstudio-server \ + --shell /usr/sbin/nologin \ + rstudio-server; \ + fi \ + && if [ "$(id -u rstudio-server)" != 999 ] || [ "$(id -g rstudio-server)" != 999 ]; then \ + echo "ERROR: rstudio-server must be uid/gid 999, got uid=$(id -u rstudio-server) gid=$(id -g rstudio-server)" >&2; \ + exit 1; \ + fi + +{% endif %} ### Setup environment ### {{ apt.run_setup() }} @@ -94,7 +113,23 @@ COPY "{{ Path.Version }}/conf/jupyter/*" "{{ Path.Version }}/conf/launcher/*" "{ ### Configure Workbench ### RUN mkdir -p /var/lib/rstudio-server/monitor/log \ +{% if Image.IsDevelopmentVersion %} + /var/lib/rstudio-server/conf \ + /var/lib/rstudio-server/body \ + /var/lib/rstudio-server/proxy \ + /var/lib/rstudio-launcher \ + /var/log/rstudio \ + /var/run/supervisor \ + && chown -R rstudio-server:rstudio-server \ + /var/lib/rstudio-server \ + /var/lib/rstudio-launcher \ + /var/log/rstudio \ + /var/run/supervisor \ + && chmod -R g+w /var/lib/rstudio-server /var/lib/rstudio-launcher /var/log/rstudio /var/run/supervisor \ + && find /var/lib/rstudio-server /var/lib/rstudio-launcher /var/log/rstudio /var/run/supervisor -type d -exec chmod g+s {} + \ +{% else %} && chown -R rstudio-server:rstudio-server /var/lib/rstudio-server/monitor \ +{% endif %} && mkdir -p /startup/custom/ \ && mkdir -p /startup/user-provisioning/ \ && printf '\n# allow home directory creation\nsession required pam_mkhomedir.so skel=/etc/skel umask=0077\n' >> /etc/pam.d/common-session \ diff --git a/workbench/template/startup/supervisord.conf.jinja2 b/workbench/template/startup/supervisord.conf.jinja2 index 7180a82..0eb6b0c 100644 --- a/workbench/template/startup/supervisord.conf.jinja2 +++ b/workbench/template/startup/supervisord.conf.jinja2 @@ -1,13 +1,22 @@ ; supervisor config file +{%- if Image.IsDevelopmentVersion %} +{%- set supervisor_socket = "/var/run/supervisor/supervisor.sock" %} +{%- set supervisor_pidfile = "/var/run/supervisor/supervisord.pid" %} +{%- else %} +{%- set supervisor_socket = "/var/run/supervisor.sock" %} +{%- set supervisor_pidfile = "/var/run/supervisord.pid" %} +{%- endif %} [unix_http_server] -file=/var/run/supervisor.sock ; (the path to the socket file) +file={{ supervisor_socket }} ; (the path to the socket file) chmod=0700 ; sockef file mode (default 0700) [supervisord] logfile=/dev/stdout ; (main log file;default $CWD/supervisord.log) +{%- if not Image.IsDevelopmentVersion %} user=root -pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid) +{%- endif %} +pidfile={{ supervisor_pidfile }} ; (supervisord pidfile;default supervisord.pid) ; should configure each program to use stdout/stderr ; childlogdir=/var/log/supervisor ; ('AUTO' child log dir, default $TEMP) logfile_maxbytes=0 @@ -22,7 +31,7 @@ nodaemon=true supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface [supervisorctl] -serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket +serverurl=unix://{{ supervisor_socket }} ; use a unix:// URL for a unix socket ; The [include] section can just contain the "files" setting. This ; setting can list multiple files (separated by whitespace or diff --git a/workbench/template/test/goss.yaml.jinja2 b/workbench/template/test/goss.yaml.jinja2 index 11f3b3f..62a5078 100644 --- a/workbench/template/test/goss.yaml.jinja2 +++ b/workbench/template/test/goss.yaml.jinja2 @@ -8,14 +8,14 @@ user: rstudio-server: exists: true uid: 999 - gid: {% raw %}{{ if and (eq .Env.IMAGE_VARIANT "Standard") (and (eq .Env.IMAGE_OS_NAME "ubuntu") (eq .Env.IMAGE_OS_VERSION "24.04")) }}997{{ else }}999{{ end }}{% endraw %} + gid: {% if Image.IsDevelopmentVersion %}999{% else %}{% raw %}{{ if and (eq .Env.IMAGE_VARIANT "Standard") (and (eq .Env.IMAGE_OS_NAME "ubuntu") (eq .Env.IMAGE_OS_VERSION "24.04")) }}997{{ else }}999{{ end }}{% endraw %}{% endif %} groups: - rstudio-server group: rstudio-server: exists: true - gid: {% raw %}{{ if and (eq .Env.IMAGE_VARIANT "Standard") (and (eq .Env.IMAGE_OS_NAME "ubuntu") (eq .Env.IMAGE_OS_VERSION "24.04")) }}997{{ else }}999{{ end }}{% endraw %} + gid: {% if Image.IsDevelopmentVersion %}999{% else %}{% raw %}{{ if and (eq .Env.IMAGE_VARIANT "Standard") (and (eq .Env.IMAGE_OS_NAME "ubuntu") (eq .Env.IMAGE_OS_VERSION "24.04")) }}997{{ else }}999{{ end }}{% endraw %}{% endif %} package: rstudio-server: @@ -82,11 +82,54 @@ file: # Check for rstudio-launcher executable /usr/lib/rstudio-server/bin/rstudio-launcher: exists: true + {%- if Image.IsDevelopmentVersion %} + # Check for rstudio-server runtime directories with correct ownership and setgid + /var/lib/rstudio-server: + exists: true + owner: rstudio-server + group: rstudio-server + mode: "2775" + /var/lib/rstudio-server/monitor/log: + exists: true + owner: rstudio-server + group: rstudio-server + /var/lib/rstudio-server/conf: + exists: true + owner: rstudio-server + group: rstudio-server + /var/lib/rstudio-server/body: + exists: true + owner: rstudio-server + group: rstudio-server + /var/lib/rstudio-server/proxy: + exists: true + owner: rstudio-server + group: rstudio-server + # rstudio-launcher resets its scratch directory to 0755 on startup, so we + # only assert ownership here; the build-time setgid mode does not survive. + /var/lib/rstudio-launcher: + exists: true + owner: rstudio-server + group: rstudio-server + /var/log/rstudio: + exists: true + owner: rstudio-server + group: rstudio-server + mode: "2775" + /var/run/rstudio-server: + exists: true + /var/run/supervisor: + exists: true + owner: rstudio-server + group: rstudio-server + mode: "2775" + {%- else %} # Check for rstudio-server monitor log directory /var/lib/rstudio-server/monitor/log: exists: true owner: rstudio-server group: rstudio-server + {%- endif %} # Check for code-server executable (path varies based on RStudio Workbench version) {{ '{{ $version_split := split "." "' | safe }}{{ Image.Version }}{{ '" }}' | safe }} {% raw -%} @@ -186,7 +229,7 @@ file: command: "Ensure rstudio-server has permissions to log directory": - exec: su rstudio-server -c 'touch /var/lib/rstudio-server/monitor/log/rstudio-server.log' + exec: {% if Image.IsDevelopmentVersion %}sudo -u rstudio-server touch /var/lib/rstudio-server/monitor/log/rstudio-server.log{% else %}su rstudio-server -c 'touch /var/lib/rstudio-server/monitor/log/rstudio-server.log'{% endif %} exit-status: 0 "Ensure server log can be created": exec: touch /var/log/rstudio-server.log