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

Implement dockerPermissionStrategy #1190

Merged
merged 4 commits into from
Jan 24, 2019
Merged

Conversation

eed3si9n
Copy link
Member

@eed3si9n eed3si9n commented Jan 18, 2019

Fixes #1189

This implements a non-root Docker container that's safer by default and compatible with Red Hat OpenShift.

Current ADD --chown=daemon:daemon opt /opt and USER daemon combination nominally implements non-root image, but by giving ownership of the working directory to the daemon user, it reduces the safety.
Instead we should use chmod to default to read-only access unless the build user opts into writable working directory.

The challenge is calling chmod without incurring the fs layer overhead (#883).
Multi-stage builds can be used to pre-stage the files with desired file permissions.

This adds new dockerPermissionStrategy setting which decides how file permissions are set for the working directory inside the Docker image generated by sbt-native-packager. The strategies are:

  • DockerPermissionStrategy.MultiStage (default): uses multi-stage Docker build to call chmod ahead of time.
  • DockerPermissionStrategy.None: does not attempt to change the file permissions, and use the host machine's file mode bits.
  • DockerPermissionStrategy.Run: calls RUN in the Dockerfile. This has regression on the resulting Docker image file size.
  • DockerPermissionStrategy.CopyChown: calls COPY --chown in the Dockerfile. Provided as a backward compatibility.

Working directory is read-only by default

For MultiStage and Run strategies, dockerChmodType is used in addition to call chmod during Docker build.

  • DockerChmodType.UserGroupReadExecute (default): chmod -R u=rX,g=rX
  • DockerChmodType.UserGroupWriteExecute: chmod -R u=rwX,g=rwX
  • DockerChmodType.SyncGroupToUser: chmod -R g=u
  • DockerChmodType.Custom: Custom argument provided by the user.

How to opt-in to writable working directory

Some application will require writing files to the working directory.
In that case the setting should be changed as follows:

import com.typesafe.sbt.packager.docker.DockerChmodType

dockerChmodType := DockerChmodType.UserGroupWriteExecute

Docker package validation

During docker:stage, Docker package validation is called to check if the selected strategy is compatible with the detected Docker version.
This fixes the current repeatability issue reported as #1187. If the incompatibility is detected, the user is advised to either upgrade their Docker, pick another strategy, or override the dockerVersion setting.

Use gid=0

daemonGroup is set to root instead of copying the value from the daemonUser setting.
This matches the semantics of USER as well as OpenShift, which uses gid=0.

@eed3si9n eed3si9n force-pushed the wip/openshift-compat branch 2 times, most recently from d8d8009 to d3e7c6b Compare January 18, 2019 16:54
@muuki88 muuki88 self-requested a review January 22, 2019 16:23
@muuki88 muuki88 added the docker label Jan 22, 2019
@muuki88
Copy link
Contributor

muuki88 commented Jan 22, 2019

Thanks for the pull request @eed3si9n . I'll take a deeper look when I have some time. Hopefully this week.

Fixes sbt#1189

This implements a non-root Docker container that's safer by default and compatible with Red Hat OpenShift.
Current `ADD --chown=daemon:daemon opt /opt` nominally implements non-root image,
but by giving ownership of the working directory to the `daemon` user, it reduces the safety.
Instead we should use `chmod` to default to read-only access unless the build user opts into writable working directory.

The challenge is calling `chmod` without incurring the fs layer overhead (sbt#883).
[Multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/) can be used to pre-stage
the files with desired file permissions.

This adds new `dockerPermissionStrategy` setting which decides how file permissions are set for the working directory inside the Docker image generated by sbt-native-packager. The strategies are:

- `DockerPermissionStrategy.MultiStage` (default): uses multi-stage Docker build to call chmod ahead of time.
- `DockerPermissionStrategy.None`: does not attempt to change the file permissions, and use the host machine's file mode bits.
- `DockerPermissionStrategy.Run`: calls `RUN` in the Dockerfile. This has regression on the resulting Docker image file size.
- `DockerPermissionStrategy.CopyChown`: calls `COPY --chown` in the Dockerfile. Provided as a backward compatibility.

For `MultiStage` and `Run` strategies, `dockerChmodType` is used in addition to call `chmod` during Docker build.

- `DockerChmodType.UserGroupReadExecute` (default): chmod -R u=rX,g=rX
- `DockerChmodType.UserGroupWriteExecute`: chmod -R u=rwX,g=rwX
- `DockerChmodType.SyncGroupToUser`: chmod -R g=u
- `DockerChmodType.Custom`: Custom argument provided by the user.

Some application will require writing files to the working directory.
In that case the setting should be changed as follows:

```scala
import com.typesafe.sbt.packager.docker.DockerChmodType

dockerChmodType := DockerChmodType.UserGroupWriteExecute
```

During `docker:stage`, Docker package validation is called to check if the selected strategy is compatible with the deteted Docker version.
This fixes the current repeatability issue reported as sbt#1187. If the incompatibility is detected, the user is advised to
either upgrade their Docker, pick another strategy, or override the `dockerVersion` setting.

`daemonGroup` is set to `root` instead of copying the value from the `daemonUser` setting.
This matches the semantics of `USER` as well as OpenShift, which uses gid=0.
@eed3si9n eed3si9n force-pushed the wip/openshift-compat branch from 914c277 to 12b5fcb Compare January 22, 2019 18:30
@eed3si9n
Copy link
Member Author

Cool. Let me squash the intermediate commits with failing tests.

Copy link
Contributor

@muuki88 muuki88 left a comment

Choose a reason for hiding this comment

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

Awesome PR @eed3si9n . I really like the approach 😍

Only minor comments. Could you also add the the setting to the docker.rst documentation.

Copy link
Contributor

@muuki88 muuki88 left a comment

Choose a reason for hiding this comment

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

Awesome! Thanks for the quick response. I'll trigger a release immediately.

@muuki88
Copy link
Contributor

muuki88 commented Jan 24, 2019

I always use the Squash and merge button from github 😁

@muuki88
Copy link
Contributor

muuki88 commented Jan 24, 2019

Documentation in the docker.rst can be added in a separate PR.

@muuki88 muuki88 merged commit 34c776d into sbt:master Jan 24, 2019
@eed3si9n eed3si9n deleted the wip/openshift-compat branch January 24, 2019 08:44
eed3si9n added a commit to eed3si9n/sbt-native-packager that referenced this pull request Jan 24, 2019
muuki88 pushed a commit that referenced this pull request Jan 24, 2019
@aoboturov
Copy link

Hi, could anyone explain, how to make non-entrypoint executables executable?

here's an example of the ls -lah:
image

I tried different combinations of permission strategies and chmodtypes to no avail.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

OpenShift compatibility
3 participants