Changing Permissions for .pem Files

I ran this command:N/A

It produced this output:

My web server is (include version): nginx

The operating system my web server runs on is (include version): Rocky 9.1

My hosting provider, if applicable, is: Linode

I can login to a root shell on my machine (yes or no, or I don't know): yes

I'm using a control panel to manage my site (no, or provide the name and version of the control panel): no

The version of my client is (e.g. output of certbot --version or certbot-auto --version if you're using Certbot): 2.5.0

Hi. I'm afraid this might be a little off-topic, but it seems to be the best place to ask if it's dangerous to make some changes to certificate permissions recommended in the the product (iRedMail) forum. Please let me know if there's a better place for this question.

I want to make clear that certbot has been and is working perfectly on this server since it was brought live two months ago. Also, I use certbot in many other servers and it's universally great there too.

Background: iRedMail is an integrated collection of email building blocks (dovecot, postfix, roundcube, etc). It orchestrates passwords for mail users across these building blocks. Users can change their password from within the webmail component (roundcube), but, as is the norm, it needs a "standard function" to create the password hash (which is what's saved in the user database). iRedMail chooses to use dovecot's "pw" function as the generic password hash function. So far standard vanilla stuff.

An issue seems to have been created by a recent change in dovecot such that its pw function wants to access the private key*. This works in dovecot proper, as it runs under root, but (when changing the password) the dovecot pw function is being called by the web server (nginx). This fails because the private key (privkey1.pem) is 600 root root, and nginx is refused access.

Several work-arounds have been suggested in the forum for this; I'm posting here because several people propose changing the private key to something like 644, or automating a copy and assigning that 644. This scares me a lot, as I don't want world access to my private key!

Am I being too paranoid? Is there a way to give just one sub-function of dovecot access to the key? (maybe change the password change function to call a root shell?)

Thanks for all help,
Paul

*No-one seems to understand why dovecot's password hash function should need to access the private key anyway!

Do you have any references to discussions or commits about this Dovecot change? Is it about doveadm pw?

To answer your question directly, I don't think there's a straightforward way on Linux to do what you want, without making the key world readable or running your web application at a higher privilege level. I think your hesitation is justified.

4 Likes

Reading my post above, I want to clarify that this password hash function is entirely unrelated to dovecot's functionality or main purpose. It's a stand-alone function that just happens to be packaged within dovecot.

Any other hash function (e.g. php has one) would presumably work just as well, but all the components are configured to use dovecot pw, and changing it system-wide would create headaches with updated versions of the product.

Yes, it is.

I have found a few posts on dovecot's forums about the issue in general, but it's the first time I've been there and am still exploring.

See

just as examples.

To summarize:

dovecot (main functionality), runs as root, accesses private key ==> succedes

roundcube (webmail app), runs as nginx, calls doveadm pw which accesses private key ==> fails (quite correctly!)

But everyone seems mystified re why doveadm pw now needs to access the private key!

It looks potentially like doveadm parses the Dovecot configuration, regardless of what subcommand is used. I was able to reproduce this on Debian 11:

alex@valery-period:~$ strace -ff -s 1024 -e trace=open,openat doveadm pw
openat(AT_FDCWD, "/usr/lib/dovecot/tls/haswell/x86_64/libz.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/dovecot/tls/haswell/libz.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/dovecot/tls/x86_64/libz.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/dovecot/tls/libz.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/dovecot/haswell/x86_64/libz.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/dovecot/haswell/libz.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/dovecot/x86_64/libz.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/dovecot/libz.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libz.so.1", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/usr/lib/dovecot/libcrypt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libcrypt.so.1", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/usr/lib/dovecot/libdovecot-storage.so.0", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/usr/lib/dovecot/libdovecot.so.0", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/usr/lib/dovecot/libsodium.so.23", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libsodium.so.23", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/usr/lib/dovecot/libm.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/usr/lib/dovecot/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/dev/null", O_WRONLY) = 3
openat(AT_FDCWD, "/usr/lib/dovecot/modules", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 4
openat(AT_FDCWD, "/usr/lib/dovecot/modules/libssl_iostream_openssl.so", O_RDONLY|O_CLOEXEC) = 4
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 4
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libssl.so.1.1", O_RDONLY|O_CLOEXEC) = 4
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libcrypto.so.1.1", O_RDONLY|O_CLOEXEC) = 4
openat(AT_FDCWD, "/usr/lib/dovecot/tls/haswell/x86_64/libdovecot.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/dovecot/tls/haswell/libdovecot.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/dovecot/tls/x86_64/libdovecot.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/dovecot/tls/libdovecot.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/dovecot/haswell/x86_64/libdovecot.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/dovecot/haswell/libdovecot.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/dovecot/x86_64/libdovecot.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/dovecot/libdovecot.so.0", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/usr/lib/dovecot/libm.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/usr/lib/dovecot/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/dev/null", O_WRONLY) = 3
openat(AT_FDCWD, "/usr/lib/dovecot/modules/settings", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 7
openat(AT_FDCWD, "/usr/lib/dovecot/modules/settings/libpigeonhole_settings.so", O_RDONLY|O_CLOEXEC) = 7
openat(AT_FDCWD, "/etc/dovecot/dovecot.conf", O_RDONLY) = 7
openat(AT_FDCWD, "/usr/share/dovecot/protocols.d", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 8
openat(AT_FDCWD, "/etc/dovecot/conf.d", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 8
openat(AT_FDCWD, "/etc/dovecot/conf.d/90-quota.conf", O_RDONLY) = 8
openat(AT_FDCWD, "/etc/dovecot/conf.d/90-plugin.conf", O_RDONLY) = 9
openat(AT_FDCWD, "/etc/dovecot/conf.d/90-acl.conf", O_RDONLY) = 10
openat(AT_FDCWD, "/etc/dovecot/conf.d/15-mailboxes.conf", O_RDONLY) = 11
openat(AT_FDCWD, "/etc/dovecot/conf.d/15-lda.conf", O_RDONLY) = 12
openat(AT_FDCWD, "/etc/dovecot/conf.d/10-tcpwrapper.conf", O_RDONLY) = 13
openat(AT_FDCWD, "/etc/dovecot/conf.d/10-ssl.conf", O_RDONLY) = 14
openat(AT_FDCWD, "/etc/dovecot/conf.d/10-master.conf", O_RDONLY) = 15
openat(AT_FDCWD, "/etc/dovecot/conf.d/10-mail.conf", O_RDONLY) = 16
openat(AT_FDCWD, "/etc/dovecot/conf.d/10-logging.conf", O_RDONLY) = 17
openat(AT_FDCWD, "/etc/dovecot/conf.d/10-director.conf", O_RDONLY) = 18
openat(AT_FDCWD, "/etc/dovecot/conf.d/10-auth.conf", O_RDONLY) = 19
openat(AT_FDCWD, "/etc/dovecot/conf.d/auth-system.conf.ext", O_RDONLY) = 20
openat(AT_FDCWD, "/etc/letsencrypt/live/valery-period.bnr.la/fullchain.pem", O_RDONLY) = -1 EACCES (Permission denied)
doveconf: Fatal: Error in configuration file /etc/dovecot/conf.d/10-ssl.conf line 12: ssl_cert: Can't open file /etc/letsencrypt/live/valery-period.bnr.la/fullchain.pem: Permission denied
+++ exited with 89 +++

If that's the reality of how the doveadm command works now, then you will probably need to get iRedMail to work around it by providing an alternate implementation.

3 Likes

Well, that seems fairly damning. Unless I'm missing something, it means it can't be used in anything except when run as root and / or when the private key access has been relaxed -- neither of which are policies that make sense!

I'm not familiar enough with the design philosophies of dovecot/doveadm vs pki in general, so it's not clear to me who should act on this. But as it stands, the two don't interoperate very effectively (who wants a hashing function that only runs as root?).

I agree that unless dovecot relents, iRedMail needs to change their standard hashing function to something that runs from the web server's security context.

Thanks for your help,
Paul

2 Likes

I received the following reply when I reported this on the Dovecot mailing list:

Haven't yet tried 2.3.20 to see if the problem's resolved though.

Would certbot play nice with the config changes they suggest?

Maybe ... maybe not.
If not, then you simply need to copy the cert files from the /live/ folder to some other location.
Where you can set the proper permissions for your service to use the copied cert files.

But it sounds like progress.
If v2.3.20 can use .pem files [in]directly, that is the way to go.

3 Likes

In the past, I've tried to get around issues like this by creating a new linux group for the various services that need to access the key.

When that fails, I use the method @rg305 mentioned - just copy the files and let the annoying/buggy service use it's own version of the file with the permissions it wants.

4 Likes

But if:

  • nginx is running the web app (RoundCube),
  • the web app calls its password change plugin ("password"),
  • which calls its configured hash function (doveadm pw),
  • which insists on opening the private key (which is only accessible to root),

then any form of letting doveadm see the private key is going to allow every other web app see it too, no?

Letting doveadm see a copy of the private key is just as bad as showing it the original!

If I could tell dovecot proper (running as root) to use the real key, and doveadm (running as nginx) to use a different (fake) one, that would solve the problem. Note that issue is not how to give doveadm the key -- it doesn't need it -- it just insists on opening every file in the main config.

Here's the text associated with the bugfix in Dovecot 2.3.17:

So all-in-all I think the safest thing to do is just upgrade to the fixed Dovecot. Unfortunately Dovecot 2.3.16 is what's in the main repos of both RHEL 9 and Debian 11 (plus all their derivatives, presumably).

How else will it be able to use a cert [without the private key for it]?

Maybe you can get a separate cert - just for dovecot use.

3 Likes

Who can access doveadm though? If this were shared hosting, that would be a problem - but if you're running this apps and have root access, you shouldn't have much to worry about unless there are other "users" with shell accounts that could be bad actors or become compromised.

3 Likes

I'm not worried about anyone accessing doveadm -- it's supposed to be a public hash function anyway -- but if I need to make the private key publicly readable in order to make doveadm work, then I worry about other ways people might access the private key file.

I think we've got our wires crossed.

Two components, both part of the same package, access the same config file, a config file that specifies the location of my private key (privkey1.key).

The first component, dovecot, is a daemon and has root access. It (correctly) reads the config and successfully accesses the private key. This is good.

The second component, doveadm, is (in this context) only called from javascript as a utility to calculate the hash of a new password entered by a user. Usually, this component ignores the part of the config file that points to the private key and thus this is good too.

Because of a bug introduced in dovecot 2.3.11, the second component stupidly goes to read the private key file, even though it doesn't need it and doesn't use it. When it's refused access, it fatally fails. This in turn makes the web app's password change feature always fail.

Both apps use the same config file, and the first needs the real private key, so I can't use a fake one for the second app. However, the workaround the dovecot people suggested would seem to get around it (I believe by using an alternate, indirect way of requiring SSL, a way that dovecot accepts but doveadm doesn't bother to fully check. I could well be wrong though).

1 Like

Down grade until the bug is fixed.

2 Likes

Yes. Or, since it's fixed in 2.3.17, upgrade to the current stable version, which is 2.3.20.

But that's not part of the mainstream repos so I'm going to get adventurous and try GhettoForge repos that have newer versions of the standard packages.

2 Likes

You could consider asking the Debian Dovecot maintainer to backport the fix. I think they are willing to do so for some issues. Certainly we've had luck with the Debian's Certbot maintainer with that kind of thing. I'm glad that the upstream project was willing to acknowledge it as a bug, though.

5 Likes

I know I'm late to the party, but Dovecot also publishes first party packages for various major distributions: https://repo.dovecot.org/

I remember hitting this bug as well (it's been a long time since though). Back then I migrated my config to embed all TLS configuration within a try_include file, which is both cleaner and prevents the issue altogether.

5 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.