chore: Dockerfile remove redundant directives#3914
chore: Dockerfile remove redundant directives#3914polarathene wants to merge 8 commits intoory:masterfrom
Dockerfile remove redundant directives#3914Conversation
|
Given the past associated issues were neglected, I am not too keen to invest more time than necessary. I'll give the CLA a look and the other checklist items if this PR actually gets acknowledged with interest to merge it. The scratch image seems redundant, you've got a replacement image now with the rough equivalent via Google distroless base image. You might as well drop it? The main difference apart from noted issues in the scratch Dockerfile is also a lack of the sqlite support. Your alpine vs sqlite (alpine) images are effectively the same too, except for the |
aeneasr
left a comment
There was a problem hiding this comment.
Given the past associated issues were neglected, I am not too keen to invest more time than necessary.
No problem!
The scratch image seems redundant, you've got a replacement image now with the rough equivalent via Google distroless base image. You might as well drop it? The main difference apart from noted issues in the scratch Dockerfile is also a lack of the sqlite support.
Some dockerfiles are only used in the repo itself (for dev purposes) and some are being pushed to our docker registry. You can find the files used for prod distribution in the goreleaser config. Generally not too keen to deprecate image variants because someone always complains about it.
Your alpine vs sqlite (alpine) images are effectively the same too, except for the sqlite package and slightly different default CMD. Probably no need or benefit maintaining the two with such a low distinction?
That makes sense but please in another pr
| # NOTE: This is broken already. Even though this image provides a shell, you'd need to configure it with | ||
| # `SHELL ["/busybox/sh", "-c"]`, however `apt-get` does not exist either in a distroless image. | ||
| # This was original an Alpine image, the refactoring was not verified properly in this commit: | ||
| # https://github.com/ory/hydra/commit/c1e1a569621d88365dceee7372ca49ecd119f939#diff-ae54bef08e3587b28ad8e93eb253a9a5cd9ea6f4251977e35b88dc6b42329e25L31 |
There was a problem hiding this comment.
The HSM image is really just to run some e2e hsm tests. It's not being distributed and should not be used.
There was a problem hiding this comment.
Yeah, e2e tests are now failing. Probably just revert the changes here
https://github.com/ory/hydra/actions/runs/12523819546/job/34933781514?pr=3914
Since they're the same though, just use the same
👍
No worries, just pointing out that it's presently broken since the switch, just AFAIK your tests don't use this final stage of the image so it's not getting caught by them.
That is an easy fix, it just wants EDIT: Actually I don't see where I made a change to affect that? 🤔 It's complaining about this part of your CI: hydra/.github/workflows/ci.yaml Lines 129 to 131 in 8e5fae4 hydra/.github/workflows/ci.yaml Lines 109 to 113 in 8e5fae4 I'm surprised the image has been building for you? The UPDATE 2: I've split this concern out to a separate issue, it's specific to the CI runner (Ubuntu) vs the images (Debian, where it's a non-issue). |
|
TL;DR: Spotted a few more bugs/issues while looking into responding to your feedback. EDIT: Extracted to separate issues, feel free to ignore the rest of this comment:
Registries and tags (publishing bug)
Is the DockerHub registry
Perhaps something is a bit iffy with your release process?
I'm not too familiar with GoReleaser, but I am with Github Actions equivalent for building and publishing images to DockerHub. I only see two variables defined here: Lines 11 to 12 in 8e5fae4 Which seems to align with your two variants on DockerHub I haven't done a thorough search for where the other Dockerfile variants are potentially being used (which may be internal for you, or referenced externally from this repo like in docs?), but unless there's another registry you had in mind it doesn't seem like your earlier concern about deprecating variants is applicable if they were never published to registries? Your install docs for Hydra with Docker reference No mention there of the individual platform variants or the
|
|
Documenting some more feedback should it appeal to any maintainers that want to pursue it. A
|
polarathene
left a comment
There was a problem hiding this comment.
Apologies about all the verbosity, I don't really want to think about all this again and move onto other things instead 😂
Here's some final feedback to consider. I understand all my input could be overwhelming so I'll wrap this up with a checklist summary and links in a follow-up comment that should help make this more digestible for anyone interested.
| # NOTE: This is required for read/write by SQLite. | ||
| install --owner ory --group ory --directory /var/lib/sqlite | ||
|
|
||
| # NOTE: Presumably this was already created by the prior RUN directive |
There was a problem hiding this comment.
| # NOTE: Presumably this was already created by the prior RUN directive | |
| # NOTE: This location was created by `softhsm2` package install with ownership `root:softhsm`, | |
| # while the `pkcs11-tool` command generated a token dir + files with permissions of 600. | |
| # To support running hydra in the container as the non-root ory user, grant ownership for RW: |
/var/lib/softhsm/tokens is created when installing the softhsm / softhsm2 package. Ownership is root:softhsm. As is the token dir/files generated by the earlier pkcs11-tool command.
Rather than chown -R this directory and contents, it'd be wiser to just add ory to the softhsm group. However, the token generated only allows access to the file owner (root):
$ ls -l /var/lib/softhsm
drwxrws--- 2 root softhsm 4096 Apr 1 2024 tokens
$ ls -l /var/lib/softhsm/tokens
drwx--S--- 2 root softhsm 4096 Jan 5 22:34 c0e04acd-3e15-0266-6003-f9394edce34b
$ ls -l /var/lib/softhsm/tokens/c0e04acd-3e15-0266-6003-f9394edce34b
-rw------- 1 root softhsm 8 Jan 5 22:34 generation
-rw------- 1 root softhsm 0 Jan 5 22:34 token.lock
-rw------- 1 root softhsm 320 Jan 5 22:34 token.objectSo in this case chown is required for the owner specifically. Probably shouldn't mess with the group ownership? 🤷♂️
Personally unless there's a clear reason for building an image to run with a non-root user, I'd suggest keeping it simple to maintain and just using root which avoids all this.
Anyone bind mounting a volume for local storage to the container is not going to have the ory UID/GID match their host user since regardless of the previous implicit UID/GID (100:101) or the static 500:500 proposed here, the typical non-root host user is 1000:1000.
That leaves the remaining benefit as a "best practice", but AFAIK is mostly moot if the user instead runs the container with Podman/Docker in rootless mode which uses /etc/subuid + /etc/subgid tables on the host to map the containers root UID + GID to the hosts user and any other container UID/GID values to the subuid / subgid range. Alternatively, when run as rootful, the container can be run with all capabilities dropped, which is implicit when running a process as a non-root user in the container.
There was a problem hiding this comment.
Personally unless there's a clear reason for building an image to run with a non-root user, I'd suggest keeping it simple to maintain and just using root which avoids all this.
That leaves the remaining benefit as a "best practice", but AFAIK is mostly moot if the user instead runs the container with Podman/Docker in rootless mode which uses /etc/subuid + /etc/subgid tables on the host to map the containers root UID + GID to the hosts user and any other container UID/GID values to the subuid / subgid range. Alternatively, when run as rootful, the container can be run with all capabilities dropped, which is implicit when running a process as a non-root user in the container.
[...] mostly moot if the user instead [...]
security is never moot if you assume that everyone is smart or experienced enough to do the right thing. Running as root will fail several CI checks, please revert the user changes made
There was a problem hiding this comment.
Uhh, I generally don't see people understanding non-root practice in containers, just pushing for it as a security practice regardless of knowing why.
As I've said, you'll have default capabilities granted to the container root user, but these aren't the same as host root (full caps). Switching to a non-root user just drops these additional caps, which if you really need to you can do so yourself as a security practice explicitly (dropping all caps, or when compatible, switching the user to non-root if you really insist).
The other reason to use non-root is if container escape is accomplished, then escaping as the root user to be root on the host would allow more damage, but this doesn't make non-root in a container exempt from this, you can still escape as non-root and become root on the host depending on the vulnerability exploited. The bulk of vulnerabilities require granting capabilities or relaxing security intentionally for a container than would otherwise be permitted by default, hence why I'm not really sold on the non-root container user "best practice".
I've seen a variety of containers that adopted non-root as a security practice, but then did workarounds that made security worse. Some granted the non-root user access to the docker socket, others granted capabilities that wouldn't be allowed by default, or mandated capabilities to support non-root but in such a way that prevented someone who is not using a feature that requires that capability and knows how to properly lock down a container to be forced to grant the capability for the binary to run...
To assume that building a container that runs as non-root is always more secure would be a mistake. Inexperienced users that make a container insecure can happen regardless too, trying to address incompetence with a blanket "fix" which often introduces more caveats, especially when the image fails to document this practice is ill-advised IMO. If you're going to defend the practice, have a good reason beyond parroting / hand-waving the justification.
More security conscious (even those inexperienced) should be using rootless Docker/Podman, where it's perfectly ok to have the container user as root, since escaping the container does not result in retaining the same UID/GID of the container user thanks to user namespace mapping.
Root in a container is not equivalent to root on the host. I've explained and demonstrated this so many times in the past to those that don't know better but parrot paranoia insisting that they know what they're talking about.
Running as root will fail several CI checks, please revert the user changes made
I'm not sure what CI checks you were referring to, but your response is to a comment that proposed dropping the non-root approach. The PR respected the non-root decision, all it did here was ensure consistency with the other images for non-root user setup, thus nothing to revert.
| # Add a user/group for Ory with a stable UID + GID: | ||
| # NOTE: This only appears relevant for supporting hydra as non-root, otherwise unnecessary. | ||
| addgroup --system --gid 500 ory | ||
| adduser --system --uid 500 \ | ||
| --gecos "Ory User" \ | ||
| --home /home/ory \ | ||
| --ingroup ory \ | ||
| --shell /sbin/nologin \ | ||
| ory |
There was a problem hiding this comment.
Proposal to consider removing this in future follow-up PR.
- Only seems relevant as a common "best practice" that some users request. It has it's fair share of caveats though, especially when not clearly communicated/documented for the image.
- The only known dependency on this is optional SQLite for a fixed DSN location at
/var/lib/sqlite, but this becomes redundant if the container has a bind mount volume, which again is not documented for clarity, but a common practice for persistence vs named data volumes. - Without any other support needed in the container, a user that wants this could better run the container with the
useroption to set the user to switch to and gain the same benefits. - If Hydra ever needs to use some linux capabilities, you'd need to add a step with
setcapto grant them for non-root users, and ideally at runtime in Go handle checking this to raise the capability rather than enforce it viasetcap(especially if the feature using it is optional). With root (including rootless Docker/Podman), the need forsetcapis avoided. Keep this simple to maintain, since historically in this repo the Docker support is already showing inconsistencies in maintenance, mostly due to redundant noise for running as a non-root user by default.
| # Create the sqlite directory with ownership to that user and group: | ||
| # NOTE: This is required for read/write by SQLite. | ||
| # - Path may be a default value somewhere, or only explicitly provided via DSN? | ||
| # - Owner/Group is only relevant to permissions allowing the hydra process to read/write to the location. | ||
| # - Bind mount volumes will replace the ownership with that of the host directory, requiring correction. | ||
| install --owner ory --group ory --directory /var/lib/sqlite |
There was a problem hiding this comment.
I've added commentary here, but from what I understand this path is only relevant when it's part of the DSN configured like in quickstart.yml? While ownership is only adjusted to support the non-root ory user to have RW access.
Some platforms like OpenShift have a feature that uses the root group for access while randomizing the UID the container runs as. I'm not entirely sure how viable that approach is and raised some concerns at another repo (caddy-docker) where someone proposed supporting that.
However, group/other permissions need to be adjusted for the DSN path regardless so that the hydra process has write access to create the DB (the execute bit on the directory is also required for that operation), thus 770 (rwxrwx---) is necessary.
services:
hydra:
image: localhost/hydra:v2.2.0
# No delay in shutdown (uses tini as PID 1), proper signal forwarding + reaping:
init: true
# Optional: Run as non-root user, but use the root group for /var/lib/sqlite to avoid needing a chown
user: 500:0
environment:
# DSN to SQLite:
# https://www.ory.sh/docs/self-hosted/deployment#sql-persistent
DSN: sqlite:///var/lib/sqlite/db.sqlite?_fk=true
# Unlike the current quickstart.yml example which relies upon the Docker Compose `depends_on`
# feature to apply DB migrations through another container instance, just run both commands here:
# NOTE: While the quickstart doesn't mention it, `migrate sql` should technically
# have you manually create a backup copy prior to running it (take caution with restart policy if automating):
# https://www.ory.sh/docs/hydra/self-hosted/dependencies-environment
entrypoint: ["ash", "-c"]
command:
- |
hydra --config /etc/hydra/config.yml migrate sql --read-from-env --yes
hydra --config /etc/hydra/config.yml serve all --dev --sqa-opt-out
# Build image locally (no need to try pull from a remote registry):
pull_policy: build
build:
dockerfile_inline: |
FROM alpine
RUN install --mode 770 --directory /var/lib/sqlite
COPY --from=oryd/hydra:v2.2.0 /usr/bin/hydra /usr/bin/hydra
# `--chmod` only required when container user is not run as root,
# Volume mounting the config instead is fine as `git clone` permissions use umask which should result in 644.
ADD --chmod=640 https://raw.githubusercontent.com/ory/hydra/refs/heads/master/contrib/quickstart/5-min/hydra.yml /etc/hydra/config.ymlSo that works well, and if the user bind mounts a host directory instead, they can just set the user field to match UID of their host user. This avoids needing to set permissions of /var/lib/sqlite to 770, or know the UID/GID for the containers ory user/group is so that they can chmod their host directory ownership to that for compatibility 👍 (both inconvenient)
There was a problem hiding this comment.
Nice! The dockerfile_inline would probably need to build the binary inline as well, otherwise the binary type might mismatch (osx versus linux for example)
There was a problem hiding this comment.
otherwise the binary type might mismatch (osx versus linux for example)
I don't have macOS environment to verify, but the compose example above is just for a quick example. It copies the binary from the platform image it has, which AFAIK there is no macOS platform for registries/containers, they only publish windows and linux images?
Unless you were referring to architecture like AMD64 vs ARM64? That shouldn't be an issue, the directive should be using the inferred platform for selecting the image to copy from. Equivalent to FROM oryd/hydra or docker pull oryd/hydra without --platform= specified for either to change the default.
The example was focused on showing how much simpler the image is without catering to a non-root user, allowing the deployment to explicitly opt-in to non-root by choosing their own UID/GID, instead of needing to be mindful of what was chosen internally.
That's possible since the image doesn't have any real dependency lock-in on a non-root user being configured. They could just as easily take the current non-root images and run them as root in the same manner.
| HEREDOC | ||
|
|
||
| USER ory | ||
| COPY hydra /usr/bin/hydra |
There was a problem hiding this comment.
Context is lacking a bit for these COPY instructions.
Since the image itself doesn't have a build stage, it's rather vague what the hydra binary was built with. I doubt it matters for most, but since this is used for the official image published to DockerHub, some context might be worthwhile.
I assume going forward both Dockerfile-alpine + Dockerfile-distroless will take the binary built via Dockerfile-build, but perhaps you typically build Hydra outside of a container? 🤷♂️
There was a problem hiding this comment.
Outside of docker compose and make docker, the binaries are built by goreleaser and then injected into the containers. So this is correct here.
|
|
||
| ENTRYPOINT ["hydra"] | ||
| CMD ["serve", "all"] | ||
| USER ory |
There was a problem hiding this comment.
As covered by earlier review comments, this could be dropped in a follow-up PR.
Technically a breaking change (but so is the 500:500 UID/GID proposed in this PR), but all a user should need to do is adjust the ownership of the /var/lib/sqlite or run with --user 101:101 (might differ by image depending on implicit UID/GID assigned to ory).
There was a problem hiding this comment.
I don't agree with this change, the hydra command should run as non-root in my view
There was a problem hiding this comment.
There is no suggestion snippet to remove the line. It was simply shifted to the end of the file. A change to drop it would be:
| USER ory |
My comment only proposed dropping non-root, and noted that this PR configures the non-root user with a stable UID/GID value, rather than relying on it implicitly (unstable as any package installed prior may affect user/groups causing the UID/GID for ory to change unexpectedly).
I then noted that the stable UID/GID change itself, would be a breaking change to existing users of the image going forward, similar to if changing from non-root to root.
Since there's no real dependency on the non-root user in the image, it's fine and someone can choose to explicitly run as root at runtime (which should be secure, especially via rootless container).
| # TODO: Remove this file in favor of distroless-static variant: | ||
| # https://github.com/ory/hydra/blob/master/.docker/Dockerfile-distroless-static | ||
| # However if published to any registry, continue to publish the variant tag but as an alias to `-distroless` tags: | ||
| # https://github.com/ory/hydra/pull/3914#pullrequestreview-2527315326 |
There was a problem hiding this comment.
This file is redundant AFAIK, I don't know where it's being used/published, so proposed changes for consistency remain here and the file could be dropped in a follow-up PR, reverting the change if required.
There was a problem hiding this comment.
I checked and we no longer distribute this scratch image but instead the distroless variant. It should be OK to be removed.
| # TODO: Remove this file in favor of the main/default Alpine image. The sqlite package is no longer required: | ||
| # https://github.com/ory/hydra/blob/master/.docker/Dockerfile-alpine | ||
| # However if published to any registry, continue to publish the variant tag but as an alias to standard Alpine image tags: | ||
| # https://github.com/ory/hydra/pull/3914#pullrequestreview-2527315326 |
There was a problem hiding this comment.
This file is redundant AFAIK, I don't know where it's being used/published, so proposed changes for consistency remain here and the file could be dropped in a follow-up PR, reverting the change if required.
There was a problem hiding this comment.
Let's remove it, I also could not find any use for it.
Overview of everything aboveThis comment should summarize everything with links for more details if you need them. Changes from this PRThis PR applies the following changes:
Overall if we strip the commentary and ignore minor differences, RUN <<HEREDOC
apk upgrade --no-cache
apk add --no-cache --upgrade ca-certificates
addgroup --system --gid 500 ory
adduser --system --uid 500 \
--gecos "Ory User" \
--home /home/ory \
--ingroup ory \
--shell /sbin/nologin \
ory
install --owner ory --group ory --directory /var/lib/sqlite
HEREDOC
COPY hydra /usr/bin/hydra
ENTRYPOINT ["hydra"]
CMD ["serve", "all"]
USER oryWhile addressing the two referenced issues at the start of the PR description (removal of legacy In future if dropping the non-root FROM alpine:3.20
RUN apk add --no-cache ca-certificates && mkdir -p /var/lib/sqlite
COPY hydra /usr/bin/hydra
ENTRYPOINT ["hydra"]
CMD ["serve", "all"]FROM gcr.io/distroless/static-debian12:nonroot
RUN --mount=from=busybox:latest,src=/bin/,dst=/bin/ mkdir -p /var/lib/sqlite
COPY hydra /usr/bin/hydra
ENTRYPOINT ["hydra"]
CMD ["serve", "all"]All the verbosity I've provided is towards instilling confidence to pursue that change via a follow-up PR. Additional context
Future PRs / tasksRelated TODOs from observations while tackling this PR, not to be resolved by this PR:
|
Unfortunately, that stage is used for CI tests and is causing CI failures |
polarathene
left a comment
There was a problem hiding this comment.
While this PR is now considered redundant as you've raised a replacement PR, I'm wrapping it up with feedback to your last review comments.
No need to respond to the feedback unless you want to engage in that discussion further. I respect your decision to keep non-root if you choose to (users tend to request it anyway).
Apologies for the late response, busy week.
| # NOTE: This is required for read/write by SQLite. | ||
| install --owner ory --group ory --directory /var/lib/sqlite | ||
|
|
||
| # NOTE: Presumably this was already created by the prior RUN directive |
There was a problem hiding this comment.
Uhh, I generally don't see people understanding non-root practice in containers, just pushing for it as a security practice regardless of knowing why.
As I've said, you'll have default capabilities granted to the container root user, but these aren't the same as host root (full caps). Switching to a non-root user just drops these additional caps, which if you really need to you can do so yourself as a security practice explicitly (dropping all caps, or when compatible, switching the user to non-root if you really insist).
The other reason to use non-root is if container escape is accomplished, then escaping as the root user to be root on the host would allow more damage, but this doesn't make non-root in a container exempt from this, you can still escape as non-root and become root on the host depending on the vulnerability exploited. The bulk of vulnerabilities require granting capabilities or relaxing security intentionally for a container than would otherwise be permitted by default, hence why I'm not really sold on the non-root container user "best practice".
I've seen a variety of containers that adopted non-root as a security practice, but then did workarounds that made security worse. Some granted the non-root user access to the docker socket, others granted capabilities that wouldn't be allowed by default, or mandated capabilities to support non-root but in such a way that prevented someone who is not using a feature that requires that capability and knows how to properly lock down a container to be forced to grant the capability for the binary to run...
To assume that building a container that runs as non-root is always more secure would be a mistake. Inexperienced users that make a container insecure can happen regardless too, trying to address incompetence with a blanket "fix" which often introduces more caveats, especially when the image fails to document this practice is ill-advised IMO. If you're going to defend the practice, have a good reason beyond parroting / hand-waving the justification.
More security conscious (even those inexperienced) should be using rootless Docker/Podman, where it's perfectly ok to have the container user as root, since escaping the container does not result in retaining the same UID/GID of the container user thanks to user namespace mapping.
Root in a container is not equivalent to root on the host. I've explained and demonstrated this so many times in the past to those that don't know better but parrot paranoia insisting that they know what they're talking about.
Running as root will fail several CI checks, please revert the user changes made
I'm not sure what CI checks you were referring to, but your response is to a comment that proposed dropping the non-root approach. The PR respected the non-root decision, all it did here was ensure consistency with the other images for non-root user setup, thus nothing to revert.
|
|
||
| ENTRYPOINT ["hydra"] | ||
| CMD ["serve", "all"] | ||
| USER ory |
There was a problem hiding this comment.
There is no suggestion snippet to remove the line. It was simply shifted to the end of the file. A change to drop it would be:
| USER ory |
My comment only proposed dropping non-root, and noted that this PR configures the non-root user with a stable UID/GID value, rather than relying on it implicitly (unstable as any package installed prior may affect user/groups causing the UID/GID for ory to change unexpectedly).
I then noted that the stable UID/GID change itself, would be a breaking change to existing users of the image going forward, similar to if changing from non-root to root.
Since there's no real dependency on the non-root user in the image, it's fine and someone can choose to explicitly run as root at runtime (which should be secure, especially via rootless container).
| # Create the sqlite directory with ownership to that user and group: | ||
| # NOTE: This is required for read/write by SQLite. | ||
| # - Path may be a default value somewhere, or only explicitly provided via DSN? | ||
| # - Owner/Group is only relevant to permissions allowing the hydra process to read/write to the location. | ||
| # - Bind mount volumes will replace the ownership with that of the host directory, requiring correction. | ||
| install --owner ory --group ory --directory /var/lib/sqlite |
There was a problem hiding this comment.
otherwise the binary type might mismatch (osx versus linux for example)
I don't have macOS environment to verify, but the compose example above is just for a quick example. It copies the binary from the platform image it has, which AFAIK there is no macOS platform for registries/containers, they only publish windows and linux images?
Unless you were referring to architecture like AMD64 vs ARM64? That shouldn't be an issue, the directive should be using the inferred platform for selecting the image to copy from. Equivalent to FROM oryd/hydra or docker pull oryd/hydra without --platform= specified for either to change the default.
The example was focused on showing how much simpler the image is without catering to a non-root user, allowing the deployment to explicitly opt-in to non-root by choosing their own UID/GID, instead of needing to be mindful of what was chosen internally.
That's possible since the image doesn't have any real dependency lock-in on a non-root user being configured. They could just as easily take the current non-root images and run them as root in the same manner.
If that were the case could you point out where. Pretty sure I identified the CI failure was unrelated to the Docker image or stage. |


/etc/nsswitch.confworkaround.VOLUMEdirectives.Related issue(s)
The associated issues have been ignored for over a year. They've now been marked as stale, this PR attempts to address them for this repo.
Dockerfile: Remove/etc/nsswitch.confworkaround #3685Dockerfile: RemoveVOLUMEinstruction #3683See the issues for detailed justification of the summarized changes.
Checklist
vulnerability. If this pull request addresses a security vulnerability, I
confirm that I got the approval (please contact
security@ory.sh) from the maintainers to push
the changes.
Further Comments
These changes should be rather straight-forward. The bulk of the changeset is just noise repeating the same lines across several Dockerfiles, but I did notice inconsistencies which hint that these files may need to be revisited by someone more familiar with them.
Especially with the HSM image which appears to have had the
runnerstage broken since this June 2023 PR. though only earlier stages are built with the Makefile:hydra/Makefile
Lines 89 to 96 in 8e71f91
The HSM quickstart compose example should attempt to build the
runnerstage and fail:hydra/quickstart-hsm.yml
Line 16 in 8e71f91