sender_acl can be set through edit/mailbox but was never returned by
get/mailbox, so an API client could not read back what it had written,
and get/mailbox/all / get/mailbox/{mailbox} both omitted it.
Add the mailbox's internal send-as ACL (the sender_acl table rows with
external = 0) to mailbox_details as sender_acl, an array of send_as
values, mirroring the field edit/mailbox accepts. It is added in the same
block as the other detailed fields, so the lightweight get/mailbox/reduced
endpoint is unaffected.
Fixes#7011
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three defects in add/time_limited_alias:
The description was read as $_data['description'] without a guard. When a
client omits it, null is bound to spamalias.description, which is TEXT NOT
NULL, so the insert raises a PDOException. The global exception handler is
terminal, so process_add_return() never echoes anything and the caller sees
HTTP 200 with an empty body while no alias was created. Default it to an
empty string instead.
The validity guard used a single condition whose else branch also caught the
success case, so every valid validity was overwritten with the 8760 hour
default and the parameter did nothing. Only invalid values were rejected.
Nest the range check so a valid value survives.
The OpenAPI spec documented only username and domain, while the code also
reads description, validity and permanent. Spec driven clients therefore
could not construct a working request. Document all three.
spamalias.description is the only NOT NULL description column in the schema,
which is why the same unguarded read in add/domain and add/resource does not
fail, both of those columns are nullable.
This does not change the generic exception handling. A database error is
still swallowed into an empty HTTP 200, and the message the handler builds
carries the raw PDOException, so surfacing it to API clients would need
sanitising first. That is left for a separate change.
Refs #7287
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The HTTP-to-HTTPS redirect server block bound `listen [::]:{{ HTTP_PORT }}
default_server` unconditionally, while every other IPv6 listen directive in
this template is guarded by `{% if ENABLE_IPV6 %}`. On hosts where IPv6 is
disabled at the kernel level, nginx cannot bind `::` and fails to start.
This only triggers when HTTP_REDIRECT is enabled and ENABLE_IPV6 is false,
which is why it survives testing with ENABLE_IPV6=false alone.
Guard the directive like the other six IPv6 listeners in the template.
Refs #7296
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
alpine does not output ms using date, therefore we use perl to get a more
accurate measurement of the dns response time.
The script output is now even more similar to nagios check_dns.
Existing mailcow installations were Not receiving the ACME_DNS_CHALLENGE,
ACME_DNS_PROVIDER, and ACME_ACCOUNT_EMAIL options when running update.sh, as they were only defined in generate_config.sh for new installations.
- Add view_autodiscover.sh helper script for testing autodiscover responses
- Support -h/--help flag for usage information
- Support -d/--domain flag to override autodiscover target (useful for testing)
- Auto-detect xmllint availability for formatted output
- Email validation with regex
- Interactive mode if no email provided
- Display response length for debugging
- Replace hardcoded error IDs with random values (1-10 billion range) for better debugging
- Cast SimpleXMLElement email to string before SQL query to prevent type errors
- Qualify ambiguous 'active' column with table names in JOIN query
- Add proper error XML response for database errors instead of die()
- Ensure all error paths return complete XML documents
- Remove HTTP Basic Authentication requirement from autodiscover.php
- Extract email address from XML request body instead of AUTH headers
- Validate mailbox existence and active status before returning config
- Improve security by eliminating password transmission
- Add comprehensive error handling for invalid/inactive mailboxes
- Follow industry standards (Microsoft, Google, Apple)
- Maintain backward compatibility with existing email clients
- Keep full logging functionality in Redis AUTODISCOVER_LOG
This change enhances security while improving user experience and
follows modern email client configuration best practices.
* Allow making spam aliases permanent
* added german translation
* updated Spamalias Twig + Rename in Spam Alias
* compose: update image tags to align to vendor version
---------
Co-authored-by: DerLinkman <niklas.meyer@servercow.de>
* Replace pigz with zstd for backup compression
This change replaces pigz (parallel gzip) with zstd (Zstandard) as the
compression algorithm for mailcow backups while maintaining full backward
compatibility with existing .tar.gz backups.
Benefits:
- Better compression ratios (12-37% improvement in tests)
- Improved compression speed with modern algorithm
- Maintains rsyncable functionality for incremental backups
- Full backward compatibility for restoring old .tar.gz backups
- Wide industry adoption and active development
Changes:
- Backup compression: pigz --rsyncable -p → zstd --rsyncable -T
- Backup decompression: pigz -d -p → zstd -d -T
- File extensions: .tar.gz → .tar.zst
- Added get_archive_info() function for intelligent format detection
- Updated backup Dockerfile to install zstd alongside pigz
- Restore function now auto-detects and handles both formats
- Updated FILE_SELECTION regex to recognize both .tar.zst and .tar.gz
- Updated comments to reflect new file extension
Backward Compatibility:
- Restore automatically detects .tar.zst (preferred) or .tar.gz (legacy)
- Existing .tar.gz backups can still be restored without issues
- pigz remains installed in backup image for legacy support
- Graceful fallback if backup file format not found
Testing:
- Added comprehensive test suite (test_backup_and_restore.sh)
- 12 automated tests covering all scenarios:
* Backup creation (both formats)
* Restore (both formats)
* Format detection and priority
* Error handling (missing files, empty dirs)
* Content integrity verification
* Multi-threading configuration
* Large file compression (8.59 MB realistic data)
Test Results:
✓ zstd compression working
✓ pigz compression working (legacy)
✓ zstd decompression working
✓ pigz decompression working (backward compatible)
✓ Archive detection working
✓ Content integrity verified
✓ Format priority correct (.tar.zst preferred)
✓ Error handling for missing files
✓ Error handling for empty directories
✓ Multi-threading configuration verified
✓ Large file compression: 37.05% improvement
✓ Small file compression: 12.18% improvement
* move testing script into development folder
---------
Co-authored-by: DerLinkman <niklas.meyer@servercow.de>
Autodiscover and autoconfig domains (autodiscover.*, autoconfig.*) are intended solely for client autoconfiguration endpoints and should not display the mailcow login page. This change check the hostname and disables unauthenticated users from seeing the login page on those domains; HTTP 404 response is returned when necessary.
This commit fixes two bugs in the cold-standby script:
1. Support digits in COMPOSE_PROJECT_NAME
The script was stripping digits from COMPOSE_PROJECT_NAME, while
backup_and_restore.sh (fixed in a71d991c) correctly supports them.
Added '0-9' to the tr character set to align behavior.
2. Support docker-compose.override.yml on remote
Lines 172 and 287 explicitly used '-f docker-compose.yml' which
causes Docker Compose to ignore docker-compose.override.yml even
when present. Changed to 'cd && compose' pattern (matching line 296)
to auto-discover override files.
Impact: Users with custom volumes/services in override file would
experience silent failures - volumes not created, images not pulled,
data syncing to wrong locations.
Both fixes ensure cold-standby works correctly with standard Docker
Compose conventions and user customizations.
Co-authored-by: Christophe Neuerburg <c.neuerburg@sdsys.ch>
* [Rspamd] Do not increment rate limit for emails from user to himself
* Lowercase username and recipient address for comparison
Normalize username and recipient address comparison to lowercase.
* watchdog: use dig instead of check_dns
check_dns is slower and uses more system resources,
dig wrapped in a script is a more performant approach and uses
fewer system resources
* added debug mode + compose image bump
---------
Co-authored-by: maxi322 <maxi322@users.noreply.github.com>
Co-authored-by: DerLinkman <niklas.meyer@servercow.de>
* postfix: split postfix-tlspol service into new container
* postfix-tls-pol: added debug mode
* pf-tlspol: removed obsoleted standalone conf from Dockerfiles
* pf-tlspol: use git instead of wget
Previously imapsync only attached the timeout1 / timeout2 arguments if the argument was negative (which is not even possible). Now the argument is added for every positive number.
Fixes#6590
* [DB][Web] optimize qhandler by keeping SHA2(id+qid) in new column quarantine.qhash, for feature #6555, might also help with #6361
* rspamd: only add qhash to new entries while passing rspamd not all existing
* compose: bump dovecot image + push to registry
---------
Co-authored-by: DerLinkman <niklas.meyer@servercow.de>
The return value of the function caused a warning
in header.inc.php:42 if no additional links were set.
header.inc.php is the only caller of this function,
thus it is safe to return an empty array here.
* Remove Olefy settings file from rspamd configuration
* Have rspamd container generate Olefy settings file at startup if not disabled
Signed-off-by: Marvin A. Ruder <signed@mruder.dev>
* dkim: Add support for 3072 and 4096 bit RSA keys
Signed-off-by: Marvin A. Ruder <signed@mruder.dev>
* php: added missing ; in dkim function
* php: make 4096 DKIM default
* db: update schema to set dkim 4096 as default
* Revert "db: update schema to set dkim 4096 as default"
This reverts commit 790b40a695.
* Revert "php: make 4096 DKIM default"
This reverts commit 7e643376c7.
---------
Signed-off-by: Marvin A. Ruder <signed@mruder.dev>
Co-authored-by: DerLinkman <niklas.meyer@servercow.de>
* Update generate_config.sh version checking for wider compatibility
fix: replace `grep -oP` with `grep -oE` for broader compatibility
The `-P` option (Perl-compatible regex) is not supported in all versions of `grep`, particularly the default BSD `grep` on macOS. This change replaces `-P` with `-E` (extended regex), which is more widely available and ensures compatibility across different environments.
Tested on macOS and Linux.
* Update generate_config.sh to remove use of platform dependent grep
Replaced version checking using free-form text. Instead, uses Docker’s built-in templating instead of parsing free-form text. This gives cross-platform consistency without dependency on particular versions of grep.
After new volumes were added to docker-compose.yml in the sogo-mailcow container, it is necessary to include the specified files in the path, in order for docker to correctly start after running `docker compose up` command, otherwise error will appear, as necessary files would be missing.
The files uploaded are original SOGo UI elements, obtained from the sogo-mailcow container. Whenever users will need to change the UI elements, they would just need to change these files. Hence simplifying the process.
This commit includes the addition of 3 lines, in the volumes part of the sogo-mailcow container, to allow for better customisation of the user interface on the web client page.
* fts-flatcurve: inital implementation
* fts: removed solr from compose.yml
* flatcurve: added heap and proc logic to dovecot
* added logic for update.sh & generate for Flatcurve
* delete old iteration of fts-flatcurve.conf
* updated default fts.conf
* updated .gitignore to exclude fts.conf for further git updates
* Remove autogeneration of fts.conf (disable override)
* cleanup all left solr stuff
* renamed SKIP_FLATCURVE to SKIP_FTS
* cleanup leftovers solr in lang files
* moved lazy_expunge plugin only to mail_plugins
* added fts timeout value
* compose: remove dev image of dovecot
* updated japanese translation
This AJAX request sends form data in $_GET request query. This is problematic and unreliable when validating superrrr loooooong conditions, especially in environments that use reverse-proxy.
Been having this problem and this PR solves it. :)
This is related to https://github.com/mailcow/mailcow-dockerized/issues/5970 and https://community.mailcow.email/d/2126-backup-restore/2
It adds `docker compose create` to the script which gets executed directly after the sync of the mailcow-dockerized directory. This way the Docker daemon on the remote side creates everything and we get rid of the warning "volume "XYZ" already exists but was not created by Docker Compose. Use `external: true` to use an existing volume"
This is helpful if you use the create-cold-standby.sh script to migrate your mailcow installation to another server and don't want to get those warnings after migration.
Co-authored-by: Niklas Meyer <niklas.meyer@servercow.de>
* Add initial Japanese language files
* Reordered language list: moved Japanese (日本語) below Italian (Italiano)
---------
Co-authored-by: Tatsuya Yokota <git@acoustype.com>
In the last months and years, the default `mime_types.conf` of rspamd has changed and it might be also useful to make some adjustments to the weight of certain file extensions.
This PR is removing all file extensions from `mime_types.conf` which are already in rspamd's default configuration at [rspamd/src/plugins/lua/mime_types.lua](https://github.com/rspamd/rspamd/blob/master/src/plugins/lua/mime_types.lua). If file extension is not present or has a different score compared to rspamd default, it is still in the list.
There are also a few major differences to certain file extensions, which might be useful to discuss and carefully adjust. For example, `.exe` files are rated very 'badly' due to high chance of being malicious, so are other extensions like `bat`, `cmd`, etc.
Current suggestion:
```lua
# Extensions that are treated as 'bad'
# Number is score multiply factor
bad_extensions = {
apk = 4,
appx = 4,
appxbundle = 4,
bat = 8,
cab = 20,
cmd = 8,
com = 20,
diagcfg = 4,
diagpack = 4,
dmg = 8,
ex = 20,
ex_ = 20,
exe = 20,
img = 4,
jar = 8,
jnlp = 8,
js = 8,
jse = 8,
lnk = 20,
mjs = 8,
msi = 4,
msix = 4,
msixbundle = 4,
ps1 = 8,
scr = 20,
sct = 20,
vb = 20,
vbe = 20,
vbs = 20,
vhd = 4,
py = 4,
reg = 8,
scf = 8,
vhdx = 4,
};
# Extensions that are particularly penalized for archives
bad_archive_extensions = {
pptx = 0.5,
docx = 0.5,
xlsx = 0.5,
pdf = 1.0,
jar = 12,
jnlp = 12,
bat = 12,
cmd = 12,
};
# Used to detect another archive in archive
archive_extensions = {
tar = 1,
['tar.gz'] = 1,
};
```
**As a important reminder**: For all remaining and additional file extensions and score weights, please check above default rspamd configuration!
* Decode rspamd-subject for pushover notifications
Fixes#6028
* Apply iconv_mime_decode to the quarantine function as well
This might contain utf-8 encoded text as well
* Moved the iconv_mime_decode "fix" back to pipe.php
* Update `dockerapi/Dockerfile` CMD from shell to exec format
* Update `postfix/Dockerfile` CMD from shell to exec format
* Update `sogo/Dockerfile` CMD from shell to exec format
* Update `unbound/Dockerfile` CMD from shell to exec format
* Update `watchdog/Dockerfile` CMD from shell to exec format
* postfix: removed TLS1.0/1.1 support (natively)
* postfix: upgrade to deb12 + pf to 3.7.9
* compose: increased postfix tag
* postfix: shortened TLS syntax with new format of 3.6+
* Update main.cf
In order to avoid unnecessary log lines, changed:
smtpd_discard_ehlo_keywords = chunking
to this one:
# The non-logging alternative:
smtpd_discard_ehlo_keywords = chunking, silent-discard
Update main.cf to remove unnecessary log lines in Postfix log
* Add ACME_DONT_FETCH_CERTS_FOR_HTTP_SUBDOMAINS to acme.sh
* Add ACME_DONT_FETCH_CERTS_FOR_HTTP_SUBDOMAINS to docker-compose.yml
* Add ACME_DONT_FETCH_CERTS_FOR_HTTP_SUBDOMAINS to generate_config.sh
* Add ACME_DONT_FETCH_CERTS_FOR_HTTP_SUBDOMAINS to update.sh
* AUTODISCOVER_SAN instead of long string
default on,
default is fetching certs for auto{discover,conf}
* AUTODISCOVER_SAN instead of long string
also flipped
* AUTODISCOVER_SAN instead of long string
flipped default meaning
* fix explanation for AUTODISCOVER_SAN
* AUTODISCOVER_SAN instead of long string
and flipped meaning of the bool
* fix AUTODISCOVER_SAN explanation
* Merge branch 'mailcow:staging' into staging
* update.sh: corrected syntax for mailcow.conf insertion
description:Prior to placing the issue, please check following:** *(fill out each checkbox with an `X` once done)*
label:Checklist prior issue creation
description:Prior to creating the issue...
options:
- label:... I understand that not following the below instructions will result in immediate closure and/or deletion of my issue.
- label:I understand that failure to follow below instructions may cause this issue to be closed.
required:true
- label:... I have understood that this bug report is dedicated for bugs, and not for support-related inquiries.
- label:I understand that vague, incomplete or inaccurate information may cause this issue to be closed.
required:true
- label:... I have understood that answers are voluntary and community-driven, and not commercial support.
- label:I understand that this form is intended solely for reporting software bugs and not for support-related inquiries.
required:true
- label:... I have verified that my issue has not been already answered in the past. I also checked previous [issues](https://github.com/mailcow/mailcow-dockerized/issues).
- label:I understand that all responses are voluntary and community-driven, and do not constitute commercial support.
required:true
- label:I confirm that I have reviewed previous [issues](https://github.com/mailcow/mailcow-dockerized/issues) to ensure this matter has not already been addressed.
required:true
- label:I confirm that my environment meets all [prerequisite requirements](https://docs.mailcow.email/getstarted/prerequisite-system/) as specified in the official documentation.
required:true
- type:textarea
attributes:
label:Description
description:Please provide a brief description of the bug in 1-2 sentences. If applicable, add screenshots to help explain your problem. Very useful for bugs in mailcow UI.
render:plain text
description:Please provide a brief description of the bug. If applicable, add screenshots to help explain your problem. (Very useful for bugs in mailcow UI.)
validations:
required:true
- type:textarea
attributes:
label:"Steps to reproduce:"
description:"Please describe the steps to reproduce the bug. Screenshots can be added, if helpful."
placeholder:|-
1. ...
2. ...
3. ...
validations:
required:true
- type:textarea
@@ -36,45 +49,36 @@ body:
render:plain text
validations:
required:true
- type:textarea
attributes:
label:"Steps to reproduce:"
description:"Please describe the steps to reproduce the bug. Screenshots can be added, if helpful."
render:plain text
placeholder:|-
1. ...
2. ...
3. ...
validations:
required:true
- type:markdown
attributes:
value:|
## System information
### In this stage we would kindly ask you to attach general system information about your setup.
In this stage we would kindly ask you to attach general system information about your setup.
# Contribution Guidelines (Last modified on 18th December 2023)
# Contribution Guidelines
**_Last modified on 12th November 2025_**
First of all, thank you for wanting to provide a bugfix or a new feature for the mailcow community, it's because of your help that the project can continue to grow!
## Pull Requests (Last modified on 18th December 2023)
As we want to keep mailcow's development structured we setup these Guidelines which helps you to create your issue/pull request accordingly.
**PLEASE NOTE, THAT WE WILL CLOSE ISSUES/PULL REQUESTS IF THEY DON'T FULFILL OUR WRITTEN GUIDELINES WRITTEN INSIDE THIS DOCUMENT**. So please check this guidelines before you propose a Issue/Pull Request.
## Topics
- [Pull Requests](#pull-requests)
- [Issue Reporting](#issue-reporting)
- [Guidelines](#issue-reporting-guidelines)
- [Issue Report Guide](#issue-report-guide)
## Pull Requests
**_Last modified on 15th August 2024_**
However, please note the following regarding pull requests:
1.**ALWAYS** create your PR using the staging branch of your locally cloned mailcow instance, as the pull request will end up in said staging branch of mailcow once approved. Ideally, you should simply create a new branch for your pull request that is named after the type of your PR (e.g. `feat/` for function updates or `fix/` for bug fixes) and the actual content (e.g. `sogo-6.0.0` for an update from SOGo to version 6 or `html-escape` for a fix that includes escaping HTML in mailcow).
2.Please **keep** this pull request branch **clean** and free of commits that have nothing to do with the changes you have made (e.g. commits from other users from other branches). *If you make changes to the `update.sh` script or other scripts that trigger a commit, there is usually a developer mode for clean working in this case.
3.**Test your changes before you commit them as a pull request.** <ins>If possible</ins>, write a small **test log** or demonstrate the functionality with a **screenshot or GIF**. *We will of course also test your pull request ourselves, but proof from you will save us the question of whether you have tested your own changes yourself.*
4.Please **ALWAYS** create the actual pull request against the staging branch and **NEVER** directly against the master branch. *If you forget to do this, our moobot will remind you to switch the branch to staging.*
5.Wait for a merge commit: It may happen that we do not accept your pull request immediately or sometimes not at all for various reasons. Please do not be disappointed if this is the case. We always endeavor to incorporate any meaningful changes from the community into the mailcow project.
6.If you are planning larger and therefore more complex pull requests, it would be advisable to first announce this in a separate issue and then start implementing it after the idea has been accepted in order to avoid unnecessary frustration and effort!
2.**ALWAYS** report/request issues/features in the english language, even though mailcow is a german based company. This is done to allow other GitHub users to reply to your issues/requests too which did not speak german or other languages besides english.
3.Please **keep** this pull request branch **clean** and free of commits that have nothing to do with the changes you have made (e.g. commits from other users from other branches). *If you make changes to the `update.sh` script or other scripts that trigger a commit, there is usually a developer mode for clean working in this case.*
4.**Test your changes before you commit them as a pull request.** <ins>If possible</ins>, write a small **test log** or demonstrate the functionality with a **screenshot or GIF**. *We will of course also test your pull request ourselves, but proof from you will save us the question of whether you have tested your own changes yourself.*
5.**Please use** the pull request template we provide once creating a pull request. *HINT: During editing you encounter comments which looks like: `<!-- CONTENT -->`. These can be removed or kept, as they will not rendered later on GitHub! Please only create actual content without the said comments.*
6.Please **ALWAYS** create the actual pull request against the staging branch and **NEVER** directly against the master branch. *If you forget to do this, our moobot will remind you to switch the branch to staging.*
7. Wait for a merge commit: It may happen that we do not accept your pull request immediately or sometimes not at all for various reasons. Please do not be disappointed if this is the case. We always endeavor to incorporate any meaningful changes from the community into the mailcow project.
8. If you are planning larger and therefore more complex pull requests, it would be advisable to first announce this in a separate issue and then start implementing it after the idea has been accepted in order to avoid unnecessary frustration and effort!
9. If your PR requires a Docker image rebuild (changes to Dockerfiles or files in data/Dockerfiles/), update the image tag in docker-compose.yml. Use the base-image versioning (e.g. ghcr.io/mailcow/sogo:5.12.4 → :5.12.5 for version bumps; append a letter for patch fixes, e.g. :5.12.4a). Follow this scheme.
---
## Issue Reporting (Last modified on 18th December 2023)
## Issue Reporting
**_Last modified on 12th November 2025_**
If you plan to report a issue within mailcow please read and understand the following rules:
### Security disclosures / Security-related fixes
- Security vulnerabilities and security fixes must always be reported confidentially first to the contact address specified in SECURITY.md before they are integrated, published, or publicly disclosed in issues/PRs. Please wait for a response from the specified contact to ensure coordinated and responsible disclosure.
### Issue Reporting Guidelines
1.**ONLY** use the issue tracker for bug reports or improvement requests and NOT for support questions. For support questions you can either contact the [mailcow community on Telegram](https://docs.mailcow.email/#community-support-and-chat) or the mailcow team directly in exchange for a [support fee](https://docs.mailcow.email/#commercial-support).
2.**ONLY** report an error if you have the **necessary know-how (at least the basics)** for the administration of an e-mail server and the usage of Docker. mailcow is a complex and fully-fledged e-mail server including groupware components on a Docker basement and it requires a bit of technical know-how for debugging and operating.
3.**ONLY** report bugs that are contained in the latest mailcow release series. *The definition of the latest release series includes the last major patch (e.g. 2023-12) and all minor patches (revisions) below it (e.g. 2023-12a, b, c etc.).* New issue reports published starting from January 1, 2024 must meet this criterion, as versions below the latest releases are no longer supported by us.
4.When reporting a problem, please be as detailed as possible and include even the smallest changes to your mailcow installation. Simply fill out the corresponding bug report form in detail and accurately to minimize possible questions.
5.**Before you open an issue/feature request**, please first check whether a similar request already exists in the mailcow tracker on GitHub. If so, please include yourself in this request.
6.When you create a issue/feature request: Please note that the creation does <ins>**not guarantee an instant implementation or fix by the mailcow team or the community**</ins>.
7.Please **ALWAYS** anonymize any sensitive information in your bug report or feature request before submitting it.
3.**ALWAYS** report/request issues/features in the english language, even though mailcow is a german based company. This is done to allow other GitHub users to reply to your issues/requests too which did not speak german or other languages besides english.
4.**ONLY** report bugs that are contained in the latest mailcow release series. *The definition of the latest release series includes the last major patch (e.g. 2023-12) and all minor patches (revisions) below it (e.g. 2023-12a, b, c etc.).* New issue reports published starting from January 1, 2024 must meet this criterion, as versions below the latest releases are no longer supported by us.
5.When reporting a problem, please be as detailed as possible and include even the smallest changes to your mailcow installation. Simply fill out the corresponding bug report form in detail and accurately to minimize possible questions.
6.**Before you open an issue/feature request**, please first check whether a similar request already exists in the mailcow tracker on GitHub. If so, please include yourself in this request.
7.When you create a issue/feature request: Please note that the creation does <ins>**not guarantee an instant implementation or fix by the mailcow team or the community**</ins>.
8. Please **ALWAYS** anonymize any sensitive information in your bug report or feature request before submitting it.
### Quick guide to reporting problems:
### Issue Report Guide
1. Read your logs; follow them to see what the reason for your problem is.
2. Follow the leads given to you in your logfiles and start investigating.
3. Restarting the troubled service or the whole stack to see if the problem persists.
@@ -36,4 +59,4 @@ If you plan to report a issue within mailcow please read and understand the foll
6. [Create an issue](https://github.com/mailcow/mailcow-dockerized/issues/new/choose) over at our GitHub repository if you think your problem might be a bug or a missing feature you badly need. But please make sure, that you include **all the logs** and a full description to your problem.
7. Ask your questions in our community-driven [support channels](https://docs.mailcow.email/#community-support-and-chat).
## When creating an issue/feature request or a pull request, you will be asked to confirm these guidelines.
## When creating an issue/feature request or a pull request, you will be asked to confirm these guidelines.
@@ -13,6 +13,25 @@ You can also [get a SAL](https://www.servercow.de/mailcow?lang=en#sal) which is
Or just spread the word: moo.
## Many thanks to our GitHub Sponsors ❤️
A big thank you to everyone supporting us on GitHub Sponsors—your contributions mean the world to us! Special thanks to the following amazing supporters:
if sed --help 2>&1| head -n 1| grep -q -i "busybox";thenecho -e "${LIGHT_RED}BusyBox sed detected, please install gnu sed, \"apk add --no-cache --upgrade sed\"${NC}";exit 1;fi
}
get_docker_version(){
# Check Docker Version (need at least 24.X)
docker_version=$(docker version --format '{{.Server.Version}}'| cut -d '.' -f 1)
}
get_compose_type(){
if docker compose > /dev/null 2>&1;then
if docker compose version --short | grep -e "^[2-9]\." -e "^v[2-9]\." -e "^[1-9][0-9]\." -e "^v[1-9][0-9]\." > /dev/null 2>&1;then
COMPOSE_VERSION=native
COMPOSE_COMMAND="docker compose"
if[["$caller"=="update.sh"]];then
sed -i 's/^DOCKER_COMPOSE_VERSION=.*/DOCKER_COMPOSE_VERSION=native/'"$SCRIPT_DIR/mailcow.conf"
echo -e "\e[33mYour server's public IP uses an AS that is blocked by Spamhaus to use their DNS public blocklists for Postfix.\e[0m"
echo -e "\e[33mmailcow did not detected a value for the variable SPAMHAUS_DQS_KEY inside mailcow.conf!\e[0m"
sleep 2
echo""
echo -e "\e[33mTo use the Spamhaus DNS Blocklists again, you will need to create a FREE account for their Data Query Service (DQS) at: https://www.spamhaus.com/free-trial/sign-up-for-a-free-data-query-service-account\e[0m"
echo -e "\e[33mOnce done, enter your DQS API key in mailcow.conf and mailcow will do the rest for you!\e[0m"
echo""
sleep 2
else
echo -e "\e[33mYour server's public IP uses an AS that is blocked by Spamhaus to use their DNS public blocklists for Postfix.\e[0m"
echo -e "\e[32mmailcow detected a Value for the variable SPAMHAUS_DQS_KEY inside mailcow.conf. Postfix will use DQS with the given API key...\e[0m"
fi
elif["$response" -eq 200];then
echo -e "\e[33mCheck completed! Your IP is \e[32mclean\e[0m"
elif["$response" -eq 429];then
echo -e "\e[33mCheck completed! \e[31mYour IP seems to be rate limited on the ASN Check service... please try again later!\e[0m"
else
echo -e "\e[31mCheck failed! \e[0mMaybe a DNS or Network problem?\e[0m"
echo"The following unused mailcow images were found:"
for id in "${IMGS_TO_DELETE[@]}";do
echo"${IMAGES_INFO[$id]} ($id)"
done
if[ -z "$FORCE"];then
read -r -p "Do you want to delete them to free up some space? [y/N] " response
if[["$response"=~ ^([yY][eE][sS]|[yY])+$ ]];then
docker rmi ${IMGS_TO_DELETE[*]}
else
echo"OK, skipped."
fi
else
echo"Running in forced mode! Force removing old mailcow images..."
docker rmi ${IMGS_TO_DELETE[*]}
fi
echo -e "\e[32mFurther cleanup...\e[0m"
echo"If you want to cleanup further garbage collected by Docker, please make sure all containers are up and running before cleaning your system by executing \"docker system prune\""
echo'# Bind SQL to 127.0.0.1 on port 13306' >> mailcow.conf
echo"SQL_PORT=127.0.0.1:13306" >> mailcow.conf
;;
API_KEY)
echo'# Create or override API key for web UI' >> mailcow.conf
echo"#API_KEY=" >> mailcow.conf
;;
API_KEY_READ_ONLY)
echo'# Create or override read-only API key for web UI' >> mailcow.conf
echo"#API_KEY_READ_ONLY=" >> mailcow.conf
;;
API_ALLOW_FROM)
echo'# Must be set for API_KEY to be active' >> mailcow.conf
echo'# IPs only, no networks (networks can be set via UI)' >> mailcow.conf
echo"#API_ALLOW_FROM=" >> mailcow.conf
;;
SNAT_TO_SOURCE)
echo'# Use this IPv4 for outgoing connections (SNAT)' >> mailcow.conf
echo"#SNAT_TO_SOURCE=" >> mailcow.conf
;;
SNAT6_TO_SOURCE)
echo'# Use this IPv6 for outgoing connections (SNAT)' >> mailcow.conf
echo"#SNAT6_TO_SOURCE=" >> mailcow.conf
;;
MAILDIR_GC_TIME)
echo'# Garbage collector cleanup' >> mailcow.conf
echo'# Deleted domains and mailboxes are moved to /var/vmail/_garbage/timestamp_sanitizedstring' >> mailcow.conf
echo'# How long should objects remain in the garbage until they are being deleted? (value in minutes)' >> mailcow.conf
echo'# Check interval is hourly' >> mailcow.conf
echo'MAILDIR_GC_TIME=1440' >> mailcow.conf
;;
ACL_ANYONE)
echo'# Set this to "allow" to enable the anyone pseudo user. Disabled by default.' >> mailcow.conf
echo'# When enabled, ACL can be created, that apply to "All authenticated users"' >> mailcow.conf
echo'# This should probably only be activated on mail hosts, that are used exclusively by one organisation.' >> mailcow.conf
echo'# Otherwise a user might share data with too many other users.' >> mailcow.conf
echo'ACL_ANYONE=disallow' >> mailcow.conf
;;
FTS_HEAP)
echo'# Dovecot Indexing (FTS) Process maximum heap size in MB, there is no recommendation, please see Dovecot docs.' >> mailcow.conf
echo'# Flatcurve is used as FTS Engine. It is supposed to be pretty efficient in CPU and RAM consumption.' >> mailcow.conf
echo'# Please always monitor your Resource consumption!' >> mailcow.conf
echo"FTS_HEAP=128" >> mailcow.conf
;;
SKIP_FTS)
echo'# Skip FTS (Fulltext Search) for Dovecot on low-memory, low-threaded systems or if you simply want to disable it.' >> mailcow.conf
echo"# Dovecot inside mailcow use Flatcurve as FTS Backend." >> mailcow.conf
echo"SKIP_FTS=y" >> mailcow.conf
;;
FTS_PROCS)
echo'# Controls how many processes the Dovecot indexing process can spawn at max.' >> mailcow.conf
echo'# Too many indexing processes can use a lot of CPU and Disk I/O' >> mailcow.conf
echo'# Please visit: https://doc.dovecot.org/configuration_manual/service_configuration/#indexer-worker for more informations' >> mailcow.conf
echo"FTS_PROCS=1" >> mailcow.conf
;;
ENABLE_SSL_SNI)
echo'# Create seperate certificates for all domains - y/n' >> mailcow.conf
echo'# this will allow adding more than 100 domains, but some email clients will not be able to connect with alternative hostnames' >> mailcow.conf
echo'# see https://wiki.dovecot.org/SSL/SNIClientSupport' >> mailcow.conf
echo"ENABLE_SSL_SNI=n" >> mailcow.conf
;;
SKIP_SOGO)
echo'# Skip SOGo: Will disable SOGo integration and therefore webmail, DAV protocols and ActiveSync support (experimental, unsupported, not fully implemented) - y/n' >> mailcow.conf
echo"SKIP_SOGO=n" >> mailcow.conf
;;
MAILDIR_SUB)
echo'# MAILDIR_SUB defines a path in a users virtual home to keep the maildir in. Leave empty for updated setups.' >> mailcow.conf
echo"#MAILDIR_SUB=Maildir" >> mailcow.conf
echo"MAILDIR_SUB=" >> mailcow.conf
;;
WATCHDOG_NOTIFY_WEBHOOK)
echo'# Send notifications to a webhook URL that receives a POST request with the content type "application/json".' >> mailcow.conf
echo'# You can use this to send notifications to services like Discord, Slack and others.' >> mailcow.conf
echo'# Additional server names for mailcow UI' >> mailcow.conf
echo'#' >> mailcow.conf
echo'# Specify alternative addresses for the mailcow UI to respond to' >> mailcow.conf
echo'# This is useful when you set mail.* as ADDITIONAL_SAN and want to make sure mail.maildomain.com will always point to the mailcow UI.' >> mailcow.conf
echo'# If the server name does not match a known site, Nginx decides by best-guess and may redirect users to the wrong web root.' >> mailcow.conf
echo'# You can understand this as server_name directive in Nginx.' >> mailcow.conf
echo'# Comma separated list without spaces! Example: ADDITIONAL_SERVER_NAMES=a.b.c,d.e.f' >> mailcow.conf
echo'# Prevent netfilter from setting an iptables/nftables rule to isolate the mailcow docker network - y/n' >> mailcow.conf
echo'# CAUTION: Disabling this may expose container ports to other neighbors on the same subnet, even if the ports are bound to localhost' >> mailcow.conf
echo'# Redirect HTTP connections to HTTPS - y/n' >> mailcow.conf
echo'HTTP_REDIRECT=n' >> mailcow.conf
;;
ENABLE_IPV6)
echo'# IPv6 Controller Section' >> mailcow.conf
echo'# This variable controls the usage of IPv6 within mailcow.' >> mailcow.conf
echo'# Can either be true or false | Defaults to true' >> mailcow.conf
echo'# WARNING: MAKE SURE TO PROPERLY CONFIGURE IPv6 ON YOUR HOST FIRST BEFORE ENABLING THIS AS FAULTY CONFIGURATIONS CAN LEAD TO OPEN RELAYS!' >> mailcow.conf
echo'# A COMPLETE DOCKER STACK REBUILD (compose down && compose up -d) IS NEEDED TO APPLY THIS.' >> mailcow.conf
echoENABLE_IPV6=${IPV6_BOOL} >> mailcow.conf
;;
SKIP_CLAMD)
echo'# Skip ClamAV (clamd-mailcow) anti-virus (Rspamd will auto-detect a missing ClamAV container) - y/n' >> mailcow.conf
echo'SKIP_CLAMD=n' >> mailcow.conf
;;
SKIP_OLEFY)
echo'# Skip Olefy (olefy-mailcow) anti-virus for Office documents (Rspamd will auto-detect a missing Olefy container) - y/n' >> mailcow.conf
SQL_DOMAINS=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -e "SELECT domain FROM domain WHERE backupmx=0 and active=1" -Bs)
SQL_DOMAINS=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -e "SELECT domain FROM domain WHERE backupmx=0 and active=1" -Bs)
if[[ ! $? -eq 0]];then
log_f "Failed to read SQL domains, retrying in 1 minute..."
sleep 1m
@@ -238,18 +249,71 @@ while true; do
done<<<"${SQL_DOMAINS}"
if[[${ONLY_MAILCOW_HOSTNAME} !="y"]];then
# Fetch all domains with an active MTA-STS policy once.
unset MTA_STS_ACTIVE_DOMAINS
declare -A MTA_STS_ACTIVE_DOMAINS
if[[${AUTODISCOVER_SAN}=="y"]];then
SQL_MTA_STS_DOMAINS=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -e "SELECT domain FROM mta_sts WHERE active = 1" -Bs)
# Fetch alias domains where target domain has MTA-STS enabled
if[[${AUTODISCOVER_SAN}=="y"]];then
SQL_ALIAS_DOMAINS=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -e "SELECT ad.alias_domain FROM alias_domain ad INNER JOIN mta_sts m ON ad.target_domain = m.domain WHERE ad.active = 1 AND m.active = 1" -Bs)
cmd=["/bin/bash","-c","/usr/bin/doveadm sieve get -u '"+request_json['username'].replace("'","'\\''")+"''"+request_json['script_name'].replace("'","'\\''")+"'"]
cmd=["/bin/bash","-c","/usr/bin/doveadm sieve get -u '"+request_json['username'].replace("'","'\\''")+"''"+request_json['script_name'].replace("'","'\\''")+"'"]
MAX_AGE=$(redis-cli --raw -h redis-mailcow GET Q_MAX_AGE)
MAX_AGE=$(redis-cli --raw -h redis-mailcow -a ${REDISPASS} --no-auth-warning GET Q_MAX_AGE)
if[[ -z ${MAX_AGE}]];then
echo"Max age for quarantine items not defined"
@@ -15,6 +15,6 @@ if ! [[ ${MAX_AGE} =~ ${NUM_REGEXP} ]] ; then
exit1
fi
TO_DELETE=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -e "SELECT COUNT(id) FROM quarantine WHERE created < NOW() - INTERVAL ${MAX_AGE//[!0-9]/} DAY" -BN)
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -e "DELETE FROM quarantine WHERE created < NOW() - INTERVAL ${MAX_AGE//[!0-9]/} DAY"
TO_DELETE=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -e "SELECT COUNT(id) FROM quarantine WHERE created < NOW() - INTERVAL ${MAX_AGE//[!0-9]/} DAY" -BN)
mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -e "DELETE FROM quarantine WHERE created < NOW() - INTERVAL ${MAX_AGE//[!0-9]/} DAY"
echo"Deleted ${TO_DELETE} items from quarantine table (max age is ${MAX_AGE//[!0-9]/} days)"
-- app passwords are only available for imap, smtp, sieve and pop3 when using sasl
if req.service == "smtp" or req.service == "imap" or req.service == "sieve" or req.service == "pop3" then
local cur,errorString = con:execute(string.format([[SELECT app_passwd.id, %s_access AS has_prot_access, app_passwd.password FROM app_passwd
INNER JOIN mailbox ON mailbox.username = app_passwd.mailbox
WHERE mailbox = '%s'
AND app_passwd.active = '1'
AND mailbox.active = '1'
AND app_passwd.domain IN (SELECT domain FROM domain WHERE domain='%s' AND active='1')]], con:escape(req.service), con:escape(req.user), con:escape(req.domain)))
local row = cur:fetch ({}, "a")
while row do
if req.password_verify(req, row.password, pass) == 1 then
-- if password is valid and protocol access is 1 OR real_rip matches SOGo, proceed
if tostring(req.real_rip) == "__IPV4_SOGO__" then
cur:close()
con:close()
return dovecot.auth.PASSDB_RESULT_OK, ""
elseif row.has_prot_access == "1" then
con:execute(string.format([[REPLACE INTO sasl_log (service, app_password, username, real_rip)
meta_query=query_mysql('SELECT SHA2(CONCAT(id, qid), 256) AS qhash, id, subject, score, sender, created, action FROM quarantine WHERE notified = 0 AND rcpt = "%s" AND score < %f AND (action = "%s" OR "all" = "%s")'%(rcpt,max_score,category,category))
meta_query=query_mysql('SELECT `qhash`, id, subject, score, sender, created, action FROM quarantine WHERE notified = 0 AND rcpt = %s AND score < %s AND (action = %s OR "all" = %s)',(rcpt,max_score,category,category))
print("%s: %d of %d messages qualify for notification"%(rcpt,len(meta_query),msg_count))
iflen(meta_query)==0:
return
msg_count=len(meta_query)
env=SandboxedEnvironment()
ifr.get('Q_HTML'):
try:
template=Template(r.get('Q_HTML'))
except:
print("Error: Cannot parse quarantine template, falling back to default template.")
withopen('/templates/quarantine.tpl')asfile_:
template=Template(file_.read())
try:
template=env.from_string(r.get('Q_HTML'))
exceptException:
print("Error: Cannot parse quarantine template, falling back to default template.")
query_mysql('UPDATE quarantine SET notified = 1 WHERE id = "%d"'%(res['id']),update=True)
query_mysql('UPDATE quarantine SET notified = 1 WHERE id = %s',(res['id'],),update=True)
r.hset('Q_LAST_NOTIFIED',record['rcpt'],time_now)
break
exceptExceptionasex:
@@ -132,7 +141,7 @@ try:
print('%s'%(ex))
time.sleep(3)
records=query_mysql('SELECT IFNULL(user_acl.quarantine, 0) AS quarantine_acl, count(id) AS counter, rcpt FROM quarantine LEFT OUTER JOIN user_acl ON user_acl.username = rcpt WHERE notified = 0 AND score < %f AND rcpt in (SELECT username FROM mailbox) GROUP BY rcpt'%(max_score))
records=query_mysql('SELECT IFNULL(user_acl.quarantine, 0) AS quarantine_acl, count(id) AS counter, rcpt FROM quarantine LEFT OUTER JOIN user_acl ON user_acl.username = rcpt WHERE notified = 0 AND score < %s AND rcpt in (SELECT username FROM mailbox) GROUP BY rcpt',(max_score,))
forrecordinrecords:
attrs=''
@@ -150,7 +159,7 @@ try:
exceptExceptionasex:
print('Could not determine last notification for %s, assuming never'%(record['rcpt']))
last_notification=0
attrs_json=query_mysql('SELECT attributes FROM mailbox WHERE username = "%s"'%(record['rcpt']))
attrs_json=query_mysql('SELECT attributes FROM mailbox WHERE username = %s',(record['rcpt'],))
@@ -113,7 +120,7 @@ def oletools( stream, tmp_file_name, lid ):
out=bytes(out.decode('utf-8','ignore').replace('','').replace('\t','').replace('\n','').replace('XLMMacroDeobfuscator: pywin32 is not installed (only is required if you want to use MS Excel)',''),encoding="utf-8")
while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent;do
while ! mariadb-admin status --ssl=false --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent;do
echo"Waiting for database to come up..."
sleep 2
done
@@ -14,118 +14,18 @@ do
done
# Wait for updated schema
DBV_NOW=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -e "SELECT version FROM versions WHERE application = 'db_schema';" -BN)
DBV_NOW=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -e "SELECT version FROM versions WHERE application = 'db_schema';" -BN)
DBV_NOW=$(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -e "SELECT version FROM versions WHERE application = 'db_schema';" -BN)
DBV_NOW=$(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -e "SELECT version FROM versions WHERE application = 'db_schema';" -BN)
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -B -e "DELETE FROM _sogo_static_view WHERE c_uid NOT IN (SELECT username FROM mailbox WHERE active = '1')"
else
echo"Waiting for database initialization..."
sleep 3
fi
done
else
while[[${STATIC_VIEW_OK} !='OK']];do
if[[ ! -z $(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '_sogo_static_view'")]];then
STATIC_VIEW_OK=OK
else
echo"Waiting for database initialization by master..."
sleep 3
fi
done
fi
# Recreate password update trigger
if[["${MASTER}"=~ ^([yY][eE][sS]|[yY])+$ ]];then
echo"We are master, preparing update trigger..."
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -e "DROP TRIGGER IF EXISTS sogo_update_password"
while[[${TRIGGER_OK} !='OK']];do
mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} << EOF
DELIMITER -
CREATE TRIGGER sogo_update_password AFTER UPDATE ON _sogo_static_view
FOR EACH ROW
BEGIN
UPDATE mailbox SET password= NEW.c_password WHERE NEW.c_uid = username;
END;
-
DELIMITER ;
EOF
if[[ ! -z $(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -B -e "SELECT 'OK' FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_NAME = 'sogo_update_password'")]];then
TRIGGER_OK=OK
else
echo"Will retry to setup SOGo password update trigger in 3s"
done < <(mysql --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -e "SELECT domain, CASE gal WHEN '1' THEN 'YES' ELSE 'NO' END AS gal FROM domain;" -B -N)
done < <(mariadb --skip-ssl --socket=/var/run/mysqld/mysqld.sock -u ${DBUSER} -p${DBPASS}${DBNAME} -e "SELECT domain, CASE gal WHEN '1' THEN 'YES' ELSE 'NO' END AS gal FROM domain;" -B -N)
# General Ping function to check general pingability
function check_ping(){
declare -a ipstoping=("1.1.1.1""8.8.8.8""9.9.9.9")
declare -a ipstoping=("1.1.1.1""8.8.8.8""9.9.9.9")
localfail_tolerance=1
localfailures=0
for ip in "${ipstoping[@]}";do
ping -q -c 3 -w 5"$ip"
if[$? -ne 0];then
log_to_file "Healthcheck: Couldn't ping $ip for 5 seconds... Gave up!"
log_to_file "Please check your internet connection or firewall rules to fix this error, because a simple ping test should always go through from the unbound container!"
return1
fi
for ip in "${ipstoping[@]}";do
success=false
for((i=1; i<=3; i++));do
ping -q -c 3 -w 5"$ip" > /dev/null
if[$? -eq 0];then
success=true
break
else
log_to_stdout "Healthcheck: Failed to ping $ip on attempt $i. Trying again..."
fi
done
if["$success"=false];then
log_to_stdout "Healthcheck: Couldn't ping $ip after 3 attempts. Marking this IP as failed."
((failures++))
fi
done
if[$failures -gt $fail_tolerance];then
log_to_stdout "Healthcheck: Too many ping failures ($fail_tolerance failures allowed, you got $failures failures), marking Healthcheck as unhealthy..."
return1
fi
return0
log_to_file "Healthcheck: Ping Checks WORKING properly!"
return0
}
# General DNS Resolve Check against Unbound Resolver himself
function check_dns(){
declare -a domains=("mailcow.email""github.com""hub.docker.com")
declare -a domains=("fuzzy.mailcow.email""github.com""hub.docker.com")
echo'Full ratelimit buckets can be emptied by deleting the ratelimit hash from within mailcow UI (see /debug -> Protocols -> Ratelimit):' >> /tmp/ratelimit
Somebody requested a new password for the {{hostname}} account associated with {{username}}.<br>
<small>Date of the password reset request: {{date}}</small><br><br>
You can reset your password by clicking the link below:<br>
<a href="{{link}}">{{link}}</a><br><br>
The link will be valid for the next {{token_lifetime}} minutes.<br><br>
If you did not request a new password, please ignore this email.<br>
</body>
</html>
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.