Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

breaking!(security): 💥 run as nobody user by default, supports read-only RootFS and control packages content #17

Merged
merged 11 commits into from
Sep 1, 2023

Conversation

dduportal
Copy link
Contributor

@dduportal dduportal commented Sep 1, 2023

While working on jenkins-infra/helpdesk#2649, I realized that this image was long forgotten.

I'm opening this PR to start a (long term) work on improving security of our containers in production.

This is a first tentative, still a lot of work (documenting and explaining practises, adding more testing, etc.). The goal is to deliver this new image version on the (not yet used) rsyncd service deployed for the new update center in jenkins-infra/helpdesk#2649.

It introduces the following changes:

  • feat: Pinned the Debian version used as base image. We do not want surprise updates which would change the behavior of the image without testing it.

    • ⚠️ a subsequent PR with updatecli is required to track this version
  • nitpick: Improved the build efficiency (speed and caching) by moving metadatas (EXPOSE, VOLUME etc.) at the end

  • fix: Allow building on arm64 (initially for macOS Silicon Docker Desktop but also for Proposal for application in publick8s to migrate to arm64 helpdesk#3619) by installing tini inside a RUN instruction where it is easier to determine the target architecture with the exact value (in the case of tini it is either arm64 or amd64 while the TARGETPLARFORM build argument would have been linux/aarch64 or linux/arm64 or linux/amd64).

  • feat: Decrease attack vectors by removing unused packages

  • 💥 (breaking) Allow image to run with a read-only rootfs by defining volumes where writing is required by default

    • Required rsyncd configuration changes: see below
  • 💥 (breaking) set default container user as nobody:nogroup (65534:65534 with numerical UID and GID)

    • Required rsyncd configuration changes: see below
    • default data volume is now /rsyncd/data and is owned by the default user nobody:nogroup
    • default PID file for the rsyncd daemon is now is the (new) volume /rsyncd/run and is owned by the default user nobody:nogroup
  • 💥 (breaking) : Changed the default rsyncd configuration:

    • Do not specify uid and gid as the default user executing the rsyncd process is restricted and not allowed to change uid of gid (kernel restriction)
    • Do not enable chroot as the default user executing the rsyncd process is restricted and not allowed to clone namespace (required by chroot) neither allowed to change uid or gid
    • Added extension points in /etc/rsyncd with &include and &merge directives to allow consumer to specify partial configurations for modules ("include") and to eventually override the main directives without having to rewrite the whole top level file ("merge"). This trick not only allows easier configurations in helm charts, but it also enabled the "non root" default user without risk of complex breakages (such as /etc/rsyncd not readabale)
    • Removed the default jenkins.motd, the whole "read only" setup and the "hosts allow" => it has to be specified in a "merge" (/etc/rsyncd.d/*.inc) partial configuration file
    • Removed the default "jenkins" module => it has to be specified in an "include" (/etc/rsyncd.d/*.conf) partial configuration file
    • PID file is now /rsyncd/run/rsyncd.pid but can be changed with a "merge" partialc configuration file (allows non root user in a read-only rootfs)
  • 🛠️ Tests: added 2 kinds of tests to ensure the above changes are present and protect from further regressions

    • Automated container unit test with a cst.yml file
      • ⚠️ A further PR on the tini updatecli manifest is required to update the new cst.yml file along the Dockerfile
    • Manual acceptance tests with a (documented) docker-compose.yml file which builds an run the image in read only + not root example reproductible scenario
      • ⚠️ Automating this one should require more work on the jenkins-infra/pipeline-library. I am thinking in adding goss (with its wrappers dgoss, dcgoss and kgoss) but this manual acceptance is sufficient as the next step will be the helm chart!

… over versions)

Signed-off-by: Damien Duportal <damien.duportal@gmail.com>
… the bottom of Dockerfile

Signed-off-by: Damien Duportal <damien.duportal@gmail.com>
Signed-off-by: Damien Duportal <damien.duportal@gmail.com>
Signed-off-by: Damien Duportal <damien.duportal@gmail.com>
Signed-off-by: Damien Duportal <damien.duportal@gmail.com>
Signed-off-by: Damien Duportal <damien.duportal@gmail.com>
Signed-off-by: Damien Duportal <damien.duportal@gmail.com>
Signed-off-by: Damien Duportal <damien.duportal@gmail.com>
@dduportal dduportal changed the title Feat/improve sec breaking!(security): 💥 run as nobody user by default, supports read-only RootFS and control packages content Sep 1, 2023
@dduportal dduportal marked this pull request as ready for review September 1, 2023 11:15
@dduportal dduportal added documentation Improvements or additions to documentation enhancement New feature or request breaking security labels Sep 1, 2023
Signed-off-by: Damien Duportal <damien.duportal@gmail.com>
Signed-off-by: Damien Duportal <damien.duportal@gmail.com>
Signed-off-by: Damien Duportal <damien.duportal@gmail.com>
@dduportal dduportal merged commit fcb867a into jenkins-infra:main Sep 1, 2023
1 check passed
@dduportal dduportal deleted the feat/improve-sec branch September 1, 2023 16:25
Comment on lines +16 to +25
ARG RSYNCD_DIR=/rsyncd
ENV RSYNCD_DIR="${RSYNCD_DIR}"
RUN mkdir -p "${RSYNCD_DIR}/run" "${RSYNCD_DIR}/data" /etc/rsyncd.d && \
chown -R nobody:nogroup "${RSYNCD_DIR}"

COPY rsyncd.conf /etc/rsyncd.conf

WORKDIR /rsyncd/data

VOLUME ["/rsyncd/run","/rsyncd/data","/tmp"]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Post-review: the build arg RSYNCD_DIR is not valuable since it cannot be consumed by the VOLUME:

  • either find a way to consume it for the WORKDIR and VOLUME to avoid repetition (I failed with multi platform builds while it works on single platform: smells like buildx issue)
  • or do not complexify code and duplicate paths


CMD [ "/usr/bin/rsync","--no-detach","--daemon","--config","/etc/rsyncd.conf" ]
CMD ["/usr/bin/rsync", "--no-detach","--daemon","--config","/etc/rsyncd.conf"]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Post-review: incoherent formatting with spaces

Comment on lines +24 to +28

# Merge any /etc/rsyncd.d/*.inc files (for global values that should stay in effect),
&merge /etc/rsyncd.d
# And then include any /etc/rsyncd.d/*.conf files (defining modules without any global-value cross-talk).
&include /etc/rsyncd.d
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Post review: add a comment about the & prefixes (link to rsyncd.conf reference?)

@@ -0,0 +1,15 @@
# Start this "lightly hardened" test with the command "docker compose up -d --build"
# Then check the result with the command "rsync -av rsync://localhost:1873/jenkins"
Copy link
Contributor Author

@dduportal dduportal Sep 4, 2023

Choose a reason for hiding this comment

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

Post review: long flags

Suggested change
# Then check the result with the command "rsync -av rsync://localhost:1873/jenkins"
# Then check the result with the command "rsync --archive --verbose rsync://localhost:1873/jenkins"

@@ -0,0 +1,3 @@
[jenkins]
path = /rsyncd/data/jenkins
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Post-review: comment why the subdir (ref. stanza name which is an rsync module)

- ./rsyncd.d:/etc/rsyncd.d:ro
- ./jenkins.motd:/etc/jenkins.motd:ro
ports:
- 1873:873
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Post review: note about why a >1024 port in this test case

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking documentation Improvements or additions to documentation enhancement New feature or request security
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant