diff --git a/target/scripts/startup/setup.d/mail_state.sh b/target/scripts/startup/setup.d/mail_state.sh index 5e72e0ef..13c0e3e2 100644 --- a/target/scripts/startup/setup.d/mail_state.sh +++ b/target/scripts/startup/setup.d/mail_state.sh @@ -3,125 +3,125 @@ # Consolidate all states into a single directory # (/var/mail-state) to allow persistence using docker volumes function _setup_save_states() { + if [[ ! -d ${DMS_STATE_DIR:?DMS_STATE_DIR is not set} ]]; then + _log 'debug' "'${DMS_STATE_DIR}' is not present - not consolidating state" + return 0 + fi + + _log 'debug' "Consolidating all state onto ${DMS_STATE_DIR}" + local DEST DESTDIR SERVICEDIR SERVICEDIRS SERVICEFILE SERVICEFILES - if [[ -d ${DMS_STATE_DIR} ]]; then - _log 'debug' "Consolidating all state onto ${DMS_STATE_DIR}" + # Always enabled features: + SERVICEDIRS=( 'lib/logrotate' 'lib/postfix' 'spool/postfix' ) - # Always enabled features: - SERVICEDIRS=( - lib/logrotate - lib/postfix - spool/postfix - ) + # Only consolidate state for services that are enabled + # Notably avoids copying over 200MB for the ClamAV database + [[ ${ENABLE_AMAVIS} -eq 1 ]] && SERVICEDIRS+=('lib/amavis') + [[ ${ENABLE_CLAMAV} -eq 1 ]] && SERVICEDIRS+=('lib/clamav') + [[ ${ENABLE_FAIL2BAN} -eq 1 ]] && SERVICEDIRS+=('lib/fail2ban') + [[ ${ENABLE_FETCHMAIL} -eq 1 ]] && SERVICEDIRS+=('lib/fetchmail') + [[ ${ENABLE_GETMAIL} -eq 1 ]] && SERVICEDIRS+=('lib/getmail') + [[ ${ENABLE_MTA_STS} -eq 1 ]] && SERVICEDIRS+=('lib/mta-sts') + [[ ${ENABLE_POSTGREY} -eq 1 ]] && SERVICEDIRS+=('lib/postgrey') + [[ ${ENABLE_RSPAMD} -eq 1 ]] && SERVICEDIRS+=('lib/rspamd') + [[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && SERVICEDIRS+=('lib/redis') + [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]] && SERVICEDIRS+=('lib/spamassassin') + [[ ${ENABLE_SRS} -eq 1 ]] && SERVICEDIRS+=('lib/postsrsd') + [[ ${SMTP_ONLY} -ne 1 ]] && SERVICEDIRS+=('lib/dovecot') - # Only consolidate state for services that are enabled - # Notably avoids copying over 200MB for the ClamAV database - [[ ${ENABLE_AMAVIS} -eq 1 ]] && SERVICEDIRS+=('lib/amavis') - [[ ${ENABLE_CLAMAV} -eq 1 ]] && SERVICEDIRS+=('lib/clamav') - [[ ${ENABLE_FAIL2BAN} -eq 1 ]] && SERVICEDIRS+=('lib/fail2ban') - [[ ${ENABLE_FETCHMAIL} -eq 1 ]] && SERVICEDIRS+=('lib/fetchmail') - [[ ${ENABLE_GETMAIL} -eq 1 ]] && SERVICEDIRS+=('lib/getmail') - [[ ${ENABLE_MTA_STS} -eq 1 ]] && SERVICEDIRS+=('lib/mta-sts') - [[ ${ENABLE_POSTGREY} -eq 1 ]] && SERVICEDIRS+=('lib/postgrey') - [[ ${ENABLE_RSPAMD} -eq 1 ]] && SERVICEDIRS+=('lib/rspamd') - [[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && SERVICEDIRS+=('lib/redis') - [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]] && SERVICEDIRS+=('lib/spamassassin') - [[ ${ENABLE_SRS} -eq 1 ]] && SERVICEDIRS+=('lib/postsrsd') - [[ ${SMTP_ONLY} -ne 1 ]] && SERVICEDIRS+=('lib/dovecot') + # Single service files + [[ ${ENABLE_SRS} -eq 1 ]] && SERVICEFILES+=('/etc/postsrsd.secret') - # Single service files - [[ ${ENABLE_SRS} -eq 1 ]] && SERVICEFILES+=('/etc/postsrsd.secret') + for SERVICEFILE in "${SERVICEFILES[@]}"; do + DEST="${DMS_STATE_DIR}/${SERVICEFILE}" + DESTDIR="${DEST%/*}" - for SERVICEFILE in "${SERVICEFILES[@]}"; do - DEST="${DMS_STATE_DIR}/${SERVICEFILE}" - DESTDIR="${DEST%/*}" + mkdir -p "${DESTDIR}" + if [[ -f ${DEST} ]]; then + _log 'trace' "Destination ${DEST} exists, linking ${SERVICEFILE} to it" + # Original content from image no longer relevant, remove it: + rm -f "${SERVICEFILE}" + elif [[ -f "${SERVICEFILE}" ]]; then + _log 'trace' "Moving ${SERVICEFILE} to ${DEST}" + # Empty volume was mounted, or new content from enabling a feature ENV: + mv "${SERVICEFILE}" "${DEST}" + # Apply SELinux security context to match the state directory, so access + # is not restricted to the current running container: + chcon -R --reference="${DMS_STATE_DIR}" "${DEST}" 2>/dev/null || true + fi - mkdir -p "${DESTDIR}" - if [[ -f ${DEST} ]]; then - _log 'trace' "Destination ${DEST} exists, linking ${SERVICEFILE} to it" - # Original content from image no longer relevant, remove it: - rm -f "${SERVICEFILE}" - elif [[ -f "${SERVICEFILE}" ]]; then - _log 'trace' "Moving ${SERVICEFILE} to ${DEST}" - # Empty volume was mounted, or new content from enabling a feature ENV: - mv "${SERVICEFILE}" "${DEST}" - # Apply SELinux security context to match the state directory, so access - # is not restricted to the current running container: - chcon -R --reference="${DMS_STATE_DIR}" "${DEST}" 2>/dev/null || true - fi + # Symlink the original file in the container ($SERVICEFILE) to be + # sourced from assocaiated path in /var/mail-state/ ($DEST): + ln -s "${DEST}" "${SERVICEFILE}" + done - # Symlink the original file in the container ($SERVICEFILE) to be - # sourced from assocaiated path in /var/mail-state/ ($DEST): - ln -s "${DEST}" "${SERVICEFILE}" - done + for SERVICEDIR in "${SERVICEDIRS[@]}"; do + DEST="${DMS_STATE_DIR}/${SERVICEDIR//\//-}" + SERVICEDIR="/var/${SERVICEDIR}" - for SERVICEDIR in "${SERVICEDIRS[@]}"; do - DEST="${DMS_STATE_DIR}/${SERVICEDIR//\//-}" - SERVICEDIR="/var/${SERVICEDIR}" + # If relevant content is found in /var/mail-state (presumably a volume mount), + # use it instead. Otherwise copy over any missing directories checked. + if [[ -d ${DEST} ]]; then + _log 'trace' "Destination ${DEST} exists, linking ${SERVICEDIR} to it" + # Original content from image no longer relevant, remove it: + rm -rf "${SERVICEDIR}" + elif [[ -d ${SERVICEDIR} ]]; then + _log 'trace' "Moving contents of ${SERVICEDIR} to ${DEST}" + # An empty volume was mounted, or new content dir now exists from enabling a feature ENV: + mv "${SERVICEDIR}" "${DEST}" + # Apply SELinux security context to match the state directory, so access + # is not restricted to the current running container: + chcon -R --reference="${DMS_STATE_DIR}" "${DEST}" 2>/dev/null || true + else + _log 'error' "${SERVICEDIR} should exist but is missing" + fi - # If relevant content is found in /var/mail-state (presumably a volume mount), - # use it instead. Otherwise copy over any missing directories checked. - if [[ -d ${DEST} ]]; then - _log 'trace' "Destination ${DEST} exists, linking ${SERVICEDIR} to it" - # Original content from image no longer relevant, remove it: - rm -rf "${SERVICEDIR}" - elif [[ -d ${SERVICEDIR} ]]; then - _log 'trace' "Moving contents of ${SERVICEDIR} to ${DEST}" - # An empty volume was mounted, or new content dir now exists from enabling a feature ENV: - mv "${SERVICEDIR}" "${DEST}" - # Apply SELinux security context to match the state directory, so access - # is not restricted to the current running container: - chcon -R --reference="${DMS_STATE_DIR}" "${DEST}" 2>/dev/null || true - else - _log 'error' "${SERVICEDIR} should exist but is missing" - fi - - # Symlink the original path in the container ($SERVICEDIR) to be - # sourced from assocaiated path in /var/mail-state/ ($DEST): - ln -s "${DEST}" "${SERVICEDIR}" - done - else - _log 'debug' "'${DMS_STATE_DIR}' is not present; Not consolidating state" - fi + # Symlink the original path in the container ($SERVICEDIR) to be + # sourced from assocaiated path in /var/mail-state/ ($DEST): + ln -s "${DEST}" "${SERVICEDIR}" + done } function _setup_adjust_state_permissions() { - if [[ -d ${DMS_STATE_DIR} ]]; then - # This ensures the user and group of the files from the external mount have their - # numeric ID values in sync. New releases where the installed packages order changes - # can change the values in the Docker image, causing an ownership mismatch. - # NOTE: More details about users and groups added during image builds are documented here: - # https://github.com/docker-mailserver/docker-mailserver/pull/3011#issuecomment-1399120252 - _log 'trace' "Fixing ${DMS_STATE_DIR}/* permissions" - [[ ${ENABLE_AMAVIS} -eq 1 ]] && chown -R amavis:amavis "${DMS_STATE_DIR}/lib-amavis" - [[ ${ENABLE_CLAMAV} -eq 1 ]] && chown -R clamav:clamav "${DMS_STATE_DIR}/lib-clamav" - [[ ${ENABLE_FETCHMAIL} -eq 1 ]] && chown -R fetchmail:nogroup "${DMS_STATE_DIR}/lib-fetchmail" - [[ ${ENABLE_MTA_STS} -eq 1 ]] && chown -R _mta-sts:_mta-sts "${DMS_STATE_DIR}/lib-mta-sts" - [[ ${ENABLE_POSTGREY} -eq 1 ]] && chown -R postgrey:postgrey "${DMS_STATE_DIR}/lib-postgrey" - [[ ${ENABLE_RSPAMD} -eq 1 ]] && chown -R _rspamd:_rspamd "${DMS_STATE_DIR}/lib-rspamd" - [[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && chown -R redis:redis "${DMS_STATE_DIR}/lib-redis" - [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]] && chown -R debian-spamd:debian-spamd "${DMS_STATE_DIR}/lib-spamassassin" - - chown -R root:root "${DMS_STATE_DIR}/lib-logrotate" - chown -R postfix:postfix "${DMS_STATE_DIR}/lib-postfix" - - # NOTE: The Postfix spool location has mixed owner/groups to take into account: - # UID = postfix(101): active, bounce, corrupt, defer, deferred, flush, hold, incoming, maildrop, private, public, saved, trace - # UID = root(0): dev, etc, lib, pid, usr - # GID = postdrop(103): maildrop, public - # GID for all other directories is root(0) - # NOTE: `spool-postfix/private/` will be set to `postfix:postfix` when Postfix starts / restarts - # Set most common ownership: - chown -R postfix:root "${DMS_STATE_DIR}/spool-postfix" - chown root:root "${DMS_STATE_DIR}/spool-postfix" - - # These two require the postdrop(103) group: - chgrp -R postdrop "${DMS_STATE_DIR}"/spool-postfix/{maildrop,public} - - # These permissions rely on the `postdrop` binary having the SGID bit set. - # Ref: https://github.com/docker-mailserver/docker-mailserver/pull/3625 - chmod 730 "${DMS_STATE_DIR}/spool-postfix/maildrop" - chmod 710 "${DMS_STATE_DIR}/spool-postfix/public" + if [[ ! -d ${DMS_STATE_DIR:?DMS_STATE_DIR is not set} ]]; then + _log 'debug' "'${DMS_STATE_DIR}' is not present - not adjusting state permissions" + return 0 fi + + # This ensures the user and group of the files from the external mount have their + # numeric ID values in sync. New releases where the installed packages order changes + # can change the values in the Docker image, causing an ownership mismatch. + # NOTE: More details about users and groups added during image builds are documented here: + # https://github.com/docker-mailserver/docker-mailserver/pull/3011#issuecomment-1399120252 + _log 'trace' "Fixing ${DMS_STATE_DIR}/* permissions" + [[ ${ENABLE_AMAVIS} -eq 1 ]] && chown -R amavis:amavis "${DMS_STATE_DIR}/lib-amavis" + [[ ${ENABLE_CLAMAV} -eq 1 ]] && chown -R clamav:clamav "${DMS_STATE_DIR}/lib-clamav" + [[ ${ENABLE_FETCHMAIL} -eq 1 ]] && chown -R fetchmail:nogroup "${DMS_STATE_DIR}/lib-fetchmail" + [[ ${ENABLE_MTA_STS} -eq 1 ]] && chown -R _mta-sts:_mta-sts "${DMS_STATE_DIR}/lib-mta-sts" + [[ ${ENABLE_POSTGREY} -eq 1 ]] && chown -R postgrey:postgrey "${DMS_STATE_DIR}/lib-postgrey" + [[ ${ENABLE_RSPAMD} -eq 1 ]] && chown -R _rspamd:_rspamd "${DMS_STATE_DIR}/lib-rspamd" + [[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && chown -R redis:redis "${DMS_STATE_DIR}/lib-redis" + [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]] && chown -R debian-spamd:debian-spamd "${DMS_STATE_DIR}/lib-spamassassin" + + chown -R root:root "${DMS_STATE_DIR}/lib-logrotate" + chown -R postfix:postfix "${DMS_STATE_DIR}/lib-postfix" + + # NOTE: The Postfix spool location has mixed owner/groups to take into account: + # UID = postfix(101): active, bounce, corrupt, defer, deferred, flush, hold, incoming, maildrop, private, public, saved, trace + # UID = root(0): dev, etc, lib, pid, usr + # GID = postdrop(103): maildrop, public + # GID for all other directories is root(0) + # NOTE: `spool-postfix/private/` will be set to `postfix:postfix` when Postfix starts / restarts + # Set most common ownership: + chown -R postfix:root "${DMS_STATE_DIR}/spool-postfix" + chown root:root "${DMS_STATE_DIR}/spool-postfix" + + # These two require the postdrop(103) group: + chgrp -R postdrop "${DMS_STATE_DIR}"/spool-postfix/{maildrop,public} + + # These permissions rely on the `postdrop` binary having the SGID bit set. + # Ref: https://github.com/docker-mailserver/docker-mailserver/pull/3625 + chmod 730 "${DMS_STATE_DIR}/spool-postfix/maildrop" + chmod 710 "${DMS_STATE_DIR}/spool-postfix/public" }