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

make sure the private key is never world-readable #2979

Closed
wants to merge 1 commit into from
Closed

make sure the private key is never world-readable #2979

wants to merge 1 commit into from

Conversation

vszakats
Copy link

@vszakats vszakats commented Jun 4, 2020

  1. Set the private key to chmod 0600 on creation.
  2. Avoid possible race condition by setting the private key backup to 0600 first and then saving the key.
  3. Reset existing backup key to chmod 0600 before saving the new key.

1. Set the private to chmod 0600 on creation.
2. Avoid possible race condition by setting the private
   key backup to 0600 _first_ and then saving the key.
3. Reset existing backup key to chmod 0600 before
   saving the new key.
@vszakats
Copy link
Author

Is there any interest in (or concern with) this?

@logankaser
Copy link

Just another user, but this seems in line with best practice for storing keys on Unix, AWS even enforces this.
I also don't see any downsides with this change, and your implementation seems to correctly handle the possible race condition.

@@ -5265,10 +5267,12 @@ _installcert() {
cp "$_real_key" "$_backup_path/key.bak"
fi
if [ -f "$_real_key" ]; then
chmod 600 "$_real_key"
Copy link
Member

Choose a reason for hiding this comment

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

don't chmod if the key file is already existing.

Copy link
Author

@vszakats vszakats Aug 2, 2020

Choose a reason for hiding this comment

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

One of the goals of this patch is to avoid leaking the private key via wrongly set file mode. Without this command, the new private key saved in the subsequent cat command may end up in a file that's world readable (if someone set them that way between two acme.sh runs). If somebody is intentionally willing to leak the private key, the "best" solution is to copy it somewhere else, but IMO acme.sh's correct behaviour is to always make sure that a saved private key don't end up in a world-readable file.

Copy link
Member

Choose a reason for hiding this comment

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

(if someone set them that way between two acme.sh runs). If somebody is intentionally willing to leak the private key, the "best" solution is to copy it somewhere else,

No, we should chmod only if the file doesn't exist. If the user changed the chmod, we should respect it.

Copy link
Author

@vszakats vszakats Aug 3, 2020

Choose a reason for hiding this comment

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

You assume that the user does this intentionally and that the user is an actual person. I'd argue this is easier done by mistake by a human user or can be made by buggy tools (including bugs in acme.sh itself). acme.sh is a tool designed to create a secure and unattended way to update and handle private keys, so I think it's rightly expected that is enforces security around the files it manages. Also note that the user is not expected to tamper with files managed by acme.sh (and similar automated tools.)

Copy link
Member

Choose a reason for hiding this comment

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

If the key file doesn't exist, we create it and chmod to 600. That's ok.
But if the key file already exists, we should not chmod to the file. We MUST respect the user's choice.

Copy link
Member

Choose a reason for hiding this comment

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

private key files are recommended to be 600, but remember that it's just a normal file, it can be any mod.
What the mod is MUST meet the user's needs.
What meets the needs is the best choice.

Copy link
Member

Choose a reason for hiding this comment

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

what acme.sh does is never to make/change choices for the user. Instead, we respect the user's choice MOST.

Copy link
Author

@vszakats vszakats Aug 5, 2020

Choose a reason for hiding this comment

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

You're repeating this notion of respecting the user's choice, but you're ignoring all the issues I've mentioned above that arises from this, in this particular case, including the fact that this isn't a user created file, nor is it a "normal file". Note that e.g. ssh will even fail with an error unless a private key is 0600. acme.sh is lucky that most web servers aren't such picky about this.

To me it looks like acme.sh is purposefully designed to be insecure and for some unknown reason maintainers want to keep it that way.

Copy link
Member

Choose a reason for hiding this comment

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

but you're ignoring all the issues I've mentioned above

Not true. I didn't ignore the issues. And I said I agree with you that the key file can be set to 600 when we first create the file.
The only argument between us is that: whether to set the key file to 600 every time we renew the cert.

You insist to set to 600 every time, no matter the file is already existing or not.
My point is not to do so if the file is already existing.

And I don't agree with you: acme.sh is secure enough.

acme.sh is lucky that most web servers aren't such picky about this.

you know why is this? why most web servers aren't such picky?
There must be a balance between Secure and Convenience.
100% secure is not always the best choice in all the cases.

Copy link
Author

Choose a reason for hiding this comment

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

Okay, so I understand you confirmed that acme.sh is purposefully designed to be insecure, and there is no desire to fix this issue for existing installations and existing private keys.

cat "$CERT_KEY_PATH" >"$_real_key" || return 1
else
cat "$CERT_KEY_PATH" >"$_real_key" || return 1
touch "$_real_key"
Copy link
Member

Choose a reason for hiding this comment

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

why do we need to "touch" it first ?

Copy link
Member

Choose a reason for hiding this comment

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

I don't think it necessary.

Copy link
Author

@vszakats vszakats Aug 2, 2020

Choose a reason for hiding this comment

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

touch is used to create a zero-length file. Without this zero-length file, the subsequent chmod command would fail.

Copy link
Member

Choose a reason for hiding this comment

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

the "cat" command can create the "$_real_key" file. No need to "touch"

Choose a reason for hiding this comment

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

The issue is that cat by itself cannot create a file with a specific mask, you can, of course, use umask
but touching then modifying the mask before writing to the file is another somewhat common way to handle this that avoids the problems with umask.
The reason this is done is to avoid the race condition where if you write the key to the file and then set the mask, an attacker could read the file after it has had the key written to it, but before the mask has been changed.

Copy link
Author

@vszakats vszakats Aug 3, 2020

Choose a reason for hiding this comment

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

The point is to set the mask before writing the secret content. If you set it after finishing writing the file, there will always be a moment when the content is world-readable. So, e.g. if an attacker knows the file location and hammers that path with reads in a loop, this can be exploited. This patch solves this by first creating the file with the secure mask and only then start outputting the secret content into it.

@vszakats vszakats closed this Aug 3, 2020
@vszakats vszakats deleted the key0600 branch August 3, 2020 14:20
@vszakats
Copy link
Author

vszakats commented Aug 5, 2020

Since this patch was rejected by maintainers, my solution was to disable further acme.sh automatic updates (via removing AUTO_UPGRADE='1' from account.conf, then apply this patch locally (curl -L https://github.com/acmesh-official/acme.sh/pull/2979.patch | patch -p1), till migrating to a different ACME client.

@skyscooby
Copy link

Honestly.. this is the craziest thing I have read on GitHub.. A certificate management tool is unwilling to enforce the protection of the sacred private key file it owns? I just tested master out and it still creates brand new keys with world read access.

@vszakats I think you were wrong to close and delete this, you should have at least left it open as there was willingness to at least have 'new' keys being created correctly.. which I totally agree is not enough but it is also a improvement for new users of the tool.

@Neilpang based on your questions in the review it's pretty clear that you are not qualified to be reviewing a security oriented project.

@vszakats
Copy link
Author

vszakats commented May 27, 2021

@skyscooby Unclosed issues were stacking up on one's profile page (at least back then, not sure if still true), so I used to close things that don't get interest/traction (or outright rejected) to declutter. The problem still seems valid indeed though. Definitely not a happy resolution, because even though you can choose a different client (certbot does support ECC by now), but acme.sh is being used as a dependency by other projects, so it's a security issue one can fall into without being aware.

I'd gladly reopen, but the option isn't available anymore (likely because I deleted my fork with the patch).

@vszakats
Copy link
Author

Somebody reported this again a few weeks after this PR and this was then merged for the most part (but without referencing or crediting this one). Ref: #3127 af19329

@acmesh-official acmesh-official deleted a comment from auto-comment bot Jan 22, 2024
@Neilpang
Copy link
Member

here is one of my test cert, the key file is 600:
image

@vmmello
Copy link
Contributor

vmmello commented Jan 22, 2024

This issue was fixed on release 3.0.4, by commit af19329, as mentioned above by @vszakats.

(this comment is almost unnecessary, but as this fix was never explicitly acknowledged anywhere, users of older versions keep reporting it, so it's confusing whether it was ever fixed or not).

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

Successfully merging this pull request may close these issues.

5 participants