@@ -82,6 +82,15 @@ ENV TZ=UTC
8282ENV LANG=C.UTF-8
8383ENV LC_ALL=C.UTF-8
8484
85+ # Runtime user identity (override at build time if host volume mapping needs it).
86+ # These are preferred IDs, not strict requirements:
87+ # - If OPENCODER_GID is already occupied, we create `opencoder` with the next free GID.
88+ # - If OPENCODER_UID is already occupied, we create `opencoder` with the next free UID.
89+ # This avoids collisions with base-image users (for example Ubuntu's default uid/gid 1000)
90+ # while still allowing operators to request specific IDs when available.
91+ ARG OPENCODER_UID=1000
92+ ARG OPENCODER_GID=1000
93+
8594# -----------------------------------------------------------------------------
8695# System Dependencies
8796# -----------------------------------------------------------------------------
@@ -182,10 +191,29 @@ RUN --mount=type=cache,target=/var/lib/apt/lists \
182191# -----------------------------------------------------------------------------
183192# Create Non-Root User
184193# -----------------------------------------------------------------------------
185- # Create 'opencode' user with passwordless sudo
186- RUN useradd -m -s /bin/bash -G sudo opencoder \
194+ # Create 'opencoder' user with passwordless sudo
195+ RUN set -eux; \
196+ # Create group first. Prefer requested GID; fall back if already in use. \
197+ if ! getent group opencoder >/dev/null; then \
198+ if getent group "${OPENCODER_GID}" >/dev/null; then \
199+ groupadd opencoder; \
200+ else \
201+ groupadd --gid "${OPENCODER_GID}" opencoder; \
202+ fi; \
203+ fi; \
204+ # Create user next. Prefer requested UID; fall back if already in use. \
205+ if ! id -u opencoder >/dev/null 2>&1; then \
206+ if getent passwd "${OPENCODER_UID}" >/dev/null; then \
207+ useradd -m -s /bin/bash --gid opencoder -G sudo opencoder; \
208+ else \
209+ useradd -m -s /bin/bash --uid "${OPENCODER_UID}" --gid opencoder -G sudo opencoder; \
210+ fi; \
211+ fi; \
212+ # Always pin the primary group to `opencoder` in case the user pre-existed. \
213+ usermod --gid opencoder opencoder \
187214 && echo "opencoder ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/opencoder \
188215 && chmod 0440 /etc/sudoers.d/opencoder \
216+ && chmod 0750 /home/opencoder \
189217 # Snapshot built-in home users from the image so runtime auth logic can
190218 # ignore defaults (e.g. ubuntu) when deciding if onboarding should run.
191219 && install -d -m 0755 /etc/opencode-cloud \
@@ -726,18 +754,14 @@ ENV PATH="/opt/opencode/bin:${PATH}"
726754# This allows opencode to authenticate users via PAM (same users as Cockpit)
727755# NOTE: Requires root privileges to write to /etc/pam.d/
728756USER root
729- COPY packages/core/src/docker/files/pam/opencode /etc/pam.d/opencode
730- RUN chmod 644 /etc/pam.d/opencode
731-
732- # Verify PAM config file exists
733- RUN ls -la /etc/pam.d/opencode && cat /etc/pam.d/opencode
757+ COPY --chown=root:root --chmod=0644 packages/core/src/docker/files/pam/opencode /etc/pam.d/opencode
734758
735759# -----------------------------------------------------------------------------
736760# opencode-broker systemd Service
737761# -----------------------------------------------------------------------------
738762# Create opencode-broker service for PAM authentication
739763# NOTE: Requires root privileges to write to /etc/systemd/system/
740- COPY packages/core/src/docker/files/opencode-broker.service /etc/systemd/system/opencode-broker.service
764+ COPY --chown=root:root --chmod=0644 packages/core/src/docker/files/opencode-broker.service /etc/systemd/system/opencode-broker.service
741765
742766# Enable opencode-broker service
743767RUN mkdir -p /etc/systemd/system/multi-user.target.wants \
@@ -748,7 +772,7 @@ RUN mkdir -p /etc/systemd/system/multi-user.target.wants \
748772# -----------------------------------------------------------------------------
749773# Create opencode as a systemd service for Cockpit integration
750774# NOTE: Requires root privileges to write to /etc/systemd/system/
751- COPY packages/core/src/docker/files/opencode.service /etc/systemd/system/opencode.service
775+ COPY --chown=root:root --chmod=0644 packages/core/src/docker/files/opencode.service /etc/systemd/system/opencode.service
752776
753777# Enable opencode service to start at boot (manual symlink since systemctl doesn't work during build)
754778RUN mkdir -p /etc/systemd/system/multi-user.target.wants \
@@ -758,33 +782,25 @@ RUN mkdir -p /etc/systemd/system/multi-user.target.wants \
758782# opencode Configuration
759783# -----------------------------------------------------------------------------
760784# Create opencode.jsonc config file with PAM authentication enabled
761- RUN mkdir -p /home/opencoder/.config/opencode
762- COPY --chown=opencoder:opencoder packages/core/src/docker/files/opencode.jsonc /home/opencoder/.config/opencode/opencode.jsonc
763- RUN chown -R opencoder:opencoder /home/opencoder/.config/opencode \
764- && chmod 644 /home/opencoder/.config/opencode/opencode.jsonc
765-
766- # Verify config file exists
767- RUN ls -la /home/opencoder/.config/opencode/opencode.jsonc && cat /home/opencoder/.config/opencode/opencode.jsonc
785+ RUN install -d -o opencoder -g opencoder -m 0750 /home/opencoder/.config/opencode
786+ COPY --chown=opencoder:opencoder --chmod=0640 packages/core/src/docker/files/opencode.jsonc /home/opencoder/.config/opencode/opencode.jsonc
768787
769788# -----------------------------------------------------------------------------
770789# Entrypoint Script (Hybrid Init Support)
771790# -----------------------------------------------------------------------------
772791# Supports both tini (default, works everywhere) and systemd (for Cockpit on Linux)
773792# Set USE_SYSTEMD=1 environment variable to use systemd init
774793# Note: Entrypoint runs as root to support both modes; tini mode drops to opencode user
775- COPY packages/core/src/docker/files/entrypoint.sh /usr/local/bin/entrypoint.sh
776- RUN chmod +x /usr/local/bin/entrypoint.sh
794+ COPY --chown=root:root --chmod=0755 packages/core/src/docker/files/entrypoint.sh /usr/local/bin/entrypoint.sh
777795
778796# Bootstrap helper for first-user onboarding (invoked by entrypoint and auth route via sudo)
779- COPY packages/core/src/docker/files/opencode-cloud-bootstrap.sh /usr/local/bin/opencode-cloud-bootstrap
780- RUN chmod 700 /usr/local/bin/opencode-cloud-bootstrap
797+ COPY --chown=root:root --chmod=0700 packages/core/src/docker/files/opencode-cloud-bootstrap.sh /usr/local/bin/opencode-cloud-bootstrap
781798
782799# Note: Don't set USER here - entrypoint needs root to use runuser
783800# The tini mode drops privileges to opencode user via runuser
784801
785802# Healthcheck script asset
786- COPY packages/core/src/docker/files/healthcheck.sh /usr/local/bin/healthcheck.sh
787- RUN chmod +x /usr/local/bin/healthcheck.sh
803+ COPY --chown=root:root --chmod=0755 packages/core/src/docker/files/healthcheck.sh /usr/local/bin/healthcheck.sh
788804
789805# -----------------------------------------------------------------------------
790806# Health Check
@@ -800,8 +816,10 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
800816COPY --from=opencode-build /opt/opencode /opt/opencode
801817COPY --from=opencode-build /usr/local/bin/opencode-broker /usr/local/bin/opencode-broker
802818
803- RUN chown -R opencoder:opencoder /opt/opencode \
819+ RUN chown -R root:root /opt/opencode \
820+ && chmod -R go-w /opt/opencode \
804821 && chmod +x /opt/opencode/bin/opencode \
822+ && chown root:root /usr/local/bin/opencode-broker \
805823 && chmod 4755 /usr/local/bin/opencode-broker
806824
807825# Verify broker binary exists and is executable
0 commit comments