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

Tighten up write permissions in Docker image #70635

Merged

Conversation

pugnascotia
Copy link
Contributor

Recursively remove write access from the bin, jdk, lib and
modules directories, since this access is not required, and removing
it makes it harder to exploit other issues in an ES distribution.

Recursively remove write access from the `bin`, `jdk`, `lib` and
`modules` directories, since this access is not required, and removing
it makes it harder to exploit other issues in an ES distribution.
@pugnascotia pugnascotia added >enhancement :Delivery/Packaging RPM and deb packaging, tar and zip archives, shell and batch scripts v8.0.0 v7.13.0 labels Mar 22, 2021
@elasticmachine elasticmachine added the Team:Delivery Meta label for Delivery team label Mar 22, 2021
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-delivery (Team:Delivery)

Copy link
Contributor

@droberts195 droberts195 left a comment

Choose a reason for hiding this comment

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

LGTM assuming it doesn't cause any failures.

If it turns out something legitimately needed write access to one of these files or directories then we should look to make the minimum necessary subset of files or directories writable again.

@rjernst
Copy link
Member

rjernst commented Mar 22, 2021

Instead of giving the entire ES install permissions and then restricting a fixed set from write permissions, can we instead explicitly give write permissions to the dirs that actually need it? Otherwise any additions (or even renames) of these directories in the future would need corresponding changes here which would be easy to miss.

@mark-vieira
Copy link
Contributor

Instead of giving the entire ES install permissions and then restricting a fixed set from write permissions, can we instead explicitly give write permissions to the dirs that actually need it?

This seems reasonable. How difficult would this be?

@pugnascotia
Copy link
Contributor Author

I pushed a change so that all permissions are explicitly set under /usr/share/elasticsearch, is that what you had in mind?

@rjernst
Copy link
Member

rjernst commented Mar 23, 2021

I was thinking more about keeping the original permissions, and just changing the owner so it is root like rpm/deb have.

@pugnascotia
Copy link
Contributor Author

@rjernst see what you think of the latest changes.

Copy link
Member

@rjernst rjernst left a comment

Choose a reason for hiding this comment

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

Thanks @pugnascotia. I have a few questions.

ln -sf /etc/pki/ca-trust/extracted/java/cacerts /usr/share/elasticsearch/jdk/lib/security/cacerts
ln -sf /etc/pki/ca-trust/extracted/java/cacerts /usr/share/elasticsearch/jdk/lib/security/cacerts && \\
chmod 0755 /usr/share/elasticsearch && \\
chgrp 1000 bin && \\
Copy link
Member

Choose a reason for hiding this comment

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

Why does bin need to be group owned by the running user? The files all are o+rx right?

ln -sf /etc/pki/ca-trust/extracted/java/cacerts /usr/share/elasticsearch/jdk/lib/security/cacerts && \\
chmod 0755 /usr/share/elasticsearch && \\
chgrp 1000 bin && \\
chgrp -R 1000 config data logs plugins
Copy link
Member

Choose a reason for hiding this comment

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

Why does the plugins dir need to be grouped for the running user?

public static final Set<PosixFilePermission> p755 = fromString("rwxr-xr-x");
public static final Set<PosixFilePermission> p750 = fromString("rwxr-x---");
public static final Set<PosixFilePermission> p660 = fromString("rw-rw----");
public static final Set<PosixFilePermission> p444 = fromString("r--r--r--");
Copy link
Member

Choose a reason for hiding this comment

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

This is now unused right?

@pugnascotia
Copy link
Contributor Author

I realised that I had changed the image to use a default group of elasticsearch, but that would break the current guidance to use GID 0 for bind-mounted volumes (which e.g. OpenShift does), and probably be considered a breaking change.

Instead, the image still uses group root but now also uses user root as well. Since the image runs as elasticsearch, that should make it more difficult to trample files via an exploit, in conjunction with the tightened permissions.

@pugnascotia
Copy link
Contributor Author

I went back over these changes again, and basically tried to lock things down as much as possible, while making it possible to install plugins without jumping through ridiculous hoops. See what you think.

@pugnascotia
Copy link
Contributor Author

@elasticmachine update branch

@pugnascotia
Copy link
Contributor Author

@elasticmachine update branch

Copy link
Member

@rjernst rjernst left a comment

Choose a reason for hiding this comment

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

I have a few more questions, and I'm not sure all my previous comments were addressed.

find . -type d -exec chmod 0555 {} + && \\
find . -type f -exec chmod 0444 {} + && \\
chmod 0555 bin/* jdk/bin/* jdk/lib/jspawnhelper modules/x-pack-ml/platform/linux-\$(arch)/bin/* && \\
chmod 0775 config config/jvm.options.d data logs plugins && \\
Copy link
Member

Choose a reason for hiding this comment

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

Including plugins here seems problematic. They are code, and should therefore not be writeable by the user that runs elasticsearch.

Copy link
Contributor

@mieciu mieciu Jun 8, 2021

Choose a reason for hiding this comment

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

I remember we discussed the plugins a while ago in #69533. Being able to install a plugin while running Elasticsearch as a non-root user (FWIW: ideally without the need to --group-add 0) is vital for Cloud deployments.

find . -type f -exec chmod o+r {} +
find . -type d -exec chmod 0555 {} + && \\
find . -type f -exec chmod 0444 {} + && \\
chmod 0555 bin/* jdk/bin/* jdk/lib/jspawnhelper modules/x-pack-ml/platform/linux-\$(arch)/bin/* && \\
Copy link
Member

Choose a reason for hiding this comment

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

The correct files already are executable in the original archive that is extracted. Why was the executable bit removed in the first place? Seems like we should just keep it as it was, instead of trying to reform what is executable here.

@@ -287,8 +285,7 @@ RUN ${package_manager} update --setopt=tsflags=nodocs -y && \\

RUN groupadd -g 1000 elasticsearch && \\
adduser -u 1000 -g 1000 -G 0 -d /usr/share/elasticsearch elasticsearch && \\
chmod 0775 /usr/share/elasticsearch && \\
chown -R 1000:0 /usr/share/elasticsearch
chown -R 0:0 /usr/share/elasticsearch
Copy link
Member

Choose a reason for hiding this comment

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

The rpm/deb packages setup ownership so that the group is the elasticsearch user, which is readable but not writable. Can we just keep the original permissions defined in the distribution and fix the ownership?

@@ -147,7 +147,7 @@ public void test041AmazonCaCertsAreInTheKeystore() {
public void test042KeystorePermissionsAreCorrect() throws Exception {
waitForElasticsearch(installation);

assertPermissionsAndOwnership(installation.config("elasticsearch.keystore"), p660);
assertPermissionsAndOwnership(installation.config("elasticsearch.keystore"), "elasticsearch", "root", p660);
Copy link
Member

Choose a reason for hiding this comment

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

This seems backwards. Shouldn't the group be elasticsearch and the owner be root?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The startup process creates this if necessary at startup, so it has to be elasticsearch:root.

@pugnascotia
Copy link
Contributor Author

I've picked up this work again, and I've been scratching my head over what we're trying to do and what are the constraints. I think it would helpful to lay these out:

  1. Docker images are immutable, therefore we don't need ES files to be writable e.g. for an upgrade. On the contrary, most files that we ship in the image should never be overwritten.
  2. On a multi-user system, it is appropriate to install ES as root:elasticsearch. However in the Docker world, it is essentially a single-user system - although popular images like postgres and redis run as root by default and then drop to another user, because our 7.x images also do this they are routinely flagged in security scans
  3. We recommend using GID 0 for sharing files via bind-mounts (OpenShift also recommends this, for example). Using an ownership of root:elasticsearch in the container would complicates this, versus root:elasticsearch

To sum up, I'm don't believe reusing the archive permissions is desirable.

What I've tried to do here is use root:root user for immutable files and directories, and use elasticsearch:root when they are writable. Rather than trying to tweak file permissions here and there, I decided that it was clearer to simply reset them all to known values that are more appropriate for the Docker environment.

Plugins complicate the picture, because not only does the executing user need write access to plugins, but it also (technically) needs write access to bin because plugins can install CLI tools. Essentially, the line between installing ES and running ES becomes non-existent, so we don't install as root (for example) and run as elasticsearch.

@elasticsearchmachine
Copy link
Collaborator

Hi @pugnascotia, I've created a changelog YAML for you.

@pugnascotia pugnascotia merged commit 3740f67 into elastic:master Aug 2, 2021
@pugnascotia pugnascotia deleted the restrict-write-perms-in-docker-image branch August 2, 2021 13:25
pugnascotia added a commit that referenced this pull request Aug 2, 2021
Explicitly set permissions for all files in the Elasticsearch home
directory to the minimum required set, and change ownership to
`root:root` where possible.
@pugnascotia
Copy link
Contributor Author

Backported to 7.x in c04dc4c.

mark-vieira added a commit that referenced this pull request Aug 2, 2021
@mark-vieira
Copy link
Contributor

FYI, I've reverted the backport to 7.x in 8effc56. It was causing some packaging test failures: https://gradle-enterprise.elastic.co/s/kvmrl5ln52pv6

pugnascotia added a commit to pugnascotia/elasticsearch that referenced this pull request Aug 3, 2021
@pugnascotia
Copy link
Contributor Author

Bah, sorry about that. New backport in #76006. FWIW, I did run the DockerTests before pushing, but I forgot about the KeystoreManagementTests.

elasticsearchmachine pushed a commit that referenced this pull request Aug 3, 2021
* Reapply "Tighten up write permissions in Docker image (#70635)"

This re-applies commit 8effc56.

* Fix perms check for Docker in KeystoreManagementTests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
:Delivery/Packaging RPM and deb packaging, tar and zip archives, shell and batch scripts >enhancement Team:Delivery Meta label for Delivery team v7.15.0 v8.0.0-alpha1
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants