Skip to content

fix: Permissions check should have find invoke chown directly on returned paths#4696

Open
neil-lcv-cs wants to merge 1 commit into
docker-mailserver:masterfrom
neil-lcv-cs:master
Open

fix: Permissions check should have find invoke chown directly on returned paths#4696
neil-lcv-cs wants to merge 1 commit into
docker-mailserver:masterfrom
neil-lcv-cs:master

Conversation

@neil-lcv-cs
Copy link
Copy Markdown

Description

As stated in #4112, this avoids running chown on a huge number of files and folders by directly using it on the results of the find command.

I renamed the function to omit the _if_necessary parts as it is not possible anymore to check when find triggers a chown or not. I am not sure if it would be useful to know this information anyway.

Note: The function does not return 1 in case of error anymore. I guess we should reimplement that before merging.

Type of change

  • Improvement (non-breaking change that does improve existing functionality)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (README.md or the documentation under docs/)
  • If necessary, I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I have added information about changes made in this PR to CHANGELOG.md

@casperklein
Copy link
Copy Markdown
Member

LGTM.

Note: The function does not return 1 in case of error anymore. I guess we should reimplement that before merging.

Any idea, what the purpose of return 1 was? The function returns the exit code of the last command by default.

@neil-lcv-cs
Copy link
Copy Markdown
Author

neil-lcv-cs commented Apr 19, 2026

Any idea, what the purpose of return 1 was?

Its only usage seems to be here:

_log 'debug' 'Checking /var/mail permissions'
if ! _chown_var_mail; then
_dms_panic__general 'Failed to fix /var/mail permissions'
fi

There was that part || return 1 in the previous code. I guess it was there to interrupt the function in case of failure.

@georglauterbach georglauterbach added area/scripts kind/improvement Improve an existing feature, configuration file or the documentation labels Apr 23, 2026
@georglauterbach georglauterbach added this to the v16.0.0 milestone Apr 23, 2026
Copy link
Copy Markdown
Member

@polarathene polarathene left a comment

Choose a reason for hiding this comment

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

I think there might be some minor concerns to address so this doesn't cause any subtle unexpected breakages (we might be lacking test coverage in some cases?)

function _chown_var_mail() {
# if needed, fix permissions for all files and folders 3 levels deep /var/mail
log 'trace' 'Fixing /var/mail permissions'
find /var/mail -maxdepth 3 \( \! -user "${DMS_VMAIL_UID}" -o \! -group "${DMS_VMAIL_GID}" \) -exec chown "${DMS_VMAIL_UID}:${DMS_VMAIL_GID}" {} +
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

While I understand why you'd like to drop the chown -R to only run chown, the -maxdepth 3 will prevent recursively applying chown when necessary.

chown -R 5000:5000 /var/mail was introduced into the source since the first pushed commit IIRC. There was no related context for that intention provided at the time unfortunately.

That was later changed to the conditional check with -maxdepth 3 because of the overhead of chown -R on larger /var/mail contents with each container start.


I'm not concerned about container setup time where a user deploys DMS with /var/mail contents that they've migrated Dovecot mail storage from another source than DMS, or even from a DMS backup where the user copied the contents but didn't preserve ownership.

We do copy over some user config however like this:

mkdir -p "/var/mail/${DOMAIN}/${USER}/home"
# copy user provided sieve file, if present
if [[ -e "/tmp/docker-mailserver/${LOGIN}.dovecot.sieve" ]]; then
cp "/tmp/docker-mailserver/${LOGIN}.dovecot.sieve" "/var/mail/${DOMAIN}/${USER}/home/.dovecot.sieve"
fi
done < <(_get_valid_lines_from_file "${DATABASE_ACCOUNTS}")

There is no ownership correction applied, that appears to be deferred to this utility command. In your case where you add a user account, that mkdir alone would be triggering the condition at runtime to cause the chown -R over all of /var/mail.

Likewise we probably don't want to drop the -maxdepth 3 limit, that is an adequate check AFAIK and covers the expectations for DMS at least (/var/mail/<domain-part>/<local-part>/*). No need to iterate over all files/folders beyond that AFAIK.

Some configurations may deviate from that convention (LDAP), but I don't recall anyone having reported performance concerns that has done such.

  • AFAIK, an existing domain-part (1st depth level) won't be affected, while new accounts will add their own local-part path (2nd depth level) and I guess anything in that respective folder (third depth level) which would be redundant for chown -R (but as you're using {} +, provided all related paths are bundled it probably makes no difference and the child content would be coalesced with the parent folder path?).
  • Any content changes that add new files like the sieve scripts, when all other content has correct ownership however may fail to get detected by find. Originally we didn't have a home/ subdirectory in the users mailbox directory, so that should probably get addressed separately in it's own related script logic.

There is also a bug with the current find -> chown affecting /var/mail/* content that breaks Postfix from delivering internal mail for system users at /var/mail/<user> via it's local delivery agent (as opposed to virtual which routes to Dovecot to handle), where ownership is expected to match the UID/GID of the user as per their /etc/passwd entry, otherwise Postfix fails to deliver new mail to that user file in /var/mail/ after ownership is modified.

Technically this shouldn't be a concern for DMS users, especially if they've got /etc/aliases setup correctly via our POSTMASTER_ADDRESS ENV + postfix-aliases.cf, DMS could improve on that concern though 😓

It is however a known issue for users preferring LDAP, with a workaround to not have per-user ownership (by configuring Dovecot to use static vmail UID/GID like the default file account provisioner does)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should be valid?:

Suggested change
find /var/mail -maxdepth 3 \( \! -user "${DMS_VMAIL_UID}" -o \! -group "${DMS_VMAIL_GID}" \) -exec chown "${DMS_VMAIL_UID}:${DMS_VMAIL_GID}" {} +
find /var/mail -maxdepth 3 \( \! -user "${DMS_VMAIL_UID}" -o \! -group "${DMS_VMAIL_GID}" \) -exec chown -R "${DMS_VMAIL_UID}:${DMS_VMAIL_GID}" {} +

Otherwise we need to be aware of more scenarios where the lack of -R flag for chown can go awry.

# Legacy workaround handled here, only seems necessary for _create_accounts:
# - `helpers/accounts.sh` logic creates folders/files with wrong ownership.
_chown_var_mail_if_necessary
_chown_var_mail
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we are certain that this is only needed for helpers/accounts.sh, we can probably fix ownership in that script itself?

Past involvement from me also suggests it could be valid solution:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If interested in pursuing this, I think we can remove the _chown_var_mail line here in favour of adding to this helpers/accounts.sh logic:

mkdir -p "/var/mail/${DOMAIN}/${USER}/home"
# copy user provided sieve file, if present
if [[ -e "/tmp/docker-mailserver/${LOGIN}.dovecot.sieve" ]]; then
cp "/tmp/docker-mailserver/${LOGIN}.dovecot.sieve" "/var/mail/${DOMAIN}/${USER}/home/.dovecot.sieve"
fi

+     local MAILBOX_HOME="/var/mail/${DOMAIN}/${USER}/home"
-     mkdir -p "/var/mail/${DOMAIN}/${USER}/home"
+     mkdir -p "${MAILBOX_HOME}"
+     chown "${DMS_VMAIL_UID}:${DMS_VMAIL_GID}" "${MAILBOX_HOME}"

-     # copy user provided sieve file, if present
+     # Copy user provided `.sieve` file:
      if [[ -e "/tmp/docker-mailserver/${LOGIN}.dovecot.sieve" ]]; then
-       cp "/tmp/docker-mailserver/${LOGIN}.dovecot.sieve" "/var/mail/${DOMAIN}/${USER}/home/.dovecot.sieve"
+       cp "/tmp/docker-mailserver/${LOGIN}.dovecot.sieve" "${MAILBOX_HOME}/.dovecot.sieve"
+       chown "${DMS_VMAIL_UID}:${DMS_VMAIL_GID}" "${MAILBOX_HOME}/.dovecot.sieve"
      fi

That way we skip the find operation, I'm just not sure if we should do that for DMS v16 given we might be overlooking something and not have adequate test coverage for it 😓 Probably better for now to just make the change the PR presently proposes.

@polarathene
Copy link
Copy Markdown
Member

Any idea, what the purpose of || return 1 was?

Its only usage seems to be here:

_log 'debug' 'Checking /var/mail permissions'
if ! _chown_var_mail; then
_dms_panic__general 'Failed to fix /var/mail permissions'
fi

There was that part || return 1 in the previous code. I guess it was there to interrupt the function in case of failure.

Yeah, it was expected that if the chown was unsuccessful that DMS would not run well so the container would exit as a result. I think due to the chown being in a conditional, to bubble up that failure status it had to use return?

I assume that's a non-issue now with the conditional dropped and just executing via find? Assuming if any of the chown commands it executes fails it'd return the non-zero status?

@polarathene polarathene changed the title use chown directly on find results during permission check fix: Permissions check should have find invoke chown directly on returned paths Apr 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/scripts kind/improvement Improve an existing feature, configuration file or the documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug report: Scaling issues with check-for-changes.sh when managing mailboxes / aliases

4 participants