Skip to content

Refactor dev container to run without root#1075

Merged
bmbouter merged 2 commits intomainfrom
rootless-dev-container
Apr 21, 2026
Merged

Refactor dev container to run without root#1075
bmbouter merged 2 commits intomainfrom
rootless-dev-container

Conversation

@bmbouter
Copy link
Copy Markdown
Member

@bmbouter bmbouter commented Apr 21, 2026

Summary

Refactors the dev container (ghcr.io/pulp/hosted-pulp-dev-env) to run entirely as a non-root user, making it compatible with OpenShift's restricted-v2 SCC and Kubernetes runAsNonRoot: true security contexts.

Changes

Dockerfile:

  • PostgreSQL initialized as pulp user (not postgres) — PostgreSQL only requires PGDATA ownership, not a specific OS user
  • All runtime directories (/var/run/postgresql, /var/lib/pgsql, /var/log/pulp, /usr/local/lib/pulp, /etc/pulp) owned by pulp at build time
  • Supervisord PID/socket moved to /var/run/supervisord/ (owned by pulp)
  • Final USER 700 directive — container runs as non-root by default
  • /workspace set to chmod 777 for arbitrary UID compatibility

entrypoint.sh:

  • Removed all runuser calls — PostgreSQL, migrations, and admin password all run directly as the current user
  • Database creation uses psql -d postgres (current user is the DB superuser since they own PGDATA)

supervisord.conf:

  • Removed user=root from [supervisord] section
  • Removed user=postgres from [program:postgresql] — runs as current user
  • Removed user=pulp from Pulp service programs — already running as pulp
  • PID/socket paths moved to /var/run/supervisord/

Why

Alcove dispatches dev containers as Kubernetes sidecars. On OpenShift (ROSA), the restricted-v2 SCC enforces runAsNonRoot: true and assigns arbitrary UIDs from the namespace range. The previous dev container required root for runuser calls, causing it to crash with "runuser: may not be used by non-root users".

Testing

Once the CI builds the image from this branch, verify with:

# Run as non-root (UID 700)
podman run --rm --user 700 ghcr.io/pulp/hosted-pulp-dev-env:rootless-dev-container

# Run with arbitrary UID (simulates OpenShift)
podman run --rm --user 1000830000 ghcr.io/pulp/hosted-pulp-dev-env:rootless-dev-container

# Verify the API responds
curl http://localhost:24817/pulp/api/v3/status/

Summary by Sourcery

Refactor the dev container to run entirely as a non-root user while remaining compatible with PostgreSQL, Redis, and supervisord orchestration.

Enhancements:

  • Run PostgreSQL, Redis, and Pulp services as the current non-root user instead of switching users at runtime.
  • Initialize PostgreSQL data directories as the pulp user and ensure all runtime paths are owned or writable by pulp for rootless operation.
  • Adjust supervisord configuration to remove explicit user directives and relocate PID/socket files into a pulp-writable runtime directory.
  • Configure the container image to default to a non-root user ID and make the workspace directory compatible with arbitrary UIDs.

All services now run as the pulp user (UID 700) instead of requiring
root for multi-user process management:

- PostgreSQL: initialized and run as pulp (not postgres). PostgreSQL
  only requires PGDATA ownership, not a specific OS user.
- Redis: runs as pulp (no user= directive in supervisord)
- Supervisord: runs as pulp (removed user=root), PID/socket files
  moved to /var/run/supervisord/ owned by pulp
- Entrypoint: removed all runuser/su calls, commands run directly
  as the current user
- Dockerfile: USER 700 at the end, all runtime directories owned
  by pulp at build time

This makes the dev container compatible with OpenShift's restricted-v2
SCC which enforces runAsNonRoot and arbitrary UID assignment.
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Apr 21, 2026

Reviewer's Guide

Refactors the dev container image so it runs entirely as a non-root user (UID 700 or arbitrary UID), by changing PostgreSQL initialization, ownership of runtime directories, the Dockerfile user, entrypoint logic, and supervisord configuration to eliminate root- and postgres-specific assumptions.

Sequence diagram for rootless dev container startup and initialization

sequenceDiagram
    participant Dev as Developer
    participant Kube as KubernetesPod
    participant C as DevContainer
    participant E as Entrypoint
    participant PG as PostgreSQL
    participant RD as Redis
    participant PM as PulpcoreManager
    participant S as Supervisord
    participant Svc as PulpServices

    Dev->>Kube: create Pod with runAsNonRoot true
    Kube->>C: start container (uid 700 or arbitrary)
    C->>E: execute /entrypoint.sh

    E->>PG: pg_ctl start using PGDATA owned by current user
    E->>PG: pg_isready (loop until ready)

    E->>PG: psql -d postgres check pulp database
    PG-->>E: database exists or not
    E->>PG: psql -d postgres CREATE DATABASE pulp (if missing)

    E->>RD: redis-server --daemonize yes (runs as current user)

    E->>PM: pulpcore-manager migrate --noinput
    E->>PM: pulpcore-manager reset-admin-password

    E->>PG: pg_ctl stop -m fast -w
    E->>RD: redis-cli shutdown

    E->>S: exec supervisord
    S->>PG: manage postgres program (no user override)
    S->>RD: manage redis program
    S->>Svc: start pulp-api, pulp-content, pulp-worker as current user
    Dev->>Svc: HTTP request to pulp api status
    Svc-->>Dev: status response
Loading

Flow diagram for Dockerfile rootless initialization steps

graph TD
    A["Base image with postgres and pulp installed"] --> B["Create /var/run/postgresql and /var/lib/pgsql/16/data"]
    B --> C["chown to pulp:pulp for PostgreSQL paths"]

    C --> D["Switch to USER pulp:pulp"]
    D --> E["Run initdb on /var/lib/pgsql/16/data"]
    E --> F["Write trust pg_hba.conf entries"]
    F --> G["Switch back to USER root:root"]

    G --> H["chown -R pulp:pulp on runtime directories\n/var/run/postgresql /var/lib/pgsql /var/log/pulp /var/lib/pulp /usr/local/lib/pulp /etc/pulp"]
    H --> I["mkdir -p /var/run/supervisord and chown pulp:pulp"]
    I --> J["chmod 777 /workspace for arbitrary uid write"]

    J --> K["Copy settings, supervisord.conf, entrypoint.sh, scripts"]
    K --> L["chmod +x entrypoint and scripts"]
    L --> M["Final USER 700 for runtime"]
    M --> N["Image ready for non-root execution"]
Loading

File-Level Changes

Change Details Files
Run PostgreSQL and initialization as a non-root, non-postgres user and make DB bootstrap independent of OS-level postgres user.
  • Initialize PGDATA as the pulp user instead of postgres during image build.
  • Run initdb directly as user pulp via USER pulp:pulp in the Dockerfile, then restore USER root for subsequent build steps.
  • Change entrypoint to start and stop PostgreSQL directly via pg_ctl as the current user instead of via runuser -l postgres.
  • Create the pulp database directly as the current DB superuser using psql -d postgres rather than creating a separate pulp DB superuser account.
  • Ensure PG runtime and data directories (/var/run/postgresql, /var/lib/pgsql) are owned by pulp so PostgreSQL can start as non-root.
dev-container/Dockerfile
dev-container/entrypoint.sh
dev-container/supervisord.conf
Ensure the container runs as a non-root user at runtime and works with arbitrary UIDs (OpenShift/Kubernetes runAsNonRoot).
  • Add USER 700 as the final Dockerfile directive so the container defaults to a non-root UID at runtime.
  • Chown all runtime and application directories (/var/run/postgresql, /var/lib/pgsql, /var/log/pulp, /var/lib/pulp, /usr/local/lib/pulp, /etc/pulp) to pulp to allow non-root writes.
  • Set /workspace permissions to 777 so an arbitrary non-root UID can write regardless of its numeric ID.
  • Remove explicit user= entries from supervisord programs so all services inherit the container user rather than assuming root, postgres, or pulp.
dev-container/Dockerfile
dev-container/supervisord.conf
Update process supervision paths and entrypoint behavior to be compatible with rootless execution.
  • Move supervisord pidfile and unix socket to /var/run/supervisord/, ensure the directory exists and is owned by pulp.
  • Update supervisord.conf to reference the new pidfile and socket paths and remove the user=root declaration.
  • Remove runuser -u pulp invocations for pulpcore-manager migration and admin password setup in entrypoint; call pulpcore-manager directly.
  • Adjust logging: keep supervisord log under /var/log/pulp while ensuring directory ownership is compatible with non-root writes.
  • Update startup banner in entrypoint to indicate rootless operation for easier debugging/observability.
dev-container/Dockerfile
dev-container/entrypoint.sh
dev-container/supervisord.conf

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The initialization logic no longer creates the pulp database role and switches to using the current OS user as superuser; double-check that the Django/Pulp DB settings (user/password) are consistent with this change or consider retaining creation of the pulp role for compatibility.
  • The combination of USER 700 in the Dockerfile and chowning runtime dirs to pulp:pulp may not behave as intended when Kubernetes/OpenShift overrides the user to an arbitrary UID; consider aligning ownership/permissions (e.g., using root-owned but group/other-writable dirs) so the container still works when runAsNonRoot forces a different UID.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The initialization logic no longer creates the `pulp` database role and switches to using the current OS user as superuser; double-check that the Django/Pulp DB settings (user/password) are consistent with this change or consider retaining creation of the `pulp` role for compatibility.
- The combination of `USER 700` in the Dockerfile and `chown`ing runtime dirs to `pulp:pulp` may not behave as intended when Kubernetes/OpenShift overrides the user to an arbitrary UID; consider aligning ownership/permissions (e.g., using root-owned but group/other-writable dirs) so the container still works when `runAsNonRoot` forces a different UID.

## Individual Comments

### Comment 1
<location path="dev-container/entrypoint.sh" line_range="34-38" />
<code_context>
+# Run database migrations (already running as pulp)
 echo "Running database migrations..."
-runuser -u pulp -- bash -c 'PATH=/usr/local/lib/pulp/bin:$PATH pulpcore-manager migrate --noinput'
+pulpcore-manager migrate --noinput

 # Set admin password
 echo "Setting admin password..."
-runuser -u pulp -- bash -c "PATH=/usr/local/lib/pulp/bin:\$PATH pulpcore-manager reset-admin-password --password '${PULP_DEFAULT_ADMIN_PASSWORD:-password}'" 2>/dev/null || true
+pulpcore-manager reset-admin-password --password "${PULP_DEFAULT_ADMIN_PASSWORD:-password}" 2>/dev/null || true

 # Stop PostgreSQL and Redis — supervisord will manage them
</code_context>
<issue_to_address>
**issue (bug_risk):** Dropping the explicit PATH override may break `pulpcore-manager` resolution for the pulp user.

We previously ensured `pulpcore-manager` was resolvable by explicitly setting `PATH=/usr/local/lib/pulp/bin:$PATH` in the command. With that removed, the script now assumes `pulpcore-manager` is already on the PATH for the runtime user, which may not hold in minimal images or if the pulp user’s environment changes. Please either reintroduce the explicit PATH prefix or use an absolute path to `pulpcore-manager` to avoid hard-to-diagnose failures.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +34 to +38
pulpcore-manager migrate --noinput

# Set admin password
echo "Setting admin password..."
runuser -u pulp -- bash -c "PATH=/usr/local/lib/pulp/bin:\$PATH pulpcore-manager reset-admin-password --password '${PULP_DEFAULT_ADMIN_PASSWORD:-password}'" 2>/dev/null || true
pulpcore-manager reset-admin-password --password "${PULP_DEFAULT_ADMIN_PASSWORD:-password}" 2>/dev/null || true
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Dropping the explicit PATH override may break pulpcore-manager resolution for the pulp user.

We previously ensured pulpcore-manager was resolvable by explicitly setting PATH=/usr/local/lib/pulp/bin:$PATH in the command. With that removed, the script now assumes pulpcore-manager is already on the PATH for the runtime user, which may not hold in minimal images or if the pulp user’s environment changes. Please either reintroduce the explicit PATH prefix or use an absolute path to pulpcore-manager to avoid hard-to-diagnose failures.

@bmbouter bmbouter merged commit b2f6fd4 into main Apr 21, 2026
5 checks passed
@bmbouter bmbouter deleted the rootless-dev-container branch April 21, 2026 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant