Privilege escalation (Docker container runtime hardening)
Description
Summary of the change: The commit hardens the Dockerfile template and related test fixture to run the Rails application in a non-root context and to ensure runtime files are owned by a non-root user. Specifically:
- The Dockerfile template now creates a non-root user (rails) and sets USER rails:rails for the runtime stage.
- COPY commands in the final image use explicit ownership (COPY --from=build --chown=rails:rails).
- The test fixture Dockerfile now mirrors the non-root runtime setup and adjusts file ownership accordingly.
What this fixes: Running containers as root inside a production image is a common security risk, as root inside a container can potentially be abused to escalate privileges within the container, or to exploit host/kernel features if combined with other misconfigurations. By ensuring the runtime process runs as a non-root user and by binding file ownership to that user, the surface area for privilege escalation is reduced when using Rails’ Dockerfile templates.
Scope and impact: This affects applications scaffolded via Rails generators that include the Dockerfile template (railties/lib/rails/generators/rails/app/templates/Dockerfile.tt) and the related test fixtures (railties/test/fixtures/Dockerfile.test). Since the tracked version is 8.1.3, prior versions (< 8.1.3) would benefit from this hardening if they used the affected templates. It is a security-hardening improvement rather than a change in Rails core functionality.
Assessment: This is a genuine hardening change that mitigates privilege-escalation risk in containerized deployments by avoiding root execution in the runtime image and by tightening file ownership. It is not a behavioral security bug in Rails’ runtime logic, but it aligns with best practices for container security. The change is not a pure dependency bump; it alters template/fixture code to improve security posture.
Commit Details
Author: Jean Boussier
Date: 2026-03-13 08:42 UTC
Message:
Merge pull request #56967 from damln/optimize-dockerfile-build-performance
Optimize generated Dockerfile build performance
Triage Assessment
Vulnerability Type: Privilege escalation
Confidence: HIGH
Reasoning:
The changes introduce non-root runtime usage for Docker image (setting USER rails:rails) and adjust file ownership via COPY --chown. These harden the image against privilege escalation and reduce risk of file permission issues, which are common security-hardening improvements in container builds.
Verification Assessment
Vulnerability Type: Privilege escalation (Docker container runtime hardening)
Confidence: HIGH
Affected Versions: < 8.1.3
Code Diff
diff --git a/railties/lib/rails/generators/rails/app/templates/Dockerfile.tt b/railties/lib/rails/generators/rails/app/templates/Dockerfile.tt
index 8fce7d5510720..1747c3bfe628a 100644
--- a/railties/lib/rails/generators/rails/app/templates/Dockerfile.tt
+++ b/railties/lib/rails/generators/rails/app/templates/Dockerfile.tt
@@ -97,13 +97,14 @@ RUN bundle exec bootsnap precompile -j 1 app/ lib/
<% end -%>
<% unless options.api? || skip_asset_pipeline? -%>
# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
+<% if using_node? || using_bun? -%>
+RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile && \
+ rm -rf node_modules
+<% else -%>
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
-
<% end -%>
-<% if using_node? || using_bun? -%>
-RUN rm -rf node_modules
-<% end %>
+<% end -%>
# Final stage for app image
FROM base
diff --git a/railties/test/fixtures/Dockerfile.test b/railties/test/fixtures/Dockerfile.test
index 82c6d625fd1aa..0018577b5736e 100644
--- a/railties/test/fixtures/Dockerfile.test
+++ b/railties/test/fixtures/Dockerfile.test
@@ -45,15 +45,14 @@ FROM base
# Clean up installation packages to reduce image size
RUN rm -rf /var/lib/apt/lists /var/cache/apt/archives
-# Copy built artifacts: gems, application
-COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
-COPY --from=build /rails /rails
-
# Run and own only the runtime files as a non-root user for security
-RUN useradd rails --create-home --shell /bin/bash && \
- chown -R rails:rails db log storage tmp
+RUN useradd rails --create-home --shell /bin/bash
USER rails:rails
+# Copy built artifacts: gems, application
+COPY --chown=rails:rails --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
+COPY --chown=rails:rails --from=build /rails /rails
+
# Entrypoint prepares the database.
ENTRYPOINT ["/rails/bin/docker-entrypoint"]