Compare commits
No commits in common. "master" and "v9.1.0" have entirely different histories.
|
@ -1,25 +1,36 @@
|
|||
# -----------------------------------------------
|
||||
# --- General -----------------------------------
|
||||
# --- https://editorconfig.org ------------------
|
||||
# -----------------------------------------------
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– General –––––––––––––––––––––––––––––––––––
|
||||
# ––– https://editorconfig.org ––––––––––––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- Specific ----------------------------------
|
||||
# -----------------------------------------------
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– Specific ––––––––––––––––––––––––––––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
[{Makefile,.gitmodules}]
|
||||
[*.{yaml,yml,sh,bats}]
|
||||
indent_size = 2
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– Git Submodules ––––––––––––––––––––––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
[{test/bats/**,test/test_helper/**,target/docker-configomat/**}]
|
||||
indent_style = none
|
||||
indent_size = none
|
||||
end_of_line = none
|
||||
|
|
|
@ -1,158 +0,0 @@
|
|||
# Normalize line endings of all non-binary files to LF upon check-in (`git add` / `git commit`):
|
||||
* text=auto
|
||||
|
||||
#################################################
|
||||
### General ###################################
|
||||
#################################################
|
||||
|
||||
## GENERIC
|
||||
### CI + docs/mkdocs.yml
|
||||
*.yml text
|
||||
### Documentation (Project, Tests, Docs site)
|
||||
*.md text
|
||||
### TLS certs (test/files/) + DHE params (target/shared/)
|
||||
*.pem text
|
||||
*.pem.sha512sum text
|
||||
|
||||
#################################################
|
||||
### Project ###################################
|
||||
#################################################
|
||||
|
||||
## BUILD:
|
||||
.dockerignore text
|
||||
Dockerfile text eol=lf
|
||||
Makefile
|
||||
|
||||
## EXAMPLE (RUNTIME):
|
||||
*.env text
|
||||
*.yaml text
|
||||
|
||||
## PROJECT
|
||||
.editorconfig text export-ignore
|
||||
.gitattributes text export-ignore
|
||||
.gitignore text export-ignore
|
||||
.gitkeep text export-ignore
|
||||
.gitmodules text export-ignore
|
||||
LICENSE text
|
||||
|
||||
## SOURCE CODE
|
||||
*.sh text eol=lf
|
||||
### acme.json extractor (target/bin/)
|
||||
*.py text eol=lf
|
||||
### Only contain scripts (glob for extensionless)
|
||||
target/bin/** text eol=lf
|
||||
|
||||
#################################################
|
||||
### Config ####################################
|
||||
#################################################
|
||||
|
||||
## CONFIG
|
||||
### Contains all text files (glob for extensionless)
|
||||
target/amavis/** text
|
||||
target/fetchmail/** text
|
||||
target/getmail/** text
|
||||
target/opendkim/** text
|
||||
target/opendmarc/** text
|
||||
target/postgrey/** text
|
||||
target/postsrsd/** text
|
||||
### Generic target/ + test/config/
|
||||
*.cf text
|
||||
*.conf text
|
||||
### Dovecot
|
||||
*.ext text
|
||||
*.sieve text
|
||||
### Dovecot + Rspamd
|
||||
*.inc text
|
||||
### Fail2Ban + Postgrey (test/config/)
|
||||
*.local text
|
||||
### Postfix
|
||||
*.pcre text
|
||||
|
||||
#################################################
|
||||
### Tests #####################################
|
||||
#################################################
|
||||
|
||||
## BATS
|
||||
*.bash text eol=lf
|
||||
*.bats text eol=lf
|
||||
|
||||
## CONFIG (test/config/)
|
||||
### OpenLDAP image
|
||||
*.ldif text
|
||||
### OpenDKIM
|
||||
*.private text
|
||||
KeyTable text
|
||||
SigningTable text
|
||||
TrustedHosts text
|
||||
### Postgrey
|
||||
whitelist_recipients text
|
||||
|
||||
## MISC
|
||||
### test/config/ + test/files/
|
||||
*.txt text
|
||||
### test/linting/ (.ecrc.json) + test/files/ (*.acme.json):
|
||||
*.json text
|
||||
|
||||
#################################################
|
||||
### Documentation Website #####################
|
||||
#################################################
|
||||
|
||||
## DOCUMENTATION
|
||||
### docs/content/assets/
|
||||
*.css text
|
||||
*.png binary
|
||||
*.svg text -diff
|
||||
*.woff binary
|
||||
### docs/overrides/
|
||||
*.html text
|
||||
*.ico binary
|
||||
*.webp binary
|
||||
|
||||
#################################################
|
||||
### Info # #####################################
|
||||
#################################################
|
||||
|
||||
### WHAT IS THIS FILE?
|
||||
# `.gitattributes` - Pattern-based overrides (Project specific)
|
||||
# Documentation: https://git-scm.com/docs/gitattributes
|
||||
#
|
||||
# Travels with the project and can override the defaults from `.gitconfig`.
|
||||
# This helps to enforce consistent line endings (CRLF / LF) where needed via
|
||||
# patterns (_when the git client supports `.gitattributes`_).
|
||||
|
||||
# `.gitconfig` - Global Git defaults (Dev environment)
|
||||
# Documentation: https://git-scm.com/docs/git-config
|
||||
#
|
||||
# Git settings `core.autocrlf` and `core.eol` can vary across dev environments.
|
||||
# Those defaults can introduce subtle bugs due to incompatible line endings.
|
||||
|
||||
|
||||
### WHY SHOULD I CARE?
|
||||
# The desired result is to ensure the repo contains normalized LF line endings,
|
||||
# notably avoiding unhelpful noise in diffs or issues incurred from mixed line
|
||||
# endings. Storing as LF ensures no surprises for line endings during checkout.
|
||||
# Additionally for checkout to the local working directory, line endings can be
|
||||
# forced to CRLF or LF per file where appropriate, which ensures the files have
|
||||
# compatible line endings where software expects a specific kind.
|
||||
#
|
||||
# Examples:
|
||||
# Diffs with nothing visual changed. Line endings appear invisible.
|
||||
# Tests that compare text from two sources where only line endings differ fail.
|
||||
# /bin/sh with a shebang fails to run a binary at the given path due to a CRLF.
|
||||
|
||||
|
||||
### ATTRIBUTES
|
||||
# `text` normalizes the line endings of a file to LF upon commit (CRLF -> LF).
|
||||
# `text=auto` sets `text` if Git doesn't consider the file as binary data.
|
||||
|
||||
# `eol` sets an explicit line ending to write files to the working directory.
|
||||
# `core.eol` is used for any files not explicitly set with an `eol` attr value.
|
||||
# `core.eol` uses the native line endings for your platform by default.
|
||||
# `core.autocrlf` (if set to `true` or `input`) overrides the `core.eol` value.
|
||||
|
||||
# `binary` is an alias for `-text -diff`. The file won't be normalized (-text).
|
||||
# `-diff` indicates to avoid creating a diff. Useful when diffs are unlikely
|
||||
# to be meaningful, such as generated content (SVG, Source Maps, Lockfiles).
|
||||
|
||||
# `export-ignore` excludes matched files and directories during `git archive`,
|
||||
# which services like Github use to create releases with archived source files.
|
|
@ -1,11 +0,0 @@
|
|||
github: [georglauterbach]
|
||||
# patreon: # Replace with a single Patreon username
|
||||
# open_collective: # Replace with a single Open Collective username
|
||||
# ko_fi: # Replace with a single Ko-fi username
|
||||
# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
# liberapay: # Replace with a single Liberapay username
|
||||
# issuehunt: # Replace with a single IssueHunt username
|
||||
# otechie: # Replace with a single Otechie username
|
||||
# lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
# custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
|
@ -0,0 +1,82 @@
|
|||
---
|
||||
name: "\U0001F41E Bug report"
|
||||
about: Create a report about a reproducible bug
|
||||
title: "[BUG]"
|
||||
labels: kind/bug, meta/needs triage, priority/medium
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
<!---
|
||||
Possible answers to your issue
|
||||
|
||||
* ClamAV keeps restarting:
|
||||
https://github.com/docker-mailserver/docker-mailserver#requirements
|
||||
|
||||
* Email seen as spam:
|
||||
https://docker-mailserver.github.io/docker-mailserver/edge/config/best-practices/spf
|
||||
https://docker-mailserver.github.io/docker-mailserver/edge/config/best-practices/dkim
|
||||
|
||||
* Creating new domains and accounts
|
||||
https://docker-mailserver.github.io/docker-mailserver/edge/config/user-management/accounts
|
||||
|
||||
* Use a relay mail server
|
||||
https://docker-mailserver.github.io/docker-mailserver/edge/config/advanced/mail-forwarding/aws-ses
|
||||
The variable name can be used for other email servers.
|
||||
|
||||
* FAQ and tips
|
||||
https://docker-mailserver.github.io/docker-mailserver/edge/faq
|
||||
|
||||
* The documentation
|
||||
https://docker-mailserver.github.io/docker-mailserver/edge
|
||||
|
||||
* Open issues
|
||||
https://github.com/docker-mailserver/docker-mailserver/issues
|
||||
-->
|
||||
|
||||
# Bug Report
|
||||
|
||||
## Context
|
||||
|
||||
<!--- Provide a general summary of the bug -->
|
||||
|
||||
### What is affected by this bug?
|
||||
|
||||
### When does this occur?
|
||||
|
||||
### How do we replicate the issue?
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
## Behavior
|
||||
|
||||
### Actual Behavior
|
||||
|
||||
### Expected Behavior
|
||||
|
||||
## Your Environment
|
||||
|
||||
<!--- Include as many relevant details about the environment you experienced the issue in -->
|
||||
|
||||
- version: `v`
|
||||
- available RAM: `GB`
|
||||
- Docker version: `v`
|
||||
- docker-compose version: `v`
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```CFG
|
||||
|
||||
```
|
||||
|
||||
### Relevant Stack Traces
|
||||
|
||||
<!-- Remember to format code using triple backticks (`) so that it is neatly formatted when the issue is posted. -->
|
||||
|
||||
```BASH
|
||||
# BEGIN
|
||||
|
||||
# END
|
||||
```
|
|
@ -1,69 +0,0 @@
|
|||
name: Bug Report
|
||||
description: Submit a bug report to help us improve
|
||||
title: 'bug report: '
|
||||
labels:
|
||||
- kind/bug/report
|
||||
- meta/needs triage
|
||||
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: preliminary-checks
|
||||
attributes:
|
||||
label: 📝 Preliminary Checks
|
||||
description: |
|
||||
By submitting this issue, you agree to our [Code of Conduct](https://github.com/docker-mailserver/docker-mailserver/blob/master/CODE_OF_CONDUCT.md).
|
||||
options:
|
||||
- label: I tried searching for an existing issue and followed the [debugging docs](https://docker-mailserver.github.io/docker-mailserver/latest/config/debugging/) advice, but still need assistance.
|
||||
required: true
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: 👀 What Happened?
|
||||
description: How did this differ from your expectations?
|
||||
placeholder: Although `LOG_LEVEL=debug` is set, the logs are missing debug output.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: steps-to-reproduce
|
||||
attributes:
|
||||
label: 👟 Reproduction Steps
|
||||
description: |
|
||||
How did you trigger this bug? Please walk us through it step by step.
|
||||
Please use [fenced code blocks](https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#fenced-code-blocks) when pasting lots of text!
|
||||
placeholder: The easier it is for us to reproduce your issue, the sooner we can help resolve it 😉
|
||||
- type: input
|
||||
id: mailserver-version
|
||||
attributes:
|
||||
label: 🐋 DMS Version
|
||||
description: On which version (image tag) did you encounter this bug?
|
||||
placeholder: v12.1.0 (do not put "latest")
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: operating-system
|
||||
attributes:
|
||||
label: 💻 Operating System and Architecture
|
||||
description: |
|
||||
Which OS is your docker host running on?
|
||||
**NOTE:** Windows and macOS have limited support.
|
||||
placeholder: Debian 11 (Bullseye) x86_64, Fedora 38 ARM64
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: container-configuration-files
|
||||
attributes:
|
||||
label: ⚙️ Container configuration files
|
||||
description: |
|
||||
Show us the `compose.yaml` file or command that you used to run DMS (and possibly other related services).
|
||||
- This field is formatted as YAML.
|
||||
- If you are using Kubernetes, you can alternatively share your manifest files here.
|
||||
render: yml
|
||||
- type: textarea
|
||||
id: relevant-log-output
|
||||
attributes:
|
||||
label: 📜 Relevant log output
|
||||
description: |
|
||||
Show us relevant log output here.
|
||||
- This field expects only plain text (_rendered as a fenced code block_).
|
||||
- You can enable debug output by setting the environment variable `LOG_LEVEL` to `debug` or `trace`.
|
||||
render: Text
|
|
@ -1,12 +1,13 @@
|
|||
# disables the possibility to choose blank issues
|
||||
blank_issues_enabled: false
|
||||
|
||||
contact_links:
|
||||
- name: Documentation | Landing Page
|
||||
url: https://docker-mailserver.github.io/docker-mailserver/latest
|
||||
about: Visit this first before opening issues!
|
||||
- name: Documentation | Environment Variables Page
|
||||
url: https://docker-mailserver.github.io/docker-mailserver/latest/config/environment/
|
||||
about: Read this page for information about mail server variables.
|
||||
- name: Documentation | Debugging Page
|
||||
url: https://docker-mailserver.github.io/docker-mailserver/latest/config/debugging/
|
||||
about: Read this page for information on how to debug DMS.
|
||||
- name: Documentation
|
||||
url: https://docker-mailserver.github.io/docker-mailserver/edge
|
||||
about: Extended documentaton - visit this first before opening issues
|
||||
- name: Default Documentation
|
||||
url: https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md
|
||||
about: Read this first and carefully
|
||||
- name: Environment Variables Section
|
||||
url: https://github.com/docker-mailserver/docker-mailserver/blob/master/ENVIRONMENT.md
|
||||
about: Read this section for information about mail server variables
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
name: "\U0001F389 Feature request"
|
||||
about: Suggest an idea for this project
|
||||
title: '[FR]'
|
||||
labels: area/enhancement, kind/feature (request), meta/needs triage, priority/low
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# Feature Request
|
||||
|
||||
## Context
|
||||
|
||||
|
||||
|
||||
### Is your Feature Request related to a Problem?
|
||||
|
||||
|
||||
|
||||
### Describe the Solution you'd like
|
||||
|
||||
|
||||
|
||||
### Are you going to implement it?
|
||||
|
||||
<!-- Select one, remove the other and do not shorten the sentence -->
|
||||
|
||||
Yes, because I know the probability of someone else doing it is low and I can learn from it.
|
||||
|
||||
No, and I understand that it is highly likely no one will implement it. Furthermore, I understand that this issue will likely become stale and will be closed.
|
||||
|
||||
### What are you going to contribute??
|
||||
|
||||
|
||||
|
||||
## Additional context
|
||||
|
||||
### Alternatives you've considered
|
||||
|
||||
|
||||
|
||||
### Who will that Feature be useful to?
|
||||
|
||||
|
||||
|
||||
### What have you done already?
|
||||
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
name: Feature Request
|
||||
description: Suggest an idea for this project
|
||||
title: 'feature request: '
|
||||
labels:
|
||||
- kind/new feature
|
||||
- meta/needs triage
|
||||
projects:
|
||||
- DMS Core Backlog
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Markdown formatting can be used in almost all text fields. The description will tell you if this is not the case for a specific field.
|
||||
|
||||
Be as precise as possible, and if in doubt, it's best to add more information that too few.
|
||||
|
||||
---
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Context
|
||||
description: Tell us how your request is related to DMS, one of its components or another issue / PR. Also **link all conected issues and PRs here**!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: Describe the solution you would like to have implemented. Be as precise as possible!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives
|
||||
description: Which alternatives have you considered?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: applicable-users
|
||||
attributes:
|
||||
label: Applicable Users
|
||||
description: Who will that feature be useful to?
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: implementer
|
||||
attributes:
|
||||
label: Are you going to implement it?
|
||||
options:
|
||||
- Yes, because I know the probability of someone else doing it is low and I can learn from it.
|
||||
- No, and I understand that it is highly likely no one will implement it. Furthermore, I understand that this issue will likely become stale and will be closed.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: contribution
|
||||
attributes:
|
||||
label: What are you going to contribute?
|
||||
description: You may also tell us what you have already done.
|
||||
validations:
|
||||
required: true
|
|
@ -1,30 +0,0 @@
|
|||
name: Other
|
||||
description: Miscellaneous questions and reports for the project (not support)
|
||||
title: 'other: '
|
||||
labels:
|
||||
- meta/help wanted
|
||||
|
||||
body:
|
||||
- type: dropdown
|
||||
id: subject
|
||||
attributes:
|
||||
label: Subject
|
||||
options:
|
||||
- I would like to contribute to the project
|
||||
- I would like to configure a not documented mail server use case
|
||||
- I would like some feedback concerning a use case
|
||||
- Something else that requires developers attention
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Description
|
||||
value: |
|
||||
<!---
|
||||
Please do not use this form to bypass doing a proper bug report.
|
||||
The issue tracker is for anything relevant to the project itself, not individual support queries.
|
||||
If you don't want to fill out a bug report, please ask support questions at our community discussions page: https://github.com/orgs/docker-mailserver/discussions
|
||||
-->
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
name: "❓ Question / Other"
|
||||
about: Ask a question about docker-mailserver
|
||||
title: ''
|
||||
labels: kind/question, priority/low, meta/help wanted, meta/needs triage
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# Subject
|
||||
|
||||
<!-- Select one, remove the others -->
|
||||
|
||||
I would like to contribute to the project
|
||||
I would like to configure a not documented mail server use case
|
||||
I would like some feedback concerning a use case
|
||||
I have questions about TLS/SSL/STARTTLS/OpenSSL
|
||||
Other
|
||||
|
||||
## Description
|
||||
|
||||
<!-- When copy/pasting code, format the code with tripe backticks (`) ! -->
|
||||
|
||||
``` BASH
|
||||
# CODE GOES HERE
|
||||
|
||||
```
|
|
@ -4,16 +4,10 @@ updates:
|
|||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
reviewers:
|
||||
- "radicand"
|
||||
- "aendeavor"
|
||||
- "wernerfred"
|
||||
labels:
|
||||
- "area/ci"
|
||||
- "kind/update"
|
||||
- "priority/low"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directory: /
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- "area/ci"
|
||||
- "kind/update"
|
||||
- "priority/medium"
|
||||
- "area/dependency"
|
||||
- "pr/needs review"
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
# Description
|
||||
|
||||
<!--
|
||||
Include a summary of the change.
|
||||
Please also include relevant motivation and context.
|
||||
-->
|
||||
<!-- Please include a summary of the change. Please also include relevant motivation and context. -->
|
||||
|
||||
<!-- Link the issue which will be fixed (if any) here: -->
|
||||
Fixes #
|
||||
<!-- Please link the issue which will be fixed (if any) here: -->
|
||||
Fixes # (issue)
|
||||
|
||||
## Type of change
|
||||
|
||||
<!-- Delete options that are not relevant. -->
|
||||
<!-- Please delete options that are not relevant. -->
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
|
@ -18,12 +15,11 @@ Fixes #
|
|||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] This change requires a documentation update
|
||||
|
||||
## Checklist
|
||||
## Checklist:
|
||||
|
||||
- [ ] My code follows the style guidelines of this project
|
||||
- [ ] I have performed a self-review of my code
|
||||
- [ ] I have performed a self-review of my own 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
|
||||
- [ ] I have made corresponding changes to the documentation (README.md or ENVIRONMENT.md or the documentation)
|
||||
- [ ] 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`**
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
name: "Close Stale Issues"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 1 * * *"
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Close stale issues
|
||||
uses: actions/stale@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 20
|
||||
days-before-close: 10
|
||||
stale-issue-label: "meta/stale"
|
||||
close-issue-label: "meta/closed due to age or inactivity"
|
||||
stale-issue-message: >
|
||||
This issue has become stale because it has been open for 20 days without
|
||||
activity. Remove the label and comment or this issue will be closed in 10 days.
|
||||
close-issue-message: >
|
||||
This issue was closed due to inactivity.
|
|
@ -1,39 +1,111 @@
|
|||
name: 'Build, Test & Deploy'
|
||||
name: "Build, Test & Deploy"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- target/**
|
||||
- .dockerignore
|
||||
- .gitmodules
|
||||
- Dockerfile
|
||||
- setup.sh
|
||||
- stable
|
||||
paths:
|
||||
- 'target/**'
|
||||
- '.dockerignore'
|
||||
- '.gitmodules'
|
||||
- 'Dockerfile'
|
||||
- 'setup.sh'
|
||||
tags:
|
||||
- '*.*.*'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build-image:
|
||||
name: 'Build AMD64 Image'
|
||||
uses: docker-mailserver/docker-mailserver/.github/workflows/generic_build.yml@master
|
||||
build-and-test-image:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
id: buildx
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2.1.4
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
- name: Build image locally
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
build-args: |
|
||||
VCS_REF=${{ github.sha }}
|
||||
VCS_VER=${{ github.ref }}
|
||||
platforms: linux/amd64
|
||||
load: true
|
||||
tags: mailserver-testing:ci
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
- name: Run test suite
|
||||
run: >
|
||||
NAME=mailserver-testing:ci
|
||||
bash -c 'make generate-accounts tests'
|
||||
env:
|
||||
CI: true
|
||||
|
||||
run-tests:
|
||||
name: 'Test AMD64 Image'
|
||||
needs: build-image
|
||||
uses: docker-mailserver/docker-mailserver/.github/workflows/generic_test.yml@master
|
||||
with:
|
||||
cache-key: ${{ needs.build-image.outputs.build-cache-key }}
|
||||
|
||||
publish-images:
|
||||
name: 'Publish AMD64 and ARM64 Image'
|
||||
needs: [build-image, run-tests]
|
||||
uses: docker-mailserver/docker-mailserver/.github/workflows/generic_publish.yml@master
|
||||
with:
|
||||
cache-key: ${{ needs.build-image.outputs.build-cache-key }}
|
||||
secrets: inherit
|
||||
build-multiarch-and-publish:
|
||||
needs: build-and-test-image
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Prepare tags
|
||||
id: prep
|
||||
uses: crazy-max/ghaction-docker-meta@v1
|
||||
with:
|
||||
images: |
|
||||
${{ secrets.DOCKER_REPOSITORY }}
|
||||
${{ secrets.GHCR_REPOSITORY }}
|
||||
tag-edge: true
|
||||
tag-semver: |
|
||||
{{major}}
|
||||
{{major}}.{{minor}}
|
||||
{{major}}.{{minor}}.{{patch}}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
id: buildx
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2.1.4
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ secrets.GHCR_USERNAME }}
|
||||
password: ${{ secrets.GHCR_PASSWORD }}
|
||||
- name: Build image locally
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
build-args: |
|
||||
VCS_REF=${{ github.sha }}
|
||||
VCS_VER=${{ github.ref }}
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.prep.outputs.tags }}
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
|
|
|
@ -23,12 +23,10 @@ env:
|
|||
|
||||
jobs:
|
||||
deploy:
|
||||
permissions:
|
||||
contents: write
|
||||
name: 'Deploy Docs'
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: 'Check if deploy is for a `v<major>.<minor>` tag version instead of `edge`'
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
|
@ -42,7 +40,8 @@ jobs:
|
|||
|
||||
- name: 'Build with mkdocs-material via Docker'
|
||||
working-directory: docs
|
||||
run: '../.github/workflows/scripts/docs/build-docs.sh'
|
||||
# --user is required for build output file ownership to match the CI user instead of the containers internal user
|
||||
run: docker run --rm --user "$(id -u):$(id -g)" -v "${PWD}:/docs" squidfunk/mkdocs-material build --strict
|
||||
|
||||
- name: 'If a tagged version, fix canonical links and remove `404.html`'
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
|
@ -52,14 +51,14 @@ jobs:
|
|||
# (Note the edge 404.html isn't useful either as it's not copied to the `gh-pages` branch root)
|
||||
rm 404.html
|
||||
|
||||
# Replace the tagged '${DOCS_VERSION}' in the 'canonical' link element of HTML files,
|
||||
# to point to the 'edge' version of docs as the authoritative source:
|
||||
# Replace '${DOCS_VERSION}' (defaults to 'edge') in the 'canonical' link element of HTML files,
|
||||
# with the tagged docs version:
|
||||
find . -type f -name "*.html" -exec \
|
||||
sed -i "s|^\(.*<link rel=\"canonical\".*\)${DOCS_VERSION}|\1latest|" \
|
||||
sed -i "s|^\(.*<link rel=\"canonical\".*\)${DOCS_VERSION}|\1edge|" \
|
||||
{} +
|
||||
|
||||
- name: 'Deploy to Github Pages'
|
||||
uses: peaceiris/actions-gh-pages@v4.0.0
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Build directory contents to publish to the `gh-pages` branch:
|
||||
|
@ -70,19 +69,17 @@ jobs:
|
|||
user_email: ${{ env.GIT_EMAIL }}
|
||||
|
||||
add-version-to-docs:
|
||||
permissions:
|
||||
contents: write
|
||||
name: 'Update `versions.json` if necessary'
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.04
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
# Avoid race condition with pushing to `gh-pages` branch by waiting for `deploy` to complete first
|
||||
needs: deploy
|
||||
steps:
|
||||
- name: 'Checkout the tagged commit (shallow clone)'
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: 'Checkout the docs deployment branch to a subdirectory'
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: gh-pages
|
||||
path: gh-pages
|
||||
|
@ -105,32 +102,3 @@ jobs:
|
|||
git add versions.json
|
||||
git commit -m "chore: Add ${{ env.DOCS_VERSION }} to version selector list"
|
||||
git push
|
||||
|
||||
update-latest-symlink:
|
||||
permissions:
|
||||
contents: write
|
||||
name: 'update `latest` symlink if neccessary'
|
||||
runs-on: ubuntu-22.04
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
needs: add-version-to-docs
|
||||
steps:
|
||||
- name: 'Checkout the docs deployment branch'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: gh-pages
|
||||
|
||||
- name: 'Ensure `latest` symlink refers to the substring from tag name'
|
||||
id: update-latest
|
||||
run: |
|
||||
DOCS_VERSION=$(grep -oE 'v[0-9]+\.[0-9]+' <<< "${GITHUB_REF}")
|
||||
echo "DOCS_VERSION=${DOCS_VERSION}" >>"${GITHUB_ENV}"
|
||||
rm latest
|
||||
ln -s "${DOCS_VERSION}" latest
|
||||
|
||||
- name: 'Push update for `latest` symlink'
|
||||
run: |
|
||||
git config user.name ${{ env.GIT_USER }}
|
||||
git config user.email ${{ env.GIT_EMAIL }}
|
||||
git add latest
|
||||
git commit -m "chore: Update \`latest\` symlink to point to ${{ env.DOCS_VERSION }}"
|
||||
git push
|
|
@ -1,166 +0,0 @@
|
|||
name: 'Documentation (Deploy)'
|
||||
|
||||
on:
|
||||
# This workflow runs off the primary branch which provides access to the `secrets` context:
|
||||
workflow_run:
|
||||
workflows: ['Documentation (PR)']
|
||||
types:
|
||||
- completed
|
||||
|
||||
permissions:
|
||||
# Required by `actions/download-artifact`:
|
||||
actions: read
|
||||
# Required by `set-pr-context`:
|
||||
contents: read
|
||||
# Required by `marocchino/sticky-pull-request-comment` (write) + `set-pr-context` (read):
|
||||
pull-requests: write
|
||||
# Required by `myrotvorets/set-commit-status-action`:
|
||||
statuses: write
|
||||
|
||||
jobs:
|
||||
# NOTE: This is handled as pre-requisite job to minimize the noise from acquiring these two outputs needed for `deploy-preview` ENV:
|
||||
pr-context:
|
||||
name: 'Acquire PR Context'
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
PR_HEADSHA: ${{ steps.set-pr-context.outputs.head-sha }}
|
||||
PR_NUMBER: ${{ steps.set-pr-context.outputs.number }}
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' }}
|
||||
steps:
|
||||
- name: 'Get PR context'
|
||||
id: set-pr-context
|
||||
env:
|
||||
# Token is required for the GH CLI:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
# Best practice for scripts is to reference via ENV at runtime. Avoid using GHA context expressions in the script content directly:
|
||||
# https://github.com/docker-mailserver/docker-mailserver/pull/4247#discussion_r1827067475
|
||||
PR_TARGET_REPO: ${{ github.repository }}
|
||||
# If the PR is from a fork, prefix it with `<owner-login>:`, otherwise only the PR branch name is relevant:
|
||||
PR_BRANCH: |-
|
||||
${{
|
||||
(github.event.workflow_run.head_repository.owner.login != github.event.workflow_run.repository.owner.login)
|
||||
&& format('{0}:{1}', github.event.workflow_run.head_repository.owner.login, github.event.workflow_run.head_branch)
|
||||
|| github.event.workflow_run.head_branch
|
||||
}}
|
||||
# Use the GH CLI to query the PR branch, which provides the PR number and head SHA to assign as outputs:
|
||||
# (`--jq` formats JSON to `key=value` pairs and renames `headRefOid` to `head-sha`)
|
||||
run: |
|
||||
gh pr view --repo "${PR_TARGET_REPO}" "${PR_BRANCH}" \
|
||||
--json 'number,headRefOid' \
|
||||
--jq '"number=\(.number)\nhead-sha=\(.headRefOid)"' \
|
||||
>> "${GITHUB_OUTPUT}"
|
||||
|
||||
deploy-preview:
|
||||
name: 'Deploy Preview'
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [pr-context]
|
||||
env:
|
||||
# NOTE: Keep this in sync with the equivalent ENV in `docs-preview-prepare.yml`:
|
||||
BUILD_DIR: docs/site/
|
||||
# PR head SHA (latest commit):
|
||||
PR_HEADSHA: ${{ needs.pr-context.outputs.PR_HEADSHA }}
|
||||
PR_NUMBER: ${{ needs.pr-context.outputs.PR_NUMBER }}
|
||||
# Deploy URL preview prefix (the site name for this prefix is managed at Netlify):
|
||||
PREVIEW_SITE_PREFIX: pullrequest-${{ needs.pr-context.outputs.PR_NUMBER }}
|
||||
steps:
|
||||
- name: 'Retrieve and extract the built docs preview'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: preview-build
|
||||
path: ${{ env.BUILD_DIR }}
|
||||
# These are needed due this approach relying on `workflow_run`, so that it can access the build artifact:
|
||||
# (uploaded from the associated `docs-preview-prepare.yml` workflow run)
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
|
||||
# ==================== #
|
||||
# Deploy preview build #
|
||||
# ==================== #
|
||||
|
||||
# Manage workflow deployment status (Part 1/2):
|
||||
# NOTE:
|
||||
# - `workflow_run` trigger does not appear on the PR/commit checks status, only the initial prepare workflow triggered.
|
||||
# This adds our own status check for this 2nd half of the workflow starting as `pending`, followed by `success` / `failure` at the end.
|
||||
# - `enable-commit-status` from `nwtgck/actions-netlify` would have handled this,
|
||||
# but the context `github.sha` that action tries to use references the primary branch commit that this workflow runs from, not the relevant PR commit.
|
||||
- name: 'Commit Status (1/2) - Set Workflow Status as Pending'
|
||||
uses: myrotvorets/set-commit-status-action@v2.0.1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
status: pending
|
||||
sha: ${{ env.PR_HEADSHA }}
|
||||
context: 'Deploy Preview (pull_request => workflow_run)'
|
||||
|
||||
- name: 'Send preview build to Netlify'
|
||||
uses: nwtgck/actions-netlify@v3.0
|
||||
id: preview-netlify
|
||||
timeout-minutes: 1
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
||||
with:
|
||||
# Fail the job when the required Netlify credentials are missing from ENV:
|
||||
fails-without-credentials: true
|
||||
# Set/create the Netlify deploy URL prefix:
|
||||
alias: ${{ env.PREVIEW_SITE_PREFIX }}
|
||||
# Only publish the contents of the build output:
|
||||
publish-dir: ${{ env.BUILD_DIR }}
|
||||
# Custom message for the deploy log on Netlify:
|
||||
deploy-message: 'Preview Build (PR #${{ env.PR_NUMBER }} @ commit: ${{ env.PR_HEADSHA }}'
|
||||
|
||||
# Disable unwanted action defaults:
|
||||
# This input does not fallback to the GITHUB_TOKEN taken from context, nor log that it will skip extra features of the action when this input is not set:
|
||||
# https://github.com/nwtgck/actions-netlify/issues/1219
|
||||
# github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# NOTE: These features won't work correctly when the triggered workflow is not run from the PR branch due to assumed `pull_request` context:
|
||||
# https://github.com/nwtgck/actions-netlify/issues/545
|
||||
# Disable adding a comment to the commit belonging to context `github.sha` about the successful deployment (redundant and often wrong commit):
|
||||
enable-commit-comment: false
|
||||
# Disable adding a "Netlify - Netlify deployment" PR check status (workflow job status is sufficient):
|
||||
enable-commit-status: false
|
||||
# Disable adding a comment about successful deployment status to the PR.
|
||||
# Prefer `marocchino/sticky-pull-request-comment` instead (more flexible and allows custom message):
|
||||
enable-pull-request-comment: false
|
||||
# Opt-out of deployment feature:
|
||||
# NOTE:
|
||||
# - When affected by `nwtgck/actions-netlify/issues/545`, the deployments published reference the wrong commit and thus information.
|
||||
# - While the feature creates or assigns a deployment to associate the build with, it is unrelated to the related environments feature (secrets/vars):
|
||||
# https://github.com/nwtgck/actions-netlify/issues/538#issuecomment-833983970
|
||||
# https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-deployments/viewing-deployment-history
|
||||
# https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-deployments/managing-environments-for-deployment
|
||||
enable-github-deployment: false
|
||||
# Assign to non-default Deployment Environment for better management:
|
||||
# github-deployment-environment: documentation-previews
|
||||
# github-deployment-description: 'Preview deploy for documentation PRs'
|
||||
|
||||
# If a `netlify.toml` config is ever needed, enable this:
|
||||
# netlify-config-path: ./docs/netlify.toml
|
||||
# If ever switching from Github Pages, enable this only when not deploying a preview build (false by default):
|
||||
# production-deploy: false
|
||||
|
||||
- name: 'Comment on PR with preview link'
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
with:
|
||||
number: ${{ env.PR_NUMBER }}
|
||||
header: preview-comment
|
||||
recreate: true
|
||||
message: |
|
||||
[Documentation preview for this PR](${{ steps.preview-netlify.outputs.deploy-url }}) is ready! :tada:
|
||||
|
||||
Built with commit: ${{ env.PR_HEADSHA }}
|
||||
|
||||
# Manage workflow deployment status (Part 2/2):
|
||||
- name: 'Commit Status (2/2) - Update deployment status'
|
||||
uses: myrotvorets/set-commit-status-action@v2.0.1
|
||||
# Always run this step regardless of the job failing early:
|
||||
if: ${{ always() }}
|
||||
# Custom status descriptions:
|
||||
env:
|
||||
DEPLOY_SUCCESS: Successfully deployed preview.
|
||||
DEPLOY_FAILURE: Failed to deploy preview.
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
status: ${{ job.status == 'success' && 'success' || 'failure' }}
|
||||
sha: ${{ env.PR_HEADSHA }}
|
||||
context: 'Deploy Preview (pull_request => workflow_run)'
|
||||
description: ${{ job.status == 'success' && env.DEPLOY_SUCCESS || env.DEPLOY_FAILURE }}
|
|
@ -1,74 +0,0 @@
|
|||
name: 'Documentation (PR)'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- '.github/workflows/scripts/docs/build-docs.sh'
|
||||
- '.github/workflows/docs-preview-prepare.yml'
|
||||
|
||||
# If this workflow is triggered while already running for the PR, cancel any earlier running instances:
|
||||
# Instances of the 2nd phase of this workflow (via `workflow_run`) lack any concurrency limits due to added complexity.
|
||||
concurrency:
|
||||
group: deploypreview-pullrequest-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
# Build output directory (created by the mkdocs-material container, keep this in sync with `build-docs.sh`):
|
||||
BUILD_DIR: docs/site/
|
||||
# These two are only needed to construct `PREVIEW_URL`:
|
||||
PREVIEW_SITE_NAME: dms-doc-previews
|
||||
PREVIEW_SITE_PREFIX: pullrequest-${{ github.event.pull_request.number }}
|
||||
|
||||
# `pull_request` workflow is unreliable alone: Non-collaborator contributions lack access to secrets for security reasons.
|
||||
# A separate workflow (docs-preview-deploy.yml) handles the deploy after the potentially untrusted code is first run in this workflow.
|
||||
# See: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
|
||||
permissions:
|
||||
# Required by `actions/checkout` for git checkout:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
prepare-preview:
|
||||
name: 'Build Preview'
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# ================== #
|
||||
# Build docs preview #
|
||||
# ================== #
|
||||
|
||||
- name: 'Build with mkdocs-material via Docker'
|
||||
working-directory: docs/
|
||||
env:
|
||||
PREVIEW_URL: 'https://${{ env.PREVIEW_SITE_PREFIX }}--${{ env.PREVIEW_SITE_NAME }}.netlify.app/'
|
||||
run: |
|
||||
# Adjust `mkdocs.yml` for the preview build requirements:
|
||||
# - Replace production `site_url` with the preview URL (only affects the canonical link: https://en.wikipedia.org/wiki/Canonical_link_element#HTML)
|
||||
# - Prepend Netlify logo link to `copyright` content
|
||||
sed -i "s|^site_url:.*|site_url: '${{ env.PREVIEW_URL }}'|" mkdocs.yml
|
||||
|
||||
# Insert branding into page content (Netlify OSS plan requirement):
|
||||
# - `mkdocs-material` does not provide a better way to do this.
|
||||
# - Prepends HTML to the copyright text and then aligns the logo to the right-side of the page.
|
||||
NETLIFY_BRANDING='<a href="https://www.netlify.com/"><img alt="Deploys by Netlify" src="https://www.netlify.com/img/global/badges/netlify-color-accent.svg" style="float: right;"></a>'
|
||||
sed -i "s|^copyright: '|copyright: '${NETLIFY_BRANDING}|" mkdocs.yml
|
||||
# Override a CSS media query for the parent element to always be full width:
|
||||
echo '.md-footer-copyright { width: 100%; }' >> content/assets/css/customizations.css
|
||||
|
||||
# Build and prepare for upload:
|
||||
echo "::group::Build (stdout)"
|
||||
bash ../.github/workflows/scripts/docs/build-docs.sh
|
||||
echo "::endgroup::"
|
||||
|
||||
# ============================== #
|
||||
# Volley over to secure workflow #
|
||||
# ============================== #
|
||||
|
||||
# Archives directory `path` into a ZIP file:
|
||||
- name: 'Upload artifact for workflow transfer'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: preview-build
|
||||
path: ${{ env.BUILD_DIR }}
|
||||
retention-days: 1
|
|
@ -1,107 +0,0 @@
|
|||
name: 'Build the DMS Container Image'
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
platforms:
|
||||
required: false
|
||||
type: string
|
||||
default: linux/amd64
|
||||
outputs:
|
||||
build-cache-key:
|
||||
description: The cache key to use when restoring an image later
|
||||
value: ${{ jobs.build-image.outputs.build-cache-key }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# `actions/cache` does not upload a new cache until completing a job successfully.
|
||||
# To better cache image builds, tests are handled in a dependent job afterwards.
|
||||
# This way failing tests will not prevent caching of an image. Useful when the build context
|
||||
# is not changed by new commits.
|
||||
|
||||
jobs:
|
||||
build-image:
|
||||
name: 'Build'
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
build-cache-key: ${{ steps.derive-image-cache-key.outputs.digest }}
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
# Can potentially be replaced by: `${{ hashFiles('target/**', 'Dockerfile') }}`
|
||||
# Must not be affected by file metadata changes and have a consistent sort order:
|
||||
# https://docs.github.com/en/actions/learn-github-actions/expressions#hashfiles
|
||||
# Keying by the relevant build context is more re-usable than a commit SHA.
|
||||
- name: 'Derive Docker image cache key from content'
|
||||
id: derive-image-cache-key
|
||||
shell: bash
|
||||
run: |
|
||||
ADDITIONAL_FILES=( 'Dockerfile' )
|
||||
|
||||
# Recursively collect file paths from `target/` and pipe a list of
|
||||
# checksums to be sorted (by hash value) and finally generate a checksum
|
||||
# of that list, using `awk` to only return the hash value (digest):
|
||||
IMAGE_CHECKSUM=$(\
|
||||
find ./target -type f -exec sha256sum "${ADDITIONAL_FILES[@]}" {} + \
|
||||
| sort \
|
||||
| sha256sum \
|
||||
| awk '{ print $1 }' \
|
||||
)
|
||||
|
||||
echo "digest=${IMAGE_CHECKSUM}" >>"${GITHUB_OUTPUT}"
|
||||
|
||||
# Attempts to restore the build cache from a prior build run.
|
||||
# If the exact key is not restored, then upon a successful job run
|
||||
# the new cache is uploaded for this key containing the contents at `path`.
|
||||
# Cache storage has a limit of 10GB, and uploads expire after 7 days.
|
||||
# When full, the least accessed cache upload is evicted to free up storage.
|
||||
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows
|
||||
- name: 'Handle Docker build layer cache'
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: cache-buildx-${{ steps.derive-image-cache-key.outputs.digest }}
|
||||
# If no exact cache-hit for key found, lookup caches with a `cache-buildx-` key prefix:
|
||||
# This is safe due to cache layer invalidation via the image build context.
|
||||
restore-keys: |
|
||||
cache-buildx-
|
||||
|
||||
- name: 'Set up QEMU'
|
||||
uses: docker/setup-qemu-action@v3.6.0
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- name: 'Set up Docker Buildx'
|
||||
uses: docker/setup-buildx-action@v3.10.0
|
||||
|
||||
# NOTE: AMD64 can build within 2 minutes
|
||||
- name: 'Build images'
|
||||
uses: docker/build-push-action@v6.15.0
|
||||
with:
|
||||
context: .
|
||||
# Build at least the AMD64 image (which runs against the test suite).
|
||||
platforms: ${{ inputs.platforms }}
|
||||
# Paired with steps `actions/cache` and `Replace cache` (replace src with dest):
|
||||
# NOTE: `mode=max` is only for `cache-to`, it configures exporting all image layers.
|
||||
# https://github.com/docker/buildx/blob/master/docs/reference/buildx_build.md#cache-from
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
|
||||
# This job just builds the image and stores to cache, no other exporting required:
|
||||
# https://github.com/docker/build-push-action/issues/546#issuecomment-1122631106
|
||||
outputs: type=cacheonly
|
||||
# Disable provenance attestation: https://docs.docker.com/build/attestations/slsa-provenance/
|
||||
provenance: false
|
||||
|
||||
# WORKAROUND: The `cache-to: type=local` input for `build-push-action` persists old-unused cache.
|
||||
# The workaround is to write the new build cache to a different location that replaces the
|
||||
# original restored cache after build, reducing frequency of eviction due to cache storage limit (10GB).
|
||||
# https://github.com/docker/build-push-action/blob/965c6a410d446a30e95d35052c67d6eded60dad6/docs/advanced/cache.md?plain=1#L193-L199
|
||||
# NOTE: This does not affect `cache-hit == 'true'` (which skips upload on direct cache key hit)
|
||||
- name: 'Replace cache'
|
||||
run: |
|
||||
rm -rf /tmp/.buildx-cache
|
||||
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
|
|
@ -1,81 +0,0 @@
|
|||
name: 'Publish the DMS Container Image'
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
cache-key:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
publish-images:
|
||||
name: 'Publish'
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: 'Prepare tags'
|
||||
id: prep
|
||||
uses: docker/metadata-action@v5.7.0
|
||||
with:
|
||||
images: |
|
||||
${{ secrets.DOCKER_REPOSITORY }}
|
||||
${{ secrets.GHCR_REPOSITORY }}
|
||||
tags: |
|
||||
type=edge,branch=master
|
||||
type=semver,pattern={{major}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}.{{minor}}.{{patch}}
|
||||
|
||||
- name: 'Set up QEMU'
|
||||
uses: docker/setup-qemu-action@v3.6.0
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- name: 'Set up Docker Buildx'
|
||||
uses: docker/setup-buildx-action@v3.10.0
|
||||
|
||||
# Try get the cached build layers from a prior `generic_build.yml` job.
|
||||
# NOTE: Until adopting `type=gha` scoped cache exporter (in `docker/build-push-action`),
|
||||
# only AMD64 image is expected to be cached, ARM images will build from scratch.
|
||||
- name: 'Retrieve image build from build cache'
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: cache-buildx-${{ inputs.cache-key }}
|
||||
restore-keys: |
|
||||
cache-buildx-
|
||||
|
||||
- name: 'Login to DockerHub'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: 'Login to GitHub Container Registry'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: 'Build and publish images'
|
||||
uses: docker/build-push-action@v6.15.0
|
||||
with:
|
||||
context: .
|
||||
build-args: |
|
||||
DMS_RELEASE=${{ github.ref_type == 'tag' && github.ref_name || 'edge' }}
|
||||
VCS_REVISION=${{ github.sha }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.prep.outputs.tags }}
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
# Disable provenance attestation: https://docs.docker.com/build/attestations/slsa-provenance/
|
||||
provenance: false
|
|
@ -1,59 +0,0 @@
|
|||
name: 'Test the DMS Container Image'
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
cache-key:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
run-tests:
|
||||
name: 'Test'
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
part: [serial, parallel/set1, parallel/set2, parallel/set3]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# Required to retrieve bats (core + extras):
|
||||
submodules: recursive
|
||||
|
||||
# Get the cached build layers from the build job:
|
||||
# This should always be a cache-hit, thus `restore-keys` fallback is not used.
|
||||
# No new cache uploads should ever happen for this job.
|
||||
- name: 'Retrieve image built from build cache'
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: cache-buildx-${{ inputs.cache-key }}
|
||||
|
||||
# Configures buildx to use `docker-container` driver,
|
||||
# Ensures consistent BuildKit version (not coupled to Docker Engine),
|
||||
# and increased compatibility of the build cache vs mixing buildx drivers.
|
||||
- name: 'Set up Docker Buildx'
|
||||
uses: docker/setup-buildx-action@v3.10.0
|
||||
|
||||
# Importing from the cache should create the image within approx 30 seconds:
|
||||
# NOTE: `qemu` step is not needed as we only test for AMD64.
|
||||
- name: 'Build AMD64 image from cache'
|
||||
uses: docker/build-push-action@v6.15.0
|
||||
with:
|
||||
context: .
|
||||
tags: mailserver-testing:ci
|
||||
# Export the built image to the Docker host for use with BATS:
|
||||
load: true
|
||||
# Rebuilds the AMD64 image from the cache:
|
||||
platforms: linux/amd64
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
# Disable provenance attestation: https://docs.docker.com/build/attestations/slsa-provenance/
|
||||
provenance: false
|
||||
|
||||
- name: 'Run tests'
|
||||
run: make generate-accounts tests/${{ matrix.part }}
|
|
@ -1,67 +0,0 @@
|
|||
# This workflow checks out code, re-builds an image from cache, performs a container image
|
||||
# vulnerability scan with Anchore's Grype tool, and integrates the results with GitHub
|
||||
# Advanced Security code scanning feature.
|
||||
#
|
||||
# For more information on the Anchore scan action usage and parameters, see
|
||||
# https://github.com/anchore/scan-action. For more information on Anchore's container
|
||||
# image scanning tool Grype, see https://github.com/anchore/grype.
|
||||
name: "Anchore Grype Vulnerability Scan"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
cache-key:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
scan-image:
|
||||
permissions:
|
||||
contents: read # for actions/checkout to fetch code
|
||||
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Get the cached build layers from the build job:
|
||||
# This should always be a cache-hit, thus `restore-keys` fallback is not used.
|
||||
# No new cache uploads should ever happen for this job.
|
||||
- name: 'Retrieve image built from build cache'
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: cache-buildx-${{ inputs.cache-key }}
|
||||
|
||||
# Configures buildx to use `docker-container` driver,
|
||||
# Ensures consistent BuildKit version (not coupled to Docker Engine),
|
||||
# and increased compatibility of the build cache vs mixing buildx drivers.
|
||||
- name: 'Set up Docker Buildx'
|
||||
uses: docker/setup-buildx-action@v3.10.0
|
||||
|
||||
# Importing from the cache should create the image within approx 30 seconds:
|
||||
# NOTE: `qemu` step is not needed as we only test for AMD64.
|
||||
- name: 'Build AMD64 image from cache'
|
||||
uses: docker/build-push-action@v6.15.0
|
||||
with:
|
||||
context: .
|
||||
tags: mailserver-testing:ci
|
||||
# Export the built image to the Docker host for later use:
|
||||
load: true
|
||||
# Rebuilds the AMD64 image from the cache:
|
||||
platforms: linux/amd64
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
# Disable provenance attestation: https://docs.docker.com/build/attestations/slsa-provenance/
|
||||
provenance: false
|
||||
|
||||
- name: 'Run the Anchore Grype scan action'
|
||||
uses: anchore/scan-action@v6.1.0
|
||||
id: scan
|
||||
with:
|
||||
image: mailserver-testing:ci
|
||||
fail-build: false
|
||||
|
||||
- name: 'Upload vulnerability report'
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: ${{ steps.scan.outputs.sarif }}
|
|
@ -1,46 +0,0 @@
|
|||
name: "Handle Stale Issues & Pull Requests"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 1 * * *"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Close stale issues
|
||||
uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 20
|
||||
days-before-close: 10
|
||||
|
||||
exempt-issue-labels: stale-bot/ignore
|
||||
stale-issue-label: meta/stale
|
||||
stale-issue-message: |
|
||||
This issue has become stale because it has been open for 20 days without activity.
|
||||
This issue will be closed in 10 days automatically unless:
|
||||
|
||||
- a maintainer removes the `meta/stale` label or adds the `stale-bot/ignore` label
|
||||
- new activity occurs, such as a new comment
|
||||
close-issue-label: "meta/closed due to age or inactivity"
|
||||
close-issue-message: >
|
||||
This issue was closed due to inactivity.
|
||||
|
||||
exempt-pr-labels: stale-bot/ignore
|
||||
stale-pr-label: meta/stale
|
||||
stale-pr-message: |
|
||||
This pull request has become stale because it has been open for 20 days without activity.
|
||||
This pull request will be closed in 10 days automatically unless:
|
||||
|
||||
- a maintainer removes the `meta/stale` label or adds the `stale-bot/ignore` label
|
||||
- new activity occurs, such as a new comment
|
||||
close-pr-label: "meta/closed due to age or inactivity"
|
||||
close-pr-message: >
|
||||
This PR was closed due to inactivity.
|
|
@ -1,28 +1,40 @@
|
|||
name: Lint
|
||||
name: "Lint"
|
||||
|
||||
on:
|
||||
# A workflow that creates a PR will not trigger this workflow,
|
||||
# Providing a manual trigger as a workaround
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
push:
|
||||
branches: [ master ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
branches: [ "master", "stable" ]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Hadolint
|
||||
run: make hadolint
|
||||
|
||||
run: |
|
||||
sudo curl -S -L https://github.com/hadolint/hadolint/releases/download/v${HADOLINT_VERSION}/hadolint-$(uname -s)-$(uname -m) -o /usr/local/bin/hadolint
|
||||
sudo chmod +rx /usr/local/bin/hadolint
|
||||
make hadolint
|
||||
env:
|
||||
HADOLINT_VERSION: 1.19.0
|
||||
- name: ShellCheck
|
||||
run: make shellcheck
|
||||
|
||||
run: |
|
||||
sudo curl -S -L "https://github.com/koalaman/shellcheck/releases/download/v${SHELLCHECK_VERSION}/shellcheck-v${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" | tar -xJ
|
||||
sudo mv "shellcheck-v${SHELLCHECK_VERSION}/shellcheck" /usr/bin/
|
||||
sudo rm -rf "shellcheck-v${SHELLCHECK_VERSION}"
|
||||
make shellcheck
|
||||
env:
|
||||
SHELLCHECK_VERSION: 0.7.1
|
||||
- name: ECLint
|
||||
run: make eclint
|
||||
run: |
|
||||
sudo curl -S -L "https://github.com/editorconfig-checker/editorconfig-checker/releases/download/${ECLINT_VERSION}/ec-linux-amd64.tar.gz" | tar -xaz
|
||||
sudo mv bin/ec-linux-amd64 /usr/bin/eclint
|
||||
sudo rm -rf bin
|
||||
sudo chmod +x /usr/bin/eclint
|
||||
make eclint
|
||||
env:
|
||||
ECLINT_VERSION: 2.3.1
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
name: 'Documentation'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/pr-docs.yml'
|
||||
- 'docs/**'
|
||||
|
||||
# Jobs will run shell commands from this subdirectory:
|
||||
defaults:
|
||||
run:
|
||||
working-directory: docs
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: 'Verify Build'
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: 'Build with mkdocs-material via Docker'
|
||||
run: docker run --rm -v ${PWD}:/docs squidfunk/mkdocs-material build --strict
|
|
@ -1,33 +1,52 @@
|
|||
name: 'Deploy :edge on Schedule'
|
||||
name: "Build Stable on Schedule"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: 0 0 * * 5
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
security-events: write
|
||||
- cron: "0 0 * * 5"
|
||||
|
||||
jobs:
|
||||
build-images:
|
||||
name: 'Build Images'
|
||||
uses: docker-mailserver/docker-mailserver/.github/workflows/generic_build.yml@master
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
||||
scan-image:
|
||||
name: 'Scan Image for Vulnerabilities'
|
||||
needs: build-images
|
||||
uses: docker-mailserver/docker-mailserver/.github/workflows/generic_vulnerability-scan.yml@master
|
||||
with:
|
||||
cache-key: ${{ needs.build-images.outputs.build-cache-key }}
|
||||
|
||||
publish-images:
|
||||
name: 'Publish Images'
|
||||
needs: build-images
|
||||
uses: docker-mailserver/docker-mailserver/.github/workflows/generic_publish.yml@master
|
||||
with:
|
||||
cache-key: ${{ needs.build-images.outputs.build-cache-key }}
|
||||
secrets: inherit
|
||||
publish:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: stable
|
||||
submodules: recursive
|
||||
- name: Prepare tags
|
||||
id: prep
|
||||
uses: crazy-max/ghaction-docker-meta@v1
|
||||
with:
|
||||
images: |
|
||||
${{ secrets.DOCKER_REPOSITORY }}
|
||||
${{ secrets.GHCR_REPOSITORY }}
|
||||
tag-custom: stable
|
||||
tag-custom-only: true
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
id: buildx
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ secrets.GHCR_USERNAME }}
|
||||
password: ${{ secrets.GHCR_PASSWORD }}
|
||||
- name: Build image locally
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
build-args: |
|
||||
VCS_REF=${{ github.sha }}
|
||||
VCS_VER=${{ github.ref }}
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.prep.outputs.tags }}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
#!/bin/bash
|
||||
set -ex
|
||||
|
||||
# PWD should be at the project docs/ folder.
|
||||
# `--user` is required for build output file ownership to match the CI user,
|
||||
# instead of the internal root user of the container.
|
||||
# `build --strict` ensures the build fails when any warnings are omitted.
|
||||
docker run \
|
||||
--rm \
|
||||
--quiet \
|
||||
--user "$(id -u):$(id -g)" \
|
||||
--volume "./:/docs" \
|
||||
--name "build-docs" \
|
||||
squidfunk/mkdocs-material:9.6 build --strict
|
||||
|
||||
# Remove unnecessary build artifacts: https://github.com/squidfunk/mkdocs-material/issues/2519
|
||||
# site/ is the build output folder.
|
||||
cd site
|
||||
find . -type f -name '*.min.js.map' -delete -o -name '*.min.css.map' -delete
|
||||
rm sitemap.xml.gz
|
||||
rm assets/images/favicon.png
|
||||
rm -r assets/javascripts/lunr
|
|
@ -3,8 +3,7 @@
|
|||
# CI ENV `GITHUB_REF` from Github Actions CI provides the tag or branch that triggered the build
|
||||
# See `github.ref`: https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#github-context
|
||||
# https://docs.github.com/en/actions/reference/environment-variables
|
||||
function _update_versions_json
|
||||
{
|
||||
function _update-versions-json {
|
||||
# Extract the version tag, truncate `<PATCH>` version and any suffix beyond it.
|
||||
local MAJOR_MINOR
|
||||
MAJOR_MINOR=$(grep -oE 'v[0-9]+\.[0-9]+' <<< "${GITHUB_REF}")
|
||||
|
@ -33,7 +32,7 @@ function _update_versions_json
|
|||
local VERSION_EXISTS
|
||||
VERSION_EXISTS=$(jq --arg version "${MAJOR_MINOR}" '[.[].version == $version] | any' "${VERSIONS_JSON}")
|
||||
|
||||
if [[ ${VERSION_EXISTS} == "true" ]]
|
||||
if [[ "${VERSION_EXISTS}" == "true" ]]
|
||||
then
|
||||
echo "${MAJOR_MINOR} docs are already supported. Nothing to change, exiting.."
|
||||
exit 1
|
||||
|
@ -57,4 +56,4 @@ function _update_versions_json
|
|||
fi
|
||||
}
|
||||
|
||||
_update_versions_json
|
||||
_update-versions-json
|
||||
|
|
|
@ -1,38 +1,37 @@
|
|||
name: "Test Merge Requests"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- target/**
|
||||
- test/**
|
||||
- .dockerignore
|
||||
- .gitmodules
|
||||
- Dockerfile
|
||||
- Makefile
|
||||
- setup.sh
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
paths:
|
||||
- 'target/**'
|
||||
- 'test/**'
|
||||
- '.dockerignore'
|
||||
- '.gitmodules'
|
||||
- 'Dockerfile'
|
||||
- 'setup.sh'
|
||||
|
||||
jobs:
|
||||
build-image-amd64:
|
||||
name: "Build AMD64 Image"
|
||||
uses: docker-mailserver/docker-mailserver/.github/workflows/generic_build.yml@master
|
||||
|
||||
run-tests:
|
||||
name: "Test AMD64 Image"
|
||||
needs: build-image-amd64
|
||||
uses: docker-mailserver/docker-mailserver/.github/workflows/generic_test.yml@master
|
||||
with:
|
||||
cache-key: ${{ needs.build-image-amd64.outputs.build-cache-key }}
|
||||
|
||||
job-build-arm:
|
||||
name: "Build ARM64 Image"
|
||||
# Dependency ensures the cache-key is only created for AMD64 builds.
|
||||
# ARM64 will not be able to use this cache, building from scratch each time.
|
||||
# Expect about 2 minutes extra build time until adopting `type=gha` with scopes for cache.
|
||||
needs: build-image-amd64
|
||||
uses: docker-mailserver/docker-mailserver/.github/workflows/generic_build.yml@master
|
||||
with:
|
||||
platforms: linux/arm64
|
||||
build-and-test:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build image locally
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
build-args: |
|
||||
VCS_REF=${{ github.sha }}
|
||||
VCS_VER=${{ github.ref }}
|
||||
platforms: linux/amd64
|
||||
load: true
|
||||
tags: mailserver-testing:ci
|
||||
- name: Run test suite
|
||||
run: >
|
||||
NAME=mailserver-testing:ci
|
||||
bash -c 'make generate-accounts tests'
|
||||
env:
|
||||
CI: true
|
||||
|
|
|
@ -2,22 +2,49 @@
|
|||
### General ###################################
|
||||
#################################################
|
||||
|
||||
.DS_Store
|
||||
.env
|
||||
compose.override.yaml
|
||||
docs/site/
|
||||
docker-data/
|
||||
config/opendkim/
|
||||
|
||||
#################################################
|
||||
### IDEs ######################################
|
||||
#################################################
|
||||
|
||||
.idea/
|
||||
.vscode/
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
|
||||
#################################################
|
||||
### Linting Tools #############################
|
||||
#################################################
|
||||
|
||||
tools/
|
||||
|
||||
#################################################
|
||||
### Tests #####################################
|
||||
#################################################
|
||||
|
||||
test/duplicate_configs/
|
||||
test/config/dovecot-masters.cf
|
||||
test/config/empty/
|
||||
test/config/postfix-accounts.cf
|
||||
test/config/letsencrypt/mail.my-domain.com/combined.pem
|
||||
test/config/dovecot-lmtp/userdb
|
||||
test/config/key*
|
||||
test/config/opendkim/keys/domain.tld/
|
||||
test/config/opendkim/keys/example.com/
|
||||
test/config/opendkim/keys/localdomain2.com/
|
||||
test/config/postfix-aliases.cf
|
||||
test/config/postfix-receive-access.cf
|
||||
test/config/postfix-receive-access.cfe
|
||||
test/config/postfix-send-access.cf
|
||||
test/config/postfix-send-access.cfe
|
||||
test/config/relay-hosts/chksum
|
||||
test/config/relay-hosts/postfix-aliases.cf
|
||||
test/config/without-virtual/
|
||||
test/config/with-domain/
|
||||
test/onedir
|
||||
test/duplicate_configs
|
||||
|
||||
config.bak
|
||||
testconfig.bak
|
||||
|
||||
docs/site
|
||||
|
|
|
@ -3,7 +3,10 @@
|
|||
url = https://github.com/bats-core/bats-core.git
|
||||
[submodule "test/test_helper/bats-support"]
|
||||
path = test/test_helper/bats-support
|
||||
url = https://github.com/bats-core/bats-support
|
||||
url = https://github.com/ztombol/bats-support
|
||||
[submodule "test/test_helper/bats-assert"]
|
||||
path = test/test_helper/bats-assert
|
||||
url = https://github.com/bats-core/bats-assert
|
||||
url = https://github.com/ztombol/bats-assert
|
||||
[submodule "target/docker-configomat"]
|
||||
path = target/docker-configomat
|
||||
url = https://github.com/alinmear/docker-configomat
|
||||
|
|
1026
CHANGELOG.md
|
@ -0,0 +1,195 @@
|
|||
# Contributing
|
||||
|
||||
This project is Open Source. That means that you can contribute on enhancements, bug fixing or improving the [documentation](https://docker-mailserver.github.io/docker-mailserver/edge).
|
||||
|
||||
1. [Issues & PRs](#issues--prs)
|
||||
1. [Opening an Issue](#opening-an-issue)
|
||||
2. [Pull Request](#pull-requests)
|
||||
2. [Coding Style](#coding-style)
|
||||
1. [Bash and Shell](#bash-and-shell)
|
||||
2. [YAML](#yaml)
|
||||
|
||||
## Issues & PRs
|
||||
|
||||
### Opening an Issue
|
||||
|
||||
**Before opening an issue**, read the [`README`](./README.md) carefully, use the [Documentation](https://docker-mailserver.github.io/docker-mailserver/edge), the Postfix/Dovecot documentation and your search engine you trust. The issue tracker is not meant to be used for unrelated questions! When opening an issue, please provide details use case to let the community reproduce your problem. Please start the mail server with env `DMS_DEBUG=1` and paste the output into the issue. **Use the issue templates** to provide the necessary information. Issues which do not use these templates are not worked on and closed. By raising issues, I agree to these terms and I understand, that the rules set for the issue tracker will help both maintainers as well as everyone to find a solution.
|
||||
|
||||
Maintainers take the time to improve on this project and help by solving issues together. It is therefore expected from others to make an effort and **comply with the rules**.
|
||||
|
||||
### Pull Requests
|
||||
|
||||
#### Submit a Pull-Request
|
||||
|
||||
You want to add a feature? Feel free to start creating an issue explaining what you want to do and how you're thinking doing it. Other users may have the same need and collaboration may lead to better results.
|
||||
|
||||
The development workflow is the following:
|
||||
|
||||
1. Fork the project and clone your fork
|
||||
1. Create a new branch to work on
|
||||
2. Run `git submodule update --init --recursive`
|
||||
2. Write the code that is needed :D
|
||||
3. Add integration tests if necessary
|
||||
4. Get the linters with `make install_linters` and install `jq` with the package manager of your OS
|
||||
5. Use `make clean all` to build image locally and run tests (note that tests work on Linux **only**)
|
||||
6. Document your improvements if necessary (e.g. if you introduced new environment variables, write the description in [`ENVIRONMENT.md`](./ENVIRONMENT.md))
|
||||
7. [Commit][commit] and [sign your commit][gpg], push and create a pull-request to merge into `master`. Please **use the pull-request template** to provide a minimum of contextual information and make sure to meet the requirements of the checklist.
|
||||
1. Pull requests are automatically tested against the CI and will be reviewed when tests pass
|
||||
2. When your changes are validated, your branch is merged
|
||||
3. CI builds the new `:edge` image immediately and your changes will be includes in the next version release.
|
||||
|
||||
## Coding Style
|
||||
|
||||
### Bash and Shell
|
||||
|
||||
When refactoring, writing or altering scripts, that is Shell and Bash scripts, in any way, adhere to these rules:
|
||||
|
||||
1. **Adjust your style of coding to the style that is already present**! Even if you do not like it, this is due to consistency. There was a lot of work involved in making all scripts consistent.
|
||||
2. **Use `shellcheck` to check your scripts**! Your contributions are checked by TravisCI too, so you will need to do this. You can **lint your work with `make lint`** to check against all targets.
|
||||
3. **Use the provided `.editorconfig`** file.
|
||||
4. Use `/bin/bash` or `/usr/bin/env bash` instead of `/bin/sh`. Adjust the style accordingly.
|
||||
5. `setup.sh` provides a good starting point to look for.
|
||||
6. When appropriate, use the `set` builtin. We recommend `set -euEo pipefail` or `set -uE`.
|
||||
|
||||
#### Styling rules
|
||||
|
||||
##### If-Else-Statements
|
||||
|
||||
``` BASH
|
||||
# when using braces, use double braces
|
||||
# remember you do not need "" when using [[ ]]
|
||||
if [[ <CONDITION1> ]] && [[ -f ${FILE} ]]
|
||||
then
|
||||
<CODE TO RUN>
|
||||
# when running commands, you don't need braces
|
||||
elif <COMMAND TO RUN>
|
||||
<CODE TO TUN>
|
||||
else
|
||||
<CODE TO TUN>
|
||||
fi
|
||||
|
||||
# equality checks with numbers are done
|
||||
# with -eq/-ne/-lt/-ge, not != or ==
|
||||
if [[ ${VAR} -ne 42 ]] || [[ ${SOME_VAR} -eq 6 ]]
|
||||
then
|
||||
<CODE TO RUN>
|
||||
fi
|
||||
```
|
||||
|
||||
##### Variables & Braces
|
||||
|
||||
Variables are always uppercase. We always use braces.
|
||||
|
||||
If you forgot this and want to change it later, you can use [this link][regex]. The used regex is `\$([^{("\\'\/])([a-zA-Z0-9_]*)([^}\/ \t'"\n.\]:(=\\-]*)`, where you should in practice be able to replace all variable occurrences without braces with occurrences with braces.
|
||||
|
||||
``` BASH
|
||||
# good
|
||||
local VAR="good"
|
||||
local NEW="${VAR}"
|
||||
|
||||
# bad -> TravisCI will fail
|
||||
var="bad"
|
||||
new=$var
|
||||
```
|
||||
|
||||
##### Loops
|
||||
|
||||
Like `if-else`, loops look like this
|
||||
|
||||
``` BASH
|
||||
for / while <LOOP CONDITION>
|
||||
do
|
||||
<CODE TO RUN>
|
||||
done
|
||||
```
|
||||
|
||||
##### Functions
|
||||
|
||||
It's always nice to see the use of functions as it also provides a clear structure. If scripts are small, this is unnecessary, but if they become larger, please consider using functions. When doing so, provide `function _main`.
|
||||
|
||||
``` BASH
|
||||
function _<name_underscored_and_lowercase>
|
||||
{
|
||||
<CODE TO RUN>
|
||||
|
||||
# variables that can be local should be local
|
||||
local <LOCAL_VARIABLE_NAME>
|
||||
}
|
||||
```
|
||||
|
||||
##### Error Tracing
|
||||
|
||||
A construct to trace error in your scripts looks like this. Remember: Remove `set -x` in the end. This is for debugging purposes only.
|
||||
|
||||
``` BASH
|
||||
set -xeuEo pipefail
|
||||
trap '__log_err ${FUNCNAME[0]:-"?"} ${BASH_COMMAND:-"?"} ${LINENO:-"?"} ${?:-"?"}' ERR
|
||||
|
||||
SCRIPT='name_of_this_script.sh'
|
||||
|
||||
function __log_err
|
||||
{
|
||||
printf "\n––– \e[1m\e[31mUNCHECKED ERROR\e[0m\n%s\n%s\n%s\n%s\n\n" \
|
||||
" – script = ${SCRIPT:-${0}}" \
|
||||
" – function = ${1} / ${2}" \
|
||||
" – line = ${3}" \
|
||||
" – exit code = ${4}" 1>&2
|
||||
|
||||
<CODE TO RUN AFTERWARDS>
|
||||
}
|
||||
```
|
||||
|
||||
##### Comments, Descriptiveness & An Example
|
||||
|
||||
Comments should only describe non-obvious matters. Comments should start lowercase when they aren't sentences. Make the code **self-descriptive** by using meaningful names! Make comments not longer than approximately 80 columns, then wrap the line.
|
||||
|
||||
A positive example, which is taken from `start-mailserver.sh`, would be
|
||||
|
||||
``` BASH
|
||||
function _setup_postfix_aliases
|
||||
{
|
||||
_notify 'task' 'Setting up Postfix Aliases'
|
||||
|
||||
: >/etc/postfix/virtual
|
||||
: >/etc/postfix/regexp
|
||||
|
||||
if [[ -f /tmp/docker-mailserver/postfix-virtual.cf ]]
|
||||
then
|
||||
# fixing old virtual user file
|
||||
if grep -q ",$" /tmp/docker-mailserver/postfix-virtual.cf
|
||||
then
|
||||
sed -i -e "s/, /,/g" -e "s/,$//g" /tmp/docker-mailserver/postfix-virtual.cf
|
||||
fi
|
||||
|
||||
cp -f /tmp/docker-mailserver/postfix-virtual.cf /etc/postfix/virtual
|
||||
|
||||
# the `to` is important, don't delete it
|
||||
# shellcheck disable=SC2034
|
||||
while read -r FROM TO
|
||||
do
|
||||
# Setting variables for better readability
|
||||
UNAME=$(echo "${FROM}" | cut -d @ -f1)
|
||||
DOMAIN=$(echo "${FROM}" | cut -d @ -f2)
|
||||
|
||||
# if they are equal it means the line looks like: "user1 other@domain.tld"
|
||||
[[ "${UNAME}" != "${DOMAIN}" ]] && echo "${DOMAIN}" >> /tmp/vhost.tmp
|
||||
done < <(grep -v "^\s*$\|^\s*\#" /tmp/docker-mailserver/postfix-virtual.cf || true)
|
||||
else
|
||||
_notify 'inf' "Warning 'config/postfix-virtual.cf' is not provided. No mail alias/forward created."
|
||||
fi
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### YAML
|
||||
|
||||
When formatting YAML files, use [Prettier][prettier], an opinionated formatter. There are many plugins for IDEs around.
|
||||
|
||||
[//]: # (Links)
|
||||
|
||||
[commit]: https://help.github.com/articles/closing-issues-via-commit-messages/
|
||||
[gpg]: https://docs.github.com/en/github/authenticating-to-github/generating-a-new-gpg-key
|
||||
[semver]: https://semver.org/
|
||||
[regex]: https://regex101.com/r/ikzJpF/7
|
||||
[prettier]: https://prettier.io
|
448
Dockerfile
|
@ -1,182 +1,180 @@
|
|||
# syntax=docker.io/docker/dockerfile:1
|
||||
|
||||
# This Dockerfile provides four stages: stage-base, stage-compile, stage-main and stage-final
|
||||
# This is in preparation for more granular stages (eg ClamAV and Fail2Ban split into their own)
|
||||
FROM docker.io/debian:buster-slim
|
||||
|
||||
ARG VCS_VER
|
||||
ARG VCS_REF
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG DOVECOT_COMMUNITY_REPO=0
|
||||
ARG LOG_LEVEL=trace
|
||||
|
||||
FROM docker.io/debian:12-slim AS stage-base
|
||||
LABEL org.opencontainers.image.version=${VCS_VER}
|
||||
LABEL org.opencontainers.image.revision=${VCS_REF}
|
||||
LABEL org.opencontainers.image.title="docker-mailserver"
|
||||
LABEL org.opencontainers.image.vendor="The Docker Mailserver Organization"
|
||||
LABEL org.opencontainers.image.authors="The Docker Mailserver Organization on GitHub"
|
||||
LABEL org.opencontainers.image.licenses="MIT"
|
||||
LABEL org.opencontainers.image.description="A fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.). Only configuration files, no SQL database."
|
||||
LABEL org.opencontainers.image.url="https://github.com/docker-mailserver"
|
||||
LABEL org.opencontainers.image.documentation="https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md"
|
||||
LABEL org.opencontainers.image.source="https://github.com/docker-mailserver/docker-mailserver"
|
||||
|
||||
ARG DEBIAN_FRONTEND
|
||||
ARG DOVECOT_COMMUNITY_REPO
|
||||
ARG LOG_LEVEL
|
||||
ENV ENABLE_POSTGREY=0
|
||||
ENV FETCHMAIL_POLL=300
|
||||
ENV ONE_DIR=0
|
||||
ENV POSTGREY_AUTO_WHITELIST_CLIENTS=5
|
||||
ENV POSTGREY_DELAY=300
|
||||
ENV POSTGREY_MAX_AGE=35
|
||||
ENV POSTGREY_TEXT="Delayed by Postgrey"
|
||||
ENV SASLAUTHD_MECHANISMS=pam
|
||||
ENV SASLAUTHD_MECH_OPTIONS=""
|
||||
ENV VIRUSMAILS_DELETE_DELAY=7
|
||||
|
||||
SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- Install Basic Software --------------------
|
||||
# -----------------------------------------------
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– Install Basic Software ––––––––––––––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
COPY target/bin/sedfile /usr/local/bin/sedfile
|
||||
RUN <<EOF
|
||||
chmod +x /usr/local/bin/sedfile
|
||||
adduser --quiet --system --group --disabled-password --home /var/lib/clamav --no-create-home --uid 200 clamav
|
||||
EOF
|
||||
RUN \
|
||||
apt-get -qq update && \
|
||||
apt-get -y install apt-utils &>/dev/null && \
|
||||
apt-get -y dist-upgrade >/dev/null && \
|
||||
apt-get -y install postfix >/dev/null && \
|
||||
apt-get -y --no-install-recommends install \
|
||||
# A - D
|
||||
altermime amavisd-new apt-transport-https arj binutils bzip2 \
|
||||
ca-certificates cabextract clamav clamav-daemon cpio curl \
|
||||
dovecot-core dovecot-imapd dovecot-ldap dovecot-lmtpd \
|
||||
dovecot-managesieved dovecot-pop3d dovecot-sieve dovecot-solr \
|
||||
dumb-init \
|
||||
# E - O
|
||||
ed fail2ban fetchmail file gamin gnupg gzip iproute2 iptables \
|
||||
locales logwatch lhasa libdate-manip-perl liblz4-tool \
|
||||
libmail-spf-perl libnet-dns-perl libsasl2-modules lrzip lzop \
|
||||
netcat-openbsd nomarch opendkim opendkim-tools opendmarc \
|
||||
# P - Z
|
||||
pax pflogsumm postgrey p7zip-full postfix-ldap postfix-pcre \
|
||||
postfix-policyd-spf-python postsrsd pyzor \
|
||||
razor rpm2cpio rsyslog sasl2-bin spamassassin supervisor \
|
||||
unrar-free unzip whois xz-utils >/dev/null && \
|
||||
# cleanup
|
||||
apt-get -qq autoclean && \
|
||||
apt-get -qq clean && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
c_rehash 2>/dev/null
|
||||
|
||||
COPY target/scripts/build/packages.sh /build/
|
||||
COPY target/scripts/helpers/log.sh /usr/local/bin/helpers/log.sh
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– ClamAV & FeshClam –––––––––––––––––––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
RUN /bin/bash /build/packages.sh && rm -r /build
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- Compile deb packages ----------------------
|
||||
# -----------------------------------------------
|
||||
|
||||
FROM stage-base AS stage-compile
|
||||
|
||||
ARG LOG_LEVEL
|
||||
ARG DEBIAN_FRONTEND
|
||||
|
||||
COPY target/scripts/build/compile.sh /build/
|
||||
RUN /bin/bash /build/compile.sh
|
||||
|
||||
#
|
||||
# main stage provides all packages, config, and adds scripts
|
||||
#
|
||||
|
||||
FROM stage-base AS stage-main
|
||||
|
||||
ARG DEBIAN_FRONTEND
|
||||
ARG LOG_LEVEL
|
||||
|
||||
SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"]
|
||||
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- ClamAV & FeshClam -------------------------
|
||||
# -----------------------------------------------
|
||||
|
||||
# Copy over latest DB updates from official ClamAV image. This is better than running `freshclam`,
|
||||
# which would require an extra memory of 500MB+ during an image build.
|
||||
# When using `COPY --link`, the `--chown` option is only compatible with numeric ID values.
|
||||
# hadolint ignore=DL3021
|
||||
COPY --link --chown=200 --from=docker.io/clamav/clamav-debian:latest /var/lib/clamav /var/lib/clamav
|
||||
|
||||
RUN <<EOF
|
||||
# `COPY --link --chown=200` has a bug when built by the buildx docker-container driver.
|
||||
# Restore ownership of parent dirs (Bug: https://github.com/moby/buildkit/issues/3912)
|
||||
chown root:root /var /var/lib
|
||||
echo '0 */6 * * * clamav /usr/bin/freshclam --quiet' >/etc/cron.d/clamav-freshclam
|
||||
chmod 644 /etc/clamav/freshclam.conf
|
||||
sedfile -i 's/Foreground false/Foreground true/g' /etc/clamav/clamd.conf
|
||||
mkdir /var/run/clamav
|
||||
chown -R clamav:root /var/run/clamav
|
||||
RUN \
|
||||
echo '0 */6 * * * clamav /usr/bin/freshclam --quiet' > /etc/cron.d/clamav-freshclam && \
|
||||
chmod 644 /etc/clamav/freshclam.conf && \
|
||||
freshclam && \
|
||||
sed -i 's/Foreground false/Foreground true/g' /etc/clamav/clamd.conf && \
|
||||
mkdir /var/run/clamav && \
|
||||
chown -R clamav:root /var/run/clamav && \
|
||||
rm -rf /var/log/clamav/
|
||||
EOF
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- Dovecot -----------------------------------
|
||||
# -----------------------------------------------
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– Dovecot & MkCert ––––––––––––––––––––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
# install fts_xapian plugin
|
||||
|
||||
COPY --from=stage-compile dovecot-fts-xapian-*.deb /
|
||||
RUN dpkg -i /dovecot-fts-xapian-*.deb && rm /dovecot-fts-xapian-*.deb
|
||||
|
||||
COPY target/dovecot/*.inc target/dovecot/*.conf /etc/dovecot/conf.d/
|
||||
COPY target/dovecot/auth-passwdfile.inc target/dovecot/??-*.conf /etc/dovecot/conf.d/
|
||||
COPY target/dovecot/sieve/ /etc/dovecot/sieve/
|
||||
COPY target/dovecot/dovecot-purge.cron /etc/cron.d/dovecot-purge.disabled
|
||||
RUN chmod 0 /etc/cron.d/dovecot-purge.disabled
|
||||
WORKDIR /usr/share/dovecot
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- Rspamd ------------------------------------
|
||||
# -----------------------------------------------
|
||||
# hadolint ignore=SC2016,SC2086,SC2069
|
||||
RUN \
|
||||
sed -i -e 's/include_try \/usr\/share\/dovecot\/protocols\.d/include_try \/etc\/dovecot\/protocols\.d/g' /etc/dovecot/dovecot.conf && \
|
||||
sed -i -e 's/#mail_plugins = \$mail_plugins/mail_plugins = \$mail_plugins sieve/g' /etc/dovecot/conf.d/15-lda.conf && \
|
||||
sed -i -e 's/^.*lda_mailbox_autocreate.*/lda_mailbox_autocreate = yes/g' /etc/dovecot/conf.d/15-lda.conf && \
|
||||
sed -i -e 's/^.*lda_mailbox_autosubscribe.*/lda_mailbox_autosubscribe = yes/g' /etc/dovecot/conf.d/15-lda.conf && \
|
||||
sed -i -e 's/^.*postmaster_address.*/postmaster_address = '${POSTMASTER_ADDRESS:="postmaster@domain.com"}'/g' /etc/dovecot/conf.d/15-lda.conf && \
|
||||
sed -i 's/#imap_idle_notify_interval = 2 mins/imap_idle_notify_interval = 29 mins/' /etc/dovecot/conf.d/20-imap.conf && \
|
||||
# adapt mkcert for Dovecot community repo
|
||||
sed -i 's/CERTDIR=.*/CERTDIR=\/etc\/dovecot\/ssl/g' /usr/share/dovecot/mkcert.sh && \
|
||||
sed -i 's/KEYDIR=.*/KEYDIR=\/etc\/dovecot\/ssl/g' /usr/share/dovecot/mkcert.sh && \
|
||||
sed -i 's/KEYFILE=.*/KEYFILE=\$KEYDIR\/dovecot.key/g' /usr/share/dovecot/mkcert.sh && \
|
||||
sed -i 's/RANDFILE.*//g' /usr/share/dovecot/dovecot-openssl.cnf && \
|
||||
mkdir /etc/dovecot/ssl && \
|
||||
chmod 755 /etc/dovecot/ssl && \
|
||||
./mkcert.sh 2>&1 >/dev/null && \
|
||||
mkdir -p /usr/lib/dovecot/sieve-pipe /usr/lib/dovecot/sieve-filter /usr/lib/dovecot/sieve-global && \
|
||||
chmod 755 -R /usr/lib/dovecot/sieve-pipe /usr/lib/dovecot/sieve-filter /usr/lib/dovecot/sieve-global
|
||||
|
||||
COPY target/rspamd/local.d/ /etc/rspamd/local.d/
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- OAUTH2 ------------------------------------
|
||||
# -----------------------------------------------
|
||||
|
||||
COPY target/dovecot/dovecot-oauth2.conf.ext /etc/dovecot
|
||||
COPY target/dovecot/auth-oauth2.conf.ext /etc/dovecot/conf.d
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- LDAP & SpamAssassin's Cron ----------------
|
||||
# -----------------------------------------------
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– LDAP & Spamassassin's Cron ––––––––––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
COPY target/dovecot/dovecot-ldap.conf.ext /etc/dovecot
|
||||
COPY target/dovecot/auth-ldap.conf.ext /etc/dovecot/conf.d
|
||||
COPY \
|
||||
target/postfix/ldap-users.cf \
|
||||
target/postfix/ldap-groups.cf \
|
||||
target/postfix/ldap-aliases.cf \
|
||||
target/postfix/ldap-domains.cf \
|
||||
target/postfix/ldap-senders.cf \
|
||||
/etc/postfix/
|
||||
|
||||
# hadolint ignore=SC2016
|
||||
RUN <<EOF
|
||||
# ref: https://github.com/docker-mailserver/docker-mailserver/pull/3403#discussion_r1306282387
|
||||
echo 'CRON=1' >/etc/default/spamassassin
|
||||
sedfile -i -r 's/^\$INIT restart/supervisorctl restart amavis/g' /etc/spamassassin/sa-update-hooks.d/amavisd-new
|
||||
mkdir /etc/spamassassin/kam/
|
||||
curl -sSfLo /etc/spamassassin/kam/kam.sa-channels.mcgrail.com.key https://mcgrail.com/downloads/kam.sa-channels.mcgrail.com.key
|
||||
EOF
|
||||
RUN \
|
||||
sed -i -r 's/^(CRON)=0/\1=1/g' /etc/default/spamassassin && \
|
||||
sed -i -r 's/^\$INIT restart/supervisorctl restart amavis/g' /etc/spamassassin/sa-update-hooks.d/amavisd-new
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- PostSRSD, Postgrey & Amavis ---------------
|
||||
# -----------------------------------------------
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– Scripts & Miscellaneous –––––––––––––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
COPY \
|
||||
./target/bin/* \
|
||||
./target/scripts/*.sh \
|
||||
./target/scripts/startup/*.sh \
|
||||
./target/docker-configomat/configomat.sh \
|
||||
/usr/local/bin/
|
||||
|
||||
RUN \
|
||||
chmod +x /usr/local/bin/* && \
|
||||
rm -rf /usr/share/locale/* && \
|
||||
rm -rf /usr/share/man/* && \
|
||||
rm -rf /usr/share/doc/* && \
|
||||
touch /var/log/auth.log && \
|
||||
update-locale && \
|
||||
rm /etc/postsrsd.secret && \
|
||||
rm /etc/cron.daily/00logwatch
|
||||
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– PostSRSD, Postgrey & Amavis –––––––––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
COPY target/postsrsd/postsrsd /etc/default/postsrsd
|
||||
COPY target/postgrey/postgrey /etc/default/postgrey
|
||||
RUN <<EOF
|
||||
mkdir /var/run/postgrey
|
||||
COPY target/postgrey/postgrey.init /etc/init.d/postgrey
|
||||
RUN \
|
||||
chmod 755 /etc/init.d/postgrey && \
|
||||
mkdir /var/run/postgrey && \
|
||||
chown postgrey:postgrey /var/run/postgrey
|
||||
curl -Lsfo /etc/postgrey/whitelist_clients https://raw.githubusercontent.com/schweikert/postgrey/master/postgrey_whitelist_clients
|
||||
EOF
|
||||
|
||||
COPY target/amavis/conf.d/* /etc/amavis/conf.d/
|
||||
COPY target/amavis/postfix-amavis.cf /etc/dms/postfix/master.d/
|
||||
RUN <<EOF
|
||||
sedfile -i -r 's/#(@| \\%)bypass/\1bypass/g' /etc/amavis/conf.d/15-content_filter_mode
|
||||
# add users clamav and amavis to each others group
|
||||
adduser clamav amavis
|
||||
adduser amavis clamav
|
||||
RUN \
|
||||
sed -i -r 's/#(@| \\%)bypass/\1bypass/g' /etc/amavis/conf.d/15-content_filter_mode && \
|
||||
adduser clamav amavis >/dev/null && \
|
||||
adduser amavis clamav >/dev/null && \
|
||||
# no syslog user in Debian compared to Ubuntu
|
||||
adduser --system syslog
|
||||
useradd -u 5000 -d /home/docker -s /bin/bash -p "$(echo docker | openssl passwd -1 -stdin)" docker
|
||||
echo "0 4 * * * /usr/local/bin/virus-wiper" | crontab -
|
||||
adduser --system syslog >/dev/null && \
|
||||
useradd -u 5000 -d /home/docker -s /bin/bash -p "$(echo docker | openssl passwd -1 -stdin)" docker >/dev/null && \
|
||||
echo "0 4 * * * /usr/local/bin/virus-wiper" | crontab - && \
|
||||
chmod 644 /etc/amavis/conf.d/*
|
||||
EOF
|
||||
|
||||
# overcomplication necessary for CI
|
||||
# hadolint ignore=SC2086
|
||||
RUN <<EOF
|
||||
for _ in {1..10}; do
|
||||
su - amavis -c "razor-admin -create"
|
||||
sleep 3
|
||||
if su - amavis -c "razor-admin -register"; then
|
||||
EC=0
|
||||
break
|
||||
else
|
||||
EC=${?}
|
||||
fi
|
||||
done
|
||||
exit ${EC}
|
||||
EOF
|
||||
RUN for _ in {1..10}; do su - amavis -c "razor-admin -create" ; sleep 3 ; \
|
||||
if su - amavis -c "razor-admin -register" &>/dev/null; then { EC=0 ; break ; } ; \
|
||||
else EC=${?} ; fi ; done ; (exit ${EC})
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- Fail2Ban, DKIM & DMARC --------------------
|
||||
# -----------------------------------------------
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– Fail2Ban, DKIM & DMARC ––––––––––––––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
COPY target/fail2ban/jail.local /etc/fail2ban/jail.local
|
||||
COPY target/fail2ban/fail2ban.d/fixes.local /etc/fail2ban/fail2ban.d/fixes.local
|
||||
RUN <<EOF
|
||||
ln -s /var/log/mail/mail.log /var/log/mail.log
|
||||
ln -sf /var/log/mail/fail2ban.log /var/log/fail2ban.log
|
||||
EOF
|
||||
COPY target/fail2ban/jail.conf /etc/fail2ban/jail.conf
|
||||
COPY target/fail2ban/filter.d/postfix-sasl.conf /etc/fail2ban/filter.d/postfix-sasl.conf
|
||||
RUN mkdir /var/run/fail2ban
|
||||
|
||||
COPY target/opendkim/opendkim.conf /etc/opendkim.conf
|
||||
COPY target/opendkim/default-opendkim /etc/default/opendkim
|
||||
|
@ -185,151 +183,71 @@ COPY target/opendmarc/opendmarc.conf /etc/opendmarc.conf
|
|||
COPY target/opendmarc/default-opendmarc /etc/default/opendmarc
|
||||
COPY target/opendmarc/ignore.hosts /etc/opendmarc/ignore.hosts
|
||||
|
||||
# --------------------------------------------------
|
||||
# --- postfix-mta-sts-daemon -----------------------
|
||||
# --------------------------------------------------
|
||||
COPY target/mta-sts-daemon/mta-sts-daemon.yml /etc/mta-sts-daemon.yml
|
||||
RUN <<EOF
|
||||
mkdir /var/run/mta-sts
|
||||
chown -R _mta-sts:root /var/run/mta-sts
|
||||
EOF
|
||||
RUN \
|
||||
# switch iptables and ip6tables to legacy for Fail2Ban
|
||||
update-alternatives --set iptables /usr/sbin/iptables-legacy && \
|
||||
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
|
||||
|
||||
# --------------------------------------------------
|
||||
# --- Fetchmail, Getmail, Postfix & Let'sEncrypt ---
|
||||
# --------------------------------------------------
|
||||
|
||||
# Remove invalid URL from SPF message
|
||||
# https://bugs.launchpad.net/spf-engine/+bug/1896912
|
||||
RUN echo 'Reason_Message = Message {rejectdefer} due to: {spf}.' >>/etc/postfix-policyd-spf-python/policyd-spf.conf
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– Fetchmail, Postfix & Let'sEncrypt –––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
COPY target/fetchmail/fetchmailrc /etc/fetchmailrc_general
|
||||
COPY target/getmail/getmailrc_general /etc/getmailrc_general
|
||||
COPY target/getmail/getmail-service.sh /usr/local/bin/
|
||||
COPY target/postfix/main.cf target/postfix/master.cf /etc/postfix/
|
||||
|
||||
# DH parameters for DHE cipher suites, ffdhe4096 is the official standard 4096-bit DH params now part of TLS 1.3
|
||||
# This file is for TLS <1.3 handshakes that rely on DHE cipher suites
|
||||
# Handled at build to avoid failures by doveadm validating ssl_dh filepath in 10-ssl.auth (eg generate-accounts)
|
||||
COPY target/shared/ffdhe4096.pem /etc/postfix/dhparams.pem
|
||||
COPY target/shared/ffdhe4096.pem /etc/dovecot/dh.pem
|
||||
|
||||
COPY target/shared/ffdhe4096.pem /etc/postfix/shared/ffdhe4096.pem
|
||||
COPY \
|
||||
target/postfix/header_checks.pcre \
|
||||
target/postfix/sender_header_filter.pcre \
|
||||
target/postfix/sender_login_maps.pcre \
|
||||
/etc/postfix/maps/
|
||||
|
||||
RUN <<EOF
|
||||
: >/etc/aliases
|
||||
sedfile -i 's/START_DAEMON=no/START_DAEMON=yes/g' /etc/default/fetchmail
|
||||
mkdir /var/run/fetchmail && chown fetchmail /var/run/fetchmail
|
||||
EOF
|
||||
RUN \
|
||||
: >/etc/aliases && \
|
||||
sed -i 's/START_DAEMON=no/START_DAEMON=yes/g' /etc/default/fetchmail && \
|
||||
mkdir /var/run/fetchmail && chown fetchmail /var/run/fetchmail && \
|
||||
curl -s https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem >/etc/ssl/certs/lets-encrypt-x3-cross-signed.pem
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- Logs --------------------------------------
|
||||
# -----------------------------------------------
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– Logs ––––––––––––––––––––––––––––––––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
RUN <<EOF
|
||||
sedfile -i -r "/^#?compress/c\compress\ncopytruncate" /etc/logrotate.conf
|
||||
mkdir /var/log/mail
|
||||
chown syslog:root /var/log/mail
|
||||
touch /var/log/mail/clamav.log
|
||||
chown -R clamav:root /var/log/mail/clamav.log
|
||||
touch /var/log/mail/freshclam.log
|
||||
chown -R clamav:root /var/log/mail/freshclam.log
|
||||
sedfile -i -r 's|/var/log/mail|/var/log/mail/mail|g' /etc/rsyslog.conf
|
||||
sedfile -i -r 's|;auth,authpriv.none|;mail.none;mail.error;auth,authpriv.none|g' /etc/rsyslog.conf
|
||||
sedfile -i -r 's|LogFile /var/log/clamav/|LogFile /var/log/mail/|g' /etc/clamav/clamd.conf
|
||||
sedfile -i -r 's|UpdateLogFile /var/log/clamav/|UpdateLogFile /var/log/mail/|g' /etc/clamav/freshclam.conf
|
||||
sedfile -i -r 's|/var/log/clamav|/var/log/mail|g' /etc/logrotate.d/clamav-daemon
|
||||
sedfile -i -r 's|invoke-rc.d.*|/usr/bin/supervisorctl signal hup clamav >/dev/null \|\| true|g' /etc/logrotate.d/clamav-daemon
|
||||
sedfile -i -r 's|/var/log/clamav|/var/log/mail|g' /etc/logrotate.d/clamav-freshclam
|
||||
sedfile -i -r '/postrotate/,/endscript/d' /etc/logrotate.d/clamav-freshclam
|
||||
sedfile -i -r 's|/var/log/mail|/var/log/mail/mail|g' /etc/logrotate.d/rsyslog
|
||||
sedfile -i -r '/\/var\/log\/mail\/mail.log/d' /etc/logrotate.d/rsyslog
|
||||
sedfile -i 's|^/var/log/fail2ban.log {$|/var/log/mail/fail2ban.log {|' /etc/logrotate.d/fail2ban
|
||||
RUN \
|
||||
sed -i -r "/^#?compress/c\compress\ncopytruncate" /etc/logrotate.conf && \
|
||||
mkdir -p /var/log/mail && \
|
||||
chown syslog:root /var/log/mail && \
|
||||
touch /var/log/mail/clamav.log && \
|
||||
chown -R clamav:root /var/log/mail/clamav.log && \
|
||||
touch /var/log/mail/freshclam.log && \
|
||||
chown -R clamav:root /var/log/mail/freshclam.log && \
|
||||
sed -i -r 's|/var/log/mail|/var/log/mail/mail|g' /etc/rsyslog.conf && \
|
||||
sed -i -r 's|;auth,authpriv.none|;mail.none;mail.error;auth,authpriv.none|g' /etc/rsyslog.conf && \
|
||||
sed -i -r 's|LogFile /var/log/clamav/|LogFile /var/log/mail/|g' /etc/clamav/clamd.conf && \
|
||||
sed -i -r 's|UpdateLogFile /var/log/clamav/|UpdateLogFile /var/log/mail/|g' /etc/clamav/freshclam.conf && \
|
||||
sed -i -r 's|/var/log/clamav|/var/log/mail|g' /etc/logrotate.d/clamav-daemon && \
|
||||
sed -i -r 's|invoke-rc.d.*|/usr/bin/supervisorctl signal hup clamav >/dev/null \|\| true|g' /etc/logrotate.d/clamav-daemon && \
|
||||
sed -i -r 's|/var/log/clamav|/var/log/mail|g' /etc/logrotate.d/clamav-freshclam && \
|
||||
sed -i -r '/postrotate/,/endscript/d' /etc/logrotate.d/clamav-freshclam && \
|
||||
sed -i -r 's|/var/log/mail|/var/log/mail/mail|g' /etc/logrotate.d/rsyslog && \
|
||||
sed -i -r '/\/var\/log\/mail\/mail.log/d' /etc/logrotate.d/rsyslog && \
|
||||
# prevent syslog logrotate warnings
|
||||
sedfile -i -e 's/\(printerror "could not determine current runlevel"\)/#\1/' /usr/sbin/invoke-rc.d
|
||||
sedfile -i -e 's/^\(POLICYHELPER=\).*/\1/' /usr/sbin/invoke-rc.d
|
||||
sed -i -e 's/\(printerror "could not determine current runlevel"\)/#\1/' /usr/sbin/invoke-rc.d && \
|
||||
sed -i -e 's/^\(POLICYHELPER=\).*/\1/' /usr/sbin/invoke-rc.d && \
|
||||
# prevent syslog warning about imklog permissions
|
||||
sedfile -i -e 's/^module(load=\"imklog\")/#module(load=\"imklog\")/' /etc/rsyslog.conf
|
||||
# this change is for our alternative process manager rather than part of
|
||||
# a fix related to the change preceding it.
|
||||
echo -e '\n/usr/bin/supervisorctl signal hup rsyslog >/dev/null' >>/usr/lib/rsyslog/rsyslog-rotate
|
||||
EOF
|
||||
sed -i -e 's/^module(load=\"imklog\")/#module(load=\"imklog\")/' /etc/rsyslog.conf && \
|
||||
# prevent email when /sbin/init or init system is not existing
|
||||
sed -i -e 's|invoke-rc.d rsyslog rotate > /dev/null|/usr/bin/supervisorctl signal hup rsyslog >/dev/null|g' /usr/lib/rsyslog/rsyslog-rotate
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- Logwatch ----------------------------------
|
||||
# -----------------------------------------------
|
||||
|
||||
COPY target/logwatch/maillog.conf /etc/logwatch/conf/logfiles/maillog.conf
|
||||
COPY target/logwatch/ignore.conf /etc/logwatch/conf/ignore.conf
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- Supervisord & Start -----------------------
|
||||
# -----------------------------------------------
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– Supervisord & Start –––––––––––––––––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
COPY target/supervisor/supervisord.conf /etc/supervisor/supervisord.conf
|
||||
COPY target/supervisor/conf.d/* /etc/supervisor/conf.d/
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- Scripts & Miscellaneous--------------------
|
||||
# -----------------------------------------------
|
||||
|
||||
RUN <<EOF
|
||||
rm -rf /usr/share/locale/*
|
||||
rm -rf /usr/share/man/*
|
||||
rm -rf /usr/share/doc/*
|
||||
update-locale
|
||||
EOF
|
||||
|
||||
COPY \
|
||||
target/bin/* \
|
||||
target/scripts/*.sh \
|
||||
target/scripts/startup/*.sh \
|
||||
/usr/local/bin/
|
||||
|
||||
RUN chmod +x /usr/local/bin/*
|
||||
|
||||
COPY target/scripts/helpers /usr/local/bin/helpers
|
||||
COPY target/scripts/startup/setup.d /usr/local/bin/setup.d
|
||||
|
||||
#
|
||||
# Final stage focuses only on image config
|
||||
#
|
||||
|
||||
FROM stage-main AS stage-final
|
||||
ARG DMS_RELEASE=edge
|
||||
ARG VCS_REVISION=unknown
|
||||
|
||||
WORKDIR /
|
||||
|
||||
EXPOSE 25 587 143 465 993 110 995 4190
|
||||
|
||||
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
|
||||
|
||||
CMD ["supervisord", "-c", "/etc/supervisor/supervisord.conf"]
|
||||
|
||||
# These ENVs are referenced in target/supervisor/conf.d/saslauth.conf
|
||||
# and must be present when supervisord starts. Introduced by PR:
|
||||
# https://github.com/docker-mailserver/docker-mailserver/pull/676
|
||||
# These ENV are also configured with the same defaults at:
|
||||
# https://github.com/docker-mailserver/docker-mailserver/blob/672e9cf19a3bb1da309e8cea6ee728e58f905366/target/scripts/helpers/variables.sh
|
||||
ENV FETCHMAIL_POLL=300
|
||||
ENV POSTGREY_AUTO_WHITELIST_CLIENTS=5
|
||||
ENV POSTGREY_DELAY=300
|
||||
ENV POSTGREY_MAX_AGE=35
|
||||
ENV POSTGREY_TEXT="Delayed by Postgrey"
|
||||
ENV SASLAUTHD_MECH_OPTIONS=""
|
||||
|
||||
# Add metadata to image:
|
||||
LABEL org.opencontainers.image.title="docker-mailserver"
|
||||
LABEL org.opencontainers.image.vendor="The Docker Mailserver Organization"
|
||||
LABEL org.opencontainers.image.authors="The Docker Mailserver Organization on GitHub"
|
||||
LABEL org.opencontainers.image.licenses="MIT"
|
||||
LABEL org.opencontainers.image.description="A fullstack but simple mail server (SMTP, IMAP, LDAP, Anti-spam, Anti-virus, etc.). Only configuration files, no SQL database."
|
||||
LABEL org.opencontainers.image.url="https://github.com/docker-mailserver"
|
||||
LABEL org.opencontainers.image.documentation="https://github.com/docker-mailserver/docker-mailserver/blob/master/README.md"
|
||||
LABEL org.opencontainers.image.source="https://github.com/docker-mailserver/docker-mailserver"
|
||||
# ARG invalidates cache when it is used by a layer (implicitly affects RUN)
|
||||
# Thus to maximize cache, keep these lines last:
|
||||
LABEL org.opencontainers.image.revision=${VCS_REVISION}
|
||||
LABEL org.opencontainers.image.version=${DMS_RELEASE}
|
||||
ENV DMS_RELEASE=${DMS_RELEASE}
|
||||
|
|
|
@ -0,0 +1,682 @@
|
|||
# Environment
|
||||
|
||||
## Variables
|
||||
|
||||
1. If an option doesn't work as documented here, check if you are running the latest image!
|
||||
2. Values in **bold** are the default values.
|
||||
3. Since `docker-mailserver v7.1.0`, comparisons for environment variables are executed differently. If you previously used `VARIABLE=''` as the `empty` value, **update** to now use `VARIABLE=`.
|
||||
|
||||
### Assignments
|
||||
|
||||
#### General
|
||||
|
||||
##### DMS_DEBUG
|
||||
|
||||
- **0** => Debug disabled
|
||||
- 1 => Enables debug on startup
|
||||
|
||||
##### SUPERVISOR_LOGLEVEL
|
||||
|
||||
Here you can adjust the [log-level for Supervisor](http://supervisord.org/logging.html#activity-log-levels). Possible values are
|
||||
|
||||
- critical => Only show critical messages
|
||||
- error => Only show erroneous output
|
||||
- **warn** => Show warnings
|
||||
- info => Normal informational output
|
||||
- debug => Also show debug messages
|
||||
|
||||
The log-level will show everything in its class and above.
|
||||
|
||||
##### ENABLE_AMAVIS
|
||||
|
||||
Amavis content filter (used for ClamAV & SpamAssassin)
|
||||
|
||||
- 0 => Amavis is disabled
|
||||
- **1** => Amavis is enabled
|
||||
|
||||
##### ENABLE_CLAMAV
|
||||
|
||||
- **0** => Clamav is disabled
|
||||
- 1 => Clamav is enabled
|
||||
|
||||
##### ONE_DIR
|
||||
|
||||
- **0** => state in default directories
|
||||
- 1 => consolidate all states into a single directory (`/var/mail-state`) to allow persistence using docker volumes
|
||||
|
||||
##### ENABLE_POP3
|
||||
|
||||
- **empty** => POP3 service disabled
|
||||
- 1 => Enables POP3 service
|
||||
|
||||
##### ENABLE_FAIL2BAN
|
||||
|
||||
- **0** => fail2ban service disabled
|
||||
- 1 => Enables fail2ban service
|
||||
|
||||
If you enable Fail2Ban, don't forget to add the following lines to your `docker-compose.yml`:
|
||||
|
||||
``` BASH
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
```
|
||||
|
||||
Otherwise, `iptables` won't be able to ban IPs.
|
||||
|
||||
##### SMTP_ONLY
|
||||
|
||||
- **empty** => all daemons start
|
||||
- 1 => only launch postfix smtp
|
||||
|
||||
##### SSL_TYPE
|
||||
|
||||
- **empty** => SSL disabled.
|
||||
- letsencrypt => Enables Let's Encrypt certificates.
|
||||
- custom => Enables custom certificates.
|
||||
- manual => Let you manually specify locations of your SSL certificates for non-standard cases
|
||||
- Requires: `SSL_CERT_PATH` and `SSL_KEY_PATH` ENV vars to be set to the location of the files within the container.
|
||||
- Optional: `SSL_ALT_CERT_PATH` and `SSL_ALT_KEY_PATH` allow providing a 2nd certificate as a fallback for dual (aka hybrid) certificate support. Useful for ECDSA with an RSA fallback. Presently only `manual` mode supports this feature.
|
||||
- self-signed => Enables self-signed certificates.
|
||||
|
||||
Please read [the SSL page in the documentation](https://docker-mailserver.github.io/docker-mailserver/edge/config/security/ssl) for more information.
|
||||
|
||||
##### TLS_LEVEL
|
||||
|
||||
- **empty** => modern
|
||||
- modern => Enables TLSv1.2 and modern ciphers only. (default)
|
||||
- intermediate => Enables TLSv1, TLSv1.1 and TLSv1.2 and broad compatibility ciphers.
|
||||
|
||||
##### SPOOF_PROTECTION
|
||||
|
||||
Configures the handling of creating mails with forged sender addresses.
|
||||
|
||||
- **empty** => Mail address spoofing allowed. Any logged in user may create email messages with a forged sender address. See also [Wikipedia](https://en.wikipedia.org/wiki/Email_spoofing)(not recommended, but default for backwards compatibility reasons)
|
||||
- 1 => (recommended) Mail spoofing denied. Each user may only send with his own or his alias addresses. Addresses with [extension delimiters](http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages.
|
||||
|
||||
##### ENABLE_SRS
|
||||
|
||||
Enables the Sender Rewriting Scheme. SRS is needed if your mail server acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/master/README.md#sender-rewriting-scheme-crash-course) for further explanation.
|
||||
|
||||
- **0** => Disabled
|
||||
- 1 => Enabled
|
||||
|
||||
##### PERMIT_DOCKER
|
||||
|
||||
Set different options for mynetworks option (can be overwrite in postfix-main.cf) **WARNING**: Adding the docker network's gateway to the list of trusted hosts, e.g. using the `network` or `connected-networks` option, can create an [**open relay**](https://en.wikipedia.org/wiki/Open_mail_relay), for instance if IPv6 is enabled on the host machine but not in Docker.
|
||||
|
||||
- **empty** => localhost only
|
||||
- host => Add docker host (ipv4 only)
|
||||
- network => Add the docker default bridge network (172.16.0.0/12); **WARNING**: `docker-compose` might use others (e.g. 192.168.0.0/16) use `PERMIT_DOCKER=connected-networks` in this case
|
||||
- connected-networks => Add all connected docker networks (ipv4 only)
|
||||
|
||||
Note: you probably want to [set `POSTFIX_INET_PROTOCOLS=ipv4`](#postfix_inet_protocols) to make it work fine with Docker.
|
||||
|
||||
##### NETWORK_INTERFACE
|
||||
|
||||
In case your network interface differs from `eth0`, e.g. when you are using HostNetworking in Kubernetes, you can set this to whatever interface you want. This interface will then be used.
|
||||
|
||||
- **empty** => `eth0`
|
||||
|
||||
##### VIRUSMAILS_DELETE_DELAY
|
||||
|
||||
Set how many days a virusmail will stay on the server before being deleted
|
||||
|
||||
- **empty** => 7 days
|
||||
|
||||
##### ENABLE_POSTFIX_VIRTUAL_TRANSPORT
|
||||
|
||||
This Option is activating the Usage of POSTFIX_DAGENT to specify a ltmp client different from default dovecot socket.
|
||||
|
||||
- **empty** => disabled
|
||||
- 1 => enabled
|
||||
|
||||
##### POSTFIX_DAGENT
|
||||
|
||||
Enabled by ENABLE_POSTFIX_VIRTUAL_TRANSPORT. Specify the final delivery of postfix
|
||||
|
||||
- **empty**: fail
|
||||
- `lmtp:unix:private/dovecot-lmtp` (use socket)
|
||||
- `lmtps:inet:<host>:<port>` (secure lmtp with starttls, take a look at <https://sys4.de/en/blog/2014/11/17/sicheres-lmtp-mit-starttls-in-dovecot/>)
|
||||
- `lmtp:<kopano-host>:2003` (use kopano as mailstore)
|
||||
- etc.
|
||||
|
||||
##### POSTFIX\_MAILBOX\_SIZE\_LIMIT
|
||||
|
||||
Set the mailbox size limit for all users. If set to zero, the size will be unlimited (default).
|
||||
|
||||
- **empty** => 0 (no limit)
|
||||
|
||||
##### ENABLE_QUOTAS
|
||||
|
||||
- **1** => Dovecot quota is enabled
|
||||
- 0 => Dovecot quota is disabled
|
||||
|
||||
See [mailbox quota](https://docker-mailserver.github.io/docker-mailserver/edge/config/user-management/accounts/#notes).
|
||||
|
||||
##### POSTFIX\_MESSAGE\_SIZE\_LIMIT
|
||||
|
||||
Set the message size limit for all users. If set to zero, the size will be unlimited (not recommended!)
|
||||
|
||||
- **empty** => 10240000 (~10 MB)
|
||||
|
||||
##### ENABLE_MANAGESIEVE
|
||||
|
||||
- **empty** => Managesieve service disabled
|
||||
- 1 => Enables Managesieve on port 4190
|
||||
|
||||
##### OVERRIDE_HOSTNAME
|
||||
|
||||
- **empty** => uses the `hostname` command to get the mail server's canonical hostname
|
||||
- => Specify a fully-qualified domainname to serve mail for. This is used for many of the config features so if you can't set your hostname (e.g. you're in a container platform that doesn't let you) specify it in this environment variable.
|
||||
|
||||
##### POSTMASTER_ADDRESS
|
||||
|
||||
- **empty** => postmaster@domain.com
|
||||
- => Specify the postmaster address
|
||||
|
||||
##### POSTSCREEN_ACTION
|
||||
|
||||
- **enforce** => Allow other tests to complete. Reject attempts to deliver mail with a 550 SMTP reply, and log the helo/sender/recipient information. Repeat this test the next time the client connects.
|
||||
- drop => Drop the connection immediately with a 521 SMTP reply. Repeat this test the next time the client connects.
|
||||
- ignore => Ignore the failure of this test. Allow other tests to complete. Repeat this test the next time the client connects. This option is useful for testing and collecting statistics without blocking mail.
|
||||
|
||||
##### DOVECOT_MAILBOX_FORMAT
|
||||
|
||||
- **maildir** => uses very common Maildir format, one file contains one message
|
||||
- sdbox => (experimental) uses Dovecot high-performance mailbox format, one file contains one message
|
||||
- mdbox ==> (experimental) uses Dovecot high-performance mailbox format, multiple messages per file and multiple files per box
|
||||
|
||||
This option has been added in November 2019. Using other format than Maildir is considered as experimental in docker-mailserver and should only be used for testing purpose. For more details, please refer to [Dovecot Documentation](https://wiki2.dovecot.org/MailboxFormat).
|
||||
|
||||
##### POSTFIX_INET_PROTOCOLS
|
||||
|
||||
- **all** => All possible protocols.
|
||||
- ipv4 => Use only IPv4 traffic. Most likely you want this behind Docker.
|
||||
- ipv6 => Use only IPv6 traffic.
|
||||
|
||||
Note: More details in <http://www.postfix.org/postconf.5.html#inet_protocols>
|
||||
|
||||
#### Reports
|
||||
|
||||
##### PFLOGSUMM_TRIGGER
|
||||
|
||||
Enables regular pflogsumm mail reports.
|
||||
|
||||
- **not set** => No report
|
||||
- daily_cron => Daily report for the previous day
|
||||
- logrotate => Full report based on the mail log when it is rotated
|
||||
|
||||
This is a new option. The old REPORT options are still supported for backwards compatibility.
|
||||
If this is not set and reports are enabled with the old options, logrotate will be used.
|
||||
|
||||
##### PFLOGSUMM_RECIPIENT
|
||||
|
||||
Recipient address for pflogsumm reports.
|
||||
|
||||
- **not set** => Use REPORT_RECIPIENT or POSTMASTER_ADDRESS
|
||||
- => Specify the recipient address(es)
|
||||
|
||||
##### PFLOGSUMM_SENDER
|
||||
|
||||
From address for pflogsumm reports.
|
||||
|
||||
- **not set** => Use REPORT_SENDER or POSTMASTER_ADDRESS
|
||||
- => Specify the sender address
|
||||
|
||||
##### LOGWATCH_INTERVAL
|
||||
|
||||
Interval for logwatch report.
|
||||
|
||||
- **none** => No report is generated
|
||||
- daily => Send a daily report
|
||||
- weekly => Send a report every week
|
||||
|
||||
##### LOGWATCH_RECIPIENT
|
||||
|
||||
Recipient address for logwatch reports if they are enabled.
|
||||
|
||||
- **not set** => Use REPORT_RECIPIENT or POSTMASTER_ADDRESS
|
||||
- => Specify the recipient address(es)
|
||||
|
||||
##### REPORT_RECIPIENT (deprecated)
|
||||
|
||||
Enables a report being sent (created by pflogsumm) on a regular basis.
|
||||
|
||||
- **0** => Report emails are disabled unless enabled by other options
|
||||
- 1 => Using POSTMASTER_ADDRESS as the recipient
|
||||
- => Specify the recipient address
|
||||
|
||||
##### REPORT_SENDER (deprecated)
|
||||
|
||||
Change the sending address for mail report
|
||||
|
||||
- **empty** => mailserver-report@hostname
|
||||
- => Specify the report sender (From) address
|
||||
|
||||
##### REPORT_INTERVAL (deprecated)
|
||||
|
||||
Changes the interval in which logs are rotated and a report is being sent (deprecated).
|
||||
|
||||
- **daily** => Send a daily report
|
||||
- weekly => Send a report every week
|
||||
- monthly => Send a report every month
|
||||
|
||||
Note: This variable used to control logrotate inside the container and sent the pflogsumm report when the logs were rotated.
|
||||
It is still supported for backwards compatibility, but the new option LOGROTATE_INTERVAL has been added that only rotates
|
||||
the logs.
|
||||
|
||||
##### LOGROTATE_INTERVAL
|
||||
|
||||
Defines the interval in which the mail log is being rotated.
|
||||
|
||||
- **daily** => Rotate daily.
|
||||
- weekly => Rotate weekly.
|
||||
- monthly => Rotate monthly.
|
||||
|
||||
Note that only the log inside the container is affected.
|
||||
The full log output is still available via `docker logs mail` (or your respective container name).
|
||||
If you want to control logrotation for the docker generated logfile see: [Docker Logging Drivers](https://docs.docker.com/config/containers/logging/configure/).
|
||||
|
||||
Also note that by default the logs are lost when the container is recycled. To keep the logs, mount a volume.
|
||||
|
||||
Finally the logrotate interval **may** affect the period for generated reports. That is the case when the reports are triggered by log rotation.
|
||||
|
||||
#### Spamassassin
|
||||
|
||||
##### ENABLE_SPAMASSASSIN
|
||||
|
||||
- **0** => Spamassassin is disabled
|
||||
- 1 => Spamassassin is enabled
|
||||
|
||||
**/!\\ Spam delivery:** when Spamassassin is enabled, messages marked as spam WILL NOT BE DELIVERED.
|
||||
Use `SPAMASSASSIN_SPAM_TO_INBOX=1` for receiving spam messages.
|
||||
|
||||
##### SPAMASSASSIN_SPAM_TO_INBOX
|
||||
|
||||
- **0** => Spam messages will be bounced (_rejected_) without any notification (_dangerous_).
|
||||
- 1 => Spam messages will be delivered to the inbox and tagged as spam using `SA_SPAM_SUBJECT`.
|
||||
|
||||
##### MOVE_SPAM_TO_JUNK
|
||||
|
||||
- **1** => Spam messages will be delivered in the `Junk` folder.
|
||||
- 0 => Spam messages will be delivered in the mailbox.
|
||||
|
||||
Note: this setting needs `SPAMASSASSIN_SPAM_TO_INBOX=1`
|
||||
|
||||
##### SA_TAG
|
||||
|
||||
- **2.0** => add spam info headers if at, or above that level
|
||||
|
||||
Note: this spamassassin setting needs `ENABLE_SPAMASSASSIN=1`
|
||||
|
||||
##### SA_TAG2
|
||||
|
||||
- **6.31** => add 'spam detected' headers at that level
|
||||
|
||||
Note: this spamassassin setting needs `ENABLE_SPAMASSASSIN=1`
|
||||
|
||||
##### SA_KILL
|
||||
|
||||
- **6.31** => triggers spam evasive actions
|
||||
|
||||
Note: this spamassassin setting needs `ENABLE_SPAMASSASSIN=1`. By default, the mailserver is configured to quarantine spam emails. If emails are quarantined, they are compressed and stored in a location dependent on the ONE_DIR setting above. If `ONE_DIR=1` the location is /var/mail-state/lib-amavis/virusmails/. If `ONE_DIR=0` it is /var/lib/amavis/virusmails/. These paths are inside the docker container. To inhibit this behaviour and deliver spam emails, set this to a very high value e.g. 100.0.
|
||||
|
||||
##### SA_SPAM_SUBJECT
|
||||
|
||||
- **\*\*\*SPAM\*\*\*** => add tag to subject if spam detected
|
||||
|
||||
Note: this spamassassin setting needs `ENABLE_SPAMASSASSIN=1`. Add the spamassassin score to the subject line by inserting the keyword \_SCORE\_: **\*\*\*SPAM(\_SCORE\_)\*\*\***.
|
||||
|
||||
##### SA_SHORTCIRCUIT_BAYES_SPAM
|
||||
|
||||
- **1** => will activate spamassassin short circuiting for bayes spam detection.
|
||||
|
||||
This will uncomment the respective line in ```/etc/spamassasin/local.cf```
|
||||
|
||||
Note: activate this only if you are confident in your bayes database for identifying spam.
|
||||
|
||||
##### SA_SHORTCIRCUIT_BAYES_HAM
|
||||
|
||||
- **1** => will activate spamassassin short circuiting for bayes ham detection
|
||||
|
||||
This will uncomment the respective line in ```/etc/spamassasin/local.cf```
|
||||
|
||||
Note: activate this only if you are confident in your bayes database for identifying ham.
|
||||
|
||||
#### Fetchmail
|
||||
|
||||
##### ENABLE_FETCHMAIL
|
||||
|
||||
- **0** => `fetchmail` disabled
|
||||
- 1 => `fetchmail` enabled
|
||||
|
||||
##### FETCHMAIL_POLL
|
||||
|
||||
- **300** => `fetchmail` The number of seconds for the interval
|
||||
|
||||
##### FETCHMAIL_PARALLEL
|
||||
|
||||
**0** => `fetchmail` runs with a single config file `/etc/fetchmailrc`
|
||||
**1** => `/etc/fetchmailrc` is split per poll entry. For every poll entry a seperate fetchmail instance is started to allow having multiple imap idle configurations defined.
|
||||
|
||||
Note: The defaults of your fetchmailrc file need to be at the top of the file. Otherwise it won't be added correctly to all separate `fetchmail` instances.
|
||||
|
||||
#### LDAP
|
||||
|
||||
##### ENABLE_LDAP
|
||||
|
||||
- **empty** => LDAP authentification is disabled
|
||||
- 1 => LDAP authentification is enabled
|
||||
- NOTE:
|
||||
- A second container for the ldap service is necessary (e.g. [docker-openldap](https://github.com/osixia/docker-openldap))
|
||||
- For preparing the ldap server to use in combination with this container [this](http://acidx.net/wordpress/2014/06/installing-a-mailserver-with-postfix-dovecot-sasl-ldap-roundcube/) article may be helpful
|
||||
|
||||
##### LDAP_START_TLS
|
||||
|
||||
- **empty** => no
|
||||
- yes => LDAP over TLS enabled for Postfix
|
||||
|
||||
##### LDAP_SERVER_HOST
|
||||
|
||||
- **empty** => mail.domain.com
|
||||
- => Specify the dns-name/ip-address where the ldap-server
|
||||
- NOTE: If you going to use the mailserver in combination with docker-compose you can set the service name here
|
||||
|
||||
##### LDAP_SEARCH_BASE
|
||||
|
||||
- **empty** => ou=people,dc=domain,dc=com
|
||||
- => e.g. LDAP_SEARCH_BASE=dc=mydomain,dc=local
|
||||
|
||||
##### LDAP_BIND_DN
|
||||
|
||||
- **empty** => cn=admin,dc=domain,dc=com
|
||||
- => take a look at examples of SASL_LDAP_BIND_DN
|
||||
|
||||
##### LDAP_BIND_PW
|
||||
|
||||
- **empty** => admin
|
||||
- => Specify the password to bind against ldap
|
||||
|
||||
##### LDAP_QUERY_FILTER_USER
|
||||
|
||||
- e.g. `(&(mail=%s)(mailEnabled=TRUE))`
|
||||
- => Specify how ldap should be asked for users
|
||||
|
||||
##### LDAP_QUERY_FILTER_GROUP
|
||||
|
||||
- e.g. `(&(mailGroupMember=%s)(mailEnabled=TRUE))`
|
||||
- => Specify how ldap should be asked for groups
|
||||
|
||||
##### LDAP_QUERY_FILTER_ALIAS
|
||||
|
||||
- e.g. `(&(mailAlias=%s)(mailEnabled=TRUE))`
|
||||
- => Specify how ldap should be asked for aliases
|
||||
|
||||
##### LDAP_QUERY_FILTER_DOMAIN
|
||||
|
||||
- e.g. `(&(|(mail=*@%s)(mailalias=*@%s)(mailGroupMember=*@%s))(mailEnabled=TRUE))`
|
||||
- => Specify how ldap should be asked for domains
|
||||
|
||||
##### DOVECOT_TLS
|
||||
|
||||
- **empty** => no
|
||||
- yes => LDAP over TLS enabled for Dovecot
|
||||
|
||||
#### Dovecot
|
||||
|
||||
The following variables overwrite the default values for ```/etc/dovecot/dovecot-ldap.conf.ext```.
|
||||
|
||||
##### DOVECOT_BASE
|
||||
|
||||
- **empty** => same as `LDAP_SEARCH_BASE`
|
||||
- => Tell Dovecot to search only below this base entry. (e.g. `ou=people,dc=domain,dc=com`)
|
||||
|
||||
##### DOVECOT_DEFAULT_PASS_SCHEME
|
||||
|
||||
- **empty** => `SSHA`
|
||||
- => Select one crypt scheme for password hashing from this list of [password schemes](https://doc.dovecot.org/configuration_manual/authentication/password_schemes/).
|
||||
|
||||
##### DOVECOT_DN
|
||||
|
||||
- **empty** => same as `LDAP_BIND_DN`
|
||||
- => Bind dn for LDAP connection. (e.g. `cn=admin,dc=domain,dc=com`)
|
||||
|
||||
##### DOVECOT_DNPASS
|
||||
|
||||
- **empty** => same as `LDAP_BIND_PW`
|
||||
- => Password for LDAP dn sepecifified in `DOVECOT_DN`.
|
||||
|
||||
##### DOVECOT_HOSTS
|
||||
|
||||
- **empty** => same as `LDAP_SERVER_HOST`
|
||||
- => Specify a space separated list of LDAP hosts.
|
||||
|
||||
##### DOVECOT_LDAP_VERSION
|
||||
|
||||
- **empty** => 3
|
||||
- 2 => LDAP version 2 is used
|
||||
- 3 => LDAP version 3 is used
|
||||
|
||||
##### DOVECOT_AUTH_BIND
|
||||
|
||||
- **empty** => no
|
||||
- yes => Enable [LDAP authentication binds](https://wiki.dovecot.org/AuthDatabase/LDAP/AuthBinds)
|
||||
|
||||
##### DOVECOT_USER_FILTER
|
||||
|
||||
- e.g. `(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))`
|
||||
|
||||
##### DOVECOT_USER_ATTRS
|
||||
|
||||
- e.g. `homeDirectory=home,qmailUID=uid,qmailGID=gid,mailMessageStore=mail`
|
||||
- => Specify the directory to dovecot attribute mapping that fits your directory structure.
|
||||
- Note: This is necessary for directories that do not use the [Postfix Book Schema](test/docker-openldap/bootstrap/schema/mmc/postfix-book.schema).
|
||||
- Note: The left-hand value is the directory attribute, the right hand value is the dovecot variable.
|
||||
- More details on the [Dovecot Wiki](https://wiki.dovecot.org/AuthDatabase/LDAP/Userdb)
|
||||
|
||||
##### DOVECOT_PASS_FILTER
|
||||
|
||||
- e.g. `(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))`
|
||||
|
||||
##### DOVECOT_PASS_ATTRS
|
||||
|
||||
- e.g. `uid=user,userPassword=password`
|
||||
- => Specify the directory to dovecot variable mapping that fits your directory structure.
|
||||
- Note: This is necessary for directories that do not use the [Postfix Book Schema](test/docker-openldap/bootstrap/schema/mmc/postfix-book.schema).
|
||||
- Note: The left-hand value is the directory attribute, the right hand value is the dovecot variable.
|
||||
- More details on the [Dovecot Wiki](https://wiki.dovecot.org/AuthDatabase/LDAP/PasswordLookups)
|
||||
|
||||
#### Postgrey
|
||||
|
||||
##### ENABLE_POSTGREY
|
||||
|
||||
- **0** => `postgrey` is disabled
|
||||
- 1 => `postgrey` is enabled
|
||||
|
||||
##### POSTGREY_DELAY
|
||||
|
||||
- **300** => greylist for N seconds
|
||||
|
||||
Note: This postgrey setting needs `ENABLE_POSTGREY=1`
|
||||
|
||||
##### POSTGREY_MAX_AGE
|
||||
|
||||
- **35** => delete entries older than N days since the last time that they have been seen
|
||||
|
||||
Note: This postgrey setting needs `ENABLE_POSTGREY=1`
|
||||
|
||||
##### POSTGREY_AUTO_WHITELIST_CLIENTS
|
||||
|
||||
- **5** => whitelist host after N successful deliveries (N=0 to disable whitelisting)
|
||||
|
||||
Note: This postgrey setting needs `ENABLE_POSTGREY=1`
|
||||
|
||||
##### POSTGREY_TEXT
|
||||
|
||||
- **Delayed by Postgrey** => response when a mail is greylisted
|
||||
|
||||
Note: This postgrey setting needs `ENABLE_POSTGREY=1`
|
||||
|
||||
#### SASL Auth
|
||||
|
||||
##### ENABLE_SASLAUTHD
|
||||
|
||||
- **0** => `saslauthd` is disabled
|
||||
- 1 => `saslauthd` is enabled
|
||||
|
||||
##### SASLAUTHD_MECHANISMS
|
||||
|
||||
- empty => pam
|
||||
- `ldap` => authenticate against ldap server
|
||||
- `shadow` => authenticate against local user db
|
||||
- `mysql` => authenticate against mysql db
|
||||
- `rimap` => authenticate against imap server
|
||||
- NOTE: can be a list of mechanisms like pam ldap shadow
|
||||
|
||||
##### SASLAUTHD_MECH_OPTIONS
|
||||
|
||||
- empty => None
|
||||
- e.g. with SASLAUTHD_MECHANISMS rimap you need to specify the ip-address/servername of the imap server ==> xxx.xxx.xxx.xxx
|
||||
|
||||
##### SASLAUTHD_LDAP_SERVER
|
||||
|
||||
- empty => localhost
|
||||
|
||||
##### SASLAUTHD_LDAP_SSL
|
||||
|
||||
- empty or 0 => `ldap://` will be used
|
||||
- 1 => `ldaps://` will be used
|
||||
|
||||
##### SASLAUTHD_LDAP_START_TLS
|
||||
|
||||
- **empty** => `no`
|
||||
- `yes` => Enable `ldap_start_tls` option
|
||||
|
||||
##### SASLAUTHD_LDAP_TLS_CHECK_PEER
|
||||
|
||||
- **empty** => `no`
|
||||
- `yes` => Enable `ldap_tls_check_peer` option
|
||||
|
||||
##### SASLAUTHD_LDAP_TLS_CACERT_DIR
|
||||
|
||||
Path to directory with CA (Certificate Authority) certificates.
|
||||
|
||||
- **empty** => Nothing is added to the configuration
|
||||
- Any value => Fills the `ldap_tls_cacert_dir` option
|
||||
|
||||
##### SASLAUTHD_LDAP_TLS_CACERT_FILE
|
||||
|
||||
File containing CA (Certificate Authority) certificate(s).
|
||||
|
||||
- **empty** => Nothing is added to the configuration
|
||||
- Any value => Fills the `ldap_tls_cacert_file` option
|
||||
|
||||
##### SASLAUTHD_LDAP_BIND_DN
|
||||
|
||||
- empty => anonymous bind
|
||||
- specify an object with privileges to search the directory tree
|
||||
- e.g. active directory: SASLAUTHD_LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=net
|
||||
- e.g. openldap: SASLAUTHD_LDAP_BIND_DN=cn=admin,dc=mydomain,dc=net
|
||||
|
||||
##### SASLAUTHD_LDAP_PASSWORD
|
||||
|
||||
- empty => anonymous bind
|
||||
|
||||
##### SASLAUTHD_LDAP_SEARCH_BASE
|
||||
|
||||
- empty => Reverting to SASLAUTHD_MECHANISMS pam
|
||||
- specify the search base
|
||||
|
||||
##### SASLAUTHD_LDAP_FILTER
|
||||
|
||||
- empty => default filter `(&(uniqueIdentifier=%u)(mailEnabled=TRUE))`
|
||||
- e.g. for active directory: `(&(sAMAccountName=%U)(objectClass=person))`
|
||||
- e.g. for openldap: `(&(uid=%U)(objectClass=person))`
|
||||
|
||||
##### SASLAUTHD_LDAP_PASSWORD_ATTR
|
||||
|
||||
Specify what password attribute to use for password verification.
|
||||
|
||||
- **empty** => Nothing is added to the configuration but the documentation says it is `userPassword` by default.
|
||||
- Any value => Fills the `ldap_password_attr` option
|
||||
|
||||
##### SASL_PASSWD
|
||||
|
||||
- **empty** => No sasl_passwd will be created
|
||||
- string => `/etc/postfix/sasl_passwd` will be created with the string as password
|
||||
|
||||
##### SASLAUTHD_LDAP_AUTH_METHOD
|
||||
|
||||
- **empty** => `bind` will be used as a default value
|
||||
- `fastbind` => The fastbind method is used
|
||||
- `custom` => The custom method uses userPassword attribute to verify the password
|
||||
|
||||
##### SASLAUTHD_LDAP_MECH
|
||||
|
||||
Specify the authentication mechanism for SASL bind.
|
||||
|
||||
- **empty** => Nothing is added to the configuration
|
||||
- Any value => Fills the `ldap_mech` option
|
||||
|
||||
#### SRS (Sender Rewriting Scheme)
|
||||
|
||||
##### SRS_SENDER_CLASSES
|
||||
|
||||
An email has an "envelope" sender (indicating the sending server) and a
|
||||
"header" sender (indicating who sent it). More strict SPF policies may require
|
||||
you to replace both instead of just the envelope sender.
|
||||
|
||||
[More info](https://www.mybluelinux.com/what-is-email-envelope-and-email-header/).
|
||||
|
||||
- **envelope_sender** => Rewrite only envelope sender address
|
||||
- header_sender => Rewrite only header sender (not recommended)
|
||||
- envelope_sender,header_sender => Rewrite both senders
|
||||
|
||||
##### SRS_EXCLUDE_DOMAINS
|
||||
|
||||
- **empty** => Envelope sender will be rewritten for all domains
|
||||
- provide comma separated list of domains to exclude from rewriting
|
||||
|
||||
##### SRS_SECRET
|
||||
|
||||
- **empty** => generated when the container is started for the first time
|
||||
- provide a secret to use in base64
|
||||
- you may specify multiple keys, comma separated. the first one is used for signing and the remaining will be used for verification. this is how you rotate and expire keys
|
||||
- if you have a cluster/swarm make sure the same keys are on all nodes
|
||||
- example command to generate a key: `dd if=/dev/urandom bs=24 count=1 2>/dev/null | base64`
|
||||
|
||||
##### SRS_DOMAINNAME
|
||||
|
||||
- **empty** => Derived from OVERRIDE_HOSTNAME, DOMAINNAME, or the container's hostname
|
||||
- Set this if auto-detection fails, isn't what you want, or you wish to have a separate container handle DSNs
|
||||
|
||||
#### Default Relay Host
|
||||
|
||||
##### DEFAULT_RELAY_HOST
|
||||
|
||||
- **empty** => don't set default relayhost setting in main.cf
|
||||
- default host and port to relay all mail through.
|
||||
Format: `[example.com]:587` (don't forget the brackets if you need this to
|
||||
be compatible with `$RELAY_USER` and `$RELAY_PASSWORD`, explained below).
|
||||
|
||||
#### Multi-domain Relay Hosts
|
||||
|
||||
##### RELAY_HOST
|
||||
|
||||
- **empty** => don't configure relay host
|
||||
- default host to relay mail through
|
||||
|
||||
##### RELAY_PORT
|
||||
|
||||
- **empty** => 25
|
||||
- default port to relay mail through
|
||||
|
||||
##### RELAY_USER
|
||||
|
||||
- **empty** => no default
|
||||
- default relay username (if no specific entry exists in postfix-sasl-password.cf)
|
||||
|
||||
##### RELAY_PASSWORD
|
||||
|
||||
- **empty** => no default
|
||||
- password for default relay user
|
138
Makefile
|
@ -1,88 +1,72 @@
|
|||
SHELL := /bin/bash
|
||||
.SHELLFLAGS += -e -u -o pipefail
|
||||
SHELL = /bin/bash
|
||||
|
||||
export REPOSITORY_ROOT := $(CURDIR)
|
||||
export IMAGE_NAME ?= mailserver-testing:ci
|
||||
export NAME ?= $(IMAGE_NAME)
|
||||
NAME ?= mailserver-testing:ci
|
||||
VCS_REF = $(shell git rev-parse --short HEAD)
|
||||
VCS_VER = $(shell git describe --tags --contains --always)
|
||||
|
||||
MAKEFLAGS += --no-print-directory
|
||||
BATS_FLAGS ?= --timing
|
||||
BATS_PARALLEL_JOBS ?= 2
|
||||
HADOLINT_VERSION = 1.19.0
|
||||
SHELLCHECK_VERSION = 0.7.1
|
||||
ECLINT_VERSION = 2.3.1
|
||||
|
||||
export CDIR = $(shell pwd)
|
||||
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– Generic Build Targets –––––––––––––––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
all: lint build backup generate-accounts tests clean
|
||||
|
||||
build:
|
||||
docker build -t $(NAME) . --build-arg VCS_VER=$(VCS_VER) --build-arg VCS_REF=$(VCS_REF)
|
||||
|
||||
backup:
|
||||
# if backup directories exist, clean hasn't been called, therefore
|
||||
# we shouldn't overwrite it. It still contains the original content.
|
||||
-@ [[ ! -d config.bak ]] && cp -rp config config.bak || :
|
||||
-@ [[ ! -d testconfig.bak ]] && cp -rp test/config testconfig.bak || :
|
||||
|
||||
clean:
|
||||
# remove running and stopped test containers
|
||||
-@ [[ -d config.bak ]] && { rm -rf config ; mv config.bak config ; } || :
|
||||
-@ [[ -d testconfig.bak ]] && { sudo rm -rf test/config ; mv testconfig.bak test/config ; } || :
|
||||
-@ docker ps -a | grep -E "mail|ldap_for_mail|mail_overri.*" | cut -f 1-1 -d ' ' | xargs --no-run-if-empty docker rm -f
|
||||
-@ sudo rm -rf test/onedir test/alias test/quota test/relay test/config/dovecot-lmtp/userdb test/config/key* test/config/opendkim/keys/domain.tld/ test/config/opendkim/keys/example.com/ test/config/opendkim/keys/localdomain2.com/ test/config/postfix-aliases.cf test/config/postfix-receive-access.cf test/config/postfix-receive-access.cfe test/config/dovecot-quotas.cf test/config/postfix-send-access.cf test/config/postfix-send-access.cfe test/config/relay-hosts/chksum test/config/relay-hosts/postfix-aliases.cf test/config/dhparams.pem test/config/dovecot-lmtp/dh.pem test/config/relay-hosts/dovecot-quotas.cf test/config/user-patches.sh test/alias/config/postfix-virtual.cf test/quota/config/dovecot-quotas.cf test/quota/config/postfix-accounts.cf test/relay/config/postfix-relaymap.cf test/relay/config/postfix-sasl-password.cf test/duplicate_configs/
|
||||
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– Tests –––––––––––––––––––––––––––––––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
generate-accounts:
|
||||
@ docker run --rm -e MAIL_USER=user1@localhost.localdomain -e MAIL_PASS=mypassword -t $(NAME) /bin/sh -c 'echo "$$MAIL_USER|$$(doveadm pw -s SHA512-CRYPT -u $$MAIL_USER -p $$MAIL_PASS)"' > test/config/postfix-accounts.cf
|
||||
@ docker run --rm -e MAIL_USER=user2@otherdomain.tld -e MAIL_PASS=mypassword -t $(NAME) /bin/sh -c 'echo "$$MAIL_USER|$$(doveadm pw -s SHA512-CRYPT -u $$MAIL_USER -p $$MAIL_PASS)"' >> test/config/postfix-accounts.cf
|
||||
@ docker run --rm -e MAIL_USER=user3@localhost.localdomain -e MAIL_PASS=mypassword -t $(NAME) /bin/sh -c 'echo "$$MAIL_USER|$$(doveadm pw -s SHA512-CRYPT -u $$MAIL_USER -p $$MAIL_PASS)|userdb_mail=mbox:~/mail:INBOX=~/inbox"' >> test/config/postfix-accounts.cf
|
||||
@ echo "# this is a test comment, please don't delete me :'(" >> test/config/postfix-accounts.cf
|
||||
@ echo " # this is also a test comment, :O" >> test/config/postfix-accounts.cf
|
||||
|
||||
tests:
|
||||
@ NAME=$(NAME) ./test/bats/bin/bats test/*.bats
|
||||
|
||||
.PHONY: ALWAYS_RUN
|
||||
test/%.bats: ALWAYS_RUN
|
||||
@ ./test/bats/bin/bats $@
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- Generic Targets ---------------------------
|
||||
# -----------------------------------------------
|
||||
lint: eclint hadolint shellcheck
|
||||
|
||||
all: lint build generate-accounts tests clean
|
||||
|
||||
build: ALWAYS_RUN
|
||||
@ docker build --tag $(IMAGE_NAME) .
|
||||
|
||||
generate-accounts: ALWAYS_RUN
|
||||
@ cp test/config/templates/postfix-accounts.cf test/config/postfix-accounts.cf
|
||||
@ cp test/config/templates/dovecot-masters.cf test/config/dovecot-masters.cf
|
||||
|
||||
# `docker ps`: Remove any lingering test containers
|
||||
# `.gitignore`: Remove `test/duplicate_configs` and files copied via `make generate-accounts`
|
||||
clean: ALWAYS_RUN
|
||||
-@ while read -r LINE; do CONTAINERS+=("$${LINE}"); done < <(docker ps -qaf name='^(dms-test|mail)_.*') ; \
|
||||
for CONTAINER in "$${CONTAINERS[@]}"; do docker rm -f "$${CONTAINER}"; done
|
||||
-@ while read -r LINE; do [[ $${LINE} =~ test/.+ ]] && FILES+=("/mnt$${LINE#test}"); done < .gitignore ; \
|
||||
docker run --rm -v "$(REPOSITORY_ROOT)/test/:/mnt" alpine ash -c "rm -rf $${FILES[@]}"
|
||||
|
||||
run-local-instance: ALWAYS_RUN
|
||||
bash -c 'sleep 8 ; ./setup.sh email add postmaster@example.test 123' &
|
||||
docker run --rm --interactive --tty --name dms-test_example \
|
||||
--env OVERRIDE_HOSTNAME=mail.example.test \
|
||||
--env POSTFIX_INET_PROTOCOLS=ipv4 \
|
||||
--env DOVECOT_INET_PROTOCOLS=ipv4 \
|
||||
--env ENABLE_CLAMAV=0 \
|
||||
--env ENABLE_AMAVIS=0 \
|
||||
--env ENABLE_RSPAMD=0 \
|
||||
--env ENABLE_OPENDKIM=0 \
|
||||
--env ENABLE_OPENDMARC=0 \
|
||||
--env ENABLE_POLICYD_SPF=0 \
|
||||
--env ENABLE_SPAMASSASSIN=0 \
|
||||
--env LOG_LEVEL=trace \
|
||||
$(IMAGE_NAME)
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- Tests ------------------------------------
|
||||
# -----------------------------------------------
|
||||
|
||||
tests: ALWAYS_RUN
|
||||
# See https://github.com/docker-mailserver/docker-mailserver/pull/2857#issuecomment-1312724303
|
||||
# on why `generate-accounts` is run before each set (TODO/FIXME)
|
||||
@ for DIR in tests/{serial,parallel/set{1,2,3}} ; do $(MAKE) generate-accounts "$${DIR}" ; done
|
||||
|
||||
tests/serial: ALWAYS_RUN
|
||||
@ shopt -s globstar ; ./test/bats/bin/bats $(BATS_FLAGS) test/$@/*.bats
|
||||
|
||||
tests/parallel/set%: ALWAYS_RUN
|
||||
@ shopt -s globstar ; $(REPOSITORY_ROOT)/test/bats/bin/bats $(BATS_FLAGS) \
|
||||
--no-parallelize-within-files \
|
||||
--jobs $(BATS_PARALLEL_JOBS) \
|
||||
test/$@/**/*.bats
|
||||
|
||||
test/%: ALWAYS_RUN
|
||||
@ shopt -s globstar nullglob ; ./test/bats/bin/bats $(BATS_FLAGS) test/tests/**/{$*,}.bats
|
||||
|
||||
# -----------------------------------------------
|
||||
# --- Lints -------------------------------------
|
||||
# -----------------------------------------------
|
||||
|
||||
lint: ALWAYS_RUN eclint hadolint bashcheck shellcheck
|
||||
|
||||
hadolint: ALWAYS_RUN
|
||||
hadolint:
|
||||
@ ./test/linting/lint.sh hadolint
|
||||
|
||||
bashcheck: ALWAYS_RUN
|
||||
@ ./test/linting/lint.sh bashcheck
|
||||
|
||||
shellcheck: ALWAYS_RUN
|
||||
shellcheck:
|
||||
@ ./test/linting/lint.sh shellcheck
|
||||
|
||||
eclint: ALWAYS_RUN
|
||||
eclint:
|
||||
@ ./test/linting/lint.sh eclint
|
||||
|
||||
install_linters:
|
||||
@ mkdir -p tools
|
||||
@ curl -S -L \
|
||||
"https://github.com/hadolint/hadolint/releases/download/v$(HADOLINT_VERSION)/hadolint-$(shell uname -s)-$(shell uname -m)" -o tools/hadolint
|
||||
@ curl -S -L \
|
||||
"https://github.com/koalaman/shellcheck/releases/download/v$(SHELLCHECK_VERSION)/shellcheck-v$(SHELLCHECK_VERSION).linux.x86_64.tar.xz" | tar -Jx shellcheck-v$(SHELLCHECK_VERSION)/shellcheck -O > tools/shellcheck
|
||||
@ curl -S -L \
|
||||
"https://github.com/editorconfig-checker/editorconfig-checker/releases/download/$(ECLINT_VERSION)/ec-linux-amd64.tar.gz" | tar -zx bin/ec-linux-amd64 -O > tools/eclint
|
||||
@ chmod u+rx tools/*
|
||||
|
|
355
README.md
|
@ -1,56 +1,331 @@
|
|||
# Docker Mailserver
|
||||
|
||||
[![ci::status]][ci::github] [![docker::pulls]][docker::hub] [![documentation::badge]][documentation::web]
|
||||
[![ci::status]][ci::github] [![docker::pulls]][docker::hub]
|
||||
|
||||
[ci::status]: https://img.shields.io/github/actions/workflow/status/docker-mailserver/docker-mailserver/default_on_push.yml?branch=master&color=blue&label=CI&logo=github&logoColor=white&style=for-the-badge
|
||||
[ci::status]: https://img.shields.io/github/workflow/status/docker-mailserver/docker-mailserver/Build%2C%20Test%20%26%20Deploy?color=blue&label=CI&logo=github&logoColor=white&style=for-the-badge
|
||||
[ci::github]: https://github.com/docker-mailserver/docker-mailserver/actions
|
||||
[docker::pulls]: https://img.shields.io/docker/pulls/mailserver/docker-mailserver.svg?style=for-the-badge&logo=docker&logoColor=white&color=blue
|
||||
[docker::pulls]: https://img.shields.io/docker/pulls/mailserver/docker-mailserver.svg?style=for-the-badge&logo=docker&logoColor=white
|
||||
[docker::hub]: https://hub.docker.com/r/mailserver/docker-mailserver/
|
||||
[documentation::badge]: https://img.shields.io/badge/DOCUMENTATION-GH%20PAGES-0078D4?style=for-the-badge&logo=googledocs&logoColor=white
|
||||
[documentation::web]: https://docker-mailserver.github.io/docker-mailserver/latest/
|
||||
|
||||
## :page_with_curl: About
|
||||
A fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.). Only configuration files, no SQL database. Keep it simple and versioned. Easy to deploy and upgrade.
|
||||
|
||||
A production-ready fullstack but simple containerized mail server (SMTP, IMAP, LDAP, Anti-spam, Anti-virus, etc.).
|
||||
- Only configuration files, no SQL database. Keep it simple and versioned. Easy to deploy and upgrade.
|
||||
- Originally created by [@tomav](https://github.com/tomav), this project is now maintained by volunteers since January 2021.
|
||||
[Why this image was created.](http://tvi.al/simple-mail-server-with-docker/)
|
||||
|
||||
## <!-- Adds a thin line break separator style -->
|
||||
1. [Included Services](#included-services)
|
||||
2. [Issues and Contributing](./CONTRIBUTING.md)
|
||||
3. [Requirements](#requirements)
|
||||
4. [Usage](#usage)
|
||||
5. [Examples](#examples)
|
||||
6. [Environment Variables](./ENVIRONMENT.md)
|
||||
7. [Release Notes](./CHANGELOG.md)
|
||||
|
||||
> [!TIP]
|
||||
> Be sure to read [our documentation][documentation::web]. It provides guidance on initial setup of your mail server.
|
||||
## Included Services
|
||||
|
||||
> [!IMPORTANT]
|
||||
> If you have issues, please search through [the documentation][documentation::web] **for your version** before opening an issue.
|
||||
>
|
||||
> The issue tracker is for issues, not for personal support.
|
||||
> Make sure the version of the documentation matches the image version you're using!
|
||||
|
||||
## :link: Links to Useful Resources
|
||||
|
||||
1. [FAQ](https://docker-mailserver.github.io/docker-mailserver/latest/faq/)
|
||||
2. [Usage](https://docker-mailserver.github.io/docker-mailserver/latest/usage/)
|
||||
3. [Examples](https://docker-mailserver.github.io/docker-mailserver/latest/examples/tutorials/basic-installation/)
|
||||
4. [Issues and Contributing](https://docker-mailserver.github.io/docker-mailserver/latest/contributing/issues-and-pull-requests/)
|
||||
5. [Release Notes](./CHANGELOG.md)
|
||||
6. [Environment Variables](https://docker-mailserver.github.io/docker-mailserver/latest/config/environment/)
|
||||
7. [Updating](https://docker-mailserver.github.io/docker-mailserver/latest/faq/#how-do-i-update-dms)
|
||||
|
||||
## :package: Included Services
|
||||
|
||||
- [Postfix](http://www.postfix.org) with SMTP or LDAP authentication and support for [extension delimiters](https://docker-mailserver.github.io/docker-mailserver/latest/config/account-management/overview/#aliases)
|
||||
- [Dovecot](https://www.dovecot.org) with SASL, IMAP, POP3, LDAP, [basic Sieve support](https://docker-mailserver.github.io/docker-mailserver/latest/config/advanced/mail-sieve) and [quotas](https://docker-mailserver.github.io/docker-mailserver/latest/config/account-management/overview/#quotas)
|
||||
- [Rspamd](https://rspamd.com/)
|
||||
- [Postfix](http://www.postfix.org) with SMTP or LDAP auth
|
||||
- [Dovecot](https://www.dovecot.org) for SASL, IMAP (or POP3), with LDAP Auth, Sieve and [quotas](https://docker-mailserver.github.io/docker-mailserver/edge/config/user-management/accounts#notes)
|
||||
- [Amavis](https://www.amavis.org/)
|
||||
- [SpamAssassin](http://spamassassin.apache.org/) supporting custom rules
|
||||
- [Spamassasin](http://spamassassin.apache.org/) supporting custom rules
|
||||
- [ClamAV](https://www.clamav.net/) with automatic updates
|
||||
- [OpenDKIM](http://www.opendkim.org) & [OpenDMARC](https://github.com/trusteddomainproject/OpenDMARC)
|
||||
- [OpenDKIM](http://www.opendkim.org)
|
||||
- [OpenDMARC](https://github.com/trusteddomainproject/OpenDMARC)
|
||||
- [Fail2ban](https://www.fail2ban.org/wiki/index.php/Main_Page)
|
||||
- [Fetchmail](http://www.fetchmail.info/fetchmail-man.html)
|
||||
- [Getmail6](https://getmail6.org/documentation.html)
|
||||
- [Postscreen](http://www.postfix.org/POSTSCREEN_README.html)
|
||||
- [Postgrey](https://postgrey.schweikert.ch/)
|
||||
- Support for [LetsEncrypt](https://letsencrypt.org/), manual and self-signed certificates
|
||||
- A [setup script](https://docker-mailserver.github.io/docker-mailserver/latest/config/setup.sh) for easy configuration and maintenance
|
||||
- SASLauthd with LDAP authentication
|
||||
- OAuth2 authentication (_via `XOAUTH2` or `OAUTHBEARER` SASL mechanisms_)
|
||||
- [LetsEncrypt](https://letsencrypt.org/) and self-signed certificates
|
||||
- [Setup script](https://docker-mailserver.github.io/docker-mailserver/edge/config/setup.sh) to easily configure and maintain your mailserver
|
||||
- Basic [Sieve support](https://docker-mailserver.github.io/docker-mailserver/edge/config/advanced/mail-sieve) using dovecot
|
||||
- SASLauthd with LDAP auth
|
||||
- Persistent data and state
|
||||
- [CI/CD](https://github.com/docker-mailserver/docker-mailserver/actions)
|
||||
- [Extension Delimiters](http://www.postfix.org/postconf.5.html#recipient_delimiter) (`you+extension@example.com` go to `you@example.com`)
|
||||
|
||||
## Requirements
|
||||
|
||||
**Recommended**:
|
||||
|
||||
- 1 Core
|
||||
- 2GB RAM
|
||||
- Swap enabled for the container
|
||||
|
||||
**Minimum**:
|
||||
|
||||
- 1 vCore
|
||||
- 512MB RAM
|
||||
|
||||
**Note:** You'll need to deactivate some services like ClamAV to be able to run on a host with 512MB of RAM. Even with 1G RAM you may run into problems without swap, see [FAQ](https://docker-mailserver.github.io/docker-mailserver/edge/faq/#what-system-requirements-are-required-to-run-docker-mailserver-effectively).
|
||||
|
||||
## Usage
|
||||
|
||||
### Available image sources / tags
|
||||
|
||||
The [CI/CD workflows](https://github.com/docker-mailserver/docker-mailserver/actions) automatically build, test and push new images to container registries. Currently, the following registries are supported:
|
||||
- [DockerHub](https://hub.docker.com/repository/docker/mailserver/docker-mailserver)
|
||||
- [GitHub Container Registry](https://github.com/orgs/docker-mailserver/packages?repo_name=docker-mailserver)
|
||||
|
||||
All workflows are using the **tagging convention** listed below. It is subsequently applied to all images pushed to supported container registries:
|
||||
|
||||
| Event | Ref | Commit SHA | Image Tags |
|
||||
|--------------|-----------------------|------------|-------------------------------|
|
||||
| `push` | `refs/heads/master` | `cf20257` | `edge` |
|
||||
| `push` | `refs/heads/stable` | `cf20257` | `stable` |
|
||||
| `push tag` | `refs/tags/1.2.3` | `ad132f5` | `1.2.3`, `1.2`, `1`, `latest` |
|
||||
| `push tag` | `refs/tags/v1.2.3` | `ad132f5` | `1.2.3`, `1.2`, `1`, `latest` |
|
||||
|
||||
### Get the tools
|
||||
|
||||
Download `docker-compose.yml`, `compose.env`, `mailserver.env`
|
||||
|
||||
``` BASH
|
||||
wget -O .env https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/compose.env
|
||||
wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/docker-compose.yml
|
||||
wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/mailserver.env
|
||||
```
|
||||
|
||||
and the `setup.sh` **in the correct version**
|
||||
|
||||
``` BASH
|
||||
# if you're using :edge as the image tag
|
||||
wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master/setup.sh
|
||||
# if you're using :latest (= :9.0.1) as the image tag
|
||||
wget https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/v9.0.1/setup.sh
|
||||
|
||||
chmod a+x ./setup.sh
|
||||
|
||||
# and make yourself familiar with the script
|
||||
./setup.sh help
|
||||
```
|
||||
|
||||
**Make sure to get the `setup.sh` that comes with the release you're using**. Look up the release and the git commit on which this release is based upon by selecting the appropriate tag on GitHub. This can done with the "Switch branches/tags" button on GitHub, choosing the right tag. This is done in order to rule out possible inconsistencies between versions.
|
||||
|
||||
### Create a docker-compose environment
|
||||
|
||||
- [Install the latest docker-compose](https://docs.docker.com/compose/install/)
|
||||
- Edit the files `.env` and `mailserver.env` to your liking:
|
||||
- `.env` contains the configuration for Docker Compose
|
||||
- `mailserver.env` contains the configuration for the mailserver container
|
||||
- these files supports [only simple `VAR=VAL`](https://docs.docker.com/compose/env-file/)
|
||||
- don't quote your values
|
||||
- variable substitution is *not* supported (e.g. `OVERRIDE_HOSTNAME=$HOSTNAME.$DOMAINNAME`).
|
||||
- Variables in `.env` are expanded in the `docker-compose.yml` file **only** and **not** in the container. The file `mailserver.env` serves this case where environment variables are used in the container.
|
||||
- If you want to use a bare domain (host name = domain name), see [FAQ](https://docker-mailserver.github.io/docker-mailserver/edge/faq#can-i-use-nakedbare-domains-no-host-name)
|
||||
|
||||
### Get up and running
|
||||
|
||||
If you'd like to use SELinux, add `-Z` to the variable `SELINUX_LABEL` in `.env`. If you want the volume bind mount to be shared among other containers switch `-Z` to `-z`
|
||||
|
||||
``` BASH
|
||||
docker-compose up -d mailserver
|
||||
|
||||
# without SELinux
|
||||
./setup.sh email add <user@domain> [<password>]
|
||||
./setup.sh alias add postmaster@<domain> <user@domain>
|
||||
./setup.sh config dkim
|
||||
|
||||
# with SELinux
|
||||
./setup.sh -Z email add <user@domain> [<password>]
|
||||
./setup.sh -Z alias add postmaster@<domain> <user@domain>
|
||||
./setup.sh -Z config dkim
|
||||
```
|
||||
|
||||
If you're seeing error messages about unchecked error, please **verify that you're using the right version of `setup.sh`**. Refer to the [Get the tools](#get-the-tools) section and / or execute `./setup.sh help` and read the `VERSION` section.
|
||||
|
||||
In case you're using LDAP, the setup looks a bit different as you do not add user accounts directly. Postfix doesn't know your domain(s) and you need to provide it when configuring DKIM:
|
||||
|
||||
``` BASH
|
||||
./setup.sh config dkim domain '<domain.tld>[,<domain2.tld>]'
|
||||
```
|
||||
|
||||
If you want to see detailed usage information, run `./setup.sh config dkim help`.
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
#### DNS - DKIM
|
||||
|
||||
When keys are generated, you can configure your DNS server by just pasting the content of `config/opendkim/keys/domain.tld/mail.txt` to [set up DKIM](https://mxtoolbox.com/dmarc/dkim/setup/how-to-setup-dkim).
|
||||
|
||||
#### Custom user changes & patches
|
||||
|
||||
If you'd like to change, patch or alter files or behavior of `docker-mailserver`, you can use a script. Just place it the `config/` folder that is created on startup and call it `user-patches.sh`. The setup is done like this:
|
||||
|
||||
``` BASH
|
||||
# 1. Either create the config/ directory yourself
|
||||
# or let docker-mailserver create it on initial
|
||||
# startup
|
||||
/where/docker-mailserver/resides/ $ mkdir config && cd config
|
||||
|
||||
# 2. Create the user-patches.sh script and make it
|
||||
# executable
|
||||
/where/docker-mailserver/resides/config/ $ touch user-patches.sh
|
||||
/where/docker-mailserver/resides/config/ $ chmod +x user-patches.sh
|
||||
|
||||
# 3. Edit it
|
||||
/where/docker-mailserver/resides/config/ $ vi user-patches.sh
|
||||
/where/docker-mailserver/resides/config/ $ cat user-patches.sh
|
||||
#! /bin/bash
|
||||
|
||||
# ! THIS IS AN EXAMPLE !
|
||||
|
||||
# If you modify any supervisord configuration, make sure
|
||||
# to run `supervisorctl update` and/or `supervisorctl reload` afterwards.
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
. /usr/local/bin/helper-functions.sh
|
||||
|
||||
_notify 'Applying user-patches'
|
||||
|
||||
if ! grep -q '192.168.0.1' /etc/hosts
|
||||
then
|
||||
echo -e '192.168.0.1 some.domain.com' >> /etc/hosts
|
||||
fi
|
||||
```
|
||||
|
||||
And you're done. The user patches script runs right before starting daemons. That means, all the other configuration is in place, so the script can make final adjustments.
|
||||
|
||||
#### Supported Operating Systems
|
||||
|
||||
We are currently providing support for Linux. Windows is _not_ supported and is known to cause problems. Similarly, macOS is _not officially_ supported - but you may get it to work there. In the end, Linux should be your preferred operating system for this image, especially when using this mailserver in production.
|
||||
|
||||
#### Support for Multiple Domains
|
||||
|
||||
`docker-mailserver` supports multiple domains out of the box, so you can do this:
|
||||
|
||||
``` BASH
|
||||
./setup.sh email add user1@docker.example.com
|
||||
./setup.sh email add user1@mail.example.de
|
||||
./setup.sh email add user1@server.example.org
|
||||
```
|
||||
|
||||
#### Updating `docker-mailserver`
|
||||
|
||||
``` BASH
|
||||
docker-compose down
|
||||
docker pull docker.io/mailserver/docker-mailserver:<VERSION TAG>
|
||||
docker-compose up -d mailserver
|
||||
```
|
||||
|
||||
You're done! And don't forget to have a look at the remaining functions of the `setup.sh` script with `./setup.sh help`.
|
||||
|
||||
#### SPF/Forwarding Problems
|
||||
|
||||
If you got any problems with SPF and/or forwarding mails, give [SRS](https://github.com/roehling/postsrsd/blob/master/README.md) a try. You enable SRS by setting `ENABLE_SRS=1`. See the variable description for further information.
|
||||
|
||||
#### Exposed ports
|
||||
|
||||
| Protocol | Opt-in Encryption ¹ | Enforced Encryption | Purpose |
|
||||
| :------: | :----------------------: | :-----------------: | :------------: |
|
||||
| SMTP | 25 | N/A | Transfer² |
|
||||
| ESMTP | 587 | 465³ | Submission |
|
||||
| POP3 | 110 | 995 | Retrieval |
|
||||
| IMAP4 | 143 | 993 | Retrieval |
|
||||
|
||||
1. A connection *may* be secured over TLS when both ends support `STARTTLS`. On ports 110, 143 and 587, `docker-mailserver` will reject a connection that cannot be secured. Port 25 is [required](https://serverfault.com/questions/623692/is-it-still-wrong-to-require-starttls-on-incoming-smtp-messages) to support insecure connections.
|
||||
2. Receives email and filters for spam and viruses. For submitting outgoing mail you should prefer the submission ports(465, 587), which require authentication. Unless a relay host is configured, outgoing email will leave the server via port 25(thus outbound traffic must not be blocked by your provider or firewall).
|
||||
3. A submission port since 2018, [RFC 8314](https://tools.ietf.org/html/rfc8314). Originally a secure variant of port 25.
|
||||
|
||||
See the [documentation](https://docker-mailserver.github.io/docker-mailserver/edge/config/security/understanding-the-ports/) for further details and best practice advice, especially regarding security concerns.
|
||||
|
||||
## Examples
|
||||
|
||||
### With Relevant Environmental Variables
|
||||
|
||||
This example provides you only with a basic example of what a minimal setup could look like. We **strongly recommend** that you go through the configuration file yourself and adjust everything to your needs. The default [docker-compose.yml](./docker-compose.yml) can be used for the purpose out-of-the-box, see the [usage section](#usage).
|
||||
|
||||
``` YAML
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
mailserver:
|
||||
image: docker.io/mailserver/docker-mailserver:latest
|
||||
hostname: mail # ${HOSTNAME}
|
||||
domainname: domain.com # ${DOMAINNAME}
|
||||
container_name: mail # ${CONTAINER_NAME}
|
||||
ports:
|
||||
- "25:25"
|
||||
- "143:143"
|
||||
- "587:587"
|
||||
- "993:993"
|
||||
volumes:
|
||||
- maildata:/var/mail
|
||||
- mailstate:/var/mail-state
|
||||
- maillogs:/var/log/mail
|
||||
- ./config/:/tmp/docker-mailserver/
|
||||
environment:
|
||||
- ENABLE_SPAMASSASSIN=1
|
||||
- SPAMASSASSIN_SPAM_TO_INBOX=1
|
||||
- ENABLE_CLAMAV=1
|
||||
- ENABLE_FAIL2BAN=1
|
||||
- ENABLE_POSTGREY=1
|
||||
- ENABLE_SASLAUTHD=0
|
||||
- ONE_DIR=1
|
||||
- DMS_DEBUG=0
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- SYS_PTRACE
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
maildata:
|
||||
mailstate:
|
||||
maillogs:
|
||||
```
|
||||
|
||||
#### LDAP setup
|
||||
|
||||
``` YAML
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
mailserver:
|
||||
image: docker.io/mailserver/docker-mailserver:latest
|
||||
hostname: mail # ${HOSTNAME}
|
||||
domainname: domain.com # ${DOMAINNAME}
|
||||
container_name: mail # ${CONTAINER_NAME}
|
||||
ports:
|
||||
- "25:25"
|
||||
- "143:143"
|
||||
- "587:587"
|
||||
- "993:993"
|
||||
volumes:
|
||||
- maildata:/var/mail
|
||||
- mailstate:/var/mail-state
|
||||
- maillogs:/var/log/mail
|
||||
- ./config/:/tmp/docker-mailserver/
|
||||
environment:
|
||||
- ENABLE_SPAMASSASSIN=1
|
||||
- SPAMASSASSIN_SPAM_TO_INBOX=1
|
||||
- ENABLE_CLAMAV=1
|
||||
- ENABLE_FAIL2BAN=1
|
||||
- ENABLE_POSTGREY=1
|
||||
- ONE_DIR=1
|
||||
- DMS_DEBUG=0
|
||||
- ENABLE_LDAP=1
|
||||
- LDAP_SERVER_HOST=ldap # your ldap container/IP/ServerName
|
||||
- LDAP_SEARCH_BASE=ou=people,dc=localhost,dc=localdomain
|
||||
- LDAP_BIND_DN=cn=admin,dc=localhost,dc=localdomain
|
||||
- LDAP_BIND_PW=admin
|
||||
- LDAP_QUERY_FILTER_USER=(&(mail=%s)(mailEnabled=TRUE))
|
||||
- LDAP_QUERY_FILTER_GROUP=(&(mailGroupMember=%s)(mailEnabled=TRUE))
|
||||
- LDAP_QUERY_FILTER_ALIAS=(|(&(mailAlias=%s)(objectClass=PostfixBookMailForward))(&(mailAlias=%s)(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE)))
|
||||
- LDAP_QUERY_FILTER_DOMAIN=(|(&(mail=*@%s)(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE))(&(mailGroupMember=*@%s)(objectClass=PostfixBookMailAccount)(mailEnabled=TRUE))(&(mailalias=*@%s)(objectClass=PostfixBookMailForward)))
|
||||
- DOVECOT_PASS_FILTER=(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))
|
||||
- DOVECOT_USER_FILTER=(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))
|
||||
- ENABLE_SASLAUTHD=1
|
||||
- SASLAUTHD_MECHANISMS=ldap
|
||||
- SASLAUTHD_LDAP_SERVER=ldap
|
||||
- SASLAUTHD_LDAP_BIND_DN=cn=admin,dc=localhost,dc=localdomain
|
||||
- SASLAUTHD_LDAP_PASSWORD=admin
|
||||
- SASLAUTHD_LDAP_SEARCH_BASE=ou=people,dc=localhost,dc=localdomain
|
||||
- SASLAUTHD_LDAP_FILTER=(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%U))
|
||||
- POSTMASTER_ADDRESS=postmaster@localhost.localdomain
|
||||
- POSTFIX_MESSAGE_SIZE_LIMIT=100000000
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- SYS_PTRACE
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
maildata:
|
||||
mailstate:
|
||||
maillogs:
|
||||
```
|
||||
|
|
14
SECURITY.md
|
@ -1,14 +0,0 @@
|
|||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Due to the way DMS is released, the most recent patches and the most current software is published on the `:edge` tag of the container image. Hence, security updates will land on this "rolling release tag". Older tags need manual updating, as we do not usually release an updated image for an existing tag; this will only be done in case of _severe_ vulnerabilities.
|
||||
|
||||
| Image Tags | Latest Packages & Patches |
|
||||
|-------------|:-------------------------:|
|
||||
| `:edge` | :white_check_mark: |
|
||||
| not `:edge` | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
When reporting a vulnerability, you can use GitHub's "Private Vulnerability Reporting". Just navigate to the [Open an Issue](https://github.com/docker-mailserver/docker-mailserver/issues/new/choose) page and choose "Report a security vulnerability". This way, maintainers will privately notified first. Afterwards, in a best-case scenario, if the vulnerability is fixed, the report will be made public.
|
|
@ -0,0 +1,14 @@
|
|||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– Docker Compose Settings Variables –––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
HOSTNAME=<INSERT 'HOSTNAME' HERE>
|
||||
DOMAINNAME=<INSERT 'DOMAIN.TLD' HERE>
|
||||
CONTAINER_NAME=<INSERT 'CONTAINER NAME' HERE>
|
||||
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
# ––– SELinux Compose File Settings Variables –––
|
||||
# ––– options: empty, '-z' or '-Z' –––––––––––––
|
||||
# –––––––––––––––––––––––––––––––––––––––––––––––
|
||||
|
||||
SELINUX_LABEL=
|
30
compose.yaml
|
@ -1,30 +0,0 @@
|
|||
services:
|
||||
mailserver:
|
||||
image: ghcr.io/docker-mailserver/docker-mailserver:latest
|
||||
container_name: mailserver
|
||||
# Provide the FQDN of your mail server here (Your DNS MX record should point to this value)
|
||||
hostname: mail.example.com
|
||||
env_file: mailserver.env
|
||||
# More information about the mail-server ports:
|
||||
# https://docker-mailserver.github.io/docker-mailserver/latest/config/security/understanding-the-ports/
|
||||
ports:
|
||||
- "25:25" # SMTP (explicit TLS => STARTTLS, Authentication is DISABLED => use port 465/587 instead)
|
||||
- "143:143" # IMAP4 (explicit TLS => STARTTLS)
|
||||
- "465:465" # ESMTP (implicit TLS)
|
||||
- "587:587" # ESMTP (explicit TLS => STARTTLS)
|
||||
- "993:993" # IMAP4 (implicit TLS)
|
||||
volumes:
|
||||
- ./docker-data/dms/mail-data/:/var/mail/
|
||||
- ./docker-data/dms/mail-state/:/var/mail-state/
|
||||
- ./docker-data/dms/mail-logs/:/var/log/mail/
|
||||
- ./docker-data/dms/config/:/tmp/docker-mailserver/
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
restart: always
|
||||
stop_grace_period: 1m
|
||||
# Uncomment if using `ENABLE_FAIL2BAN=1`:
|
||||
# cap_add:
|
||||
# - NET_ADMIN
|
||||
healthcheck:
|
||||
test: "ss --listening --tcp | grep -P 'LISTEN.+:smtp' || exit 1"
|
||||
timeout: 3s
|
||||
retries: 0
|
|
@ -1,4 +0,0 @@
|
|||
# File for additional dovecot configurations.
|
||||
# For more information read https://doc.dovecot.org/configuration_manual/quick_configuration/
|
||||
|
||||
#mail_max_userip_connections = 50
|
|
@ -1,92 +0,0 @@
|
|||
# Fail2Ban main configuration file
|
||||
#
|
||||
# Comments: use '#' for comment lines and ';' (following a space) for inline comments
|
||||
#
|
||||
# Changes: in most of the cases you should not modify this
|
||||
# file, but provide customizations in fail2ban.local file, e.g.:
|
||||
#
|
||||
# [DEFAULT]
|
||||
# loglevel = DEBUG
|
||||
#
|
||||
|
||||
[DEFAULT]
|
||||
|
||||
# Option: loglevel
|
||||
# Notes.: Set the log level output.
|
||||
# CRITICAL
|
||||
# ERROR
|
||||
# WARNING
|
||||
# NOTICE
|
||||
# INFO
|
||||
# DEBUG
|
||||
# Values: [ LEVEL ] Default: INFO
|
||||
#
|
||||
loglevel = INFO
|
||||
|
||||
# Option: logtarget
|
||||
# Notes.: Set the log target. This could be a file, SYSTEMD-JOURNAL, SYSLOG, STDERR or STDOUT.
|
||||
# Only one log target can be specified.
|
||||
# If you change logtarget from the default value and you are
|
||||
# using logrotate -- also adjust or disable rotation in the
|
||||
# corresponding configuration file
|
||||
# (e.g. /etc/logrotate.d/fail2ban on Debian systems)
|
||||
# Values: [ STDOUT | STDERR | SYSLOG | SYSOUT | SYSTEMD-JOURNAL | FILE ] Default: STDERR
|
||||
#
|
||||
logtarget = /var/log/fail2ban.log
|
||||
|
||||
# Option: syslogsocket
|
||||
# Notes: Set the syslog socket file. Only used when logtarget is SYSLOG
|
||||
# auto uses platform.system() to determine predefined paths
|
||||
# Values: [ auto | FILE ] Default: auto
|
||||
syslogsocket = auto
|
||||
|
||||
# Option: socket
|
||||
# Notes.: Set the socket file. This is used to communicate with the daemon. Do
|
||||
# not remove this file when Fail2ban runs. It will not be possible to
|
||||
# communicate with the server afterwards.
|
||||
# Values: [ FILE ] Default: /var/run/fail2ban/fail2ban.sock
|
||||
#
|
||||
socket = /var/run/fail2ban/fail2ban.sock
|
||||
|
||||
# Option: pidfile
|
||||
# Notes.: Set the PID file. This is used to store the process ID of the
|
||||
# fail2ban server.
|
||||
# Values: [ FILE ] Default: /var/run/fail2ban/fail2ban.pid
|
||||
#
|
||||
pidfile = /var/run/fail2ban/fail2ban.pid
|
||||
|
||||
# Option: allowipv6
|
||||
# Notes.: Allows IPv6 interface:
|
||||
# Default: auto
|
||||
# Values: [ auto yes (on, true, 1) no (off, false, 0) ] Default: auto
|
||||
#allowipv6 = auto
|
||||
|
||||
# Options: dbfile
|
||||
# Notes.: Set the file for the fail2ban persistent data to be stored.
|
||||
# A value of ":memory:" means database is only stored in memory
|
||||
# and data is lost when fail2ban is stopped.
|
||||
# A value of "None" disables the database.
|
||||
# Values: [ None :memory: FILE ] Default: /var/lib/fail2ban/fail2ban.sqlite3
|
||||
dbfile = /var/lib/fail2ban/fail2ban.sqlite3
|
||||
|
||||
# Options: dbpurgeage
|
||||
# Notes.: Sets age at which bans should be purged from the database
|
||||
# Values: [ SECONDS ] Default: 86400 (24hours)
|
||||
dbpurgeage = 1d
|
||||
|
||||
# Options: dbmaxmatches
|
||||
# Notes.: Number of matches stored in database per ticket (resolvable via
|
||||
# tags <ipmatches>/<ipjailmatches> in actions)
|
||||
# Values: [ INT ] Default: 10
|
||||
dbmaxmatches = 10
|
||||
|
||||
[Definition]
|
||||
|
||||
|
||||
[Thread]
|
||||
|
||||
# Options: stacksize
|
||||
# Notes.: Specifies the stack size (in KiB) to be used for subsequently created threads,
|
||||
# and must be 0 or a positive integer value of at least 32.
|
||||
# Values: [ SIZE ] Default: 0 (use platform or configured default)
|
||||
#stacksize = 0
|
|
@ -1,40 +0,0 @@
|
|||
[DEFAULT]
|
||||
|
||||
# "bantime" is the number of seconds that a host is banned.
|
||||
bantime = 1w
|
||||
|
||||
# A host is banned if it has generated "maxretry" during the last "findtime"
|
||||
# seconds.
|
||||
findtime = 1w
|
||||
|
||||
# "maxretry" is the number of failures before a host get banned.
|
||||
maxretry = 6
|
||||
|
||||
# "ignoreip" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban
|
||||
# will not ban a host which matches an address in this list. Several addresses
|
||||
# can be defined using space (and/or comma) separator.
|
||||
ignoreip = 127.0.0.1/8
|
||||
|
||||
# default ban action
|
||||
# nftables-multiport: block IP only on affected port
|
||||
# nftables-allports: block IP on all ports
|
||||
banaction = nftables-allports
|
||||
|
||||
[dovecot]
|
||||
enabled = true
|
||||
|
||||
[postfix]
|
||||
enabled = true
|
||||
# For a reference on why this mode was chose, see
|
||||
# https://github.com/docker-mailserver/docker-mailserver/issues/3256#issuecomment-1511188760
|
||||
mode = extra
|
||||
|
||||
[postfix-sasl]
|
||||
enabled = true
|
||||
|
||||
# This jail is used for manual bans.
|
||||
# To ban an IP address use: setup.sh fail2ban ban <IP>
|
||||
[custom]
|
||||
enabled = true
|
||||
bantime = 180d
|
||||
port = smtp,pop3,pop3s,imap,imaps,submission,submissions,sieve
|
|
@ -1,11 +0,0 @@
|
|||
# https://getmail6.org/configuration.html#conf-options
|
||||
|
||||
[options]
|
||||
verbose = 0
|
||||
read_all = false
|
||||
delete = false
|
||||
max_messages_per_session = 500
|
||||
received = false
|
||||
delivered_to = false
|
||||
message_log_syslog = true
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
# https://getmail6.org/configuration.html
|
||||
|
||||
[retriever]
|
||||
type = SimpleIMAPSSLRetriever
|
||||
server = imap.gmail.com
|
||||
username = alice
|
||||
password = notsecure
|
||||
|
||||
[destination]
|
||||
type = MDA_external
|
||||
path = /usr/lib/dovecot/deliver
|
||||
allow_root_commands = true
|
||||
arguments =("-d","user1@example.com")
|
|
@ -1,13 +0,0 @@
|
|||
# https://getmail6.org/configuration.html
|
||||
|
||||
[retriever]
|
||||
type = SimplePOP3SSLRetriever
|
||||
server = pop3.gmail.com
|
||||
username = alice
|
||||
password = notsecure
|
||||
|
||||
[destination]
|
||||
type = MDA_external
|
||||
path = /usr/lib/dovecot/deliver
|
||||
allow_root_commands = true
|
||||
arguments =("-d","user1@example.com")
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# This user patches script runs right before starting the daemons.
|
||||
# That means, all the other configuration is in place, so the script
|
||||
# can make final adjustments.
|
||||
# If you modify any supervisord configuration, make sure to run
|
||||
# "supervisorctl update" or "supervisorctl reload" afterwards.
|
||||
|
||||
# For more information, see
|
||||
# https://docker-mailserver.github.io/docker-mailserver/edge/config/advanced/override-defaults/user-patches/
|
||||
|
||||
echo 'user-patches.sh successfully executed'
|
|
@ -0,0 +1,4 @@
|
|||
# File for additional dovecot configurations.
|
||||
# For more informations read http://wiki.dovecot.org/BasicConfiguration
|
||||
|
||||
#mail_max_userip_connections = 50
|
|
@ -0,0 +1,19 @@
|
|||
[Definition]
|
||||
|
||||
# Option: loglevel
|
||||
# Notes.: Set the log level output.
|
||||
# CRITICAL
|
||||
# ERROR
|
||||
# WARNING
|
||||
# NOTICE
|
||||
# INFO
|
||||
# DEBUG
|
||||
# Values: [ LEVEL ] Default: ERROR
|
||||
#
|
||||
|
||||
# loglevel = INFO
|
||||
|
||||
# Options: dbpurgeage
|
||||
# Notes.: Sets age at which bans should be purged from the database
|
||||
# Values: [ SECONDS ] Default: 86400 (24hours), 604800 (1week)
|
||||
# dbpurgeage = 604800
|
|
@ -0,0 +1,11 @@
|
|||
[DEFAULT]
|
||||
|
||||
# "bantime" is the number of seconds that a host is banned.
|
||||
#bantime = 10800
|
||||
|
||||
# A host is banned if it has generated "maxretry" during the last "findtime"
|
||||
# seconds.
|
||||
#findtime = 600
|
||||
|
||||
# "maxretry" is the number of failures before a host get banned.
|
||||
#maxretry = 3
|
|
@ -0,0 +1,7 @@
|
|||
#! /bin/bash
|
||||
##
|
||||
# This user patches script runs right before starting the daemons. That means, all the other configuration is in place, so the script can make final adjustments.
|
||||
# If you modify any supervisord configuration, make sure to run "supervisorctl update" or "supervisorctl reload" afterwards.
|
||||
# To enable the script, you must save it in your config directory as "user-patches.sh".
|
||||
##
|
||||
echo "Default user-patches.sh successfully executed"
|
|
@ -1,60 +0,0 @@
|
|||
# Docs: https://docker-mailserver.github.io/docker-mailserver/v15.0/config/advanced/mail-fetchmail
|
||||
# Additional context, with CLI commands for verification:
|
||||
# https://github.com/orgs/docker-mailserver/discussions/3994#discussioncomment-9290570
|
||||
|
||||
services:
|
||||
dms-fetch:
|
||||
image: ghcr.io/docker-mailserver/docker-mailserver:latest # :15.0
|
||||
hostname: mail.example.test
|
||||
environment:
|
||||
ENABLE_FETCHMAIL: 1
|
||||
# We change this setting to 10 for quicker testing:
|
||||
FETCHMAIL_POLL: 10
|
||||
# Link the DNS lookup `remote.test` to resolve to the `dms-remote` container IP (for `@remote.test` address):
|
||||
# This is only for this example, since no real DNS service is configured, this is a Docker internal DNS feature:
|
||||
links:
|
||||
- "dms-remote:remote.test"
|
||||
# NOTE: Optional, You only need to publish ports if you want to verify via your own mail client.
|
||||
#ports:
|
||||
# - "465:465" # ESMTP (implicit TLS)
|
||||
# - "993:993" # IMAP4 (implicit TLS)
|
||||
# You'd normally use `volumes` here but for simplicity of the example, all config is contained within `compose.yaml`:
|
||||
configs:
|
||||
- source: dms-accounts-fetch
|
||||
target: /tmp/docker-mailserver/postfix-accounts.cf
|
||||
- source: fetchmail
|
||||
target: /tmp/docker-mailserver/fetchmail.cf
|
||||
|
||||
dms-remote:
|
||||
image: ghcr.io/docker-mailserver/docker-mailserver:latest # :15.0
|
||||
hostname: mail.remote.test
|
||||
environment:
|
||||
# Allows for us send a test mail easily by trusting any mail client run within this container (`swaks`):
|
||||
PERMIT_DOCKER: container
|
||||
# Alternatively, trust and accept any mail received from clients in same subnet of dms-fetch:
|
||||
#PERMIT_DOCKER: connected-networks
|
||||
configs:
|
||||
- source: dms-accounts-remote
|
||||
target: /tmp/docker-mailserver/postfix-accounts.cf
|
||||
|
||||
# Using the Docker Compose `configs.content` feature instead of volume mounting separate files.
|
||||
# NOTE: This feature requires Docker Compose v2.23.1 (Nov 2023) or newer:
|
||||
# https://github.com/compose-spec/compose-spec/pull/446
|
||||
configs:
|
||||
fetchmail:
|
||||
content: |
|
||||
poll 'mail.remote.test' proto imap
|
||||
user 'jane.doe@remote.test'
|
||||
pass 'secret'
|
||||
is 'john.doe@example.test'
|
||||
no sslcertck
|
||||
|
||||
# DMS requires an account to complete setup, configure one for each instance:
|
||||
# NOTE: Both accounts are configured with the same password (SHA512-CRYPT hashed), `secret`.
|
||||
dms-accounts-fetch:
|
||||
content: |
|
||||
john.doe@example.test|{SHA512-CRYPT}$$6$$sbgFRCmQ.KWS5ryb$$EsWrlYosiadgdUOxCBHY0DQ3qFbeudDhNMqHs6jZt.8gmxUwiLVy738knqkHD4zj4amkb296HFqQ3yDq4UXt8.
|
||||
|
||||
dms-accounts-remote:
|
||||
content: |
|
||||
jane.doe@remote.test|{SHA512-CRYPT}$$6$$sbgFRCmQ.KWS5ryb$$EsWrlYosiadgdUOxCBHY0DQ3qFbeudDhNMqHs6jZt.8gmxUwiLVy738knqkHD4zj4amkb296HFqQ3yDq4UXt8.
|
|
@ -1,147 +0,0 @@
|
|||
# Docs: https://docker-mailserver.github.io/docker-mailserver/v15.0/config/advanced/mail-forwarding/relay-hosts/
|
||||
# Additional context, with CLI commands for verification:
|
||||
# https://github.com/docker-mailserver/docker-mailserver/issues/4136#issuecomment-2253693490
|
||||
|
||||
services:
|
||||
# This would represent your actual DMS container:
|
||||
dms-sender:
|
||||
image: mailserver/docker-mailserver:latest # :15.0
|
||||
hostname: mail.example.test
|
||||
environment:
|
||||
# All outbound mail will be relayed through this host
|
||||
# (change the port to 587 if you do not want the postfix-main.cf override)
|
||||
- DEFAULT_RELAY_HOST=[smtp.relay-service.test]:465
|
||||
# Your relay host credentials.
|
||||
# (since the relay in the example is DMS, the relay account username is a full email address)
|
||||
- RELAY_USER=relay-user@relay-service.test
|
||||
- RELAY_PASSWORD=secret
|
||||
# The mail client (swaks) needs to connect with TLS:
|
||||
- SSL_TYPE=manual
|
||||
- SSL_KEY_PATH=/tmp/tls/key.pem
|
||||
- SSL_CERT_PATH=/tmp/tls/cert.pem
|
||||
# You would usually have `volumes` instead of this `configs`:
|
||||
configs:
|
||||
- source: dms-main
|
||||
target: /tmp/docker-mailserver/postfix-main.cf
|
||||
- source: dms-accounts
|
||||
target: /tmp/docker-mailserver/postfix-accounts.cf
|
||||
# Authenticating on port 587 or 465 enforces TLS requirement:
|
||||
- source: tls-cert
|
||||
target: /tmp/tls/cert.pem
|
||||
- source: tls-key
|
||||
target: /tmp/tls/key.pem
|
||||
# This is only needed if you want to verify the TLS cert chain with swaks
|
||||
# (normally with public CA providers like LetsEncrypt this file is already available to a mail client)
|
||||
- source: tls-ca-cert
|
||||
target: /tmp/tls/ca-cert.pem
|
||||
|
||||
# Pretend this is your third-party relay service:
|
||||
dms-relay:
|
||||
image: mailserver/docker-mailserver:latest # :15.0
|
||||
hostname: smtp.relay-service.test
|
||||
environment:
|
||||
# WORKAROUND: Bypass security checks from the mail-client (dms-sender container)
|
||||
# (avoids needing expected DNS records to run this example)
|
||||
- PERMIT_DOCKER=connected-networks
|
||||
# TLS is required when relaying to dms-relay via ports 587 / 465
|
||||
# (dms-relay will then relay the mail to dms-destination over port 25)
|
||||
- SSL_TYPE=manual
|
||||
- SSL_KEY_PATH=/tmp/tls/key.pem
|
||||
- SSL_CERT_PATH=/tmp/tls/cert.pem
|
||||
configs:
|
||||
- source: dms-accounts-relay
|
||||
target: /tmp/docker-mailserver/postfix-accounts.cf
|
||||
- source: tls-cert
|
||||
target: /tmp/tls/cert.pem
|
||||
- source: tls-key
|
||||
target: /tmp/tls/key.pem
|
||||
|
||||
# Pretend this is another mail server that your target recipient belongs to (like Gmail):
|
||||
dms-destination:
|
||||
image: mailserver/docker-mailserver:latest # :15.0
|
||||
hostname: mail.destination.test
|
||||
# WORKAROUND: dms-relay must be able to resolve DNS for `@destination.test` to the IP of this container:
|
||||
# Normally a MX record would direct mail to the MTA (eg: `mail.destination.test`)
|
||||
networks:
|
||||
default:
|
||||
aliases:
|
||||
- destination.test
|
||||
environment:
|
||||
# WORKAROUND: Same workaround as needed for dms-relay
|
||||
- PERMIT_DOCKER=connected-networks
|
||||
configs:
|
||||
- source: dms-accounts-destination
|
||||
target: /tmp/docker-mailserver/postfix-accounts.cf
|
||||
|
||||
# Using the Docker Compose `configs.content` feature instead of volume mounting separate files.
|
||||
# NOTE: This feature requires Docker Compose v2.23.1 (Nov 2023) or newer:
|
||||
# https://github.com/compose-spec/compose-spec/pull/446
|
||||
configs:
|
||||
# `postfix-main.cf`, a single line change to make all outbound SMTP connections over implicit TLS instead of the default explicit TLS (StartTLS).
|
||||
# NOTE: If you need to only selectively relay mail, you would need to instead adjust this on the relay service in `/etc/postfix/master.cf`,
|
||||
# However DMS presently modifies this when using the DMS Relay Host feature support, which may override `postfix-master.cf` or `user-patches.sh` due to `check-for-changes.sh`.
|
||||
dms-main:
|
||||
content: |
|
||||
smtp_tls_wrappermode=yes
|
||||
|
||||
# DMS expects an account to be configured to run, this example provides accounts already created.
|
||||
# Login credentials:
|
||||
# user: "john.doe@example.test" password: "secret"
|
||||
# user: "relay-user@relay-service.test" password: "secret"
|
||||
# user: "jane.doe@destination.test" password: "secret"
|
||||
dms-accounts:
|
||||
# NOTE: `$` needed to be repeated to escape it,
|
||||
# which opts out of the `compose.yaml` variable interpolation feature.
|
||||
content: |
|
||||
john.doe@example.test|{SHA512-CRYPT}$$6$$sbgFRCmQ.KWS5ryb$$EsWrlYosiadgdUOxCBHY0DQ3qFbeudDhNMqHs6jZt.8gmxUwiLVy738knqkHD4zj4amkb296HFqQ3yDq4UXt8.
|
||||
|
||||
dms-accounts-relay:
|
||||
content: |
|
||||
relay-user@relay-service.test|{SHA512-CRYPT}$$6$$o65y1ZXC4ooOPLwZ$$7TF1nYowEtNJpH6BwJBgdj2pPAxaCvhIKQA6ww5zdHm/AA7aemY9eoHC91DOgYNaKj1HLxSeWNDdvrp6mbtUY.
|
||||
|
||||
dms-accounts-destination:
|
||||
content: |
|
||||
jane.doe@destination.test|{SHA512-CRYPT}$$6$$o65y1ZXC4ooOPLwZ$$7TF1nYowEtNJpH6BwJBgdj2pPAxaCvhIKQA6ww5zdHm/AA7aemY9eoHC91DOgYNaKj1HLxSeWNDdvrp6mbtUY.
|
||||
|
||||
# TLS files:
|
||||
# - Use an ECDSA cert that's been signed by a self-signed CA for TLS cert verification.
|
||||
# - This cert is only valid for mail.example.test, mail.destination.test, smtp.relay-service.test
|
||||
|
||||
# `swaks` run in the container will need to reference this CA cert file for successful verficiation (optional).
|
||||
tls-ca-cert:
|
||||
content: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBfTCCASKgAwIBAgIRAMAZttlRlkcuSun0yV0z4RwwCgYIKoZIzj0EAwIwHDEa
|
||||
MBgGA1UEAxMRU21hbGxzdGVwIFJvb3QgQ0EwHhcNMjEwMTAxMDAwMDAwWhcNMzEw
|
||||
MTAxMDAwMDAwWjAcMRowGAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTBZMBMGByqG
|
||||
SM49AgEGCCqGSM49AwEHA0IABJX2hCtoK3+bM5I3rmyApXLJ1gOcVhtoSSwM8XXR
|
||||
SEl25Kkc0n6mINuMK8UrBkiBUgexf6CYayx3xVr9TmMkg4KjRTBDMA4GA1UdDwEB
|
||||
/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBQD8sBrApbyYyqU
|
||||
y+/TlwGynx2V5jAKBggqhkjOPQQDAgNJADBGAiEAi8N2eOETI+6hY3+G+kzNMd3K
|
||||
Sd3Ke8b++/nlwr5Fb/sCIQDYAjpKp/MpTDWICeHC2tcB5ptxoTdWkTBuG4rKcktA
|
||||
0w==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
tls-key:
|
||||
content: |
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIOc6wqZmSDmT336K4O26dMk1RCVc0+cmnsO2eK4P5K5yoAoGCCqGSM49
|
||||
AwEHoUQDQgAEFOWNgekKKvUZE89vJ7henUYxODYIvCiHitRc2ylwttjqt1KUY1cp
|
||||
q3jof2fhURHfBUH3dHPXLHig5V9Jw5gqeg==
|
||||
-----END EC PRIVATE KEY-----
|
||||
|
||||
tls-cert:
|
||||
content: |
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB9DCCAZqgAwIBAgIQE53a/y2c//YXRsz2kLm6gDAKBggqhkjOPQQDAjAcMRow
|
||||
GAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTAeFw0yMTAxMDEwMDAwMDBaFw0zMTAx
|
||||
MDEwMDAwMDBaMBkxFzAVBgNVBAMTDlNtYWxsc3RlcCBMZWFmMFkwEwYHKoZIzj0C
|
||||
AQYIKoZIzj0DAQcDQgAEFOWNgekKKvUZE89vJ7henUYxODYIvCiHitRc2ylwttjq
|
||||
t1KUY1cpq3jof2fhURHfBUH3dHPXLHig5V9Jw5gqeqOBwDCBvTAOBgNVHQ8BAf8E
|
||||
BAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBSz
|
||||
w74g+O6dcBbwienD70D8A9ESmDAfBgNVHSMEGDAWgBQD8sBrApbyYyqUy+/TlwGy
|
||||
nx2V5jBMBgNVHREERTBDghFtYWlsLmV4YW1wbGUudGVzdIIVbWFpbC5kZXN0aW5h
|
||||
dGlvbi50ZXN0ghdzbXRwLnJlbGF5LXNlcnZpY2UudGVzdDAKBggqhkjOPQQDAgNI
|
||||
ADBFAiEAoety5oClZtuBMkvlUIWRmWlyg1VIOZ544LSEbplsIhcCIHb6awMwNdXP
|
||||
m/xHjFkuwH1+UjDDRW53Ih7KZoLrQ6Cp
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,28 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
mailserver:
|
||||
image: docker.io/mailserver/docker-mailserver:latest
|
||||
hostname: ${HOSTNAME}
|
||||
domainname: ${DOMAINNAME}
|
||||
container_name: ${CONTAINER_NAME}
|
||||
env_file: mailserver.env
|
||||
# To avoid conflicts with yaml base-60 float, DO NOT remove the quotation marks.
|
||||
ports:
|
||||
- "25:25"
|
||||
- "143:143"
|
||||
- "587:587"
|
||||
- "993:993"
|
||||
volumes:
|
||||
- maildata:/var/mail
|
||||
- mailstate:/var/mail-state
|
||||
- maillogs:/var/log/mail
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ./config/:/tmp/docker-mailserver/${SELINUX_LABEL}
|
||||
restart: always
|
||||
cap_add: [ "NET_ADMIN", "SYS_PTRACE" ]
|
||||
|
||||
volumes:
|
||||
maildata:
|
||||
mailstate:
|
||||
maillogs:
|
|
@ -1,7 +1,4 @@
|
|||
/*
|
||||
This file adds our styling additions / fixes to maintain.
|
||||
Some of which are overly specific and may break with future updates by upstream.
|
||||
*/
|
||||
/* This file adds our styling additions / fixes to maintain. */
|
||||
|
||||
/* ============================================================================================================= */
|
||||
|
||||
|
@ -16,16 +13,12 @@ If you want to append instead, switch `::before` to `::after`.
|
|||
src: url('../fonts/external-link.woff') format('woff');
|
||||
}
|
||||
|
||||
/*
|
||||
Since mkdocs-material 9.5.5 broke support in our docs from DMS v13.3.1, we now use our own class name,
|
||||
which has been included for the two external nav links in mkdocs.yml via workaround (insert HTML).
|
||||
*/
|
||||
.icon-external-link::before {
|
||||
/* Matches the two nav link classes that start with `http` `href` values, regular docs pages use relative URLs instead. */
|
||||
.md-tabs__link[href^="http"]::before, .md-nav__link[href^="http"]::before {
|
||||
display: inline-block; /* treat similar to text */
|
||||
font-family: 'external-link';
|
||||
content:'\0041'; /* represents "A" which our font renders as an icon instead of the "A" glyph */
|
||||
font-size: 80%; /* icon is a little too big by default, scale it down */
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
/* ============================================================================================================= */
|
||||
|
@ -47,19 +40,6 @@ If you want to append instead, switch `::before` to `::after`.
|
|||
|
||||
/* ============================================================================================================= */
|
||||
|
||||
/*
|
||||
UX Bugfix for left navbar visibility on top-level (tabbed) pages with no nested sub-pages.
|
||||
Upstream will not fix: https://github.com/squidfunk/mkdocs-material/issues/3109
|
||||
*/
|
||||
|
||||
@media screen and (min-width: 76.25em) {
|
||||
.md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================================================================= */
|
||||
|
||||
/*
|
||||
UX Bugfix for permalink affecting typography in headings.
|
||||
Upstream will not fix: https://github.com/squidfunk/mkdocs-material/issues/2369
|
||||
|
@ -102,42 +82,3 @@ div.md-content article.md-content__inner a.toclink code {
|
|||
.highlight.no-copy .md-clipboard { display: none; }
|
||||
|
||||
/* ============================================================================================================= */
|
||||
|
||||
/* Make the left-sidebar nav categories better distinguished from page links (bold text) */
|
||||
.md-nav__item--nested > .md-nav__link {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* ============================================================================================================= */
|
||||
|
||||
/*
|
||||
TaskList style for a pro/con list. Presently only used for this type of list in the kubernetes docs.
|
||||
Uses a custom icon for the unchecked (con) state: :octicons-x-circle-fill-24:
|
||||
https://github.com/squidfunk/mkdocs-material/discussions/6811#discussioncomment-8700795
|
||||
|
||||
TODO: Can better scope the style under a class name when migrating to block extension syntax:
|
||||
https://github.com/facelessuser/pymdown-extensions/discussions/1973
|
||||
*/
|
||||
|
||||
:root {
|
||||
--md-tasklist-icon--failed: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M1 12C1 5.925 5.925 1 12 1s11 4.925 11 11-4.925 11-11 11S1 18.075 1 12Zm8.036-4.024a.751.751 0 0 0-1.042.018.751.751 0 0 0-.018 1.042L10.939 12l-2.963 2.963a.749.749 0 0 0 .326 1.275.749.749 0 0 0 .734-.215L12 13.06l2.963 2.964a.75.75 0 0 0 1.061-1.06L13.061 12l2.963-2.964a.749.749 0 0 0-.326-1.275.749.749 0 0 0-.734.215L12 10.939Z"/></svg>');
|
||||
}
|
||||
|
||||
.md-typeset [type="checkbox"] + .task-list-indicator::before {
|
||||
background-color: rgb(216, 87, 48);
|
||||
-webkit-mask-image: var(--md-tasklist-icon--failed);
|
||||
mask-image: var(--md-tasklist-icon--failed);
|
||||
}
|
||||
|
||||
/* More suitable shade of green */
|
||||
.md-typeset [type=checkbox]:checked+.task-list-indicator:before {
|
||||
background-color: rgb(97, 216, 42);
|
||||
}
|
||||
|
||||
/* Tiny layout shift */
|
||||
[dir=ltr] .md-typeset .task-list-indicator:before {
|
||||
left: -1.6em;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
/* ============================================================================================================= */
|
||||
|
|
Before Width: | Height: | Size: 32 KiB |
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="dmo-logo-monochromatic"
|
||||
viewBox="20 244 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g id="envelope">
|
||||
<path
|
||||
id="base"
|
||||
d="m 122,490 h 172 l 70,-27 a 10,10 0 0 0 6,-12 L 315,305 A 10,10 0 0 0 302,300 L 90,380 a 10,10 0 0 0 -6,12 z"
|
||||
fill="#e3e3e3"
|
||||
/>
|
||||
<path
|
||||
id="bottom"
|
||||
d="m 294,490 70,-27 a 10,10 0 0 0 5,-5 L 220,404 174,490 Z"
|
||||
fill="#d4d4d4"
|
||||
/>
|
||||
<path
|
||||
id="top"
|
||||
d="m 84,387 150,53 75,-140 a 10,10 0 0 0 -7,0 l -212,80 a 10,10 0 0 0 -6,6 z"
|
||||
fill="#ffffff"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
id="whale-silhouette"
|
||||
d="M 403,153 C 401,153 398,154 396,156 c -2,3 -20,23 -16,69 1,3 0,7 -2,9 -1,2 -4,3 -7,3 H 16.6 c -2.6,0 -5.4,1 -7.6,3 -2,2 -3,5 -3,8 0,160 117,177 168,177 129,0 219,-78 258,-148 52,-8 74,-43 75,-45 3,-5 1,-11 -4,-14 -1,-1 -31,-20 -59,-15 -7,-29 -33,-47 -35,-48 -1.75,-1.3 -3.7,-1.86 -5.66,-1.8 z M 112,300 a 21,21 0 0 1 21,21 21,21 0 0 1 -21,21 21,21 0 0 1 -21,-21 21,21 0 0 1 21,-21 z"
|
||||
fill="#ffffff"
|
||||
transform="translate(20,244)"
|
||||
/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 52 KiB |
|
@ -1,55 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="dmo-logo"
|
||||
viewBox="20 244 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g id="envelope">
|
||||
<path
|
||||
id="base"
|
||||
d="m 122,490 h 172 l 70,-27 a 10,10 0 0 0 6,-12 L 315,305 A 10,10 0 0 0 302,300 L 90,380 a 10,10 0 0 0 -6,12 z"
|
||||
fill="#f3ac47"
|
||||
/>
|
||||
<path
|
||||
id="bottom"
|
||||
d="m 294,490 70,-27 a 10,10 0 0 0 5,-5 L 220,404 174,490 Z"
|
||||
fill="#f19a3d"
|
||||
/>
|
||||
<path
|
||||
id="top"
|
||||
d="m 84,387 150,53 75,-140 a 10,10 0 0 0 -7,0 l -212,80 a 10,10 0 0 0 -6,6 z"
|
||||
fill="#ffd15c"
|
||||
/>
|
||||
</g>
|
||||
<g id="whale">
|
||||
<path
|
||||
id="body-outline"
|
||||
d="M 523 462 C 522 461 492 442 464 447 C 457 418 431 400 429 399 C 425 396 420 397 416 400 C 414 403 396 423 400 469 C 401 472 400 476 398 478 C 397 480 394 481 391 481 L 36.6 481 C 34 481 31.2 482 29 484 C 27 486 26 489 26 492 C 26 652 143 669 194 669 C 323 669 413 591 452 521 C 504 513 526 478 527 476 C 530 471 528 465 523 462 Z"
|
||||
fill="#303c42"
|
||||
/>
|
||||
<path
|
||||
id="body-fill"
|
||||
d="M 445 501 C 441 502 438 504 437 507 C 402 572 317 649 194 649 C 140 649 52 629 47 502 L 391 502 C 400 502 408 498 414 492 C 420 485 423 476 422 467 C 420 446 423 432 426 424 C 434 431 445 444 445 460 C 445 464 446 467 450 469 C 453 471 456 471 460 470 C 472 463 490 469 502 474 C 493 484 475 498 445 501 Z"
|
||||
fill="#42a5f5"
|
||||
/>
|
||||
<path
|
||||
id="body-shadow"
|
||||
d="M 445 491 C 441 491 438 493 437 496 C 402 562 317 638 194 638 C 142 638 57 620 48 502 L 47 502 C 52 629 140 649 194 649 C 317 649 402 572 437 507 C 438 504 441 502 445 501 C 475 498 493 484 502 474 C 499 473 497 472 494 471 C 484 479 468 488 445 491 Z"
|
||||
opacity="0.1"
|
||||
/>
|
||||
<circle
|
||||
id="eye"
|
||||
cx="132"
|
||||
cy="565"
|
||||
r="21"
|
||||
fill="#303c42"
|
||||
/>
|
||||
<circle
|
||||
id="eye-highlight"
|
||||
cx="141"
|
||||
cy="559"
|
||||
r="6.76"
|
||||
fill="#ffffff"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.9 KiB |
|
@ -1 +0,0 @@
|
|||
<svg viewBox="20 244 512 512" xmlns="http://www.w3.org/2000/svg"><path d="M122 490h172l70-27a10 10 0 0 0 6-12l-55-146a10 10 0 0 0-13-5L90 380a10 10 0 0 0-6 12z" fill="#e3e3e3"/><path d="m294 490 70-27a10 10 0 0 0 5-5l-149-54-46 86z" fill="#d4d4d4"/><path d="m84 387 150 53 75-140a10 10 0 0 0-7 0L90 380a10 10 0 0 0-6 6z" fill="#fff"/><path d="M423 397c-2 0-5 1-7 3-2 3-20 23-16 69 1 3 0 7-2 9-1 2-4 3-7 3H36.6c-2.6 0-5.4 1-7.6 3-2 2-3 5-3 8 0 160 117 177 168 177 129 0 219-78 258-148 52-8 74-43 75-45 3-5 1-11-4-14-1-1-31-20-59-15-7-29-33-47-35-48-1.75-1.3-3.7-1.86-5.66-1.8zM132 544a21 21 0 0 1 21 21 21 21 0 0 1-21 21 21 21 0 0 1-21-21 21 21 0 0 1 21-21z" fill="#fff"/></svg>
|
Before Width: | Height: | Size: 677 B |
|
@ -0,0 +1 @@
|
|||
<svg viewBox="20.7 244.9 512 512" xmlns="http://www.w3.org/2000/svg"><defs><filter id="a" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB"><feColorMatrix values="0.21 0.72 0.072 0.18 0 0.21 0.72 0.072 0.18 0 0.21 0.72 0.072 0.18 0 0 0 0 1 0"/></filter></defs><g fill="none" stroke-miterlimit="10" transform="matrix(2.4982 0 0 2.4982 29.044 225.184)" filter="url(#a)"><path d="M37.133 105.617h68.54l28.007-10.634a3.822 3.822 0 002.216-4.93L113.933 32.2a3.821 3.821 0 00-4.93-2.216l-84.34 32.021a3.821 3.821 0 00-2.215 4.93z" fill="#f3ac47"/><path d="M105.674 105.617l28.009-10.634a3.8 3.8 0 002.073-1.907L76.443 71.304l-18.448 34.313z" fill="#f19a3d"/><path d="M22.386 64.451l59.517 21.233 29.918-55.65a3.799 3.799 0 00-2.817-.05l-84.34 32.022a3.796 3.796 0 00-2.278 2.445z" fill="#ffd15c"/></g><path d="M422.26 398.804a10.4 10.4 0 00-6.387 3.14c-2.084 2.168-20.154 22.384-15.048 68.401a10.838 10.838 0 01-2.626 8.568 9.316 9.316 0 01-7.127 3.208H37.852a10.42 10.42 0 00-10.42 10.421c0 159.895 116.607 177.151 166.73 177.151 129.072 0 218.793-78.405 257.954-147.494 51.603-8.233 73.133-43.642 74.07-45.184a10.442 10.442 0 00-3.355-14.088c-1.438-.938-31.22-19.736-59.377-15.255-6.815-28.532-32.991-46.31-34.283-47.165a10.4 10.4 0 00-6.911-1.703zM132.494 544.646a20.841 20.841 0 0120.84 20.84 20.841 20.841 0 01-20.84 20.843 20.841 20.841 0 01-20.842-20.842 20.841 20.841 0 0120.842-20.84z" fill="#fff"/><path d="M23.77 11.079c-.069-.045-1.498-.947-2.849-.732-.327-1.369-1.583-2.222-1.645-2.263a.499.499 0 00-.638.069c-.1.104-.967 1.074-.722 3.282a.52.52 0 01-.126.411.447.447 0 01-.342.154H17V9.5a.5.5 0 00-.5-.5H14V3.5a.5.5 0 00-.5-.5h-3a.5.5 0 00-.5.5V6H4.5a.5.5 0 00-.5.5V9H1.5a.5.5 0 00-.5.5V12H.5a.5.5 0 00-.5.5C0 20.172 5.595 21 8 21c6.193 0 10.498-3.762 12.377-7.077 2.476-.395 3.509-2.094 3.554-2.168a.501.501 0 00-.161-.676z" fill="url(#a)" transform="matrix(20.84137 0 0 20.84137 27.432 232.025)"/></svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -1 +0,0 @@
|
|||
<svg viewBox="20 244 512 512" xmlns="http://www.w3.org/2000/svg"><path d="M122 490h172l70-27a10 10 0 0 0 6-12l-55-146a10 10 0 0 0-13-5L90 380a10 10 0 0 0-6 12z" fill="#f3ac47"/><path d="m294 490 70-27a10 10 0 0 0 5-5l-149-54-46 86z" fill="#f19a3d"/><path d="m84 387 150 53 75-140a10 10 0 0 0-7 0L90 380a10 10 0 0 0-6 6z" fill="#ffd15c"/><g><path d="M523 462c-1-1-31-20-59-15-7-29-33-47-35-48-4-3-9-2-13 1-2 3-20 23-16 69 1 3 0 7-2 9-1 2-4 3-7 3H36.6c-2.6 0-5.4 1-7.6 3-2 2-3 5-3 8 0 160 117 177 168 177 129 0 219-78 258-148 52-8 74-43 75-45 3-5 1-11-4-14z" fill="#303c42"/><path d="M445 501c-4 1-7 3-8 6-35 65-120 142-243 142-54 0-142-20-147-147h344c9 0 17-4 23-10 6-7 9-16 8-25-2-21 1-35 4-43 8 7 19 20 19 36 0 4 1 7 5 9 3 2 6 2 10 1 12-7 30-1 42 4-9 10-27 24-57 27z" fill="#42a5f5"/><path d="M445 491c-4 0-7 2-8 5-35 66-120 142-243 142-52 0-137-18-146-136h-1c5 127 93 147 147 147 123 0 208-77 243-142 1-3 4-5 8-6 30-3 48-17 57-27-3-1-5-2-8-3-10 8-26 17-49 20z" opacity=".1"/><circle cx="132" cy="565" r="21" fill="#303c42"/><circle cx="141" cy="559" r="6.76" fill="#fff"/></g></svg>
|
Before Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1 @@
|
|||
<svg viewBox="20.7 244.9 512 512" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke-miterlimit="10"><path d="M121.45 489.894h172.28l70.397-26.729a9.607 9.607 0 005.57-12.392l-55.205-145.417a9.604 9.604 0 00-12.392-5.57L90.106 380.273a9.604 9.604 0 00-5.567 12.392z" fill="#f3ac47"/><path d="M293.732 489.894l70.403-26.729a9.552 9.552 0 005.21-4.793L220.26 403.646l-46.37 86.248z" fill="#f19a3d"/><path d="M84.383 386.421l149.6 53.37 75.2-139.88a9.549 9.549 0 00-7.08-.125l-211.994 80.49a9.541 9.541 0 00-5.726 6.145z" fill="#ffd15c"/></g><g transform="matrix(20.9196 0 0 20.9196 26.176 230.103)"><path d="M23.77 11.079c-.069-.045-1.498-.947-2.849-.732-.327-1.369-1.583-2.222-1.645-2.263a.499.499 0 00-.638.069c-.1.104-.967 1.074-.722 3.282a.52.52 0 01-.126.411.447.447 0 01-.342.154H.5a.5.5 0 00-.5.5C0 20.172 5.595 21 8 21c6.193 0 10.498-3.762 12.377-7.077 2.476-.395 3.509-2.094 3.554-2.168a.501.501 0 00-.161-.676z" fill="#303c42"/><path d="M20.002 12.965a.5.5 0 00-.382.261C17.942 16.351 13.891 20 8 20c-2.546 0-6.772-.924-6.991-7h16.439c.422 0 .809-.173 1.089-.486.287-.321.424-.754.375-1.189-.113-1.02.053-1.687.214-2.069.375.361.874.985.874 1.744 0 .173.09.334.237.426a.503.503 0 00.486.021c.595-.3 1.44-.048 2.015.212-.432.455-1.301 1.138-2.736 1.306z" fill="#42a5f5"/><path d="M20.002 12.465a.5.5 0 00-.382.261C17.942 15.851 13.891 19.5 8 19.5c-2.472 0-6.518-.886-6.951-6.5h-.04c.218 6.076 4.445 7 6.991 7 5.892 0 9.942-3.649 11.62-6.774a.5.5 0 01.382-.261c1.436-.168 2.305-.851 2.736-1.306a4.894 4.894 0 00-.384-.15c-.492.399-1.254.827-2.352.956z" opacity=".1"/><circle cx="5.041" cy="16" r="1" fill="#303c42"/><circle cx="5.469" cy="15.729" r=".323" fill="#fff"/><path d="M23.77 11.079c-.069-.045-1.498-.947-2.849-.732-.327-1.369-1.583-2.222-1.645-2.263a.499.499 0 00-.638.069c-.1.104-.967 1.074-.722 3.282a.52.52 0 01-.126.411.447.447 0 01-.342.154H17V9.5a.5.5 0 00-.5-.5H14V3.5a.5.5 0 00-.5-.5h-3a.5.5 0 00-.5.5V6H4.5a.5.5 0 00-.5.5V9H1.5a.5.5 0 00-.5.5V12H.5a.5.5 0 00-.5.5C0 20.172 5.595 21 8 21c6.193 0 10.498-3.762 12.377-7.077 2.476-.395 3.509-2.094 3.554-2.168a.501.501 0 00-.161-.676z" fill="url(#a)"/></g><path d="M23.77 11.079c-.069-.045-1.498-.947-2.849-.732-.327-1.369-1.583-2.222-1.645-2.263a.499.499 0 00-.638.069c-.1.104-.967 1.074-.722 3.282a.52.52 0 01-.126.411.447.447 0 01-.342.154H17V9.5a.5.5 0 00-.5-.5H14V3.5a.5.5 0 00-.5-.5h-3a.5.5 0 00-.5.5V6H4.5a.5.5 0 00-.5.5V9H1.5a.5.5 0 00-.5.5V12H.5a.5.5 0 00-.5.5C0 20.172 5.595 21 8 21c6.193 0 10.498-3.762 12.377-7.077 2.476-.395 3.509-2.094 3.554-2.168a.501.501 0 00-.161-.676z" fill="url(#a)" transform="matrix(20.84137 0 0 20.84137 27.432 232.025)"/></svg>
|
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 33 KiB |
|
@ -0,0 +1,109 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="512"
|
||||
viewBox="20.7 244.9 512 512"
|
||||
width="512"
|
||||
version="1.1"
|
||||
id="svg10"
|
||||
sodipodi:docname="dmo-logo-white.svg"
|
||||
inkscape:version="1.1-alpha (91d7437e58, 2021-02-12, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview155"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
objecttolerance="10.0"
|
||||
gridtolerance="10.0"
|
||||
guidetolerance="10.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.5996094"
|
||||
inkscape:cx="185.04518"
|
||||
inkscape:cy="256.31258"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1005"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg10" />
|
||||
<defs
|
||||
id="defs14">
|
||||
<linearGradient
|
||||
id="linearGradient4612">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4610" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4588">
|
||||
<stop
|
||||
style="stop-color:#ff0000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4586" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4528">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4526" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4348">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4346" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
id="filter5959"
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1">
|
||||
<feColorMatrix
|
||||
values="0.21 0.72 0.072 0.18 0 0.21 0.72 0.072 0.18 0 0.21 0.72 0.072 0.18 0 0 0 0 1 0 "
|
||||
id="feColorMatrix5957" />
|
||||
</filter>
|
||||
</defs>
|
||||
<g
|
||||
id="g12703">
|
||||
<g
|
||||
fill="none"
|
||||
stroke-miterlimit="10"
|
||||
id="g8"
|
||||
transform="matrix(2.4981995,0,0,2.4981995,29.044426,225.18359)"
|
||||
style="filter:url(#filter5959)">
|
||||
<path
|
||||
d="m 37.133,105.617 h 68.54 L 133.68,94.983 a 3.822,3.822 0 0 0 2.216,-4.93 L 113.933,32.2 a 3.821,3.821 0 0 0 -4.93,-2.216 l -84.34,32.021 a 3.821,3.821 0 0 0 -2.215,4.93 z"
|
||||
fill="#f3ac47"
|
||||
id="path2" />
|
||||
<path
|
||||
d="m 105.674,105.617 28.009,-10.634 a 3.8,3.8 0 0 0 2.073,-1.907 L 76.443,71.304 57.995,105.617 Z"
|
||||
fill="#f19a3d"
|
||||
id="path4-5" />
|
||||
<path
|
||||
d="m 22.386,64.451 59.517,21.233 29.918,-55.65 a 3.799,3.799 0 0 0 -2.817,-0.05 l -84.34,32.022 a 3.796,3.796 0 0 0 -2.278,2.445 z"
|
||||
fill="#ffd15c"
|
||||
id="path6" />
|
||||
</g>
|
||||
<path
|
||||
id="path10"
|
||||
style="stroke-width:21.581;fill:#ffffff;fill-opacity:1"
|
||||
d="m 418.52031,389.19102 a 10.768909,10.768909 0 0 0 -6.61328,3.25195 c -2.1581,2.24442 -20.86937,23.17732 -15.58203,70.82812 a 11.22211,11.22211 0 0 1 -2.71875,8.8711 9.6466981,9.6466981 0 0 1 -7.38086,3.32226 H 20.471484 A 10.79049,10.79049 0 0 0 9.680469,486.25547 c 0,165.56928 120.746181,183.4375 172.648441,183.4375 133.65101,0 226.55676,-81.18757 267.10742,-152.72852 53.4345,-8.52448 75.72807,-45.19011 76.69922,-46.78711 a 10.812071,10.812071 0 0 0 -3.47461,-14.58789 c -1.48909,-0.97114 -32.32847,-20.43678 -61.48438,-15.79687 -7.05698,-29.54436 -34.16198,-47.95307 -35.5,-48.83789 a 10.768909,10.768909 0 0 0 -7.15625,-1.76367 z M 118.47148,540.20859 a 21.58098,21.58098 0 0 1 21.58008,21.58008 21.58098,21.58098 0 0 1 -21.58008,21.58203 21.58098,21.58098 0 0 1 -21.582027,-21.58203 21.58098,21.58098 0 0 1 21.582027,-21.58008 z"
|
||||
transform="matrix(0.96572879,0,0,0.96572879,18.08261,22.951217)" />
|
||||
</g>
|
||||
<path
|
||||
d="M 23.77,11.079 C 23.701,11.034 22.272,10.132 20.921,10.347 20.594,8.978 19.338,8.125 19.276,8.084 a 0.499,0.499 0 0 0 -0.638,0.069 c -0.1,0.104 -0.967,1.074 -0.722,3.282 A 0.52,0.52 0 0 1 17.79,11.846 0.447,0.447 0 0 1 17.448,12 H 17 V 9.5 A 0.5,0.5 0 0 0 16.5,9 H 14 V 3.5 A 0.5,0.5 0 0 0 13.5,3 h -3 A 0.5,0.5 0 0 0 10,3.5 V 6 H 4.5 A 0.5,0.5 0 0 0 4,6.5 V 9 H 1.5 A 0.5,0.5 0 0 0 1,9.5 V 12 H 0.5 A 0.5,0.5 0 0 0 0,12.5 c 0,7.672 5.595,8.5 8,8.5 6.193,0 10.498,-3.762 12.377,-7.077 2.476,-0.395 3.509,-2.094 3.554,-2.168 A 0.501,0.501 0 0 0 23.77,11.079 Z"
|
||||
fill="url(#a)"
|
||||
id="path20"
|
||||
transform="matrix(20.841374,0,0,20.841374,27.431789,232.02503)" />
|
||||
</svg>
|
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 52 KiB |
|
@ -0,0 +1,126 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="512"
|
||||
viewBox="20.7 244.9 512 512"
|
||||
width="512"
|
||||
version="1.1"
|
||||
id="svg10"
|
||||
sodipodi:docname="dmo-logo.svg"
|
||||
inkscape:version="1.1-alpha (91d7437e58, 2021-02-12, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview33"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
objecttolerance="10.0"
|
||||
gridtolerance="10.0"
|
||||
guidetolerance="10.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.5996094"
|
||||
inkscape:cx="195.04762"
|
||||
inkscape:cy="256.31258"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1005"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg10" />
|
||||
<defs
|
||||
id="defs14">
|
||||
<linearGradient
|
||||
id="linearGradient4612">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4610" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4588">
|
||||
<stop
|
||||
style="stop-color:#ff0000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4586" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4528">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4526" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4348">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4346" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g
|
||||
id="g12293">
|
||||
<g
|
||||
fill="none"
|
||||
stroke-miterlimit="10"
|
||||
id="g8-7"
|
||||
transform="matrix(2.5135595,0,0,2.5135595,28.114541,224.41955)"
|
||||
style="opacity:1">
|
||||
<path
|
||||
d="m 37.133,105.617 h 68.54 L 133.68,94.983 a 3.822,3.822 0 0 0 2.216,-4.93 L 113.933,32.2 a 3.821,3.821 0 0 0 -4.93,-2.216 l -84.34,32.021 a 3.821,3.821 0 0 0 -2.215,4.93 z"
|
||||
fill="#f3ac47"
|
||||
id="path2-3" />
|
||||
<path
|
||||
d="m 105.674,105.617 28.009,-10.634 a 3.8,3.8 0 0 0 2.073,-1.907 L 76.443,71.304 57.995,105.617 Z"
|
||||
fill="#f19a3d"
|
||||
id="path4" />
|
||||
<path
|
||||
d="m 22.386,64.451 59.517,21.233 29.918,-55.65 a 3.799,3.799 0 0 0 -2.817,-0.05 l -84.34,32.022 a 3.796,3.796 0 0 0 -2.278,2.445 z"
|
||||
fill="#ffd15c"
|
||||
id="path6-6" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(20.919594,0,0,20.919594,26.175505,230.10256)"
|
||||
id="g22"
|
||||
style="opacity:1">
|
||||
<path
|
||||
d="M 23.77,11.079 C 23.701,11.034 22.272,10.132 20.921,10.347 20.594,8.978 19.338,8.125 19.276,8.084 a 0.499,0.499 0 0 0 -0.638,0.069 c -0.1,0.104 -0.967,1.074 -0.722,3.282 A 0.52,0.52 0 0 1 17.79,11.846 0.447,0.447 0 0 1 17.448,12 H 0.5 A 0.5,0.5 0 0 0 0,12.5 c 0,7.672 5.595,8.5 8,8.5 6.193,0 10.498,-3.762 12.377,-7.077 2.476,-0.395 3.509,-2.094 3.554,-2.168 A 0.501,0.501 0 0 0 23.77,11.079 Z"
|
||||
fill="#303c42"
|
||||
id="path10-5" />
|
||||
<path
|
||||
d="M 20.002,12.965 A 0.5,0.5 0 0 0 19.62,13.226 C 17.942,16.351 13.891,20 8,20 5.454,20 1.228,19.076 1.009,13 h 16.439 c 0.422,0 0.809,-0.173 1.089,-0.486 0.287,-0.321 0.424,-0.754 0.375,-1.189 -0.113,-1.02 0.053,-1.687 0.214,-2.069 C 19.501,9.617 20,10.241 20,11 c 0,0.173 0.09,0.334 0.237,0.426 a 0.503,0.503 0 0 0 0.486,0.021 c 0.595,-0.3 1.44,-0.048 2.015,0.212 -0.432,0.455 -1.301,1.138 -2.736,1.306 z"
|
||||
fill="#42a5f5"
|
||||
id="path12" />
|
||||
<path
|
||||
d="M 20.002,12.465 A 0.5,0.5 0 0 0 19.62,12.726 C 17.942,15.851 13.891,19.5 8,19.5 5.528,19.5 1.482,18.614 1.049,13 h -0.04 c 0.218,6.076 4.445,7 6.991,7 5.892,0 9.942,-3.649 11.62,-6.774 a 0.5,0.5 0 0 1 0.382,-0.261 c 1.436,-0.168 2.305,-0.851 2.736,-1.306 a 4.894,4.894 0 0 0 -0.384,-0.15 c -0.492,0.399 -1.254,0.827 -2.352,0.956 z"
|
||||
opacity="0.1"
|
||||
id="path14" />
|
||||
<circle
|
||||
cx="5.0409999"
|
||||
cy="16"
|
||||
r="1"
|
||||
fill="#303c42"
|
||||
id="circle16" />
|
||||
<circle
|
||||
cx="5.4689999"
|
||||
cy="15.729"
|
||||
r="0.32300001"
|
||||
fill="#ffffff"
|
||||
id="circle18" />
|
||||
<path
|
||||
d="M 23.77,11.079 C 23.701,11.034 22.272,10.132 20.921,10.347 20.594,8.978 19.338,8.125 19.276,8.084 a 0.499,0.499 0 0 0 -0.638,0.069 c -0.1,0.104 -0.967,1.074 -0.722,3.282 A 0.52,0.52 0 0 1 17.79,11.846 0.447,0.447 0 0 1 17.448,12 H 17 V 9.5 A 0.5,0.5 0 0 0 16.5,9 H 14 V 3.5 A 0.5,0.5 0 0 0 13.5,3 h -3 A 0.5,0.5 0 0 0 10,3.5 V 6 H 4.5 A 0.5,0.5 0 0 0 4,6.5 V 9 H 1.5 A 0.5,0.5 0 0 0 1,9.5 V 12 H 0.5 A 0.5,0.5 0 0 0 0,12.5 c 0,7.672 5.595,8.5 8,8.5 6.193,0 10.498,-3.762 12.377,-7.077 2.476,-0.395 3.509,-2.094 3.554,-2.168 A 0.501,0.501 0 0 0 23.77,11.079 Z"
|
||||
fill="url(#a)"
|
||||
id="path20-6" />
|
||||
</g>
|
||||
</g>
|
||||
<path
|
||||
d="M 23.77,11.079 C 23.701,11.034 22.272,10.132 20.921,10.347 20.594,8.978 19.338,8.125 19.276,8.084 a 0.499,0.499 0 0 0 -0.638,0.069 c -0.1,0.104 -0.967,1.074 -0.722,3.282 A 0.52,0.52 0 0 1 17.79,11.846 0.447,0.447 0 0 1 17.448,12 H 17 V 9.5 A 0.5,0.5 0 0 0 16.5,9 H 14 V 3.5 A 0.5,0.5 0 0 0 13.5,3 h -3 A 0.5,0.5 0 0 0 10,3.5 V 6 H 4.5 A 0.5,0.5 0 0 0 4,6.5 V 9 H 1.5 A 0.5,0.5 0 0 0 1,9.5 V 12 H 0.5 A 0.5,0.5 0 0 0 0,12.5 c 0,7.672 5.595,8.5 8,8.5 6.193,0 10.498,-3.762 12.377,-7.077 2.476,-0.395 3.509,-2.094 3.554,-2.168 A 0.501,0.501 0 0 0 23.77,11.079 Z"
|
||||
fill="url(#a)"
|
||||
id="path20"
|
||||
transform="matrix(20.841374,0,0,20.841374,27.431789,232.02503)" />
|
||||
</svg>
|
After Width: | Height: | Size: 5.6 KiB |
|
@ -1,252 +0,0 @@
|
|||
# Account Management - Overview
|
||||
|
||||
This page provides a technical reference for account management in DMS.
|
||||
|
||||
!!! note "Account provisioners and alternative authentication support"
|
||||
|
||||
Each [`ACCOUNT_PROVISIONER`][docs::env::account-provisioner] has a separate page for configuration guidance and caveats:
|
||||
|
||||
- [`FILE` provisioner docs][docs::account-provisioner::file]
|
||||
- [`LDAP` provisioner docs][docs::account-provisioner::ldap]
|
||||
|
||||
Authentication from the provisioner can be supplemented with additional methods:
|
||||
|
||||
- [OAuth2 / OIDC][docs::account-auth::oauth2] (_allow login from an external authentication service_)
|
||||
- [Master Accounts][docs::account-auth::master-accounts] (_access the mailbox of any DMS account_)
|
||||
|
||||
---
|
||||
|
||||
For custom authentication requirements, you could [implement this with Lua][docs::examples::auth-lua].
|
||||
|
||||
## Accounts
|
||||
|
||||
!!! info
|
||||
|
||||
To receive or send mail, you'll need to provision user accounts into DMS (_as each provisioner page documents_).
|
||||
|
||||
---
|
||||
|
||||
A DMS account represents a user with their _login username_ + password, and optional config like aliases and quota.
|
||||
|
||||
- Sending mail from different addresses **does not require** aliases or separate accounts.
|
||||
- Each account is configured with a _primary email address_ that a mailbox is associated to.
|
||||
|
||||
??? info "Primary email address"
|
||||
|
||||
The email address associated to an account creates a mailbox. This address is relevant:
|
||||
|
||||
- When DMS **receives mail** for that address as the recipient (_or an alias that resolves to it_), to identify which mailbox to deliver into.
|
||||
- With **mail submission**:
|
||||
- `SPOOF_PROTECTION=1` **restricts the sender address** to the DMS account email address (_unless additional sender addresses have been permitted via supported config_).
|
||||
- `SPOOF_PROTECTION=0` allows DMS accounts to **use any sender address** (_only a single DMS account is necessary to send mail with different sender addresses_).
|
||||
|
||||
---
|
||||
|
||||
For more details, see the [Technical Overview](#technical-overview) section.
|
||||
|
||||
??? note "Support for multiple mail domains"
|
||||
|
||||
No extra configuration in DMS is required after provisioning an account with an email address.
|
||||
|
||||
- The DNS records for a domain should direct mail to DMS and allow DMS to send mail on behalf of that domain.
|
||||
- DMS does not need TLS certificates for your mail domains, only for the DMS FQDN (_the `hostname` setting_).
|
||||
|
||||
??? warning "Choosing a compatible email address"
|
||||
|
||||
An email address should conform to the standard [permitted charset and format][email-syntax::valid-charset-format] (`local-part@domain-part`).
|
||||
|
||||
---
|
||||
|
||||
DMS has features that need to reserve special characters to work correctly. Ensure those characters are not present in email addresses you configure for DMS, otherwise disable / opt-out of the feature.
|
||||
|
||||
- [Sub-addressing](#sub-addressing) is enabled by default with `+` as the _tag delimiter_. The tag can be changed, feature opt-out when the tag is explicitly unset.
|
||||
|
||||
### Aliases
|
||||
|
||||
!!! info
|
||||
|
||||
Aliases allow receiving mail:
|
||||
|
||||
- As an alternative delivery address for a DMS account mailbox.
|
||||
- To redirect / forward to an external address outside of DMS like `@gmail.com`.
|
||||
|
||||
??? abstract "Technical Details (_Local vs Virtual aliases_)"
|
||||
|
||||
Aliases are managed through Postfix which supports _local_ and _virtual_ aliases:
|
||||
|
||||
- **Local aliases** are for mail routed to the [`local` delivery agent][postfix::delivery-agent::local] (see [associated alias config format][postfix::config-table::local-alias])
|
||||
- You rarely need to configure this. It is used internally for system unix accounts belonging to the services running in DMS (_including `root`_).
|
||||
- `postmaster` may be a local alias to `root`, and `root` to a virtual alias or real email address.
|
||||
- Any mail sent through the `local` delivery agent will not be delivered to an inbox managed by Dovecot (_unless you have configured a local alias to redirect mail to a valid address or alias_).
|
||||
- The domain-part of an these aliases belongs to your DMS FQDN (_`hostname: mail.example.com`, thus `user@mail.example.com`_). Technically there is no domain-part at this point, that context is used when routing delivery, the local delivery agent only knows of the local-part (_an alias or unix account_).
|
||||
- [**Virtual aliases**][postfix-docs::virtual-alias] are for mail routed to the [`virtual` delivery agent][postfix::delivery-agent::virtual] (see [associated alias config format][postfix::config-table::virtual-alias])
|
||||
- When alias support in DMS is discussed without the context of being a local or virtual alias, it's likely the virtual kind (_but could also be agnostic_).
|
||||
- The domain-part of an these aliases belongs to a mail domain managed by DMS (_like `user@example.com`_).
|
||||
|
||||
!!! tip "Verify alias resolves correctly"
|
||||
|
||||
You can run `postmap -q <alias> <table>` in the container to verify an alias resolves to the expected target. If the target is also an alias, the command will not expand that alias to resolve the actual recipient(s).
|
||||
|
||||
For the `FILE` provisioner, an example would be: `postmap -q alias1@example.com /etc/postfix/virtual`. For the `LDAP` provisioner you'd need to adjust the table path.
|
||||
|
||||
!!! info "Side effect - Dovecot Quotas (`ENABLE_QUOTAS=1`)"
|
||||
|
||||
As a side effect of the alias workaround for the `FILE` provisioner with this feature, aliases can be used for account login. This is not intentional.
|
||||
|
||||
### Quotas
|
||||
|
||||
!!! info
|
||||
|
||||
Enables mail clients with the capability to query a mailbox for disk-space used and capacity limit.
|
||||
|
||||
- This feature is enabled by default, opt-out via [`ENABLE_QUOTAS=0`][docs::env::enable-quotas]
|
||||
- **Not implemented** for the LDAP provisioner (_PR welcome! View the [feature request for implementation advice][gh-issue::dms-feature-request::dovecot-quotas-ldap]_)
|
||||
|
||||
??? tip "How are quotas useful?"
|
||||
|
||||
Without quota limits for disk storage, a mailbox could fill up the available storage which would cause delivery failures to all mailboxes.
|
||||
|
||||
Quotas help by preventing that abuse, so that only a mailbox exceeding the assigned quota experiences a delivery failure instead of negatively impacting others (_provided disk space is available_).
|
||||
|
||||
??? abstract "Technical Details"
|
||||
|
||||
The [Dovecot Quotas feature][gh-pr::dms-feature::dovecot-quotas] is configured by enabling the [Dovecot `imap-quota` plugin][dovecot-docs::plugin::imap-quota] and using the [`count` quota backend][dovecot-docs::config::quota-backend-count].
|
||||
|
||||
---
|
||||
|
||||
**Dovecot workaround for Postfix aliases**
|
||||
|
||||
When mail is delivered to DMS, Postfix will query Dovecot with the recipient(s) to verify quota has not been exceeded.
|
||||
|
||||
This allows early rejection of mail arriving to DMS, preventing a spammer from taking advantage of a [backscatter][wikipedia::backscatter] source if the mail was accepted by Postfix, only to later be rejected by Dovecot for storage when the quota limit was already reached.
|
||||
|
||||
However, Postfix does not resolve aliases until after the incoming mail is accepted.
|
||||
|
||||
1. Postfix queries Dovecot (_a [`check_policy_service` restriction tied to the Dovecot `quota-status` service][dms::workaround::dovecot-quotas::notes-1]_) with the recipient (_the alias_).
|
||||
2. `dovecot: auth: passwd-file(alias@example.com): unknown user` is logged, Postfix is then informed that the recipient mailbox is not full even if it actually was (_since no such user exists in the Dovecot UserDB_).
|
||||
3. However, when the real mailbox address that the alias would later resolve into does have a quota that exceeded the configured limit, Dovecot will refuse the mail delivery from Postfix which introduces a backscatter source for spammers.
|
||||
|
||||
As a [workaround to this problem with the `ENABLE_QUOTAS=1` feature][dms::workaround::dovecot-quotas::summary], DMS will add aliases as fake users into Dovecot UserDB (_that are configured with the same data as the real address the alias would resolve to, thus sharing the same mailbox location and quota limit_). This allows Postfix to properly be aware of an aliased mailbox having exceeded the allowed quota.
|
||||
|
||||
**NOTE:** This workaround **only supports** aliases to a single target recipient of a real account address / mailbox.
|
||||
|
||||
- Additionally, aliases that resolve to another alias or to an external address would both fail the UserDB lookup, unable to determine if enough storage is available.
|
||||
- A proper fix would [implement a Postfix policy service][dms::workaround::dovecot-quotas::notes-2] that could correctly resolve aliases to valid entries in the Dovecot UserDB, querying the `quota-status` service and returning that response to Postfix.
|
||||
|
||||
## Sub-addressing
|
||||
|
||||
!!! info
|
||||
|
||||
[Subaddressing][wikipedia::subaddressing] (_aka **Plus Addressing** or **Address Tags**_) is a feature that allows you to receive mail to an address which includes a tag appended to the `local-part` of a valid account address.
|
||||
|
||||
- A subaddress has a tag delimiter (_default: `+`_), followed by the tag: `<local-part>+<tag>@<domain-part>`
|
||||
- The subaddress `user+github@example.com` would deliver mail to the same mailbox as `user@example.com`.
|
||||
- Tags are dynamic. Anything between the `+` and `@` is understood as the tag, no additional configuration required.
|
||||
- Only the first occurence of the tag delimiter is recognized. Any additional occurences become part of the tag value itself.
|
||||
|
||||
??? tip "When is subaddressing useful?"
|
||||
|
||||
A common use-case is to use a unique tag for each service you register your email address with.
|
||||
|
||||
- Routing delivery to different folders in your mailbox based on the tag (_via a [Sieve filter][docs::sieve::subaddressing]_).
|
||||
- Data leaks or bulk sales of email addresses.
|
||||
- If spam / phishing mail you receive has not removed the tag, you will have better insight into where your address was compromised from.
|
||||
- When the expected tag is missing, this additionally helps identify bad actors. Especially when mail delivery is routed to subfolders by tag.
|
||||
- For more use-cases, view the end of [this article][web::subaddress-use-cases].
|
||||
|
||||
??? tip "Changing the tag delimiter"
|
||||
|
||||
Add `recipient_delimiter = +` to these config override files (_replacing `+` with your preferred delimiter_):
|
||||
|
||||
- Postfix: `docker-data/dms/config/postfix-main.cf`
|
||||
- Dovecot: `docker-data/dms/config/dovecot.cf`
|
||||
|
||||
??? tip "Opt-out of subaddressing"
|
||||
|
||||
Follow the advice to change the tag delimiter, but instead set an empty value (`recipient_delimiter =`).
|
||||
|
||||
??? warning "Only for receiving, not sending"
|
||||
|
||||
Do not attempt to send mail from these tagged addresses, they are not equivalent to aliases.
|
||||
|
||||
This feature is only intended to be used when a mail client sends to a DMS managed recipient address. While DMS does not restrict the sender address you choose to send mail from (_provided `SPOOF_PROTECTION` has not been enabled_), it is often [forbidden by mail services][ms-exchange-docs::limitations].
|
||||
|
||||
??? abstract "Technical Details"
|
||||
|
||||
The configured tag delimiter (`+`) allows both Postfix and Dovecot to recognize subaddresses. Without this feature configured, the subaddresses would be considered as separate mail accounts rather than routed to a common account address.
|
||||
|
||||
---
|
||||
|
||||
Internally DMS has the tag delimiter configured by:
|
||||
|
||||
- Applying the Postfix `main.cf` setting: [`recipient_delimiter = +`][postfix-docs::recipient-delimiter]
|
||||
- Dovecot has the equivalent setting set as `+` by default: [`recipient_delimiter = +`][dovecot-docs::config::recipient-delimiter]
|
||||
|
||||
## Technical Overview
|
||||
|
||||
!!! info
|
||||
|
||||
This section provides insight for understanding how Postfix and Dovecot services are involved. It is intended as a reference for maintainers and contributors.
|
||||
|
||||
- **Postfix** - Handles when mail is delivered (inbound) to DMS, or sent (outbound) from DMS.
|
||||
- **Dovecot** - Manages access and storage for mail delivered to the DMS account mailboxes of your users.
|
||||
|
||||
??? abstract "Technical Details - Postfix (Inbound vs Outbound)"
|
||||
|
||||
Postfix needs to know how to handle inbound and outbound mail by asking these queries:
|
||||
|
||||
=== "Inbound"
|
||||
|
||||
- What mail domains is DMS responsible for handling? (_for accepting mail delivered_)
|
||||
- What are valid mail addresses for those mail domains? (_reject delivery for users that don't exist_)
|
||||
- Are there any aliases to redirect mail to 1 or more users, or forward to externally?
|
||||
|
||||
=== "Outbound"
|
||||
|
||||
- When `SPOOF_PROTECTION=1`, how should DMS restrict the sender address? (_eg: Users may only send mail from their associated mailbox address_)
|
||||
|
||||
??? abstract "Technical Details - Dovecot (Authentication)"
|
||||
|
||||
Dovecot additionally handles authenticating user accounts for sending and retrieving mail:
|
||||
|
||||
- Over the ports for IMAP and POP3 connections (_110, 143, 993, 995_).
|
||||
- As the default configured SASL provider, which Postfix delegates user authentication through (_for the submission(s) ports 465 & 587_). Saslauthd can be configured as an alternative SASL provider.
|
||||
|
||||
Dovecot splits all authentication lookups into two categories:
|
||||
|
||||
- A [PassDB][dovecot::docs::passdb] lookup most importantly authenticates the user. It may also provide any other necessary pre-login information.
|
||||
- A [UserDB][dovecot::docs::userdb] lookup retrieves post-login information specific to a user.
|
||||
|
||||
[docs::env::account-provisioner]: ../environment.md#account_provisioner
|
||||
[docs::account-provisioner::file]: ./provisioner/file.md
|
||||
[docs::account-provisioner::ldap]: ./provisioner/ldap.md
|
||||
[docs::account-auth::oauth2]: ./supplementary/oauth2.md
|
||||
[docs::account-auth::master-accounts]: ./supplementary/master-accounts.md
|
||||
[docs::examples::auth-lua]: ../../examples/use-cases/auth-lua.md
|
||||
[email-syntax::valid-charset-format]: https://stackoverflow.com/questions/2049502/what-characters-are-allowed-in-an-email-address/2049510#2049510
|
||||
|
||||
[postfix-docs::virtual-alias]: http://www.postfix.org/VIRTUAL_README.html#virtual_alias
|
||||
[postfix-docs::recipient-delimiter]: http://www.postfix.org/postconf.5.html#recipient_delimiter
|
||||
[dovecot-docs::config::recipient-delimiter]: https://doc.dovecot.org/settings/core/#core_setting-recipient_delimiter
|
||||
[postfix::delivery-agent::local]: https://www.postfix.org/local.8.html
|
||||
[postfix::delivery-agent::virtual]: https://www.postfix.org/virtual.8.html
|
||||
[postfix::config-table::local-alias]: https://www.postfix.org/aliases.5.html
|
||||
[postfix::config-table::virtual-alias]: https://www.postfix.org/virtual.5.html
|
||||
|
||||
[docs::env::enable-quotas]: ../environment.md#enable_quotas
|
||||
[gh-issue::dms-feature-request::dovecot-quotas-ldap]: https://github.com/docker-mailserver/docker-mailserver/issues/2957
|
||||
[dovecot-docs::config::quota-backend-count]: https://doc.dovecot.org/configuration_manual/quota/quota_count/#quota-backend-count
|
||||
[dovecot-docs::plugin::imap-quota]: https://doc.dovecot.org/settings/plugin/imap-quota-plugin/
|
||||
[gh-pr::dms-feature::dovecot-quotas]: https://github.com/docker-mailserver/docker-mailserver/pull/1469
|
||||
[wikipedia::backscatter]: https://en.wikipedia.org/wiki/Backscatter_%28email%29
|
||||
[dms::workaround::dovecot-quotas::notes-1]: https://github.com/docker-mailserver/docker-mailserver/issues/2091#issuecomment-954298788
|
||||
[dms::workaround::dovecot-quotas::notes-2]: https://github.com/docker-mailserver/docker-mailserver/pull/2248#issuecomment-953754532
|
||||
[dms::workaround::dovecot-quotas::summary]: https://github.com/docker-mailserver/docker-mailserver/pull/2248#issuecomment-955088677
|
||||
|
||||
[docs::sieve::subaddressing]: ../advanced/mail-sieve.md#subaddress-mailbox-routing
|
||||
[web::subaddress-use-cases]: https://www.codetwo.com/admins-blog/plus-addressing
|
||||
[wikipedia::subaddressing]: https://en.wikipedia.org/wiki/Email_address#Sub-addressing
|
||||
[ms-exchange-docs::limitations]: https://learn.microsoft.com/en-us/exchange/recipients-in-exchange-online/plus-addressing-in-exchange-online#using-plus-addresses
|
||||
|
||||
[dovecot::docs::passdb]: https://doc.dovecot.org/configuration_manual/authentication/password_databases_passdb
|
||||
[dovecot::docs::userdb]: https://doc.dovecot.org/configuration_manual/authentication/user_databases_userdb
|
|
@ -1,206 +0,0 @@
|
|||
---
|
||||
title: 'Account Management | Provisioner (File)'
|
||||
---
|
||||
|
||||
# Provisioner - File
|
||||
|
||||
## Management via the `setup` CLI
|
||||
|
||||
The best way to manage DMS accounts and related config files is through our `setup` CLI provided within the container.
|
||||
|
||||
!!! example "Using the `setup` CLI"
|
||||
|
||||
Try the following within the DMS container (`docker exec -it <CONTAINER NAME> bash`):
|
||||
|
||||
- Add an account: `setup email add <EMAIL ADDRESS>`
|
||||
- Add an alias: `setup alias add <FROM ALIAS> <TO TARGET ADDRESS>`
|
||||
- Learn more about the available subcommands via: `setup help`
|
||||
|
||||
```bash
|
||||
# Starts a basic DMS instance and then shells into the container to use the `setup` CLI:
|
||||
docker run --rm -itd --name dms --hostname mail.example.com mailserver/docker-mailserver
|
||||
docker exec -it dms bash
|
||||
|
||||
# Create an account:
|
||||
setup email add hello@example.com your-password-here
|
||||
|
||||
# Create an alias:
|
||||
setup alias add your-alias-here@example.com hello@example.com
|
||||
|
||||
# Limit the mailbox capacity to 10 MiB:
|
||||
setup quota set hello@example.com 10M
|
||||
```
|
||||
|
||||
??? tip "Secure password input"
|
||||
|
||||
When you don't provide a password to the command, you will be prompted for one. This avoids the password being captured in your shell history.
|
||||
|
||||
```bash
|
||||
# As you input your password it will not update.
|
||||
# Press the ENTER key to apply the hidden password input.
|
||||
$ setup email add hello@example.com
|
||||
Enter Password:
|
||||
Confirm Password:
|
||||
```
|
||||
|
||||
!!! note "Account removal via `setup email del`"
|
||||
|
||||
When you remove a DMS account with this command, it will also remove any associated aliases and quota.
|
||||
|
||||
The command will also prompt for deleting the account mailbox from disk, or can be forced with the `-y` flag.
|
||||
|
||||
## Config Reference
|
||||
|
||||
These config files belong to the [Config Volume][docs::volumes::config].
|
||||
|
||||
### Accounts
|
||||
|
||||
!!! info
|
||||
|
||||
**Config file:** `docker-data/dms/config/postfix-accounts.cf`
|
||||
|
||||
---
|
||||
|
||||
The config format is line-based with two fields separated by the delimiter `|`:
|
||||
|
||||
- **User:** The primary email address for the account mailbox to use.
|
||||
- **Password:** A SHA512-CRYPT hash of the account password (_in this example it is `secret`_).
|
||||
|
||||
??? tip "Password hash without the `setup email add` command"
|
||||
|
||||
A compatible password hash can be generated with:
|
||||
|
||||
```bash
|
||||
doveadm pw -s SHA512-CRYPT -u hello@example.com -p secret
|
||||
```
|
||||
|
||||
!!! example "`postfix-accounts.cf` config file"
|
||||
|
||||
In this example DMS manages mail for the domain `example.com`:
|
||||
|
||||
```cf title="postfix-accounts.cf"
|
||||
hello@example.com|{SHA512-CRYPT}$6$W4rxRQwI6HNMt9n3$riCi5/OqUxnU8eZsOlZwoCnrNgu1gBGPkJc.ER.LhJCu7sOg9i1kBrRIistlBIp938GdBgMlYuoXYUU5A4Qiv0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Dovecot "extra fields"**
|
||||
|
||||
[Appending a third column will customize "extra fields"][gh-issue::provisioner-file::accounts-extra-fields] when converting account data into a Dovecot UserDB entry.
|
||||
|
||||
DMS is not aware of these customizations beyond carrying them over, expect potential for bugs when this feature breaks any assumed conventions used in the scripts (_such as changing the mailbox path or type_).
|
||||
|
||||
!!! note
|
||||
|
||||
Account creation will normalize the provided email address to lowercase, as DMS does not support multiple case-sensitive address variants.
|
||||
|
||||
The email address chosen will also represent the _login username_ credential for mail clients to authenticate with.
|
||||
|
||||
### Aliases
|
||||
|
||||
!!! info
|
||||
|
||||
**Config file:** `docker-data/dms/config/postfix-virtual.cf`
|
||||
|
||||
---
|
||||
|
||||
The config format is line-based with key value pairs (**alias** --> **target address**), with white-space as a delimiter.
|
||||
|
||||
!!! example "`postfix-virtual.cf` config file"
|
||||
|
||||
In this example DMS manages mail for the domain `example.com`:
|
||||
|
||||
```cf-extra title="postfix-virtual.cf"
|
||||
# Alias delivers to an existing account:
|
||||
alias1@example.com hello@example.com
|
||||
|
||||
# Alias forwards to an external email address:
|
||||
alias2@example.com external-account@gmail.com
|
||||
```
|
||||
|
||||
??? warning "Known Issues"
|
||||
|
||||
**`setup` CLI prevents an alias and account sharing an address:**
|
||||
|
||||
You cannot presently add a new account (`setup email add`) or alias (`setup alias add`) with an address which already exists as an alias or account in DMS.
|
||||
|
||||
This [restriction was enforced][gh-issue::bugs::account-alias-overlap] due to [problems it could cause][gh-issue::bugs::account-alias-overlap-problem], although there are [use-cases where you may legitimately require this functionality][gh-issue::feature-request::allow-account-alias-overlap].
|
||||
|
||||
For now you must manually edit the `postfix-virtual.cf` file as a workaround. There are no run-time checks outside of the `setup` CLI related to this restriction.
|
||||
|
||||
---
|
||||
|
||||
**Wildcard catch-all support (`@example.com`):**
|
||||
|
||||
While this type of alias without a local-part is supported, you must keep in mind that aliases in Postfix have a higher precedence than a real address associated to a DMS account.
|
||||
|
||||
As a result, the wildcard is matched first and will direct mail for that entire domain to the alias target address. To work around this, [you will need an alias for each non-alias address of that domain][gh-issue::bugs::wildcard-catchall].
|
||||
|
||||
Additionally, Postfix will read the alias config and choose the alias value that matches the recipient address first. Ensure your more specific aliases for the domain are declared above the wildcard alias in the config file.
|
||||
|
||||
---
|
||||
|
||||
**Aliasing to another alias or multiple recipients:**
|
||||
|
||||
[While aliasing to multiple recipients is possible][gh-discussions::no-support::alias-multiple-targets], DMS does not officially support that.
|
||||
|
||||
- You may experience issues when our feature integrations don't expect more than one target per alias.
|
||||
- These concerns also apply to the usage of nested aliases (_where the recipient target provided is to an alias instead of a real address_). An example is the [incompatibility with `setup alias add`][gh-issue::bugs::alias-nested].
|
||||
|
||||
#### Configuring RegEx aliases
|
||||
|
||||
!!! info
|
||||
|
||||
**Config file:** `docker-data/dms/config/postfix-regexp.cf`
|
||||
|
||||
---
|
||||
|
||||
This config file is similar to the above `postfix-virtual.cf`, but the alias value is instead configured with a regex pattern.
|
||||
|
||||
There is **no `setup` CLI support** for this feature, it is config only.
|
||||
|
||||
!!! example "`postfix-regexp.cf` config file"
|
||||
|
||||
Deliver all mail for `test` users to `qa@example.com` instead:
|
||||
|
||||
```cf-extra title="postfix-regexp.cf"
|
||||
# Remember to escape regex tokens like `.` => `\.`, otherwise
|
||||
# your alias pattern may be more permissive than you intended:
|
||||
/^test[0-9][0-9]*@example\.com/ qa@example.com
|
||||
```
|
||||
|
||||
??? abstract "Technical Details"
|
||||
|
||||
`postfix-virtual.cf` has precedence, `postfix-regexp.cf` will only be checked if no alias match was found in `postfix-virtual.cf`.
|
||||
|
||||
These files are both copied internally to `/etc/postfix/` and configured in `main.cf` for the `virtual_alias_maps` setting. As `postfix-virtual.cf` is declared first for that setting, it will be processed before using `postfix-regexp.cf` as a fallback.
|
||||
|
||||
### Quotas
|
||||
|
||||
!!! info
|
||||
|
||||
**Config file:** `docker-data/dms/config/dovecot-quotas.cf`
|
||||
|
||||
----
|
||||
|
||||
The config format is line-based with two fields separated by the delimiter `:`:
|
||||
|
||||
- **Dovecot UserDB account:** The user DMS account. It should have a matching field in `postfix-accounts.cf`.
|
||||
- **Quota limit:** Expressed in bytes (_binary unit suffix is supported: `M` => `MiB`, `G` => `GiB`_).
|
||||
|
||||
!!! example "`dovecot-quotas.cf` config file"
|
||||
|
||||
For the account with the mailbox address of `hello@example.com`, it may not exceed 5 GiB in storage:
|
||||
|
||||
```cf-extra title="dovecot-quotas.cf"
|
||||
hello@example.com:5G
|
||||
```
|
||||
|
||||
[docs::volumes::config]: ../../advanced/optional-config.md#volumes-config
|
||||
[gh-issue::provisioner-file::accounts-extra-fields]: https://github.com/docker-mailserver/docker-mailserver/issues/4117
|
||||
[gh-issue::feature-request::allow-account-alias-overlap]: https://github.com/docker-mailserver/docker-mailserver/issues/3528
|
||||
[gh-issue::bugs::account-alias-overlap-problem]: https://github.com/docker-mailserver/docker-mailserver/issues/3350#issuecomment-1550528898
|
||||
[gh-issue::bugs::account-alias-overlap]: https://github.com/docker-mailserver/docker-mailserver/issues/3022#issuecomment-1807816689
|
||||
[gh-issue::bugs::wildcard-catchall]: https://github.com/docker-mailserver/docker-mailserver/issues/3022#issuecomment-1610452561
|
||||
[gh-issue::bugs::alias-nested]: https://github.com/docker-mailserver/docker-mailserver/issues/3622#issuecomment-1794504849
|
||||
[gh-discussions::no-support::alias-multiple-targets]: https://github.com/orgs/docker-mailserver/discussions/3805#discussioncomment-8215417
|
|
@ -1,308 +0,0 @@
|
|||
---
|
||||
title: 'Account Management | Provisioner (LDAP)'
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Getting started with ldap and DMS we need to take 3 parts in account:
|
||||
|
||||
- `postfix` for incoming & outgoing email
|
||||
- `dovecot` for accessing mailboxes
|
||||
- `saslauthd` for SMTP authentication (this can also be delegated to dovecot)
|
||||
|
||||
## Variables to Control Provisioning by the Container
|
||||
|
||||
Have a look at [the ENV page][docs-environment] for information on the default values.
|
||||
|
||||
### `LDAP_QUERY_FILTER_*`
|
||||
|
||||
Those variables contain the LDAP lookup filters for postfix, using `%s` as the placeholder for the domain or email address in question. This means that...
|
||||
|
||||
- ...for incoming email, the domain must return an entry for the `DOMAIN` filter (see [`virtual_alias_domains`](http://www.postfix.org/postconf.5.html#virtual_alias_domains)).
|
||||
- ...for incoming email, the inboxes which receive the email are chosen by the `USER`, `ALIAS` and `GROUP` filters.
|
||||
- The `USER` filter specifies personal mailboxes, for which only one should exist per address, for example `(mail=%s)` (also see [`virtual_mailbox_maps`](http://www.postfix.org/postconf.5.html#virtual_mailbox_maps))
|
||||
- The `ALIAS` filter specifies aliases for mailboxes, using [`virtual_alias_maps`](http://www.postfix.org/postconf.5.html#virtual_alias_maps), for example `(mailAlias=%s)`
|
||||
- The `GROUP` filter specifies the personal mailboxes in a group (for emails that multiple people shall receive), using [`virtual_alias_maps`](http://www.postfix.org/postconf.5.html#virtual_alias_maps), for example `(mailGroupMember=%s)`.
|
||||
- Technically, there is no difference between `ALIAS` and `GROUP`, but ideally you should use `ALIAS` for personal aliases for a singular person (like `ceo@example.org`) and `GROUP` for multiple people (like `hr@example.org`).
|
||||
- ...for outgoing email, the sender address is put through the `SENDERS` filter, and only if the authenticated user is one of the returned entries, the email can be sent.
|
||||
- This only applies if `SPOOF_PROTECTION=1`.
|
||||
- If the `SENDERS` filter is missing, the `USER`, `ALIAS` and `GROUP` filters will be used in a disjunction (OR).
|
||||
- To for example allow users from the `admin` group to spoof any sender email address, and to force everyone else to only use their personal mailbox address for outgoing email, you can use something like this: `(|(memberOf=cn=admin,*)(mail=%s))`
|
||||
|
||||
???+ example
|
||||
|
||||
A really simple `LDAP_QUERY_FILTER` configuration, using only the _user filter_ and allowing only `admin@*` to spoof any sender addresses.
|
||||
|
||||
```yaml
|
||||
- LDAP_START_TLS=yes
|
||||
- ACCOUNT_PROVISIONER=LDAP
|
||||
- LDAP_SERVER_HOST=ldap.example.org
|
||||
- LDAP_SEARCH_BASE=dc=example,dc=org"
|
||||
- LDAP_BIND_DN=cn=admin,dc=example,dc=org
|
||||
- LDAP_BIND_PW=mypassword
|
||||
- SPOOF_PROTECTION=1
|
||||
|
||||
- LDAP_QUERY_FILTER_DOMAIN=(mail=*@%s)
|
||||
- LDAP_QUERY_FILTER_USER=(mail=%s)
|
||||
- LDAP_QUERY_FILTER_ALIAS=(|) # doesn't match anything
|
||||
- LDAP_QUERY_FILTER_GROUP=(|) # doesn't match anything
|
||||
- LDAP_QUERY_FILTER_SENDERS=(|(mail=%s)(mail=admin@*))
|
||||
```
|
||||
|
||||
### `DOVECOT_*_FILTER` & `DOVECOT_*_ATTRS`
|
||||
|
||||
These variables specify the LDAP filters that dovecot uses to determine if a user can log in to their IMAP account, and which mailbox is responsible to receive email for a specific postfix user.
|
||||
|
||||
This is split into the following two lookups, both using `%u` as the placeholder for the full login name ([see dovecot documentation for a full list of placeholders](https://doc.dovecot.org/configuration_manual/config_file/config_variables/)). Usually you only need to set `DOVECOT_USER_FILTER`, in which case it will be used for both filters.
|
||||
|
||||
- `DOVECOT_USER_FILTER` is used to get the account details (uid, gid, home directory, quota, ...) of a user.
|
||||
- `DOVECOT_PASS_FILTER` is used to get the password information of the user, and is in pretty much all cases identical to `DOVECOT_USER_FILTER` (which is the default behavior if left away).
|
||||
|
||||
If your directory doesn't have the [postfix-book schema](https://github.com/variablenix/ldap-mail-schema/blob/master/postfix-book.schema) installed, then you must change the internal attribute handling for dovecot. For this you have to change the `pass_attr` and the `user_attr` mapping, as shown in the example below:
|
||||
|
||||
```yaml
|
||||
- DOVECOT_PASS_ATTRS=<YOUR_USER_IDENTIFIER_ATTRIBUTE>=user,<YOUR_USER_PASSWORD_ATTRIBUTE>=password
|
||||
- DOVECOT_USER_ATTRS=<YOUR_USER_HOME_DIRECTORY_ATTRIBUTE>=home,<YOUR_USER_MAILSTORE_ATTRIBUTE>=mail,<YOUR_USER_MAIL_UID_ATTRIBUTE>=uid,<YOUR_USER_MAIL_GID_ATTRIBUTE>=gid
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
For `DOVECOT_*_ATTRS`, you can replace `ldapAttr=dovecotAttr` with `=dovecotAttr=%{ldap:ldapAttr}` for more flexibility, like for example `=home=/var/mail/%{ldap:uid}` or just `=uid=5000`.
|
||||
|
||||
A list of dovecot attributes can be found [in the dovecot documentation](https://doc.dovecot.org/configuration_manual/authentication/user_databases_userdb/#authentication-user-database).
|
||||
|
||||
???+ example "Defaults"
|
||||
|
||||
```yaml
|
||||
- DOVECOT_USER_ATTRS=mailHomeDirectory=home,mailUidNumber=uid,mailGidNumber=gid,mailStorageDirectory=mail
|
||||
- DOVECOT_PASS_ATTRS=uniqueIdentifier=user,userPassword=password
|
||||
- DOVECOT_USER_FILTER=(&(objectClass=PostfixBookMailAccount)(uniqueIdentifier=%n))
|
||||
```
|
||||
|
||||
???+ example
|
||||
|
||||
Setup for a directory that has the [qmail-schema](https://github.com/amery/qmail/blob/master/qmail.schema) installed and uses `uid`:
|
||||
|
||||
```yaml
|
||||
- DOVECOT_PASS_ATTRS=uid=user,userPassword=password
|
||||
- DOVECOT_USER_ATTRS=homeDirectory=home,qmailUID=uid,qmailGID=gid,mailMessageStore=mail
|
||||
- DOVECOT_USER_FILTER=(&(objectClass=qmailUser)(uid=%u)(accountStatus=active))
|
||||
```
|
||||
|
||||
The LDAP server configuration for dovecot will be taken mostly from postfix, other options can be found in [the environment section in the docs][docs-environment].
|
||||
|
||||
### `DOVECOT_AUTH_BIND`
|
||||
|
||||
Set this to `yes` to enable authentication binds ([more details in the dovecot documentation](https://wiki.dovecot.org/AuthDatabase/LDAP/AuthBinds)). Currently, only DN lookup is supported without further changes to the configuration files, so this is only useful when you want to bind as a readonly user without the permission to read passwords.
|
||||
|
||||
### `SASLAUTHD_LDAP_FILTER`
|
||||
|
||||
This filter is used for `saslauthd`, which is called by postfix when someone is authenticating through SMTP (assuming that `SASLAUTHD_MECHANISMS=ldap` is being used). Note that you'll need to set up the LDAP server for saslauthd separately from postfix.
|
||||
|
||||
The filter variables are explained in detail [in the `LDAP_SASLAUTHD` file](https://github.com/winlibs/cyrus-sasl/blob/master/saslauthd/LDAP_SASLAUTHD#L121), but unfortunately, this method doesn't really support domains right now - that means that `%U` is the only token that makes sense in this variable.
|
||||
|
||||
!!! note "When to use this and how to avoid it"
|
||||
|
||||
Using a separate filter for SMTP authentication allows you to for example allow `noreply@example.org` to send email, but not log in to IMAP or receive email: `(&(mail=%U@example.org)(|(memberOf=cn=email,*)(mail=noreply@example.org)))`
|
||||
|
||||
If you don't want to use a separate filter for SMTP authentication, you can set `SASLAUTHD_MECHANISMS=rimap` and `SASLAUTHD_MECH_OPTIONS=127.0.0.1` to authenticate against dovecot instead - this means that the `DOVECOT_USER_FILTER` and `DOVECOT_PASS_FILTER` will be used for SMTP authentication as well.
|
||||
|
||||
???+ example "Configure LDAP with `saslauthd`"
|
||||
|
||||
```yaml
|
||||
- ENABLE_SASLAUTHD=1
|
||||
- SASLAUTHD_MECHANISMS=ldap
|
||||
- SASLAUTHD_LDAP_FILTER=(mail=%U@example.org)
|
||||
```
|
||||
|
||||
## Secure Connection with LDAPS or StartTLS
|
||||
|
||||
To enable LDAPS, all you need to do is to add the protocol to `LDAP_SERVER_HOST`, for example `ldaps://example.org:636`.
|
||||
|
||||
To enable LDAP over StartTLS (on port 389), you need to set the following environment variables instead (the **protocol must not be `ldaps://`** in this case!):
|
||||
|
||||
```yaml
|
||||
- LDAP_START_TLS=yes
|
||||
- DOVECOT_TLS=yes
|
||||
- SASLAUTHD_LDAP_START_TLS=yes
|
||||
```
|
||||
|
||||
## Active Directory Configurations (Tested with Samba4 AD Implementation)
|
||||
|
||||
In addition to LDAP explanation above, when Docker Mailserver is intended to be used with Active Directory (or the equivalent implementations like Samba4 AD DC) the following points should be taken into consideration:
|
||||
|
||||
- Samba4 Active Directory requires a **secure connection** to the domain controller (DC), either via SSL/TLS (LDAPS) or via StartTLS.
|
||||
- The username equivalent in Active Directory is: `sAMAccountName`.
|
||||
- `proxyAddresses` can be used to store email aliases of single users. The convention is to prefix the email aliases with `smtp:` (e.g: `smtp:some.name@example.com`).
|
||||
- Active Directory is used typically not only as LDAP Directory storage, but also as a _domain controller_, i.e., it will do many things including authenticating users. Mixing Linux and Windows clients requires the usage of [RFC2307 attributes](https://wiki.samba.org/index.php/Administer_Unix_Attributes_in_AD_using_samba-tool_and_ldb-tools), namely `uidNumber`, `gidNumber` instead of the typical `uid`. Assigning different owner to email folders can also be done in this approach, nevertheless [there is a bug at the moment in Docker Mailserver that overwrites all permissions](https://github.com/docker-mailserver/docker-mailserver/pull/2256) when starting the container. Either a manual fix is necessary now, or a temporary workaround to use a hard-coded `ldap:uidNumber` that equals to `5000` until this issue is fixed.
|
||||
- To deliver the emails to different members of Active Directory **Security Group** or **Distribution Group** (similar to mailing lists), use a [`user-patches.sh` script][docs-userpatches] to modify `ldap-groups.cf` so that it includes `leaf_result_attribute = mail` and `special_result_attribute = member`. This can be achieved simply by:
|
||||
|
||||
The configuration shown to get the Group to work is from [here](https://doc.zarafa.com/trunk/Administrator_Manual/en-US/html/_MTAIntegration.html) and [here](https://kb.kopano.io/display/WIKI/Postfix).
|
||||
|
||||
```bash
|
||||
# user-patches.sh
|
||||
|
||||
...
|
||||
grep -q '^leaf_result_attribute = mail$' /etc/postfix/ldap-groups.cf || echo "leaf_result_attribute = mail" >> /etc/postfix/ldap-groups.cf
|
||||
grep -q '^special_result_attribute = member$' /etc/postfix/ldap-groups.cf || echo "special_result_attribute = member" >> /etc/postfix/ldap-groups.cf
|
||||
...
|
||||
```
|
||||
|
||||
- In `/etc/ldap/ldap.conf`, if the `TLS_REQCERT` is `demand` / `hard` (default), the CA certificate used to verify the LDAP server certificate must be recognized as a trusted CA. This can be done by volume mounting the `ca.crt` file and updating the trust store via a `user-patches.sh` script:
|
||||
|
||||
```bash
|
||||
# user-patches.sh
|
||||
|
||||
...
|
||||
cp /MOUNTED_FOLDER/ca.crt /usr/local/share/ca-certificates/
|
||||
update-ca-certificates
|
||||
...
|
||||
```
|
||||
|
||||
The changes on the configurations necessary to work with Active Directory (**only changes are listed, the rest of the LDAP configuration can be taken from the other examples** shown in this documentation):
|
||||
|
||||
```yaml
|
||||
# If StartTLS is the chosen method to establish a secure connection with Active Directory.
|
||||
- LDAP_START_TLS=yes
|
||||
- SASLAUTHD_LDAP_START_TLS=yes
|
||||
- DOVECOT_TLS=yes
|
||||
|
||||
- LDAP_QUERY_FILTER_USER=(&(objectclass=person)(mail=%s))
|
||||
- LDAP_QUERY_FILTER_ALIAS=(&(objectclass=person)(proxyAddresses=smtp:%s))
|
||||
# Filters Active Directory groups (mail lists). Additional changes on ldap-groups.cf are also required as shown above.
|
||||
- LDAP_QUERY_FILTER_GROUP=(&(objectClass=group)(mail=%s))
|
||||
- LDAP_QUERY_FILTER_DOMAIN=(mail=*@%s)
|
||||
# Allows only Domain admins to send any sender email address, otherwise the sender address must match the LDAP attribute `mail`.
|
||||
- SPOOF_PROTECTION=1
|
||||
- LDAP_QUERY_FILTER_SENDERS=(|(mail=%s)(proxyAddresses=smtp:%s)(memberOf=cn=Domain Admins,cn=Users,dc=*))
|
||||
|
||||
- DOVECOT_USER_FILTER=(&(objectclass=person)(sAMAccountName=%n))
|
||||
# At the moment to be able to use %{ldap:uidNumber}, a manual bug fix as described above must be used. Otherwise %{ldap:uidNumber} %{ldap:uidNumber} must be replaced by the hard-coded value 5000.
|
||||
- DOVECOT_USER_ATTRS==uid=%{ldap:uidNumber},=gid=5000,=home=/var/mail/%Ln,=mail=maildir:~/Maildir
|
||||
- DOVECOT_PASS_ATTRS=sAMAccountName=user,userPassword=password
|
||||
- SASLAUTHD_LDAP_FILTER=(&(sAMAccountName=%U)(objectClass=person))
|
||||
```
|
||||
|
||||
## LDAP Setup Examples
|
||||
|
||||
???+ example "Basic Setup"
|
||||
|
||||
```yaml
|
||||
services:
|
||||
mailserver:
|
||||
image: ghcr.io/docker-mailserver/docker-mailserver:latest
|
||||
container_name: mailserver
|
||||
hostname: mail.example.com
|
||||
|
||||
ports:
|
||||
- "25:25"
|
||||
- "143:143"
|
||||
- "587:587"
|
||||
- "993:993"
|
||||
|
||||
volumes:
|
||||
- ./docker-data/dms/mail-data/:/var/mail/
|
||||
- ./docker-data/dms/mail-state/:/var/mail-state/
|
||||
- ./docker-data/dms/mail-logs/:/var/log/mail/
|
||||
- ./docker-data/dms/config/:/tmp/docker-mailserver/
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
|
||||
environment:
|
||||
- ENABLE_SPAMASSASSIN=1
|
||||
- ENABLE_CLAMAV=1
|
||||
- ENABLE_FAIL2BAN=1
|
||||
- ENABLE_POSTGREY=1
|
||||
|
||||
# >>> Postfix LDAP Integration
|
||||
- ACCOUNT_PROVISIONER=LDAP
|
||||
- LDAP_SERVER_HOST=ldap.example.org
|
||||
- LDAP_BIND_DN=cn=admin,ou=users,dc=example,dc=org
|
||||
- LDAP_BIND_PW=mypassword
|
||||
- LDAP_SEARCH_BASE=dc=example,dc=org
|
||||
- LDAP_QUERY_FILTER_DOMAIN=(|(mail=*@%s)(mailAlias=*@%s)(mailGroupMember=*@%s))
|
||||
- LDAP_QUERY_FILTER_USER=(&(objectClass=inetOrgPerson)(mail=%s))
|
||||
- LDAP_QUERY_FILTER_ALIAS=(&(objectClass=inetOrgPerson)(mailAlias=%s))
|
||||
- LDAP_QUERY_FILTER_GROUP=(&(objectClass=inetOrgPerson)(mailGroupMember=%s))
|
||||
- LDAP_QUERY_FILTER_SENDERS=(&(objectClass=inetOrgPerson)(|(mail=%s)(mailAlias=%s)(mailGroupMember=%s)))
|
||||
- SPOOF_PROTECTION=1
|
||||
# <<< Postfix LDAP Integration
|
||||
|
||||
# >>> Dovecot LDAP Integration
|
||||
- DOVECOT_USER_FILTER=(&(objectClass=inetOrgPerson)(mail=%u))
|
||||
- DOVECOT_PASS_ATTRS=uid=user,userPassword=password
|
||||
- DOVECOT_USER_ATTRS==home=/var/mail/%{ldap:uid},=mail=maildir:~/Maildir,uidNumber=uid,gidNumber=gid
|
||||
# <<< Dovecot LDAP Integration
|
||||
|
||||
# >>> SASL LDAP Authentication
|
||||
- ENABLE_SASLAUTHD=1
|
||||
- SASLAUTHD_MECHANISMS=ldap
|
||||
- SASLAUTHD_LDAP_FILTER=(&(mail=%U@example.org)(objectClass=inetOrgPerson))
|
||||
# <<< SASL LDAP Authentication
|
||||
|
||||
- SSL_TYPE=letsencrypt
|
||||
- PERMIT_DOCKER=host
|
||||
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
```
|
||||
|
||||
??? example "Kopano / Zarafa"
|
||||
|
||||
```yaml
|
||||
services:
|
||||
mailserver:
|
||||
image: ghcr.io/docker-mailserver/docker-mailserver:latest
|
||||
container_name: mailserver
|
||||
hostname: mail.example.com
|
||||
|
||||
ports:
|
||||
- "25:25"
|
||||
- "143:143"
|
||||
- "587:587"
|
||||
- "993:993"
|
||||
|
||||
volumes:
|
||||
- ./docker-data/dms/mail-data/:/var/mail/
|
||||
- ./docker-data/dms/mail-state/:/var/mail-state/
|
||||
- ./docker-data/dms/config/:/tmp/docker-mailserver/
|
||||
|
||||
environment:
|
||||
# We are not using dovecot here
|
||||
- SMTP_ONLY=1
|
||||
- ENABLE_SPAMASSASSIN=1
|
||||
- ENABLE_CLAMAV=1
|
||||
- ENABLE_FAIL2BAN=1
|
||||
- ENABLE_POSTGREY=1
|
||||
- SASLAUTHD_PASSWD=
|
||||
|
||||
# >>> SASL Authentication
|
||||
- ENABLE_SASLAUTHD=1
|
||||
- SASLAUTHD_LDAP_FILTER=(&(sAMAccountName=%U)(objectClass=person))
|
||||
- SASLAUTHD_MECHANISMS=ldap
|
||||
# <<< SASL Authentication
|
||||
|
||||
# >>> Postfix Ldap Integration
|
||||
- ACCOUNT_PROVISIONER=LDAP
|
||||
- LDAP_SERVER_HOST=<yourLdapContainer/yourLdapServer>
|
||||
- LDAP_SEARCH_BASE=dc=mydomain,dc=loc
|
||||
- LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=loc
|
||||
- LDAP_BIND_PW=mypassword
|
||||
- LDAP_QUERY_FILTER_USER=(&(objectClass=user)(mail=%s))
|
||||
- LDAP_QUERY_FILTER_GROUP=(&(objectclass=group)(mail=%s))
|
||||
- LDAP_QUERY_FILTER_ALIAS=(&(objectClass=user)(otherMailbox=%s))
|
||||
- LDAP_QUERY_FILTER_DOMAIN=(&(|(mail=*@%s)(mailalias=*@%s)(mailGroupMember=*@%s))(mailEnabled=TRUE))
|
||||
# <<< Postfix Ldap Integration
|
||||
|
||||
# >>> Kopano Integration
|
||||
- POSTFIX_DAGENT=lmtp:kopano:2003
|
||||
# <<< Kopano Integration
|
||||
|
||||
- SSL_TYPE=letsencrypt
|
||||
- PERMIT_DOCKER=host
|
||||
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
```
|
||||
|
||||
[docs-environment]: ../../environment.md
|
||||
[docs-userpatches]: ../../advanced/override-defaults/user-patches.md
|
|
@ -1,70 +0,0 @@
|
|||
---
|
||||
title: 'Account Management | Master Accounts (Dovecot)'
|
||||
hide:
|
||||
- toc # Hide Table of Contents for this page
|
||||
---
|
||||
|
||||
This feature is useful for administrative tasks like hot backups.
|
||||
|
||||
!!! note
|
||||
|
||||
This feature is presently [not supported with `ACCOUNT_PROVISIONER=LDAP`][dms::feature::dovecot-master-accounts::caveat-ldap].
|
||||
|
||||
!!! info
|
||||
|
||||
A _Master Account_:
|
||||
|
||||
- Can login as any user (DMS account) and access their mailbox.
|
||||
- Is not associated to a separate DMS account, nor is it a DMS account itself.
|
||||
|
||||
---
|
||||
|
||||
**`setup` CLI support**
|
||||
|
||||
Use the `setup dovecot-master <add|update|del|list>` commands. These are roughly equivalent to the `setup email` subcommands.
|
||||
|
||||
---
|
||||
|
||||
**Config file:** `docker-data/dms/config/dovecot-masters.cf`
|
||||
|
||||
The config format is the same as [`postfix-accounts.cf` for `ACCOUNT_PROVISIONER=FILE`][docs::account-management::file::accounts].
|
||||
|
||||
The only difference is the account field has no `@domain-part` suffix, it is only a username.
|
||||
|
||||
??? abstract "Technical Details"
|
||||
|
||||
[The _Master Accounts_ feature][dms::feature::dovecot-master-accounts] in DMS configures the [Dovecot Master Users][dovecot-docs::auth::master-users] feature with the Dovecot setting [`auth_master_user_separator`][dovecot-docs::config::auth-master-user-separator] (_where the default value is `*`_).
|
||||
|
||||
## Login via Master Account
|
||||
|
||||
!!! info
|
||||
|
||||
To login as another DMS account (`user@example.com`) with POP3 or IMAP, use the following credentials format:
|
||||
|
||||
- Username: `<LOGIN USERNAME>*<MASTER USER>` (`user@example.com*admin`)
|
||||
- Password: `<MASTER PASSWORD>`
|
||||
|
||||
!!! example "Verify login functionality"
|
||||
|
||||
In the DMS container, you can verify with the `testsaslauthd` command:
|
||||
|
||||
```bash
|
||||
# Prerequisites:
|
||||
# A regular DMS account to test login through a Master Account:
|
||||
setup email add user@example.com secret
|
||||
# Add a new Master Account:
|
||||
setup dovecot-master add admin top-secret
|
||||
```
|
||||
|
||||
```bash
|
||||
# Login with credentials format as described earlier:
|
||||
testsaslauthd -u 'user@example.com*admin' -p 'top-secret'
|
||||
```
|
||||
|
||||
Alternatively, any mail client should be able to login the equivalent credentials.
|
||||
|
||||
[dms::feature::dovecot-master-accounts]: https://github.com/docker-mailserver/docker-mailserver/pull/2535
|
||||
[dms::feature::dovecot-master-accounts::caveat-ldap]: https://github.com/docker-mailserver/docker-mailserver/pull/2535#issuecomment-1118056745
|
||||
[dovecot-docs::auth::master-users]: https://doc.dovecot.org/configuration_manual/authentication/master_users/
|
||||
[dovecot-docs::config::auth-master-user-separator]: https://doc.dovecot.org/settings/core/#core_setting-auth_master_user_separator
|
||||
[docs::account-management::file::accounts]: ../provisioner/file.md#accounts
|
|
@ -1,145 +0,0 @@
|
|||
---
|
||||
title: 'Account Management | OAuth2 Support'
|
||||
hide:
|
||||
- toc # Hide Table of Contents for this page
|
||||
---
|
||||
|
||||
# Authentication - OAuth2 / OIDC
|
||||
|
||||
This feature enables support for delegating DMS account authentication through to an external _Identity Provider_ (IdP).
|
||||
|
||||
!!! warning "Receiving mail requires a DMS account to exist"
|
||||
|
||||
If you expect DMS to receive mail, you must provision an account into DMS in advance. Otherwise DMS has no awareness of your externally manmaged users and will reject delivery.
|
||||
|
||||
There are [plans to implement support to provision users through a SCIM 2.0 API][dms-feature-request::scim-api]. An IdP that can operate as a SCIM Client (eg: Authentik) would then integrate with DMS for user provisioning. Until then you must keep your user accounts in sync manually via your configured [`ACCOUNT_PROVISIONER`][docs::env::account-provisioner].
|
||||
|
||||
??? info "How the feature works"
|
||||
|
||||
1. A **mail client must have support** to acquire an OAuth2 token from your IdP (_however many clients lack generic OAuth2 / OIDC provider support_).
|
||||
2. The mail client then provides that token as the user password via the login mechanism `XOAUTH2` or `OAUTHBEARER`.
|
||||
3. DMS (Dovecot) will then check the validity of that token against the Authentication Service it was configured with.
|
||||
4. If the response returned is valid for the user account, authentication is successful.
|
||||
|
||||
[**XOAUTH2**][google::xoauth2-docs] (_Googles widely adopted implementation_) and **OAUTHBEARER** (_the newer variant standardized by [RFC 7628][rfc::7628] in 2015_) are supported as standards for verifying that a OAuth Bearer Token (_[RFC 6750][rfc::6750] from 2012_) is valid at the identity provider that created the token. The token itself in both cases is expected to be can an opaque _Access Token_, but it is possible to use a JWT _ID Token_ (_which encodes additional information into the token itself_).
|
||||
|
||||
A mail client like Thunderbird has limited OAuth2 / OIDC support. The software maintains a hard-coded list of providers supported. Roundcube is a webmail client that does have support for generic providers, allowing you to integrate with a broader range of IdP services.
|
||||
|
||||
---
|
||||
|
||||
**Documentation for this feature is WIP**
|
||||
|
||||
See the [initial feature support][dms-feature::oauth2-pr] and [existing issues][dms-feature::oidc-issues] for guidance that has not yet been documented officially.
|
||||
|
||||
??? tip "Verify authentication works"
|
||||
|
||||
If you have a compatible mail client you can verify login through that.
|
||||
|
||||
---
|
||||
|
||||
??? example "CLI - Verify with `curl`"
|
||||
|
||||
```bash
|
||||
# Shell into your DMS container:
|
||||
docker exec -it dms bash
|
||||
|
||||
# Adjust these variables for the methods below to use:
|
||||
export AUTH_METHOD='OAUTHBEARER' USER_ACCOUNT='hello@example.com' ACCESS_TOKEN='DMS_YWNjZXNzX3Rva2Vu'
|
||||
|
||||
# Authenticate via IMAP (Dovecot):
|
||||
curl --silent --url 'imap://localhost:143' \
|
||||
--login-options "AUTH=${AUTH_METHOD}" --user "${USER_ACCOUNT}" --oauth2-bearer "${ACCESS_TOKEN}" \
|
||||
--request 'LOGOUT' \
|
||||
&& grep "dovecot: imap-login: Login: user=<${USER_ACCOUNT}>, method=${AUTH_METHOD}" /var/log/mail/mail.log
|
||||
|
||||
# Authenticate via SMTP (Postfix), sending a mail with the same sender(from) and recipient(to) address:
|
||||
# NOTE: `curl` seems to require `--upload-file` with some mail content provided to test SMTP auth.
|
||||
curl --silent --url 'smtp://localhost:587' \
|
||||
--login-options "AUTH=${AUTH_METHOD}" --user "${USER_ACCOUNT}" --oauth2-bearer "${ACCESS_TOKEN}" \
|
||||
--mail-from "${USER_ACCOUNT}" --mail-rcpt "${USER_ACCOUNT}" --upload-file - <<< 'RFC 5322 content - not important' \
|
||||
&& grep "postfix/submission/smtpd.*, sasl_method=${AUTH_METHOD}, sasl_username=${USER_ACCOUNT}" /var/log/mail/mail.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Troubleshooting:**
|
||||
|
||||
- Add `--verbose` to the curl options. This will output the protocol exchange which includes if authentication was successful or failed.
|
||||
- The above example chains the `curl` commands with `grep` on DMS logs (_for Dovecot and Postfix services_). When not running `curl` from the DMS container, ensure you check the logs correctly, or inspect the `--verbose` output instead.
|
||||
|
||||
!!! warning "`curl` bug with `XOAUTH2`"
|
||||
|
||||
[Older releases of `curl` have a bug with `XOAUTH2` support][gh-issue::curl::xoauth2-bug] since `7.80.0` (Nov 2021) but fixed from `8.6.0` (Jan 2024). It treats `XOAUTH2` as `OAUTHBEARER`.
|
||||
|
||||
If you use `docker exec` to run `curl` from within DMS, the current DMS v14 release (_Debian 12 with curl `7.88.1`_) is affected by this bug.
|
||||
|
||||
## Config Examples
|
||||
|
||||
### Authentik with Roundcube
|
||||
|
||||
This example assumes you have already set up:
|
||||
|
||||
- A working DMS server
|
||||
- An Authentik server ([documentation][authentik::docs::install])
|
||||
- A Roundcube server ([docker image][roundcube::dockerhub-image] or [bare metal install][roundcube::docs::install])
|
||||
|
||||
!!! example "Setup Instructions"
|
||||
|
||||
=== "1. Docker Mailserver"
|
||||
|
||||
Update your Docker Compose ENV config to include:
|
||||
|
||||
```env title="compose.yaml"
|
||||
services:
|
||||
mailserver:
|
||||
env:
|
||||
# Enable the feature:
|
||||
- ENABLE_OAUTH2=1
|
||||
# Specify the user info endpoint URL of the oauth2 server for token inspection:
|
||||
- OAUTH2_INTROSPECTION_URL=https://authentik.example.com/application/o/userinfo/
|
||||
```
|
||||
|
||||
=== "2. Authentik"
|
||||
|
||||
1. Create a new OAuth2 provider.
|
||||
2. Note the client id and client secret. Roundcube will need this.
|
||||
3. Set the allowed redirect url to the equivalent of `https://roundcube.example.com/index.php/login/oauth` for your RoundCube instance.
|
||||
|
||||
=== "3. Roundcube"
|
||||
|
||||
Add the following to `oauth2.inc.php` ([documentation][roundcube::docs::config]):
|
||||
|
||||
```php
|
||||
$config['oauth_provider'] = 'generic';
|
||||
$config['oauth_provider_name'] = 'Authentik';
|
||||
$config['oauth_client_id'] = '<insert client id here>';
|
||||
$config['oauth_client_secret'] = '<insert client secret here>';
|
||||
$config['oauth_auth_uri'] = 'https://authentik.example.com/application/o/authorize/';
|
||||
$config['oauth_token_uri'] = 'https://authentik.example.com/application/o/token/';
|
||||
$config['oauth_identity_uri'] = 'https://authentik.example.com/application/o/userinfo/';
|
||||
|
||||
// Optional: disable SSL certificate check on HTTP requests to OAuth server. For possible values, see:
|
||||
// http://docs.guzzlephp.org/en/stable/request-options.html#verify
|
||||
$config['oauth_verify_peer'] = false;
|
||||
|
||||
$config['oauth_scope'] = 'email openid profile';
|
||||
$config['oauth_identity_fields'] = ['email'];
|
||||
|
||||
// Boolean: automatically redirect to OAuth login when opening Roundcube without a valid session
|
||||
$config['oauth_login_redirect'] = false;
|
||||
```
|
||||
|
||||
[dms-feature::oauth2-pr]: https://github.com/docker-mailserver/docker-mailserver/pull/3480
|
||||
[dms-feature::oidc-issues]: https://github.com/docker-mailserver/docker-mailserver/issues?q=label%3Afeature%2Fauth-oidc
|
||||
[docs::env::account-provisioner]: ../../environment.md#account_provisioner
|
||||
[dms-feature-request::scim-api]: https://github.com/docker-mailserver/docker-mailserver/issues/4090
|
||||
|
||||
[google::xoauth2-docs]: https://developers.google.com/gmail/imap/xoauth2-protocol#the_sasl_xoauth2_mechanism
|
||||
[rfc::6750]: https://datatracker.ietf.org/doc/html/rfc6750
|
||||
[rfc::7628]: https://datatracker.ietf.org/doc/html/rfc7628
|
||||
[gh-issue::curl::xoauth2-bug]: https://github.com/curl/curl/issues/10259#issuecomment-1907192556
|
||||
|
||||
[authentik::docs::install]: https://goauthentik.io/docs/installation/
|
||||
[roundcube::dockerhub-image]: https://hub.docker.com/r/roundcube/roundcubemail
|
||||
[roundcube::docs::install]: https://github.com/roundcube/roundcubemail/wiki/Installation
|
||||
[roundcube::docs::config]: https://github.com/roundcube/roundcubemail/wiki/Configuration
|
|
@ -0,0 +1,127 @@
|
|||
---
|
||||
title: 'Advanced | LDAP Authentication'
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Getting started with ldap and this mailserver we need to take 3 parts in account:
|
||||
|
||||
- `postfix`
|
||||
- `dovecot`
|
||||
- `saslauthd` (this can also be handled by dovecot)
|
||||
|
||||
## Variables to Control Provisioning by the Container
|
||||
|
||||
Have a look at the [`ENVIRONMENT.md`][github-file-env] for information on the default values.
|
||||
|
||||
!!! example "postfix"
|
||||
|
||||
- `LDAP_QUERY_FILTER_USER`
|
||||
- `LDAP_QUERY_FILTER_GROUP`
|
||||
- `LDAP_QUERY_FILTER_ALIAS`
|
||||
- `LDAP_QUERY_FILTER_DOMAIN`
|
||||
|
||||
!!! example "saslauthd"
|
||||
|
||||
- `SASLAUTHD_LDAP_FILTER`
|
||||
|
||||
!!! example "dovecot"
|
||||
|
||||
- `DOVECOT_USER_FILTER`
|
||||
- `DOVECOT_PASS_FILTER`
|
||||
|
||||
## LDAP Setup - Kopano / Zarafa
|
||||
|
||||
???+ example "Example Code"
|
||||
|
||||
```yaml
|
||||
---
|
||||
version: '2'
|
||||
|
||||
services:
|
||||
mail:
|
||||
image: mailserver/docker-mailserver:latest
|
||||
hostname: mail
|
||||
domainname: domain.com
|
||||
container_name: mail
|
||||
|
||||
ports:
|
||||
- "25:25"
|
||||
- "143:143"
|
||||
- "587:587"
|
||||
- "993:993"
|
||||
|
||||
volumes:
|
||||
- maildata:/var/mail
|
||||
- mailstate:/var/mail-state
|
||||
- ./config/:/tmp/docker-mailserver/
|
||||
|
||||
environment:
|
||||
# We are not using dovecot here
|
||||
- SMTP_ONLY=1
|
||||
- ENABLE_SPAMASSASSIN=1
|
||||
- ENABLE_CLAMAV=1
|
||||
- ENABLE_FAIL2BAN=1
|
||||
- ENABLE_POSTGREY=1
|
||||
- SASLAUTHD_PASSWD=
|
||||
|
||||
# >>> SASL Authentication
|
||||
- ENABLE_SASLAUTHD=1
|
||||
- SASLAUTHD_LDAP_SERVER=<yourLdapContainer/yourLdapServer>
|
||||
- SASLAUTHD_LDAP_PROTO=
|
||||
- SASLAUTHD_LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=loc
|
||||
- SASLAUTHD_LDAP_PASSWORD=mypassword
|
||||
- SASLAUTHD_LDAP_SEARCH_BASE=dc=mydomain,dc=loc
|
||||
- SASLAUTHD_LDAP_FILTER=(&(sAMAccountName=%U)(objectClass=person))
|
||||
- SASLAUTHD_MECHANISMS=ldap
|
||||
# <<< SASL Authentication
|
||||
|
||||
# >>> Postfix Ldap Integration
|
||||
- ENABLE_LDAP=1
|
||||
- LDAP_SERVER_HOST=<yourLdapContainer/yourLdapServer>
|
||||
- LDAP_SEARCH_BASE=dc=mydomain,dc=loc
|
||||
- LDAP_BIND_DN=cn=Administrator,cn=Users,dc=mydomain,dc=loc
|
||||
- LDAP_BIND_PW=mypassword
|
||||
- LDAP_QUERY_FILTER_USER=(&(objectClass=user)(mail=%s))
|
||||
- LDAP_QUERY_FILTER_GROUP=(&(objectclass=group)(mail=%s))
|
||||
- LDAP_QUERY_FILTER_ALIAS=(&(objectClass=user)(otherMailbox=%s))
|
||||
- LDAP_QUERY_FILTER_DOMAIN=(&(|(mail=*@%s)(mailalias=*@%s)(mailGroupMember=*@%s))(mailEnabled=TRUE))
|
||||
# <<< Postfix Ldap Integration
|
||||
|
||||
# >>> Kopano Integration
|
||||
- ENABLE_POSTFIX_VIRTUAL_TRANSPORT=1
|
||||
- POSTFIX_DAGENT=lmtp:kopano:2003
|
||||
# <<< Kopano Integration
|
||||
|
||||
- ONE_DIR=1
|
||||
- DMS_DEBUG=0
|
||||
- SSL_TYPE=letsencrypt
|
||||
- PERMIT_DOCKER=host
|
||||
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
|
||||
volumes:
|
||||
maildata:
|
||||
driver: local
|
||||
mailstate:
|
||||
driver: local
|
||||
```
|
||||
|
||||
If your directory has not the postfix-book schema installed, then you must change the internal attribute handling for dovecot. For this you have to change the `pass_attr` and the `user_attr` mapping, as shown in the example below:
|
||||
|
||||
```yaml
|
||||
- DOVECOT_PASS_ATTR=<YOUR_USER_IDENTIFYER_ATTRIBUTE>=user,<YOUR_USER_PASSWORD_ATTRIBUTE>=password
|
||||
- DOVECOT_USER_ATTR=<YOUR_USER_HOME_DIRECTORY_ATTRIBUTE>=home,<YOUR_USER_MAILSTORE_ATTRIBUTE>=mail,<YOUR_USER_MAIL_UID_ATTRIBUTE>=uid, <YOUR_USER_MAIL_GID_ATTRIBUTE>=gid
|
||||
```
|
||||
|
||||
The following example illustrates this for a directory that has the qmail-schema installed and that uses `uid`:
|
||||
|
||||
```yaml
|
||||
- DOVECOT_PASS_ATTRS=uid=user,userPassword=password
|
||||
- DOVECOT_USER_ATTRS=homeDirectory=home,qmailUID=uid,qmailGID=gid,mailMessageStore=mail
|
||||
- DOVECOT_PASS_FILTER=(&(objectClass=qmailUser)(uid=%u)(accountStatus=active))
|
||||
- DOVECOT_USER_FILTER=(&(objectClass=qmailUser)(uid=%u)(accountStatus=active))
|
||||
```
|
||||
|
||||
[github-file-env]: https://github.com/docker-mailserver/docker-mailserver/blob/master/ENVIRONMENT.md
|
|
@ -4,141 +4,55 @@ title: 'Advanced | Full-Text Search'
|
|||
|
||||
## Overview
|
||||
|
||||
Full-text search allows all messages to be indexed, so that mail clients can quickly and efficiently search messages by their full text content. Dovecot supports a variety of community supported [FTS indexing backends](https://doc.dovecot.org/configuration_manual/fts/).
|
||||
Full-text search allows all messages to be indexed, so that mail clients can quickly and efficiently search messages by their full text content.
|
||||
|
||||
DMS comes pre-installed with two plugins that can be enabled with a dovecot config file.
|
||||
The [dovecot-solr Plugin](https://wiki2.dovecot.org/Plugins/FTS/Solr) is used in conjunction with [Apache Solr](https://lucene.apache.org/solr/) running in a separate container. This is quite straightforward to setup using the following instructions.
|
||||
|
||||
Please be aware that indexing consumes memory and takes up additional disk space.
|
||||
## Setup Steps
|
||||
|
||||
### Xapian
|
||||
|
||||
The [dovecot-fts-xapian](https://github.com/grosjo/fts-xapian) plugin makes use of [Xapian](https://xapian.org/). Xapian enables embedding an FTS engine without the need for additional backends.
|
||||
|
||||
The indexes will be stored as a subfolder named `xapian-indexes` inside your local `mail-data` folder (_`/var/mail` internally_). With the default settings, 10GB of email data may generate around 4GB of indexed data.
|
||||
|
||||
While indexing is memory intensive, you can configure the plugin to limit the amount of memory consumed by the index workers. With Xapian being small and fast, this plugin is a good choice for low memory environments (2GB).
|
||||
|
||||
#### Setup
|
||||
|
||||
1. To configure `fts-xapian` as a dovecot plugin, create a file at `docker-data/dms/config/dovecot/fts-xapian-plugin.conf` and place the following in it:
|
||||
|
||||
```
|
||||
mail_plugins = $mail_plugins fts fts_xapian
|
||||
|
||||
plugin {
|
||||
fts = xapian
|
||||
fts_xapian = partial=3 full=20 verbose=0
|
||||
|
||||
fts_autoindex = yes
|
||||
fts_enforced = yes
|
||||
|
||||
# disable indexing of folders
|
||||
# fts_autoindex_exclude = \Trash
|
||||
|
||||
# Index attachements
|
||||
# fts_decoder = decode2text
|
||||
}
|
||||
|
||||
service indexer-worker {
|
||||
# limit size of indexer-worker RAM usage, ex: 512MB, 1GB, 2GB
|
||||
vsz_limit = 1GB
|
||||
}
|
||||
|
||||
# service decode2text {
|
||||
# executable = script /usr/libexec/dovecot/decode2text.sh
|
||||
# user = dovecot
|
||||
# unix_listener decode2text {
|
||||
# mode = 0666
|
||||
# }
|
||||
# }
|
||||
```
|
||||
|
||||
adjust the settings to tune for your desired memory limits, exclude folders and enable searching text inside of attachments
|
||||
|
||||
2. Update `compose.yaml` to load the previously created dovecot plugin config file:
|
||||
1. `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
mailserver:
|
||||
image: ghcr.io/docker-mailserver/docker-mailserver:latest
|
||||
container_name: mailserver
|
||||
hostname: mail.example.com
|
||||
env_file: mailserver.env
|
||||
ports:
|
||||
- "25:25" # SMTP (explicit TLS => STARTTLS)
|
||||
- "143:143" # IMAP4 (explicit TLS => STARTTLS)
|
||||
- "465:465" # ESMTP (implicit TLS)
|
||||
- "587:587" # ESMTP (explicit TLS => STARTTLS)
|
||||
- "993:993" # IMAP4 (implicit TLS)
|
||||
volumes:
|
||||
- ./docker-data/dms/mail-data/:/var/mail/
|
||||
- ./docker-data/dms/mail-state/:/var/mail-state/
|
||||
- ./docker-data/dms/mail-logs/:/var/log/mail/
|
||||
- ./docker-data/dms/config/:/tmp/docker-mailserver/
|
||||
- ./docker-data/dms/config/dovecot/fts-xapian-plugin.conf:/etc/dovecot/conf.d/10-plugin.conf:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
restart: always
|
||||
stop_grace_period: 1m
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
solr:
|
||||
image: lmmdock/dovecot-solr:latest
|
||||
volumes:
|
||||
- solr-dovecot:/opt/solr/server/solr/dovecot
|
||||
restart: always
|
||||
|
||||
mailserver:
|
||||
image: mailserver/docker-mailserver:latest
|
||||
...
|
||||
volumes:
|
||||
...
|
||||
- ./etc/dovecot/conf.d/10-plugin.conf:/etc/dovecot/conf.d/10-plugin.conf:ro
|
||||
...
|
||||
|
||||
volumes:
|
||||
solr-dovecot:
|
||||
driver: local
|
||||
```
|
||||
|
||||
3. Recreate containers:
|
||||
|
||||
```
|
||||
docker compose down
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
4. Initialize indexing on all users for all mail:
|
||||
|
||||
```
|
||||
docker compose exec mailserver doveadm index -A -q \*
|
||||
```
|
||||
|
||||
5. Run the following command in a daily cron job:
|
||||
|
||||
```
|
||||
docker compose exec mailserver doveadm fts optimize -A
|
||||
```
|
||||
Or like the [Spamassassin example][docs-faq-sa-learn-cron] shows, you can instead use `cron` from within DMS to avoid potential errors if the mail server is not running:
|
||||
|
||||
??? example
|
||||
|
||||
Create a _system_ cron file:
|
||||
|
||||
```sh
|
||||
# in the compose.yaml root directory
|
||||
mkdir -p ./docker-data/dms/cron # if you didn't have this folder before
|
||||
touch ./docker-data/dms/cron/fts_xapian
|
||||
chown root:root ./docker-data/dms/cron/fts_xapian
|
||||
chmod 0644 ./docker-data/dms/cron/fts_xapian
|
||||
```
|
||||
|
||||
Edit the system cron file `nano ./docker-data/dms/cron/fts_xapian`, and set an appropriate configuration:
|
||||
2. `etc/dovecot/conf.d/10-plugin.conf`:
|
||||
|
||||
```conf
|
||||
# Adding `MAILTO=""` prevents cron emailing notifications of the task outcome each run
|
||||
MAILTO=""
|
||||
#
|
||||
# m h dom mon dow user command
|
||||
#
|
||||
# Everyday 4:00AM, optimize index files
|
||||
0 4 * * * root doveadm fts optimize -A
|
||||
mail_plugins = $mail_plugins fts fts_solr
|
||||
|
||||
plugin {
|
||||
fts = solr
|
||||
fts_autoindex = yes
|
||||
fts_solr = url=http://solr:8983/solr/dovecot/
|
||||
}
|
||||
```
|
||||
|
||||
Then with `compose.yaml`:
|
||||
3. Start the solr container: `docker-compose up -d --remove-orphans solr`
|
||||
|
||||
```yaml
|
||||
services:
|
||||
mailserver:
|
||||
image: ghcr.io/docker-mailserver/docker-mailserver:latest
|
||||
volumes:
|
||||
- ./docker-data/dms/cron/fts_xapian:/etc/cron.d/fts_xapian
|
||||
```
|
||||
4. Restart the mailserver container: `docker-compose restart mailserver`
|
||||
|
||||
#### Further Discussion
|
||||
5. Flag all user mailbox FTS indexes as invalid, so they are rescanned on demand when they are next searched: `docker-compose exec mailserver doveadm fts rescan -A`
|
||||
|
||||
See [#905](https://github.com/docker-mailserver/docker-mailserver/issues/905)
|
||||
|
||||
[docs-faq-sa-learn-cron]: ../../faq.md#how-can-i-make-spamassassin-better-recognize-spam
|
||||
## Further Discussion
|
||||
|
||||
See [#905][github-issue-905]
|
||||
|
||||
[github-issue-905]: https://github.com/docker-mailserver/docker-mailserver/issues/905
|
||||
|
|
|
@ -2,216 +2,47 @@
|
|||
title: 'Advanced | IPv6'
|
||||
---
|
||||
|
||||
!!! bug "Ample Opportunities for Issues"
|
||||
## Background
|
||||
|
||||
Numerous bug reports have been raised in the past about IPv6. Please make sure your setup around DMS is correct when using IPv6!
|
||||
If your container host supports IPv6, then `docker-mailserver` will automatically accept IPv6 connections by way of the docker host's IPv6. However, incoming mail will fail SPF checks because they will appear to come from the IPv4 gateway that docker is using to proxy the IPv6 connection (`172.20.0.1` is the gateway).
|
||||
|
||||
## IPv6 networking problems with Docker defaults
|
||||
This can be solved by supporting IPv6 connections all the way to the `docker-mailserver` container.
|
||||
|
||||
### What can go wrong?
|
||||
## Setup steps
|
||||
|
||||
If your host system supports IPv6 and an `AAAA` DNS record exists to direct IPv6 traffic to DMS, you may experience issues when an IPv6 connection is made:
|
||||
```diff
|
||||
+++ b/serv/docker-compose.yml
|
||||
@@ -1,4 +1,4 @@
|
||||
-version: '2'
|
||||
+version: '2.1'
|
||||
|
||||
- The original client IP is replaced with the gateway IP of a docker network.
|
||||
- Connections fail or hang.
|
||||
@@ -32,6 +32,16 @@ services:
|
||||
|
||||
The impact of losing the real IP of the client connection can negatively affect DMS:
|
||||
+ ipv6nat:
|
||||
+ image: robbertkl/ipv6nat
|
||||
+ restart: always
|
||||
+ network_mode: "host"
|
||||
+ cap_add:
|
||||
+ - NET_ADMIN
|
||||
+ - SYS_MODULE
|
||||
+ volumes:
|
||||
+ - /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
+ - /lib/modules:/lib/modules:ro
|
||||
|
||||
- Users unable to login (_Fail2Ban action triggered by repeated login failures all seen as from the same internal Gateway IP_)
|
||||
- Mail inbound to DMS is rejected (_[SPF verification failure][gh-issue-1438-spf], IP mismatch_)
|
||||
- Delivery failures from [sender reputation][sender-score] being reduced (_due to [bouncing inbound mail][gh-issue-3057-bounce] from rejected IPv6 clients_)
|
||||
- Some services may be configured to trust connecting clients within the containers subnet, which includes the Gateway IP. This can risk bypassing or relaxing security measures, such as exposing an [open relay][wikipedia-openrelay].
|
||||
@@ -306,4 +316,13 @@ networks:
|
||||
|
||||
### Why does this happen?
|
||||
|
||||
When the host network receives a connection to a containers published port, it is routed to the containers internal network managed by Docker (_typically a bridge network_).
|
||||
|
||||
By default, the Docker daemon only assigns IPv4 addresses to containers, thus it will only accept IPv4 connections (_unless a `docker-proxy` process is listening, which the default daemon setting `userland-proxy: true` enables_). With the daemon setting `userland-proxy: true` (default), IPv6 connections from the host can also be accepted and routed to containers (_even when they only have IPv4 addresses assigned_). `userland-proxy: false` will require the container to have atleast an IPv6 address assigned.
|
||||
|
||||
This can be problematic for IPv6 host connections when internally the container is no longer aware of the original client IPv6 address, as it has been proxied through the IPv4 or IPv6 gateway address of it's connected network (_eg: `172.17.0.1` - Docker allocates networks from a set of [default subnets][docker-subnets]_).
|
||||
|
||||
This can be fixed by enabling a Docker network to assign IPv6 addresses to containers, along with some additional configuration. Alternatively you could configure the opposite to prevent IPv6 connections being made.
|
||||
|
||||
## Prevent IPv6 connections
|
||||
|
||||
- Avoiding an `AAAA` DNS record for your DMS FQDN would prevent resolving an IPv6 address to connect to.
|
||||
- You can also use `userland-proxy: false`, which will fail to establish a remote connection to DMS (_provided no IPv6 address was assigned_).
|
||||
|
||||
!!! tip "With UFW or Firewalld"
|
||||
|
||||
When one of these firewall frontends are active, remote clients should fail to connect instead of being masqueraded as the docker network gateway IP. Keep in mind that this only affects remote clients, it does not affect local IPv6 connections originating within the same host.
|
||||
|
||||
## Enable proper IPv6 support
|
||||
|
||||
You can enable IPv6 support in Docker for container networks, however [compatibility concerns][docs-compat] may affect your success.
|
||||
|
||||
The [official Docker documentation on enabling IPv6][docker-docs-enable-ipv6] has been improving and is a good resource to reference.
|
||||
|
||||
Enable `ip6tables` support so that Docker will manage IPv6 networking rules as well. This will allow for IPv6 NAT to work like the existing IPv4 NAT already does for your containers, avoiding the above issue with external connections having their IP address seen as the container network gateway IP (_provided an IPv6 address is also assigned to the container_).
|
||||
|
||||
!!! example "Configure the following in `/etc/docker/daemon.json`"
|
||||
|
||||
```json
|
||||
{
|
||||
"ip6tables": true,
|
||||
"experimental" : true,
|
||||
"userland-proxy": true
|
||||
}
|
||||
```
|
||||
|
||||
- `experimental: true` is currently required for `ip6tables: true` to work.
|
||||
- `userland-proxy` setting [can potentially affect connection behavior][gh-pull-3244-proxy] for local connections.
|
||||
|
||||
Now restart the daemon if it's running: `systemctl restart docker`.
|
||||
|
||||
Next, configure a network with an IPv6 subnet for your container with any of these examples:
|
||||
|
||||
???+ example "Create an IPv6 ULA subnet"
|
||||
|
||||
??? info "About these examples"
|
||||
|
||||
These examples are focused on a [IPv6 ULA subnet][wikipedia-ipv6-ula] which is suitable for most users as described in the next section.
|
||||
|
||||
- You may prefer a subnet size smaller than `/64` (eg: `/112`, which still provides over 65k IPv6 addresses), especially if instead configuring for an IPv6 GUA subnet.
|
||||
- The network will also implicitly be assigned an IPv4 subnet (_from the Docker daemon config `default-address-pools`_).
|
||||
|
||||
=== "User-defined Network"
|
||||
|
||||
The preferred approach is with [user-defined networks][docker-docs-ipv6-create-custom] via `compose.yaml` (recommended) or CLI with `docker network create`:
|
||||
|
||||
=== "Compose"
|
||||
|
||||
Create the network in `compose.yaml` and attach a service to it:
|
||||
|
||||
```yaml title="compose.yaml"
|
||||
services:
|
||||
mailserver:
|
||||
networks:
|
||||
- dms-ipv6
|
||||
|
||||
networks:
|
||||
dms-ipv6:
|
||||
enable_ipv6: true
|
||||
ipam:
|
||||
config:
|
||||
- subnet: fd00:cafe:face:feed::/64
|
||||
```
|
||||
|
||||
??? tip "Override the implicit `default` network"
|
||||
|
||||
You can optionally avoid the service assignment by [overriding the `default` user-defined network that Docker Compose generates][docker-docs-network-compose-default]. Just replace `dms-ipv6` with `default`.
|
||||
|
||||
The Docker Compose `default` bridge is not affected by settings for the default `bridge` (aka `docker0`) in `/etc/docker/daemon.json`.
|
||||
|
||||
??? tip "Using the network outside of this `compose.yaml`"
|
||||
|
||||
To reference this network externally (_from other compose files or `docker run`_), assign the [networks `name` key in `compose.yaml`][docker-docs-network-external].
|
||||
|
||||
=== "CLI"
|
||||
|
||||
Create the network via a CLI command (_which can then be used with `docker run --network dms-ipv6`_):
|
||||
|
||||
```bash
|
||||
docker network create --ipv6 --subnet fd00:cafe:face:feed::/64 dms-ipv6
|
||||
```
|
||||
|
||||
Optionally reference it from one or more `compose.yaml` files:
|
||||
|
||||
```yaml title="compose.yaml"
|
||||
services:
|
||||
mailserver:
|
||||
networks:
|
||||
- dms-ipv6
|
||||
|
||||
networks:
|
||||
dms-ipv6:
|
||||
external: true
|
||||
```
|
||||
|
||||
=== "Default Bridge (daemon)"
|
||||
|
||||
!!! warning "This approach is discouraged"
|
||||
|
||||
The [`bridge` network is considered legacy][docker-docs-network-bridge-legacy].
|
||||
|
||||
Add these two extra IPv6 settings to your daemon config. They only apply to the [default `bridge` docker network][docker-docs-ipv6-create-default] aka `docker0` (_which containers are attached to by default when using `docker run`_).
|
||||
|
||||
```json title="/etc/docker/daemon.json"
|
||||
{
|
||||
"ipv6": true,
|
||||
"fixed-cidr-v6": "fd00:cafe:face:feed::/64",
|
||||
}
|
||||
```
|
||||
|
||||
Compose projects can also use this network via `network_mode`:
|
||||
|
||||
```yaml title="compose.yaml"
|
||||
services:
|
||||
mailserver:
|
||||
network_mode: bridge
|
||||
```
|
||||
|
||||
!!! danger "Do not use `2001:db8:1::/64` for your private subnet"
|
||||
|
||||
The `2001:db8` address prefix is [reserved for documentation][wikipedia-ipv6-reserved]. Avoid creating a subnet with this prefix.
|
||||
|
||||
Presently this is used in examples for Dockers IPv6 docs as a placeholder, while mixed in with private IPv4 addresses which can be misleading.
|
||||
|
||||
### Configuring an IPv6 subnet
|
||||
|
||||
If you've [configured IPv6 address pools in `/etc/docker/daemon.json`][docker-docs-ipv6-supernets], you do not need to specify a subnet explicitly. Otherwise if you're unsure what value to provide, here's a quick guide (_Tip: Prefer IPv6 ULA, it's the least hassle_):
|
||||
|
||||
- `fd00:cafe:face:feed::/64` is an example of a [IPv6 ULA subnet][wikipedia-ipv6-ula]. ULA addresses are akin to the [private IPv4 subnets][wikipedia-ipv4-private] you may already be familiar with. You can use that example, or choose your own ULA address. This is a good choice for getting Docker containers to their have networks support IPv6 via NAT like they already do by default with IPv4.
|
||||
- IPv6 without NAT, using public address space like your server is assigned belongs to an [IPv6 GUA subnet][wikipedia-ipv6-gua].
|
||||
- Typically these will be a `/64` block assigned to your host, but this varies by provider.
|
||||
- These addresses do not need to publish ports of a container to another IP to be publicly reached (_thus `ip6tables: true` is not required_), you will want a firewall configured to manage which ports are accessible instead as no NAT is involved. Note that this may not be desired if the container should also be reachable via the host IPv4 public address.
|
||||
- You may want to subdivide the `/64` into smaller subnets for Docker to use only portions of the `/64`. This can reduce some routing features, and [require additional setup / management via a NDP Proxy][gh-pull-3244-gua] for your public interface to know of IPv6 assignments managed by Docker and accept external traffic.
|
||||
|
||||
### Verify remote IP is correct
|
||||
|
||||
With Docker CLI or Docker Compose, run a `traefik/whoami` container with your IPv6 docker network and port 80 published. You can then send a curl request (or via address in the browser) from another host (as your remote client) with an IPv6 network, the `RemoteAddr` value returned should match your client IPv6 address.
|
||||
|
||||
```bash
|
||||
docker run --rm -d --network dms-ipv6 -p 80:80 traefik/whoami
|
||||
# On a different host, replace `2001:db8::1` with your DMS host IPv6 address
|
||||
curl --max-time 5 http://[2001:db8::1]:80
|
||||
+ default:
|
||||
+ driver: bridge
|
||||
+ enable_ipv6: true
|
||||
+ ipam:
|
||||
+ driver: default
|
||||
+ config:
|
||||
+ - subnet: fd00:0123:4567::/48
|
||||
+ gateway: fd00:0123:4567::1
|
||||
```
|
||||
|
||||
!!! warning "IPv6 gateway IP"
|
||||
## Further Discussion
|
||||
|
||||
If instead of the remote IPv6 address, you may notice the gateway IP for the IPv6 subnet your DMS container belongs to.
|
||||
See [#1438][github-issue-1438]
|
||||
|
||||
This will happen when DMS has an IPv6 IP address assigned, for the same reason as with IPv4, `userland-proxy: true`. It indicates that your `daemon.json` has not been configured correctly or had the updated config applied for `ip6tables :true` + `experimental: true`. Make sure you used `systemctl restart docker` after updating `daemon.json`.
|
||||
|
||||
!!! info "IPv6 ULA address priority"
|
||||
|
||||
DNS lookups that have records for both IPv4 and IPv6 addresses (_eg: `localhost`_) may prefer IPv4 over IPv6 (ULA) for private addresses, whereas for public addresses IPv6 has priority. This shouldn't be anything to worry about, but can come across as a surprise when testing your IPv6 setup on the same host instead of from a remote client.
|
||||
|
||||
The preference can be controlled with [`/etc/gai.conf`][networking-gai], and appears was configured this way based on [the assumption that IPv6 ULA would never be used with NAT][networking-gai-blog]. It should only affect the destination resolved for outgoing connections, which for IPv6 ULA should only really affect connections between your containers / host. In future [IPv6 ULA may also be prioritized][networking-gai-rfc].
|
||||
|
||||
[docker-subnets]: https://straz.to/2021-09-08-docker-address-pools/#what-are-the-default-address-pools-when-no-configuration-is-given-vanilla-pools
|
||||
[sender-score]: https://senderscore.org/assess/get-your-score/
|
||||
[gh-issue-1438-spf]: https://github.com/docker-mailserver/docker-mailserver/issues/1438
|
||||
[gh-issue-3057-bounce]: https://github.com/docker-mailserver/docker-mailserver/pull/3057#issuecomment-1416700046
|
||||
[wikipedia-openrelay]: https://en.wikipedia.org/wiki/Open_mail_relay
|
||||
|
||||
[docs-compat]: ../debugging.md#compatibility
|
||||
|
||||
[gh-pull-3244-proxy]: https://github.com/docker-mailserver/docker-mailserver/pull/3244#issuecomment-1603436809
|
||||
[docker-docs-enable-ipv6]: https://docs.docker.com/config/daemon/ipv6/
|
||||
[docker-docs-ipv6-create-custom]: https://docs.docker.com/config/daemon/ipv6/#create-an-ipv6-network
|
||||
[docker-docs-ipv6-create-default]: https://docs.docker.com/config/daemon/ipv6/#use-ipv6-for-the-default-bridge-network
|
||||
[docker-docs-ipv6-supernets]: https://docs.docker.com/config/daemon/ipv6/#dynamic-ipv6-subnet-allocation
|
||||
[docker-docs-network-external]: https://docs.docker.com/compose/compose-file/06-networks/#name
|
||||
[docker-docs-network-compose-default]: https://docs.docker.com/compose/networking/#configure-the-default-network
|
||||
[docker-docs-network-bridge-legacy]: https://docs.docker.com/network/drivers/bridge/#use-the-default-bridge-network
|
||||
|
||||
[wikipedia-ipv6-reserved]: https://en.wikipedia.org/wiki/IPv6_address#Documentation
|
||||
[wikipedia-ipv4-private]: https://en.wikipedia.org/wiki/Private_network#Private_IPv4_addresses
|
||||
[wikipedia-ipv6-ula]: https://en.wikipedia.org/wiki/Unique_local_address
|
||||
[wikipedia-ipv6-gua]: https://en.wikipedia.org/wiki/IPv6#Global_addressing
|
||||
[gh-pull-3244-gua]: https://github.com/docker-mailserver/docker-mailserver/pull/3244#issuecomment-1528984894
|
||||
|
||||
[networking-gai]: https://linux.die.net/man/5/gai.conf
|
||||
[networking-gai-blog]: https://thomas-leister.de/en/lxd-prefer-ipv6-outgoing/
|
||||
[networking-gai-rfc]:https://datatracker.ietf.org/doc/html/draft-ietf-v6ops-ula
|
||||
[github-issue-1438]: https://github.com/docker-mailserver/docker-mailserver/issues/1438
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
title: 'Advanced | Email Gathering with Fetchmail'
|
||||
---
|
||||
|
||||
To enable the [fetchmail][fetchmail-website] service to retrieve e-mails, set the environment variable `ENABLE_FETCHMAIL` to `1`. Your `compose.yaml` file should look like following snippet:
|
||||
To enable the [fetchmail][fetchmail-website] service to retrieve e-mails set the environment variable `ENABLE_FETCHMAIL` to `1`. Your `docker-compose.yml` file should look like following snippet:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
|
@ -10,143 +10,115 @@ environment:
|
|||
- FETCHMAIL_POLL=300
|
||||
```
|
||||
|
||||
Generate a file called `fetchmail.cf` and place it in the `docker-data/dms/config/` folder. Your DMS folder should look like this example:
|
||||
Generate a file called `fetchmail.cf` and place it in the `config` folder. Your `docker-mailserver` folder should look like this example:
|
||||
|
||||
```txt
|
||||
├── docker-data/dms/config
|
||||
├── config
|
||||
│ ├── dovecot.cf
|
||||
│ ├── fetchmail.cf
|
||||
│ ├── postfix-accounts.cf
|
||||
│ └── postfix-virtual.cf
|
||||
└── compose.yaml
|
||||
├── docker-compose.yml
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Configuration options for `fetchmail.cf` are covered at the [official fetchmail docs][fetchmail-docs-config] (_see the section "The run control file" and the table with "keyword" column for all settings_).
|
||||
A detailed description of the configuration options can be found in the [online version of the manual page][fetchmail-docs].
|
||||
|
||||
!!! example "Basic `fetchmail.cf` configuration"
|
||||
### IMAP Configuration
|
||||
|
||||
Retrieve mail from `remote-user@somewhere.com` and deliver it to `dms-user@example.com`:
|
||||
!!! example
|
||||
|
||||
```fetchmailrc
|
||||
poll 'mail.somewhere.com'
|
||||
proto imap
|
||||
user 'remote-user'
|
||||
pass 'secret'
|
||||
is 'dms-user@example.com'
|
||||
poll 'imap.example.com' proto imap
|
||||
user 'username'
|
||||
pass 'secret'
|
||||
is 'user1@domain.tld'
|
||||
ssl
|
||||
```
|
||||
|
||||
- `poll` sets the remote mail server to connect to retrieve mail from.
|
||||
- `proto` lets you connect via IMAP or POP3.
|
||||
- `user` and `pass` provide the login credentials for the remote mail service account to access.
|
||||
- `is` configures where the fetched mail will be sent to (_eg: your local DMS account in `docker-data/dms/config/postfix-accounts.cf`_).
|
||||
### POP3 Configuration
|
||||
|
||||
---
|
||||
!!! example
|
||||
|
||||
??? warning "`proto imap` will still delete remote mail once fetched"
|
||||
|
||||
This is due to a separate default setting `no keep`. Adding the setting `keep` to your config on a new line will prevent deleting the remote copy.
|
||||
|
||||
??? example "Multiple users or remote servers"
|
||||
|
||||
The official docs [config examples][fetchmail-config-examples] show a common convention to indent settings on subsequent lines for visually grouping per server.
|
||||
|
||||
=== "Minimal syntax"
|
||||
|
||||
```fetchmailrc
|
||||
poll 'mail.somewhere.com' proto imap
|
||||
user 'john.doe' pass 'secret' is 'johnny@example.com'
|
||||
user 'jane.doe' pass 'secret' is 'jane@example.com'
|
||||
|
||||
poll 'mail.somewhere-else.com' proto pop3
|
||||
user 'john.doe@somewhere-else.com' pass 'secret' is 'johnny@example.com'
|
||||
```
|
||||
|
||||
=== "With optional syntax"
|
||||
|
||||
- `#` for adding comments.
|
||||
- The config file may include "noise" keywords to improve readability.
|
||||
|
||||
```fetchmailrc
|
||||
# Retrieve mail for users `john.doe` and `jane.doe` via IMAP at this remote mail server:
|
||||
poll 'mail.somewhere.com' with proto imap wants:
|
||||
user 'john.doe' with pass 'secret', is 'johnny@example.com' here
|
||||
user 'jane.doe' with pass 'secret', is 'jane@example.com' here
|
||||
|
||||
# Also retrieve mail from this mail server (but via POP3).
|
||||
# NOTE: This could also be all on a single line, or with each key + value as a separate line.
|
||||
# Notice how the remote username includes a full email address,
|
||||
# Some mail servers like DMS use the full email address as the username:
|
||||
poll 'mail.somewhere-else.com' with proto pop3 wants:
|
||||
user 'john.doe@somewhere-else.com' with pass 'secret', is 'johnny@example.com' here
|
||||
```
|
||||
|
||||
!!! tip "`FETCHMAIL_POLL` ENV: Override default polling interval"
|
||||
|
||||
By default the fetchmail service will check every 5 minutes for new mail at the configured mail accounts.
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
# The fetchmail polling interval in seconds:
|
||||
FETCHMAIL_POLL: 60
|
||||
```fetchmailrc
|
||||
poll 'pop3.example.com' proto pop3
|
||||
user 'username'
|
||||
pass 'secret'
|
||||
is 'user2@domain.tld'
|
||||
ssl
|
||||
```
|
||||
|
||||
!!! caution
|
||||
Don’t forget the last line: eg: `is 'user1@domain.tld'`. After `is` you have to specify one email address from the configuration file `config/postfix-accounts.cf`.
|
||||
|
||||
More details how to configure fetchmail can be found in the [fetchmail man page in the chapter “The run control file”][fetchmail-docs-run].
|
||||
|
||||
### Polling Interval
|
||||
|
||||
By default the fetchmail service searches every 5 minutes for new mails on your external mail accounts. You can override this default value by changing the ENV variable `FETCHMAIL_POLL`:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
- FETCHMAIL_POLL=60
|
||||
```
|
||||
|
||||
You must specify a numeric argument which is a polling interval in seconds. The example above polls every minute for new mails.
|
||||
|
||||
## Debugging
|
||||
|
||||
To debug your `fetchmail.cf` configuration run this `setup debug` command:
|
||||
To debug your `fetchmail.cf` configuration run this command:
|
||||
|
||||
```sh
|
||||
docker exec -it dms-container-name setup debug fetchmail
|
||||
./setup.sh debug fetchmail
|
||||
```
|
||||
|
||||
??? example "Sample output of `setup debug fetchmail`"
|
||||
For more informations about the configuration script `setup.sh` [read the corresponding docs][docs-setup].
|
||||
|
||||
```log
|
||||
fetchmail: 6.3.26 querying outlook.office365.com (protocol POP3) at Mon Aug 29 22:11:09 2016: poll started
|
||||
Trying to connect to 132.245.48.18/995...connected.
|
||||
fetchmail: Server certificate:
|
||||
fetchmail: Issuer Organization: Microsoft Corporation
|
||||
fetchmail: Issuer CommonName: Microsoft IT SSL SHA2
|
||||
fetchmail: Subject CommonName: outlook.com
|
||||
fetchmail: Subject Alternative Name: outlook.com
|
||||
fetchmail: Subject Alternative Name: *.outlook.com
|
||||
fetchmail: Subject Alternative Name: office365.com
|
||||
fetchmail: Subject Alternative Name: *.office365.com
|
||||
fetchmail: Subject Alternative Name: *.live.com
|
||||
fetchmail: Subject Alternative Name: *.internal.outlook.com
|
||||
fetchmail: Subject Alternative Name: *.outlook.office365.com
|
||||
fetchmail: Subject Alternative Name: outlook.office.com
|
||||
fetchmail: Subject Alternative Name: attachment.outlook.office.net
|
||||
fetchmail: Subject Alternative Name: attachment.outlook.officeppe.net
|
||||
fetchmail: Subject Alternative Name: *.office.com
|
||||
fetchmail: outlook.office365.com key fingerprint: 3A:A4:58:42:56:CD:BD:11:19:5B:CF:1E:85:16:8E:4D
|
||||
fetchmail: POP3< +OK The Microsoft Exchange POP3 service is ready. [SABFADEAUABSADAAMQBDAEEAMAAwADAANwAuAGUAdQByAHAAcgBkADAAMQAuAHAAcgBvAGQALgBlAHgAYwBoAGEAbgBnAGUAbABhAGIAcwAuAGMAbwBtAA==]
|
||||
fetchmail: POP3> CAPA
|
||||
fetchmail: POP3< +OK
|
||||
fetchmail: POP3< TOP
|
||||
fetchmail: POP3< UIDL
|
||||
fetchmail: POP3< SASL PLAIN
|
||||
fetchmail: POP3< USER
|
||||
fetchmail: POP3< .
|
||||
fetchmail: POP3> USER user1@outlook.com
|
||||
fetchmail: POP3< +OK
|
||||
fetchmail: POP3> PASS *
|
||||
fetchmail: POP3< +OK User successfully logged on.
|
||||
fetchmail: POP3> STAT
|
||||
fetchmail: POP3< +OK 0 0
|
||||
fetchmail: No mail for user1@outlook.com at outlook.office365.com
|
||||
fetchmail: POP3> QUIT
|
||||
fetchmail: POP3< +OK Microsoft Exchange Server 2016 POP3 server signing off.
|
||||
fetchmail: 6.3.26 querying outlook.office365.com (protocol POP3) at Mon Aug 29 22:11:11 2016: poll completed
|
||||
fetchmail: normal termination, status 1
|
||||
```
|
||||
Here a sample output of `./setup.sh debug fetchmail`:
|
||||
|
||||
!!! tip "Troubleshoot with this reference `compose.yaml`"
|
||||
|
||||
[A minimal `compose.yaml` example][fetchmail-compose-example] demonstrates how to run two instances of DMS locally, with one instance configured with `fetchmail.cf` and the other to simulate a remote mail server to fetch from.
|
||||
```log
|
||||
fetchmail: 6.3.26 querying outlook.office365.com (protocol POP3) at Mon Aug 29 22:11:09 2016: poll started
|
||||
Trying to connect to 132.245.48.18/995...connected.
|
||||
fetchmail: Server certificate:
|
||||
fetchmail: Issuer Organization: Microsoft Corporation
|
||||
fetchmail: Issuer CommonName: Microsoft IT SSL SHA2
|
||||
fetchmail: Subject CommonName: outlook.com
|
||||
fetchmail: Subject Alternative Name: outlook.com
|
||||
fetchmail: Subject Alternative Name: *.outlook.com
|
||||
fetchmail: Subject Alternative Name: office365.com
|
||||
fetchmail: Subject Alternative Name: *.office365.com
|
||||
fetchmail: Subject Alternative Name: *.live.com
|
||||
fetchmail: Subject Alternative Name: *.internal.outlook.com
|
||||
fetchmail: Subject Alternative Name: *.outlook.office365.com
|
||||
fetchmail: Subject Alternative Name: outlook.office.com
|
||||
fetchmail: Subject Alternative Name: attachment.outlook.office.net
|
||||
fetchmail: Subject Alternative Name: attachment.outlook.officeppe.net
|
||||
fetchmail: Subject Alternative Name: *.office.com
|
||||
fetchmail: outlook.office365.com key fingerprint: 3A:A4:58:42:56:CD:BD:11:19:5B:CF:1E:85:16:8E:4D
|
||||
fetchmail: POP3< +OK The Microsoft Exchange POP3 service is ready. [SABFADEAUABSADAAMQBDAEEAMAAwADAANwAuAGUAdQByAHAAcgBkADAAMQAuAHAAcgBvAGQALgBlAHgAYwBoAGEAbgBnAGUAbABhAGIAcwAuAGMAbwBtAA==]
|
||||
fetchmail: POP3> CAPA
|
||||
fetchmail: POP3< +OK
|
||||
fetchmail: POP3< TOP
|
||||
fetchmail: POP3< UIDL
|
||||
fetchmail: POP3< SASL PLAIN
|
||||
fetchmail: POP3< USER
|
||||
fetchmail: POP3< .
|
||||
fetchmail: POP3> USER user1@outlook.com
|
||||
fetchmail: POP3< +OK
|
||||
fetchmail: POP3> PASS *
|
||||
fetchmail: POP3< +OK User successfully logged on.
|
||||
fetchmail: POP3> STAT
|
||||
fetchmail: POP3< +OK 0 0
|
||||
fetchmail: No mail for user1@outlook.com at outlook.office365.com
|
||||
fetchmail: POP3> QUIT
|
||||
fetchmail: POP3< +OK Microsoft Exchange Server 2016 POP3 server signing off.
|
||||
fetchmail: 6.3.26 querying outlook.office365.com (protocol POP3) at Mon Aug 29 22:11:11 2016: poll completed
|
||||
fetchmail: normal termination, status 1
|
||||
```
|
||||
|
||||
[docs-setup]: ../../config/setup.sh.md
|
||||
[fetchmail-website]: https://www.fetchmail.info
|
||||
[fetchmail-docs-config]: https://www.fetchmail.info/fetchmail-man.html#the-run-control-file
|
||||
[fetchmail-config-examples]: https://www.fetchmail.info/fetchmail-man.html#configuration-examples
|
||||
[fetchmail-compose-example]: https://github.com/orgs/docker-mailserver/discussions/3994#discussioncomment-9290570
|
||||
[fetchmail-docs]: https://www.fetchmail.info/fetchmail-man.html
|
||||
[fetchmail-docs-run]: https://www.fetchmail.info/fetchmail-man.html#31
|
||||
|
|
|
@ -2,46 +2,34 @@
|
|||
title: 'Mail Forwarding | AWS SES'
|
||||
---
|
||||
|
||||
[Amazon SES (Simple Email Service)][aws-ses] provides a simple way for cloud based applications to send and receive email.
|
||||
!!! warning
|
||||
New configuration, see [Configure Relay Hosts][docs-relay]
|
||||
|
||||
!!! example "Configuration via ENV"
|
||||
Instead of letting postfix deliver mail directly it is possible to configure it to deliver outgoing email via Amazon SES (Simple Email Service). (Receiving inbound email via SES is not implemented.) The configuration follows the guidelines provided by AWS in https://docs.aws.amazon.com/ses/latest/DeveloperGuide/postfix.html, specifically, the `STARTTLS` method.
|
||||
|
||||
[Configure a relay host in DMS][docs::relay] to forward all your mail through AWS SES:
|
||||
As described in the AWS Developer Guide you will have to generate SMTP credentials and define the following two environment variables in the docker-compose.yml with the appropriate values for your AWS SES subscription (the values for `AWS_SES_USERPASS` are the "SMTP username" and "SMTP password" provided when you create SMTP credentials for SES):
|
||||
|
||||
- `RELAY_HOST` should match your [AWS SES region][aws-ses::region].
|
||||
- `RELAY_PORT` should be set to [one of the supported AWS SES SMTP ports][aws-ses::smtp-ports] (_eg: 587 for STARTTLS_).
|
||||
- `RELAY_USER` and `RELAY_PASSWORD` should be set to your [Amazon SES SMTP credentials][aws-ses::credentials].
|
||||
```yaml
|
||||
environment:
|
||||
- AWS_SES_HOST=email-smtp.us-east-1.amazonaws.com
|
||||
- AWS_SES_USERPASS=AKIAXXXXXXXXXXXXXXXX:kqXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
```
|
||||
|
||||
```env
|
||||
RELAY_HOST=email-smtp.us-west-2.amazonaws.com
|
||||
RELAY_PORT=587
|
||||
# Alternative to RELAY_HOST + RELAY_PORT which is compatible with LDAP:
|
||||
DEFAULT_RELAY_HOST=[email-smtp.us-west-2.amazonaws.com]:587
|
||||
If necessary, you can also provide `AWS_SES_PORT`. If not provided, it defaults to 25.
|
||||
|
||||
RELAY_USER=aws-user
|
||||
RELAY_PASSWORD=secret
|
||||
```
|
||||
When you start the container you will see a log line as follows confirming the configuration:
|
||||
|
||||
!!! tip
|
||||
```log
|
||||
Setting up outgoing email via AWS SES host email-smtp.us-east-1.amazonaws.com
|
||||
```
|
||||
|
||||
If you have set up [AWS Easy DKIM][aws-ses::easy-dkim], you can safely skip setting up DKIM as AWS SES will take care of signing your outbound mail.
|
||||
To verify proper operation, send an email to some external account of yours and inspect the mail headers. You will also see the connection to SES in the mail logs. For example:
|
||||
|
||||
!!! note "Verify the relay host is configured correctly"
|
||||
```log
|
||||
May 23 07:09:36 mail postfix/smtp[692]: Trusted TLS connection established to email-smtp.us-east-1.amazonaws.com[107.20.142.169]:25:
|
||||
TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
|
||||
May 23 07:09:36 mail postfix/smtp[692]: 8C82A7E7: to=<someone@example.com>, relay=email-smtp.us-east-1.amazonaws.com[107.20.142.169]:25,
|
||||
delay=0.35, delays=0/0.02/0.13/0.2, dsn=2.0.0, status=sent (250 Ok 01000154dc729264-93fdd7ea-f039-43d6-91ed-653e8547867c-000000)
|
||||
```
|
||||
|
||||
To verify proper operation, send an email to some external account of yours and inspect the mail headers.
|
||||
|
||||
You will also see the connection to SES in the mail logs:
|
||||
|
||||
```log
|
||||
postfix/smtp[692]: Trusted TLS connection established to email-smtp.us-west-1.amazonaws.com[107.20.142.169]:25:
|
||||
TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)
|
||||
postfix/smtp[692]: 8C82A7E7: to=<someone@example.com>, relay=email-smtp.us-west-1.amazonaws.com[107.20.142.169]:25,
|
||||
delay=0.35, delays=0/0.02/0.13/0.2, dsn=2.0.0, status=sent (250 Ok 01000154dc729264-93fdd7ea-f039-43d6-91ed-653e8547867c-000000)
|
||||
```
|
||||
|
||||
[docs::relay]: ./relay-hosts.md
|
||||
[aws-ses]: https://aws.amazon.com/ses/
|
||||
[aws-ses::credentials]: https://docs.aws.amazon.com/ses/latest/dg/smtp-credentials.html
|
||||
[aws-ses::smtp-ports]: https://docs.aws.amazon.com/ses/latest/dg/smtp-connect.html
|
||||
[aws-ses::region]: https://docs.aws.amazon.com/general/latest/gr/ses.html
|
||||
[aws-ses::easy-dkim]: https://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-email-authentication-dkim-easy.html
|
||||
[docs-relay]: ./relay-hosts.md
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
---
|
||||
title: 'Mail Forwarding | Configure Gmail as a relay host'
|
||||
---
|
||||
|
||||
This page provides a guide for configuring DMS to use [GMAIL as an SMTP relay host][gmail-smtp].
|
||||
|
||||
!!! example "Configuration via ENV"
|
||||
|
||||
[Configure a relay host in DMS][docs::relay]. This example shows how the related ENV settings map to the Gmail service config:
|
||||
|
||||
- `RELAY_HOST` should be configured as [advised by Gmail][gmail-smtp::relay-host], there are two SMTP endpoints to choose:
|
||||
- `smtp.gmail.com` (_for a personal Gmail account_)
|
||||
- `smtp-relay.gmail.com` (_when using Google Workspace_)
|
||||
- `RELAY_PORT` should be set to [one of the supported Gmail SMTP ports][gmail-smtp::relay-port] (_eg: 587 for STARTTLS_).
|
||||
- `RELAY_USER` should be your gmail address (`user@gmail.com`).
|
||||
- `RELAY_PASSWORD` should be your [App Password][gmail-smtp::app-password], **not** your personal gmail account password.
|
||||
|
||||
```env
|
||||
RELAY_HOST=smtp.gmail.com
|
||||
RELAY_PORT=587
|
||||
# Alternative to RELAY_HOST + RELAY_PORT which is compatible with LDAP:
|
||||
DEFAULT_RELAY_HOST=[smtp.gmail.com]:587
|
||||
|
||||
RELAY_USER=username@gmail.com
|
||||
RELAY_PASSWORD=secret
|
||||
```
|
||||
|
||||
!!! tip
|
||||
|
||||
- As per our main [relay host docs page][docs::relay], you may prefer to configure your credentials via `setup relay add-auth` instead of the `RELAY_USER` + `RELAY_PASSWORD` ENV.
|
||||
- If you configure for `smtp-relay.gmail.com`, the `DEFAULT_RELAY_HOST` ENV should be all you need as shown in the above example. Credentials can be optional when using Google Workspace (`smtp-relay.gmail.com`), which supports restricting connections to trusted IP addresses.
|
||||
|
||||
!!! note "Verify the relay host is configured correctly"
|
||||
|
||||
To verify proper operation, send an email to an external account of yours and inspect the mail headers.
|
||||
|
||||
You will also see the connection to the Gmail relay host (`smtp.gmail.com`) in the mail logs:
|
||||
|
||||
```log
|
||||
postfix/smtp[910]: Trusted TLS connection established to smtp.gmail.com[64.233.188.109]:587:
|
||||
TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
|
||||
postfix/smtp[910]: 4BCB547D9D: to=<username@gmail.com>, relay=smtp.gmail.com[64.233.188.109]:587,
|
||||
delay=2.9, delays=0.01/0.02/1.7/1.2, dsn=2.0.0, status=sent (250 2.0.0 OK 17... - gsmtp)
|
||||
```
|
||||
|
||||
[docs::relay]: ./relay-hosts.md
|
||||
[gmail-smtp]: https://support.google.com/a/answer/2956491
|
||||
[gmail-smtp::relay-host]: https://support.google.com/a/answer/176600
|
||||
[gmail-smtp::relay-port]: https://support.google.com/a/answer/2956491
|
||||
[gmail-smtp::app-password]: https://support.google.com/accounts/answer/185833
|
|
@ -2,155 +2,89 @@
|
|||
title: 'Mail Forwarding | Relay Hosts'
|
||||
---
|
||||
|
||||
## What is a Relay Host?
|
||||
## Introduction
|
||||
|
||||
An SMTP relay service (_aka relay host / [smarthost][wikipedia::smarthost]_) is an MTA that relays (_forwards_) mail on behalf of third-parties (_it does not manage the mail domains_).
|
||||
Rather than having Postfix deliver mail directly, you can configure Postfix to send mail via another mail relay (smarthost). Examples include [Mailgun](https://www.mailgun.com/), [Sendgrid](https://sendgrid.com/) and [AWS SES](https://aws.amazon.com/ses/).
|
||||
|
||||
- Instead of DMS handling SMTP delivery directly itself (_via Postfix_), it can be configured to delegate delivery by sending all outbound mail through a relay service.
|
||||
- Examples of popular mail relay services: [AWS SES][smarthost::aws-ses], [Mailgun][smarthost::mailgun], [Mailjet][smarthost::mailjet], [SendGrid][smarthost::sendgrid]
|
||||
Depending on the domain of the sender, you may want to send via a different relay, or authenticate in a different way.
|
||||
|
||||
!!! info "When can a relay service can be helpful?"
|
||||
## Basic Configuration
|
||||
|
||||
- Your network provider has blocked outbound connections on port 25 (_required for direct delivery_).
|
||||
- To improve delivery success via better established reputation (trust) of a relay service.
|
||||
Basic configuration is done via environment variables:
|
||||
|
||||
## Configuration
|
||||
* `RELAY_HOST`: _default host to relay mail through, empty will disable this feature_
|
||||
* `RELAY_PORT`: _port on default relay, defaults to port 25_
|
||||
* `RELAY_USER`: _username for the default relay_
|
||||
* `RELAY_PASSWORD`: _password for the default user_
|
||||
|
||||
All mail sent outbound from DMS (_where the sender address is a DMS account or a virtual alias_) will be relayed through the configured relay host.
|
||||
Setting these environment variables will cause mail for all sender domains to be routed via the specified host, authenticating with the user/password combination.
|
||||
|
||||
!!! info "Configuration via ENV"
|
||||
!!! warning
|
||||
For users of the previous `AWS_SES_*` variables: please update your configuration to use these new variables, no other configuration is required.
|
||||
|
||||
Configure the default relayhost with either of these ENV:
|
||||
## Advanced Configuration
|
||||
|
||||
- Preferable (_LDAP compatible_): `DEFAULT_RELAY_HOST` (eg: `[mail.relay-service.com]:25`)
|
||||
- `RELAY_HOST` (eg: `mail.relay-service.com`) + `RELAY_PORT` (default: 25)
|
||||
### Sender-dependent Authentication
|
||||
|
||||
Most relay services also require authentication configured:
|
||||
Sender dependent authentication is done in `config/postfix-sasl-password.cf`. You can create this file manually, or use:
|
||||
|
||||
- `RELAY_USER` + `RELAY_PASSWORD` provides credentials for authenticating with the default relayhost.
|
||||
```sh
|
||||
setup.sh relay add-auth <domain> <username> [<password>]
|
||||
```
|
||||
|
||||
!!! warning "Providing secrets via ENV"
|
||||
An example configuration file looks like this:
|
||||
|
||||
While ENV is convenient, the risk of exposing secrets is higher.
|
||||
```txt
|
||||
@domain1.com relay_user_1:password_1
|
||||
@domain2.com relay_user_2:password_2
|
||||
```
|
||||
|
||||
`setup relay add-auth` is a better alternative, which manages the credentials via a config file.
|
||||
If there is no other configuration, this will cause Postfix to deliver email throught the relay specified in `RELAY_HOST` env variable, authenticating as `relay_user_1` when sent from `domain1.com` and authenticating as `relay_user_2` when sending from domain2.com.
|
||||
|
||||
??? tip "Excluding specific sender domains from relay"
|
||||
!!! note
|
||||
To activate the configuration you must either restart the container, or you can also trigger an update by modifying a mail account.
|
||||
|
||||
You can opt-out with: `setup relay exclude-domain <domain>`
|
||||
### Sender-dependent Relay Host
|
||||
|
||||
Outbound mail from senders of that domain will be sent normally (_instead of through the configured `RELAY_HOST`_).
|
||||
Sender dependent relay hosts are configured in `config/postfix-relaymap.cf`. You can create this file manually, or use:
|
||||
|
||||
!!! warning "When any relay host credentials are configured"
|
||||
```sh
|
||||
setup.sh relay add-domain <domain> <host> [<port>]
|
||||
```
|
||||
|
||||
It will still be expected that mail is sent over a secure connection with credentials provided.
|
||||
An example configuration file looks like this:
|
||||
|
||||
Thus this opt-out feature is rarely practical.
|
||||
```txt
|
||||
@domain1.com [relay1.org]:587
|
||||
@domain2.com [relay2.org]:2525
|
||||
```
|
||||
|
||||
### Advanced Configuration
|
||||
Combined with the previous configuration in `config/postfix-sasl-password.cf`, this will cause Postfix to deliver mail sent from domain1.com via `relay1.org:587`, authenticating as `relay_user_1`, and mail sent from domain2.com via `relay2.org:2525` authenticating as `relay_user_2`.
|
||||
|
||||
When mail is sent, there is support to change the relay service or the credentials configured based on the sender address domain used.
|
||||
!!! note
|
||||
You still have to define `RELAY_HOST` to activate the feature
|
||||
|
||||
We provide this support via two config files:
|
||||
### Excluding Sender Domains
|
||||
|
||||
- Sender-dependent Relay Host: `docker-data/dms/config/postfix-relaymap.cf`
|
||||
- Sender-dependent Authentication: `docker-data/dms/config/postfix-sasl-password.cf`
|
||||
If you want mail sent from some domains to be delivered directly, you can exclude them from being delivered via the default relay by adding them to `config/postfix-relaymap.cf` with no destination. You can also do this via:
|
||||
|
||||
!!! tip "Configure with our `setup relay` commands"
|
||||
```sh
|
||||
setup.sh relay exclude-domain <domain>
|
||||
```
|
||||
|
||||
While you can edit those configs directly, DMS provides these helpful config management commands:
|
||||
Extending the configuration file from above:
|
||||
|
||||
```cli-syntax
|
||||
# Configure a sender domain to use a specific relay host:
|
||||
setup relay add-domain <domain> <host> [<port>]
|
||||
```txt
|
||||
@domain1.com [relay1.org]:587
|
||||
@domain2.com [relay2.org]:2525
|
||||
@domain3.com
|
||||
```
|
||||
|
||||
# Configure relay host credentials for a sender domain to use:
|
||||
setup relay add-auth <domain> <username> [<password>]
|
||||
This will cause email sent from domain3.com to be delivered directly.
|
||||
|
||||
# Optionally avoid relaying from senders of this domain:
|
||||
# NOTE: Only supported when configured with the `RELAY_HOST` ENV!
|
||||
setup relay exclude-domain <domain>
|
||||
```
|
||||
#### References
|
||||
|
||||
!!! example "Config file: `postfix-sasl-password.cf`"
|
||||
Thanks to the author of [this article][1] for the inspiration. This is also worth reading to understand a bit more about how to set up Mailgun to work with this.
|
||||
|
||||
```cf-extra title="docker-data/dms/config/postfix-sasl-password.cf"
|
||||
@domain1.com mailgun-user:secret
|
||||
@domain2.com sendgrid-user:secret
|
||||
[1]: https://community.rackspace.com/products/f/email-products-forum/3897/how-to-setup-postfix-with-a-mailgun-smtp-relay-when-using-multiple-domains
|
||||
|
||||
# NOTE: This must have an exact match with the relay host in `postfix-relaymap.cf`,
|
||||
# `/etc/postfix/relayhost_map`, or the `DEFAULT_RELAY_HOST` ENV.
|
||||
# NOTE: Not supported via our setup CLI, but valid config for Postfix.
|
||||
[email-smtp.us-west-2.amazonaws.com]:2587 aws-user:secret
|
||||
```
|
||||
|
||||
When Postfix needs to lookup credentials for mail sent outbound, the above config will:
|
||||
|
||||
- Authenticate as `mailgun-user` for mail sent with a sender belonging to `@domain1.com`
|
||||
- Authenticate as `sendgrid-user` for mail sent with a sender belonging to `@domain2.com`
|
||||
- Authenticate as `aws-user` for mail sent through a configured AWS SES relay host (any sender domain).
|
||||
|
||||
!!! example "Config file: `postfix-relaymap.cf`"
|
||||
|
||||
```cf-extra title="docker-data/dms/config/postfix-relaymap.cf"
|
||||
@domain1.com [smtp.mailgun.org]:587
|
||||
@domain2.com [smtp.sendgrid.net]:2525
|
||||
|
||||
# Opt-out of relaying:
|
||||
@domain3.com
|
||||
```
|
||||
|
||||
When Postfix sends mail outbound from these sender domains, the above config will:
|
||||
|
||||
- Relay mail through `[smtp.mailgun.org]:587` when mail is sent from a sender of `@domain1.com`
|
||||
- Relay mail through `[smtp.sendgrid.net]:2525` when mail is sent from a sender of `@domain1.com`
|
||||
- Mail with a sender from `@domain3.com` is not sent through a relay (_**Only applicable** when using `RELAY_HOST`_)
|
||||
|
||||
### Technical Details
|
||||
|
||||
- Both the supported ENV and config files for this feature have additional details covered in our ENV docs [Relay Host section][docs::env-relay].
|
||||
- For troubleshooting, a [minimal `compose.yaml` config with several DMS instances][dms-gh::relay-example] demonstrates this feature for local testing.
|
||||
- [Subscribe to this tracking issue][dms-gh::pr-3607] for future improvements intended for this feature.
|
||||
|
||||
!!! abstract "Postfix Settings"
|
||||
|
||||
Internally this feature is implemented in DMS by [`relay.sh`][dms-repo::helpers-relay].
|
||||
|
||||
The `relay.sh` script manages configuring these Postfix settings:
|
||||
|
||||
```cf-extra
|
||||
# Send all outbound mail through this relay service:
|
||||
relayhost = [smtp.relay-service.com]:587
|
||||
|
||||
# Credentials to use:
|
||||
smtp_sasl_password_maps = texthash:/etc/postfix/sasl_passwd
|
||||
# Alternative table type examples which do not require a separate file:
|
||||
#smtp_sasl_password_maps = static:john.doe@relay-service.com:secret
|
||||
#smtp_sasl_password_maps = inline:{ [smtp.relay-service.com]:587=john.doe@relay-service.com:secret }
|
||||
|
||||
## Authentication support:
|
||||
# Required to provide credentials to the relay service:
|
||||
smtp_sasl_auth_enable = yes
|
||||
# Enforces requiring credentials when sending mail outbound:
|
||||
smtp_sasl_security_options = noanonymous
|
||||
# Enforces a secure connection (TLS required) to the relay service:
|
||||
smtp_tls_security_level = encrypt
|
||||
|
||||
## Support for advanced requirements:
|
||||
# Relay service(s) to use instead of direct delivery for specific sender domains:
|
||||
sender_dependent_relayhost_maps = texthash:/etc/postfix/relayhost_map
|
||||
# Support credentials to a relay service(s) that vary by relay host used or sender domain:
|
||||
smtp_sender_dependent_authentication = yes
|
||||
```
|
||||
|
||||
|
||||
[smarthost::mailgun]: https://www.mailgun.com/
|
||||
[smarthost::mailjet]: https://www.mailjet.com
|
||||
[smarthost::sendgrid]: https://sendgrid.com/
|
||||
[smarthost::aws-ses]: https://aws.amazon.com/ses/
|
||||
[wikipedia::smarthost]: https://en.wikipedia.org/wiki/Smart_host
|
||||
|
||||
[docs::env-relay]: ../../environment.md#relay-host
|
||||
[dms-repo::helpers-relay]: https://github.com/docker-mailserver/docker-mailserver/blob/v15.0.0/target/scripts/helpers/relay.sh
|
||||
[dms-gh::pr-3607]: https://github.com/docker-mailserver/docker-mailserver/issues/3607
|
||||
[dms-gh::relay-example]: https://github.com/docker-mailserver/docker-mailserver/issues/3842#issuecomment-1913380639
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
---
|
||||
title: 'Advanced | Email Gathering with Getmail'
|
||||
---
|
||||
|
||||
To enable the [getmail][getmail-website] service to retrieve e-mails set the environment variable `ENABLE_GETMAIL` to `1`. Your `compose.yaml` file should include the following:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
- ENABLE_GETMAIL=1
|
||||
- GETMAIL_POLL=5
|
||||
```
|
||||
|
||||
In your DMS config volume (eg: `docker-data/dms/config/`), add a subdirectory `getmail/` for including your getmail config files (eg: `imap-example.cf`) for each remote account that you want to retrieve mail from and deliver to the mailbox of a DMS account.
|
||||
|
||||
The content of these config files is documented in the next section with an IMAP and POP3 example to reference.
|
||||
|
||||
The directory structure should look similar to this:
|
||||
|
||||
```txt
|
||||
├── docker-data/dms/config
|
||||
│ ├── dovecot.cf
|
||||
│ ├── getmail
|
||||
│ │ ├── getmailrc_general.cf
|
||||
│ │ ├── remote-account1.cf
|
||||
│ │ ├── remote-account2.cf
|
||||
│ ├── postfix-accounts.cf
|
||||
│ └── postfix-virtual.cf
|
||||
├── docker-compose.yml
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
A detailed description of the configuration options can be found in the [online version of the manual page][getmail-docs].
|
||||
|
||||
### Common Options
|
||||
|
||||
The default options added to each `getmail` config are:
|
||||
|
||||
```getmailrc
|
||||
[options]
|
||||
verbose = 0
|
||||
read_all = false
|
||||
delete = false
|
||||
max_messages_per_session = 500
|
||||
received = false
|
||||
delivered_to = false
|
||||
```
|
||||
|
||||
The DMS integration for Getmail generates a `getmailrc` config that prepends the common options of the base config to each remote account config file (`*.cf`) found in the DMS Config Volume `getmail/` directory.
|
||||
|
||||
!!! tip "Change the base options"
|
||||
|
||||
Add your own base config as `getmail/getmailrc_general.cf` into the DMS Config Volume. It will replace the DMS defaults shown above.
|
||||
|
||||
??? example "IMAP Configuration"
|
||||
|
||||
This example will:
|
||||
|
||||
1. Connect to the remote IMAP server from Gmail.
|
||||
2. Retrieve mail from the gmail account `alice` with password `notsecure`.
|
||||
3. Store any mail retrieved from the remote mail-server into DMS for the `user1@example.com` account that DMS manages.
|
||||
|
||||
```getmailrc
|
||||
[retriever]
|
||||
type = SimpleIMAPSSLRetriever
|
||||
server = imap.gmail.com
|
||||
username = alice
|
||||
password = notsecure
|
||||
[destination]
|
||||
type = MDA_external
|
||||
path = /usr/lib/dovecot/deliver
|
||||
allow_root_commands = true
|
||||
arguments =("-d","user1@example.com")
|
||||
```
|
||||
|
||||
??? example "POP3 Configuration"
|
||||
|
||||
Just like the IMAP example above, but instead via POP3 protocol if you prefer that over IMAP.
|
||||
|
||||
```getmailrc
|
||||
[retriever]
|
||||
type = SimplePOP3SSLRetriever
|
||||
server = pop3.gmail.com
|
||||
username = alice
|
||||
password = notsecure
|
||||
[destination]
|
||||
type = MDA_external
|
||||
path = /usr/lib/dovecot/deliver
|
||||
allow_root_commands = true
|
||||
arguments =("-d","user1@example.com")
|
||||
```
|
||||
|
||||
### Polling Interval
|
||||
|
||||
By default the `getmail` service checks external mail accounts for new mail every 5 minutes. That polling interval is configurable via the `GETMAIL_POLL` ENV variable, with a value in minutes (_default: 5, min: 1_):
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
- GETMAIL_POLL=1
|
||||
```
|
||||
|
||||
### XOAUTH2 Authentication
|
||||
|
||||
It is possible to utilize the `getmail-gmail-xoauth-tokens` helper to provide authentication using `xoauth2` for [gmail (example 12)][getmail-docs-xoauth-12] or [Microsoft Office 365 (example 13)][getmail-docs-xoauth-13]
|
||||
|
||||
[getmail-website]: https://www.getmail6.org
|
||||
[getmail-docs]: https://getmail6.org/configuration.html
|
||||
[getmail-docs-xoauth-12]: https://github.com/getmail6/getmail6/blob/1f95606156231f1e074ba62a9baa64f892a92ef8/docs/getmailrc-examples#L286
|
||||
[getmail-docs-xoauth-13]: https://github.com/getmail6/getmail6/blob/1f95606156231f1e074ba62a9baa64f892a92ef8/docs/getmailrc-examples#L351
|
||||
|
||||
## Debugging
|
||||
|
||||
To debug your `getmail` configurations, run this `setup debug` command:
|
||||
|
||||
```sh
|
||||
docker exec -it dms-container-name setup debug getmail
|
||||
```
|
|
@ -4,26 +4,18 @@ title: 'Advanced | Email Filtering with Sieve'
|
|||
|
||||
## User-Defined Sieve Filters
|
||||
|
||||
!!! warning "Advice may be outdated"
|
||||
[Sieve](http://sieve.info/) allows to specify filtering rules for incoming emails that allow for example sorting mails into different folders depending on the title of an email.
|
||||
There are global and user specific filters which are filtering the incoming emails in the following order:
|
||||
|
||||
This section was contributed by the community some time ago and some configuration examples may be outdated.
|
||||
- Global-before -> User specific -> Global-after
|
||||
|
||||
[Sieve][sieve-info] allows to specify filtering rules for incoming emails that allow for example sorting mails into different folders depending on the title of an email.
|
||||
Global filters are applied to EVERY incoming mail for EVERY email address.
|
||||
To specify a global Sieve filter provide a `config/before.dovecot.sieve` or a `config/after.dovecot.sieve` file with your filter rules.
|
||||
If any filter in this filtering chain discards an incoming mail, the delivery process will stop as well and the mail will not reach any following filters(e.g. global-before stops an incoming spam mail: The mail will get discarded and a user-specific filter won't get applied.)
|
||||
|
||||
!!! info "Global vs User order"
|
||||
To specify a user-defined Sieve filter place a `.dovecot.sieve` file into a virtual user's mail folder e.g. `/var/mail/domain.com/user1/.dovecot.sieve`. If this file exists dovecot will apply the filtering rules.
|
||||
|
||||
There are global and user specific filters which are filtering the incoming emails in the following order:
|
||||
|
||||
Global-before -> User specific -> Global-after
|
||||
|
||||
Global filters are applied to EVERY incoming mail for EVERY email address.
|
||||
|
||||
- To specify a global Sieve filter provide a `docker-data/dms/config/before.dovecot.sieve` or a `docker-data/dms/config/after.dovecot.sieve` file with your filter rules.
|
||||
- If any filter in this filtering chain discards an incoming mail, the delivery process will stop as well and the mail will not reach any following filters (e.g. global-before stops an incoming spam mail: The mail will get discarded and a user-specific filter won't get applied.)
|
||||
|
||||
To specify a user-defined Sieve filter place a `.dovecot.sieve` file into a virtual user's mail folder (e.g. `/var/mail/example.com/user1/home/.dovecot.sieve`). If this file exists dovecot will apply the filtering rules.
|
||||
|
||||
It's even possible to install a user provided Sieve filter at startup during users setup: simply include a Sieve file in the `docker-data/dms/config/` path for each user login that needs a filter. The file name provided should be in the form `<user_login>.dovecot.sieve`, so for example for `user1@example.com` you should provide a Sieve file named `docker-data/dms/config/user1@example.com.dovecot.sieve`.
|
||||
It's even possible to install a user provided Sieve filter at startup during users setup: simply include a Sieve file in the `config` path for each user login that need a filter. The file name provided should be in the form `<user_login>.dovecot.sieve`, so for example for `user1@domain.tld` you should provide a Sieve file named `config/user1@domain.tld.dovecot.sieve`.
|
||||
|
||||
An example of a sieve filter that moves mails to a folder `INBOX/spam` depending on the sender address:
|
||||
|
||||
|
@ -40,17 +32,16 @@ An example of a sieve filter that moves mails to a folder `INBOX/spam` depending
|
|||
```
|
||||
|
||||
!!! warning
|
||||
|
||||
That folders have to exist beforehand if sieve should move them.
|
||||
|
||||
Another example of a sieve filter that forward mails to a different address:
|
||||
|
||||
!!! example
|
||||
!!! example
|
||||
|
||||
```sieve
|
||||
require ["copy"];
|
||||
|
||||
redirect :copy "user2@not-example.com";
|
||||
redirect :copy "user2@otherdomain.tld";
|
||||
```
|
||||
|
||||
Just forward all incoming emails and do not save them locally:
|
||||
|
@ -58,99 +49,17 @@ Just forward all incoming emails and do not save them locally:
|
|||
!!! example
|
||||
|
||||
```sieve
|
||||
redirect "user2@not-example.com";
|
||||
redirect "user2@otherdomain.tld";
|
||||
```
|
||||
|
||||
You can also use external programs to filter or pipe (process) messages by adding executable scripts in `docker-data/dms/config/sieve-pipe` or `docker-data/dms/config/sieve-filter`.
|
||||
|
||||
This can be used in lieu of a local alias file, for instance to forward an email to a webservice.
|
||||
|
||||
- These programs can then be referenced by filename, by all users.
|
||||
- Note that the process running the scripts run as a privileged user.
|
||||
- For further information see [Dovecot's docs][dovecot-docs::sieve-pipe].
|
||||
You can also use external programs to filter or pipe (process) messages by adding executable scripts in `config/sieve-pipe` or `config/sieve-filter`. This can be used in lieu of a local alias file, for instance to forward an email to a webservice. These programs can then be referenced by filename, by all users. Note that the process running the scripts run as a privileged user. For further information see [Dovecot's wiki](https://wiki.dovecot.org/Pigeonhole/Sieve/Plugins/Pipe).
|
||||
|
||||
```sieve
|
||||
require ["vnd.dovecot.pipe"];
|
||||
pipe "external-program";
|
||||
```
|
||||
|
||||
For more examples or a detailed description of the Sieve language have a look at [the official site][sieve-info::examples]. Other resources are available on the internet where you can find several [examples][third-party::sieve-examples].
|
||||
|
||||
[dovecot-docs::sieve-pipe]: https://doc.dovecot.org/configuration_manual/sieve/plugins/extprograms/#pigeonhole-plugin-extprograms
|
||||
[sieve-info]: http://sieve.info/
|
||||
[sieve-info::examples]: http://sieve.info/examplescripts
|
||||
[third-party::sieve-examples]: https://support.tigertech.net/sieve#sieve-example-rules-jmp
|
||||
|
||||
## Automatic Sorting Based on Sub-addresses { #subaddress-mailbox-routing }
|
||||
|
||||
When mail is delivered to your account, it is possible to organize storing mail into folders by the [subaddress (tag)][docs::accounts-subaddressing] used.
|
||||
|
||||
!!! example "Example: `user+<tag>@example.com` to `INBOX/<Tag>`"
|
||||
|
||||
This example sorts mail into inbox folders by their tag:
|
||||
|
||||
```sieve title="docker-data/dms/config/user@example.com.dovecot.sieve"
|
||||
require ["envelope", "fileinto", "mailbox", "subaddress", "variables"];
|
||||
|
||||
# Check if the mail recipient address has a tag (:detail)
|
||||
if envelope :detail :matches "to" "*" {
|
||||
# Create a variable `tag`, with the the captured `to` value normalized (SoCIAL => Social)
|
||||
set :lower :upperfirst "tag" "${1}";
|
||||
|
||||
# Store the mail into a folder with the tag name, nested under your inbox folder:
|
||||
if mailboxexists "INBOX.${tag}" {
|
||||
fileinto "INBOX.${tag}";
|
||||
} else {
|
||||
fileinto :create "INBOX.${tag}";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When receiving mail for `user+social@example.com` it would be delivered into the `INBOX/Social` folder.
|
||||
|
||||
??? tip "Only redirect mail for specific tags"
|
||||
|
||||
If you want to only handle specific tags, you could replace the envelope condition and tag assignment from the prior example with:
|
||||
|
||||
```sieve title="docker-data/dms/config/user@example.com.dovecot.sieve"
|
||||
# Instead of `:matches`, use the default comparator `:is` (exact match)
|
||||
if envelope :detail "to" "social" {
|
||||
set "tag" "Social";
|
||||
```
|
||||
|
||||
```sieve title="docker-data/dms/config/user@example.com.dovecot.sieve"
|
||||
# Alternatively you can also provide a list of values to match:
|
||||
if envelope :detail "to" ["azure", "aws"] {
|
||||
set "tag" "Cloud";
|
||||
```
|
||||
|
||||
```sieve title="docker-data/dms/config/user@example.com.dovecot.sieve"
|
||||
# Similar to `:matches`, except `:regex` provides enhanced pattern matching.
|
||||
# NOTE: This example needs you to `require` the "regex" extension
|
||||
if envelope :detail :regex "to" "^cloud-(azure|aws)$" {
|
||||
# Normalize the captured azure/aws tag as the resolved value is no longer fixed:
|
||||
set :lower :upperfirst "vendor" "${1}";
|
||||
# If a `.` exists in the tag, it will create nested folders:
|
||||
set "tag" "Cloud.${vendor}";
|
||||
```
|
||||
|
||||
**NOTE:** There is no need to lowercase the tag in the conditional as the [`to` value is a case-insensitive check][sieve-docs::envelope].
|
||||
|
||||
??? abstract "Technical Details"
|
||||
|
||||
- Dovecot supports this feature via the _Sieve subaddress extension_ ([RFC 5233][rfc::5233::sieve-subaddress]).
|
||||
- Only a single tag per subaddress is supported. Any additional tag delimiters are part of the tag value itself.
|
||||
- The Dovecot setting [`recipient_delimiter`][dovecot-docs::config::recipient_delimiter] (default: `+`) configures the tag delimiter. This is where the `local-part` of the recipient address will split at, providing the `:detail` (tag) value for Sieve.
|
||||
|
||||
---
|
||||
|
||||
`INBOX` is the [default namespace configured by Dovecot][dovecot-docs::namespace].
|
||||
|
||||
- If you omit the `INBOX.` prefix from the sieve script above, the mailbox (folder) for that tag is created at the top-level alongside your Trash and Junk folders.
|
||||
- The `.` between `INBOX` and `${tag}` is important as a [separator to distinguish mailbox names][dovecot-docs::mailbox-names]. This can vary by mailbox format or configuration. DMS uses [`Maildir`][dovecot-docs::mailbox-formats::maildir] by default, which uses `.` as the separator.
|
||||
- [`lmtp_save_to_detail_mailbox = yes`][dovecot-docs::config::lmtp_save_to_detail_mailbox] can be set in `/etc/dovecot/conf.d/20-lmtp.conf`:
|
||||
- This implements the feature globally, except for the tag normalization and `INBOX.` prefix parts of the example script.
|
||||
- However, if the sieve script is also present, the script has precedence and will handle this task instead when the condition is successful, otherwise falling back to the global feature.
|
||||
For more examples or a detailed description of the Sieve language have a look at [the official site](http://sieve.info/examplescripts). Other resources are available on the internet where you can find several [examples](https://support.tigertech.net/sieve#sieve-example-rules-jmp).
|
||||
|
||||
## Manage Sieve
|
||||
|
||||
|
@ -158,31 +67,19 @@ The [Manage Sieve](https://doc.dovecot.org/admin_manual/pigeonhole_managesieve_s
|
|||
|
||||
!!! example
|
||||
|
||||
```yaml title="compose.yaml"
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
ports:
|
||||
- "4190:4190"
|
||||
environment:
|
||||
- ENABLE_MANAGESIEVE=1
|
||||
```
|
||||
|
||||
All user defined sieve scripts that are managed by ManageSieve are stored in the user's home folder in `/var/mail/example.com/user1/home/sieve`. Just one Sieve script might be active for a user and is sym-linked to `/var/mail/example.com/user1/home/.dovecot.sieve` automatically.
|
||||
All user defined sieve scripts that are managed by ManageSieve are stored in the user's home folder in `/var/mail/domain.com/user1/sieve`. Just one sieve script might be active for a user and is sym-linked to `/var/mail/domain.com/user1/.dovecot.sieve` automatically.
|
||||
|
||||
!!! note
|
||||
|
||||
ManageSieve makes sure to not overwrite an existing `.dovecot.sieve` file. If a user activates a new sieve script the old one is backed up and moved to the `sieve` folder.
|
||||
ManageSieve makes sure to not overwrite an existing `.dovecot.sieve` file. If a user activates a new sieve script the old one is backuped and moved to the `sieve` folder.
|
||||
|
||||
The extension is known to work with the following ManageSieve clients:
|
||||
|
||||
- **[Sieve Editor](https://github.com/thsmi/sieve)** a portable standalone application based on the former Thunderbird plugin.
|
||||
- **[Kmail](https://kontact.kde.org/components/kmail/)** the mail client of [KDE](https://kde.org/)'s Kontact Suite.
|
||||
|
||||
[docs::accounts-subaddressing]: ../account-management/overview.md#sub-addressing
|
||||
|
||||
[dovecot-docs::namespace]: https://doc.dovecot.org/configuration_manual/namespace/
|
||||
[dovecot-docs::mailbox-names]: https://doc.dovecot.org/configuration_manual/sieve/usage/#mailbox-names
|
||||
[dovecot-docs::mailbox-formats::maildir]: https://doc.dovecot.org/admin_manual/mailbox_formats/maildir/#maildir-mbox-format
|
||||
[dovecot-docs::config::lmtp_save_to_detail_mailbox]: https://doc.dovecot.org/settings/core/#core_setting-lmtp_save_to_detail_mailbox
|
||||
[dovecot-docs::config::recipient_delimiter]: https://doc.dovecot.org/settings/core/#core_setting-recipient_delimiter
|
||||
|
||||
[rfc::5233::sieve-subaddress]: https://datatracker.ietf.org/doc/html/rfc5233
|
||||
[sieve-docs::envelope]: https://thsmi.github.io/sieve-reference/en/test/core/envelope.html
|
||||
- **Sieve Editor** a portable standalone application based on the former Thunderbird plugin (https://github.com/thsmi/sieve).
|
||||
|
|
|
@ -2,52 +2,40 @@
|
|||
title: 'Maintenance | Update and Cleanup'
|
||||
---
|
||||
|
||||
[`containrrr/watchtower`][watchtower-dockerhub] is a service that monitors Docker images for updates, automatically applying them to running containers.
|
||||
## Automatic Update
|
||||
|
||||
!!! example "Automatic image updates + cleanup"
|
||||
Docker images are handy but it can get a a hassle to keep them updated. Also when a repository is automated you want to get these images when they get out.
|
||||
|
||||
Run a `watchtower` container with access to `docker.sock`, enabling the service to manage Docker:
|
||||
One could setup a complex action/hook-based workflow using probes, but there is a nice, easy to use docker image that solves this issue and could prove useful: [`watchtower`](https://hub.docker.com/r/containrrr/watchtower).
|
||||
|
||||
```yaml title="compose.yaml"
|
||||
services:
|
||||
watchtower:
|
||||
image: containrrr/watchtower:latest
|
||||
# Automatic cleanup (removes older image pulls from wasting disk space):
|
||||
environment:
|
||||
- WATCHTOWER_CLEANUP=true
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
```
|
||||
A docker-compose example:
|
||||
|
||||
!!! tip "The image tag used for a container is monitored for updates (eg: `:latest`, `:edge`, `:13`)"
|
||||
```yaml
|
||||
services:
|
||||
watchtower:
|
||||
restart: always
|
||||
image: containrrr/watchtower:latest
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
```
|
||||
|
||||
The automatic update support is **only for updates to that specific image tag**.
|
||||
For more details, see the [manual](https://containrrr.github.io/watchtower/)
|
||||
|
||||
- Your container will not update to a new major version tag (_unless using `:latest`_).
|
||||
- Omit the minor or patch portion of the semver tag to receive updates for the omitted portion (_eg: `13` will represent the latest minor + patch release of `v13`_).
|
||||
## Automatic Cleanup
|
||||
|
||||
!!! tip "Updating only specific containers"
|
||||
When you are pulling new images in automatically, it would be nice to have them cleaned up as well. There is also a docker image for this: [`spotify/docker-gc`](https://hub.docker.com/r/spotify/docker-gc/).
|
||||
|
||||
By default the `watchtower` service will check every 24 hours for new image updates to pull, based on currently running containers (_**not restricted** to only those running within your `compose.yaml`_).
|
||||
A docker-compose example:
|
||||
|
||||
Images eligible for updates can configured with a [custom `command`][docker-docs-compose-command] that provides a list of container names, or via other supported options (eg: labels). This configuration is detailed in the [`watchtower` docs][watchtower-docs].
|
||||
```yaml
|
||||
services:
|
||||
docker-gc:
|
||||
restart: always
|
||||
image: spotify/docker-gc:latest
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
```
|
||||
|
||||
!!! info "Manual cleanup"
|
||||
For more details, see the [manual](https://github.com/spotify/docker-gc/blob/master/README.md)
|
||||
|
||||
`watchtower` also supports running on-demand with `docker run` or `compose.yaml` via the `--run-once` option.
|
||||
|
||||
You can alternatively invoke cleanup of Docker storage directly with:
|
||||
|
||||
- [`docker image prune --all`][docker-docs-prune-image]
|
||||
- [`docker system prune --all`][docker-docs-prune-system] (_also removes unused containers, networks, build cache_).
|
||||
|
||||
If you omit the `--all` option, this will instead only remove ["dangling" content][docker-prune-dangling] (_eg: Orphaned images_).
|
||||
|
||||
[watchtower-dockerhub]: https://hub.docker.com/r/containrrr/watchtower
|
||||
[watchtower-cleanup]: https://containrrr.github.io/watchtower/arguments/#cleanup
|
||||
[watchtower-docs]: https://containrrr.dev/watchtower/
|
||||
|
||||
[docker-docs-compose-command]: https://docs.docker.com/compose/compose-file/05-services/#command
|
||||
[docker-docs-prune-image]: https://docs.docker.com/engine/reference/commandline/image_prune/
|
||||
[docker-docs-prune-system]: https://docs.docker.com/engine/reference/commandline/system_prune/
|
||||
[docker-prune-dangling]: https://stackoverflow.com/questions/45142528/what-is-a-dangling-image-and-what-is-an-unused-image/60756668#60756668
|
||||
Or you can just use the [`--cleanup`](https://containrrr.github.io/watchtower/arguments/#cleanup) option provided by `containrrr/watchtower`.
|
||||
|
|
|
@ -4,69 +4,16 @@ hide:
|
|||
- toc # Hide Table of Contents for this page
|
||||
---
|
||||
|
||||
## Volumes
|
||||
This is a list of all configuration files and directories which are optional or automatically generated in your `config` directory.
|
||||
|
||||
DMS has several locations in the container which may be worth persisting externally via [Docker Volumes][docker-docs::volumes].
|
||||
|
||||
- Often you will want to prefer [bind mount volumes][docker-docs::volumes::bind-mount] for easy access to files at a local location on your filesystem.
|
||||
- As a convention for our docs and example configs, the local location has the common prefix `docker-data/dms/` for grouping these related volumes.
|
||||
|
||||
!!! info "Reference - Volmes for DMS"
|
||||
|
||||
Our docs may refer to these DMS specific volumes only by name, or the host/container path for brevity.
|
||||
|
||||
- [Config](#volumes-config): `docker-data/dms/config/` => `/tmp/docker-mailserver/`
|
||||
- [Mail Storage](#volumes-mail): `docker-data/dms/mail-data/` => `/var/mail/`
|
||||
- [State](#volumes-state): `docker-data/dms/mail-state/` => `/var/mail-state/`
|
||||
- [Logs](#volumes-log): `docker-data/dms/mail-logs/` => `/var/log/mail/`
|
||||
|
||||
### Mail Storage Volume { #volumes-mail }
|
||||
|
||||
This is the location where mail is delivered to your mailboxes.
|
||||
|
||||
### State Volume { #volumes-state }
|
||||
|
||||
Run-time specific state lives here, but so does some data you may want to keep if a failure event occurs (_crash, power loss_).
|
||||
|
||||
!!! example "Examples of relevant data"
|
||||
|
||||
- The Postfix queue (eg: mail pending delivery attempt)
|
||||
- Fail2Ban blocks.
|
||||
- ClamAV signature updates.
|
||||
- Redis storage for Rspamd.
|
||||
|
||||
!!! info "When a volume is mounted to `/var/mail-state/`"
|
||||
|
||||
- Service run-time data is [consolidated into the `/var/mail-state/` directory][mail-state-folders]. Otherwise the original locations vary and would need to be mounted individually.
|
||||
- The original locations are updated with symlinks to redirect to their new path in `/var/mail-state/` (_eg: `/var/lib/redis` => `/var/mail-state/lib-redis/`_).
|
||||
|
||||
Supported services: Postfix, Dovecot, Fail2Ban, Amavis, PostGrey, ClamAV, SpamAssassin, Rspamd & Redis, Fetchmail, Getmail, LogRotate, PostSRSd, MTA-STS.
|
||||
|
||||
!!! tip
|
||||
|
||||
Sometimes it is helpful to disable this volume when troubleshooting to verify if the data stored here is in a bad state (_eg: caused by a failure event_).
|
||||
|
||||
[mail-state-folders]: https://github.com/docker-mailserver/docker-mailserver/blob/v13.3.1/target/scripts/startup/setup.d/mail_state.sh#L13-L33
|
||||
|
||||
### Logs Volume { #volumes-log }
|
||||
|
||||
This can be a useful volume to persist for troubleshooting needs for the full set of log files.
|
||||
|
||||
### Config Volume { #volumes-config }
|
||||
|
||||
Most configuration files for Postfix, Dovecot, etc. are persisted here.
|
||||
|
||||
This is a list of all configuration files and directories which are optional, automatically generated / updated by our `setup` CLI, or other internal scripts.
|
||||
|
||||
#### Directories
|
||||
## Directories
|
||||
|
||||
- **sieve-filter:** directory for sieve filter scripts. (Docs: [Sieve][docs-sieve])
|
||||
- **sieve-pipe:** directory for sieve pipe scripts. (Docs: [Sieve][docs-sieve])
|
||||
- **opendkim:** DKIM directory. Auto-configurable via [`setup.sh config dkim`][docs-setupsh]. (Docs: [DKIM][docs-dkim])
|
||||
- **ssl:** SSL Certificate directory if `SSL_TYPE` is set to `self-signed` or `custom`. (Docs: [SSL][docs-ssl])
|
||||
- **rspamd:** Override directory for custom settings when using Rspamd (Docs: [Rspamd][docs-rspamd-override-d])
|
||||
- **ssl:** SSL Certificate directory. Auto-configurable via [`setup.sh config ssl`][docs-setupsh]. (Docs: [SSL][docs-ssl])
|
||||
|
||||
#### Files
|
||||
## Files
|
||||
|
||||
- **{user_email_address}.dovecot.sieve:** User specific Sieve filter file. (Docs: [Sieve][docs-sieve])
|
||||
- **before.dovecot.sieve:** Global Sieve filter file, applied prior to the `${login}.dovecot.sieve` filter. (Docs: [Sieve][docs-sieve])
|
||||
|
@ -77,38 +24,33 @@ This is a list of all configuration files and directories which are optional, au
|
|||
- **postfix-send-access.cf:** List of users denied sending. Modify via [`setup.sh email restrict`][docs-setupsh].
|
||||
- **postfix-receive-access.cf:** List of users denied receiving. Modify via [`setup.sh email restrict`][docs-setupsh].
|
||||
- **postfix-virtual.cf:** Alias configuration file. Modify via [`setup.sh alias`][docs-setupsh].
|
||||
- **postfix-sasl-password.cf:** listing of relayed domains with their respective `<username>:<password>`. Modify via `setup.sh relay add-auth <domain> <username> [<password>]`. (Docs: [Relay-Hosts Auth][docs::relay-hosts::advanced])
|
||||
- **postfix-relaymap.cf:** domain-specific relays and exclusions. Modify via `setup.sh relay add-domain` and `setup.sh relay exclude-domain`. (Docs: [Relay-Hosts Senders][docs::relay-hosts::advanced])
|
||||
- **postfix-sasl-password.cf:** listing of relayed domains with their respective `<username>:<password>`. Modify via `setup.sh relay add-auth <domain> <username> [<password>]`. (Docs: [Relay-Hosts Auth][docs-relayhosts-senderauth])
|
||||
- **postfix-relaymap.cf:** domain-specific relays and exclusions. Modify via `setup.sh relay add-domain` and `setup.sh relay exclude-domain`. (Docs: [Relay-Hosts Senders][docs-relayhosts-senderhost])
|
||||
- **postfix-regexp.cf:** Regular expression alias file. (Docs: [Aliases][docs-aliases-regex])
|
||||
- **ldap-users.cf:** Configuration for the virtual user mapping `virtual_mailbox_maps`. See the [`setup-stack.sh`][github-commit-setup-stack.sh-L411] script.
|
||||
- **ldap-groups.cf:** Configuration for the virtual alias mapping `virtual_alias_maps`. See the [`setup-stack.sh`][github-commit-setup-stack.sh-L411] script.
|
||||
- **ldap-aliases.cf:** Configuration for the virtual alias mapping `virtual_alias_maps`. See the [`setup-stack.sh`][github-commit-setup-stack.sh-L411] script.
|
||||
- **ldap-domains.cf:** Configuration for the virtual domain mapping `virtual_mailbox_domains`. See the [`setup-stack.sh`][github-commit-setup-stack.sh-L411] script.
|
||||
- **whitelist_clients.local:** Whitelisted domains, not considered by postgrey. Enter one host or domain per line.
|
||||
- **spamassassin-rules.cf:** Anti-spam rules for Spamassassin. (Docs: [FAQ - SpamAssassin Rules][docs-faq-spamrules])
|
||||
- **spamassassin-rules.cf:** Antispam rules for Spamassassin. (Docs: [FAQ - SpamAssassin Rules][docs-faq-spamrules])
|
||||
- **fail2ban-fail2ban.cf:** Additional config options for `fail2ban.cf`. (Docs: [Fail2Ban][docs-fail2ban])
|
||||
- **fail2ban-jail.cf:** Additional config options for fail2ban's jail behavior. (Docs: [Fail2Ban][docs-fail2ban])
|
||||
- **fail2ban-jail.cf:** Additional config options for fail2ban's jail behaviour. (Docs: [Fail2Ban][docs-fail2ban])
|
||||
- **amavis.cf:** replaces the `/etc/amavis/conf.d/50-user` file
|
||||
- **dovecot.cf:** replaces `/etc/dovecot/local.conf`. (Docs: [Override Dovecot Defaults][docs-override-dovecot])
|
||||
- **dovecot-quotas.cf:** list of custom quotas per mailbox. (Docs: [Accounts][docs-accounts-quota])
|
||||
- **user-patches.sh:** this file will be run after all configuration files are set up, but before the postfix, amavis and other daemons are started. (Docs: [FAQ - How to adjust settings with the `user-patches.sh` script][docs-faq-userpatches])
|
||||
- **rspamd/custom-commands.conf:** list of simple commands to adjust Rspamd modules in an easy way (Docs: [Rspamd][docs-rspamd-commands])
|
||||
|
||||
[docker-docs::volumes]: https://docs.docker.com/storage/volumes/
|
||||
[docker-docs::volumes::bind-mount]: https://docs.docker.com/storage/bind-mounts/
|
||||
|
||||
[docs-accounts-quota]: ../../config/account-management/provisioner/file.md#quotas
|
||||
[docs-aliases-regex]: ../../config/account-management/provisioner/file.md#configuring-regex-aliases
|
||||
[docs-dkim]: ../../config/best-practices/dkim_dmarc_spf.md#dkim
|
||||
[docs-accounts-quota]: ../../config/user-management/accounts.md#notes
|
||||
[docs-aliases-regex]: ../../config/user-management/aliases.md#configuring-regexp-aliases
|
||||
[docs-dkim]: ../../config/best-practices/dkim.md
|
||||
[docs-fail2ban]: ../../config/security/fail2ban.md
|
||||
[docs-faq-spamrules]: ../../faq.md#how-can-i-manage-my-custom-spamassassin-rules
|
||||
[docs-faq-userpatches]: ../../faq.md#how-to-adjust-settings-with-the-user-patchessh-script
|
||||
[docs-override-postfix]: ./override-defaults/postfix.md
|
||||
[docs-override-dovecot]: ./override-defaults/dovecot.md
|
||||
[docs::relay-hosts::advanced]: ./mail-forwarding/relay-hosts.md#advanced-configuration
|
||||
[docs-relayhosts-senderauth]: ./mail-forwarding/relay-hosts.md#sender-dependent-authentication
|
||||
[docs-relayhosts-senderhost]: ./mail-forwarding/relay-hosts.md#sender-dependent-relay-host
|
||||
[docs-sieve]: ./mail-sieve.md
|
||||
[docs-setupsh]: ../../config/setup.sh.md
|
||||
[docs-ssl]: ../../config/security/ssl.md
|
||||
[docs-rspamd-override-d]: ../security/rspamd.md#manually
|
||||
[docs-rspamd-commands]: ../security/rspamd.md#with-the-help-of-a-custom-file
|
||||
[github-commit-setup-stack.sh-L411]: https://github.com/docker-mailserver/docker-mailserver/blob/941e7acdaebe271eaf3d296b36d4d81df4c54b90/target/scripts/startup/setup-stack.sh#L411
|
||||
|
|
|
@ -4,17 +4,17 @@ title: 'Override the Default Configs | Dovecot'
|
|||
|
||||
## Add Configuration
|
||||
|
||||
The Dovecot default configuration can easily be extended providing a `docker-data/dms/config/dovecot.cf` file.
|
||||
[Dovecot documentation](https://doc.dovecot.org/configuration_manual/) remains the best place to find configuration options.
|
||||
The Dovecot default configuration can easily be extended providing a `config/dovecot.cf` file.
|
||||
[Dovecot documentation](https://wiki.dovecot.org) remains the best place to find configuration options.
|
||||
|
||||
Your DMS folder structure should look like this example:
|
||||
Your `docker-mailserver` folder should look like this example:
|
||||
|
||||
```txt
|
||||
├── docker-data/dms/config
|
||||
├── config
|
||||
│ ├── dovecot.cf
|
||||
│ ├── postfix-accounts.cf
|
||||
│ └── postfix-virtual.cf
|
||||
├── compose.yaml
|
||||
├── docker-compose.yml
|
||||
└── README.md
|
||||
```
|
||||
|
||||
|
@ -26,7 +26,7 @@ mail_max_userip_connections = 100
|
|||
|
||||
Another important option is the `default_process_limit` (defaults to `100`). If high-security mode is enabled you'll need to make sure this count is higher than the maximum number of users that can be logged in simultaneously.
|
||||
|
||||
This limit is quickly reached if users connect to DMS with multiple end devices.
|
||||
This limit is quickly reached if users connect to the mail server with multiple end devices.
|
||||
|
||||
## Override Configuration
|
||||
|
||||
|
@ -34,17 +34,10 @@ For major configuration changes it’s best to override the dovecot configuratio
|
|||
|
||||
```yaml
|
||||
services:
|
||||
mailserver:
|
||||
mail:
|
||||
volumes:
|
||||
- ./docker-data/dms/mail-data/:/var/mail/
|
||||
- ./docker-data/dms/config/dovecot/10-master.conf:/etc/dovecot/conf.d/10-master.conf
|
||||
```
|
||||
|
||||
You will first need to obtain the configuration from the running container (_where `mailserver` is the container name_):
|
||||
|
||||
```sh
|
||||
mkdir -p ./docker-data/dms/config/dovecot
|
||||
docker cp mailserver:/etc/dovecot/conf.d/10-master.conf ./docker-data/dms/config/dovecot/10-master.conf
|
||||
- maildata:/var/mail
|
||||
- ./config/dovecot/10-master.conf:/etc/dovecot/conf.d/10-master.conf
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
@ -52,15 +45,15 @@ docker cp mailserver:/etc/dovecot/conf.d/10-master.conf ./docker-data/dms/config
|
|||
To debug your dovecot configuration you can use:
|
||||
|
||||
- This command: `./setup.sh debug login doveconf | grep <some-keyword>`
|
||||
- Or: `docker exec -it mailserver doveconf | grep <some-keyword>`
|
||||
- Or: `docker exec -it <your-container-name> doveconf | grep <some-keyword>`
|
||||
|
||||
!!! note
|
||||
[`setup.sh`][github-file-setupsh] is included in the DMS repository. Make sure to use the one matching your image version release.
|
||||
[`setup.sh`][github-file-setupsh] is included in the `docker-mailserver` repository. Make sure to grap the one matching your image version.
|
||||
|
||||
The file `docker-data/dms/config/dovecot.cf` is copied internally to `/etc/dovecot/local.conf`. To verify the file content, run:
|
||||
The `config/dovecot.cf` is copied internally to `/etc/dovecot/local.conf`. To check this file run:
|
||||
|
||||
```sh
|
||||
docker exec -it mailserver cat /etc/dovecot/local.conf
|
||||
docker exec -it <your-container-name> cat /etc/dovecot/local.conf
|
||||
```
|
||||
|
||||
[github-file-setupsh]: https://github.com/docker-mailserver/docker-mailserver/blob/master/setup.sh
|
||||
|
|
|
@ -2,34 +2,33 @@
|
|||
title: 'Override the Default Configs | Postfix'
|
||||
---
|
||||
|
||||
[Our default Postfix configuration](https://github.com/docker-mailserver/docker-mailserver/blob/master/target/postfix/main.cf) can easily be extended to add parameters or modify existing ones by providing a `docker-data/dms/config/postfix-main.cf`. This file uses the same format as Postfix `main.cf` does ([See official docs](http://www.postfix.org/postconf.5.html) for all parameters and syntax rules).
|
||||
The Postfix default configuration can easily be extended by providing a `config/postfix-main.cf` in postfix format.
|
||||
This can also be used to add configuration that is not in our default configuration.
|
||||
|
||||
!!! example "Example"
|
||||
|
||||
One can easily increase the [backwards-compatibility level](http://www.postfix.org/postconf.5.html#compatibility_level) and set new Postscreen options:
|
||||
|
||||
```cf
|
||||
# increase the compatibility level from 2 (default) to 3
|
||||
compatibility_level = 3
|
||||
# set a threshold value for Spam detection
|
||||
postscreen_dnsbl_threshold = 4
|
||||
```
|
||||
|
||||
|
||||
!!! help "How are your changes applied?"
|
||||
|
||||
The custom configuration you supply is appended to the default configuration located at `/etc/postfix/main.cf`, and then `postconf -nf` is run to remove earlier duplicate entries that have since been replaced. This happens early during container startup before Postfix is started.
|
||||
|
||||
---
|
||||
|
||||
Similarly, it is possible to add a custom `docker-data/dms/config/postfix-master.cf` file that will override the standard `master.cf`. **Note**: Each line in this file will be passed to `postconf -P`, i.e. **the file is not appended as a whole** to `/etc/postfix/master.cf` like `docker-data/dms/config/postfix-main.cf`! The expected format is `<service_name>/<type>/<parameter>`, for example:
|
||||
For example, one common use of this file is for increasing the default maximum message size:
|
||||
|
||||
```cf
|
||||
# increase maximum message size
|
||||
message_size_limit = 52428800
|
||||
```
|
||||
|
||||
That specific example is now supported and can be handled by setting `POSTFIX_MESSAGE_SIZE_LIMIT`.
|
||||
|
||||
!!! seealso
|
||||
|
||||
[Postfix documentation](http://www.postfix.org/documentation.html) remains the best place to find configuration options.
|
||||
|
||||
Each line in the provided file will be loaded into postfix.
|
||||
|
||||
In the same way it is possible to add a custom `config/postfix-master.cf` file that will override the standard `master.cf`. Each line in the file will be passed to `postconf -P`. The expected format is `<service_name>/<type>/<parameter>`, for example:
|
||||
|
||||
```cf
|
||||
# adjust the submission "reject_unlisted_recipient" option
|
||||
submission/inet/smtpd_reject_unlisted_recipient=no
|
||||
```
|
||||
|
||||
!!! attention
|
||||
Run `postconf -P` in the container without arguments to see the active master options.
|
||||
|
||||
!!! note
|
||||
There should be no space between the parameter and the value.
|
||||
|
||||
Run `postconf -Mf` in the container without arguments to see the active master options.
|
||||
Have a look at the code for more information.
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
---
|
||||
title: 'Custom User Changes & Patches | Scripting'
|
||||
---
|
||||
|
||||
If you'd like to change, patch or alter files or behavior of DMS, you can use a script.
|
||||
|
||||
In case you cloned this repository, you can copy the file [`user-patches.sh.dist` (_under `config/`_)][github-file-userpatches] with `#!sh cp config/user-patches.sh.dist docker-data/dms/config/user-patches.sh` in order to create the `user-patches.sh` script.
|
||||
|
||||
If you are managing your directory structure yourself, create a `docker-data/dms/config/` directory and add the `user-patches.sh` file yourself.
|
||||
|
||||
``` sh
|
||||
# 1. Either create the docker-data/dms/config/ directory yourself
|
||||
# or let docker-mailserver create it on initial startup
|
||||
/tmp $ mkdir -p docker-data/dms/config/ && cd docker-data/dms/config/
|
||||
|
||||
# 2. Create the user-patches.sh file and edit it
|
||||
/tmp/docker-data/dms/config $ touch user-patches.sh
|
||||
/tmp/docker-data/dms/config $ nano user-patches.sh
|
||||
```
|
||||
|
||||
The contents could look like this:
|
||||
|
||||
``` sh
|
||||
#!/bin/bash
|
||||
|
||||
cat >/etc/amavis/conf.d/50-user << "END"
|
||||
use strict;
|
||||
|
||||
$undecipherable_subject_tag = undef;
|
||||
$admin_maps_by_ccat{+CC_UNCHECKED} = undef;
|
||||
|
||||
#------------ Do not modify anything below this line -------------
|
||||
1; # ensure a defined return
|
||||
END
|
||||
|
||||
```
|
||||
|
||||
And you're done. The user patches script runs right before starting daemons. That means, all the other configuration is in place, so the script can make final adjustments.
|
||||
|
||||
!!! note
|
||||
Many "patches" can already be done with the Docker Compose-/Stack-file. Adding hostnames to `/etc/hosts` is done with the `#!yaml extra_hosts:` section, `sysctl` commands can be managed with the `#!yaml sysctls:` section, etc.
|
||||
|
||||
[github-file-userpatches]: https://github.com/docker-mailserver/docker-mailserver/blob/master/config-examples/user-patches.sh
|
|
@ -1,177 +0,0 @@
|
|||
---
|
||||
title: 'Advanced | Podman'
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Podman is a daemonless container engine for developing, managing, and running OCI Containers on your Linux System.
|
||||
|
||||
!!! warning "About Support for Podman"
|
||||
|
||||
Please note that Podman **is not** officially supported as DMS is built and verified on top of the _Docker Engine_. This content is entirely community supported. If you find errors, please open an issue and provide a PR.
|
||||
|
||||
!!! warning "About this Guide"
|
||||
|
||||
This guide was tested with Fedora 34 using `systemd` and `firewalld`. Moreover, it requires Podman version >= 3.2. You may be able to substitute `dnf` - Fedora's package manager - with others such as `apt`.
|
||||
|
||||
!!! warning "About Security"
|
||||
|
||||
Running podman in rootless mode requires additional modifications in order to keep your mailserver secure.
|
||||
Make sure to read the related documentation.
|
||||
|
||||
## Installation in Rootfull Mode
|
||||
|
||||
While using Podman, you can just manage docker-mailserver as what you did with Docker. Your best friend `setup.sh` includes the minimum code in order to support Podman since it's 100% compatible with the Docker CLI.
|
||||
|
||||
The installation is basically the same. Podman v3.2 introduced a RESTful API that is 100% compatible with the Docker API, so you can use Docker Compose with Podman easily. Install Podman and Docker Compose with your package manager first.
|
||||
|
||||
```bash
|
||||
sudo dnf install podman docker-compose
|
||||
```
|
||||
|
||||
Then enable `podman.socket` using `systemctl`.
|
||||
|
||||
```bash
|
||||
systemctl enable --now podman.socket
|
||||
```
|
||||
|
||||
This will create a unix socket locate under `/run/podman/podman.sock`, which is the entrypoint of Podman's API. Now, configure docker-mailserver and start it.
|
||||
|
||||
```bash
|
||||
export DOCKER_HOST="unix:///run/podman/podman.sock"
|
||||
docker compose up -d mailserver
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
You should see that docker-mailserver is running now.
|
||||
|
||||
### Self-start in Rootfull Mode
|
||||
|
||||
Podman is daemonless, that means if you want docker-mailserver self-start while boot up the system, you have to generate a systemd file with Podman CLI.
|
||||
|
||||
```bash
|
||||
podman generate systemd mailserver > /etc/systemd/system/mailserver.service
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now mailserver.service
|
||||
```
|
||||
|
||||
## Installation in Rootless Mode
|
||||
|
||||
Running rootless containers is one of Podman's major features. But due to some restrictions, deploying docker-mailserver in rootless mode is not as easy compared to rootfull mode.
|
||||
|
||||
- a rootless container is running in a user namespace so you cannot bind ports lower than 1024
|
||||
- a rootless container's systemd file can only be placed in folder under `~/.config`
|
||||
- a rootless container can result in an open relay, make sure to read the [security section](#security-in-rootless-mode).
|
||||
|
||||
Also notice that Podman's rootless mode is not about running as a non-root user inside the container, but about the mapping of (normal, non-root) host users to root inside the container.
|
||||
|
||||
!!! warning
|
||||
|
||||
In order to make rootless DMS work we must modify some settings in the Linux system, it requires some basic linux server knowledge so don't follow this guide if you not sure what this guide is talking about. Podman rootfull mode and Docker are still good and security enough for normal daily usage.
|
||||
|
||||
First, enable `podman.socket` in systemd's userspace with a non-root user.
|
||||
|
||||
```bash
|
||||
systemctl enable --now --user podman.socket
|
||||
```
|
||||
|
||||
The socket file should be located at `/var/run/user/$(id -u)/podman/podman.sock`. Then, modify `compose.yaml` to make sure all ports are bindings are on non-privileged ports.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
mailserver:
|
||||
ports:
|
||||
- "10025:25" # SMTP (explicit TLS => STARTTLS)
|
||||
- "10143:143" # IMAP4 (explicit TLS => STARTTLS)
|
||||
- "10465:465" # ESMTP (implicit TLS)
|
||||
- "10587:587" # ESMTP (explicit TLS => STARTTLS)
|
||||
- "10993:993" # IMAP4 (implicit TLS)
|
||||
```
|
||||
|
||||
Then, setup your `mailserver.env` file follow the documentation and use Docker Compose to start the container.
|
||||
|
||||
```bash
|
||||
export DOCKER_HOST="unix:///var/run/user/$(id -u)/podman/podman.sock"
|
||||
docker compose up -d mailserver
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
### Security in Rootless Mode
|
||||
|
||||
In rootless mode, podman resolves all incoming IPs as localhost, which results in an open gateway in the default configuration. There are two workarounds to fix this problem, both of which have their own drawbacks.
|
||||
|
||||
#### Enforce authentication from localhost
|
||||
|
||||
The `PERMIT_DOCKER` variable in the `mailserver.env` file allows to specify trusted networks that do not need to authenticate. If the variable is left empty, only requests from localhost and the container IP are allowed, but in the case of rootless podman any IP will be resolved as localhost. Setting `PERMIT_DOCKER=none` enforces authentication also from localhost, which prevents sending unauthenticated emails.
|
||||
|
||||
#### Use the slip4netns network driver
|
||||
|
||||
The second workaround is slightly more complicated because the `compose.yaml` has to be modified.
|
||||
As shown in the [fail2ban section][docs::fail2ban::rootless] the `slirp4netns` network driver has to be enabled.
|
||||
This network driver enables podman to correctly resolve IP addresses but it is not compatible with
|
||||
user defined networks which might be a problem depending on your setup.
|
||||
|
||||
[Rootless Podman][rootless::podman] requires adding the value `slirp4netns:port_handler=slirp4netns` to the `--network` CLI option, or `network_mode` setting in your `compose.yaml`.
|
||||
|
||||
You must also add the ENV `NETWORK_INTERFACE=tap0`, because Podman uses a [hard-coded interface name][rootless::podman::interface] for `slirp4netns`.
|
||||
|
||||
!!! example
|
||||
|
||||
```yaml
|
||||
services:
|
||||
mailserver:
|
||||
network_mode: "slirp4netns:port_handler=slirp4netns"
|
||||
environment:
|
||||
- NETWORK_INTERFACE=tap0
|
||||
...
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
`podman-compose` is not compatible with this configuration.
|
||||
|
||||
### Self-start in Rootless Mode
|
||||
|
||||
Generate a systemd file with the Podman CLI.
|
||||
|
||||
```bash
|
||||
podman generate systemd mailserver > ~/.config/systemd/user/mailserver.service
|
||||
systemctl --user daemon-reload
|
||||
systemctl enable --user --now mailserver.service
|
||||
```
|
||||
|
||||
Systemd's user space service is only started when a specific user logs in and stops when you log out. In order to make it to start with the system, we need to enable linger with `loginctl`
|
||||
|
||||
```bash
|
||||
loginctl enable-linger <username>
|
||||
```
|
||||
|
||||
Remember to run this command as root user.
|
||||
|
||||
### Port Forwarding
|
||||
|
||||
When it comes to forwarding ports using `firewalld`, see [these port forwarding docs][firewalld-port-forwarding] for more information.
|
||||
|
||||
```bash
|
||||
firewall-cmd --permanent --add-forward-port=port=<25|143|465|587|993>:proto=<tcp>:toport=<10025|10143|10465|10587|10993>
|
||||
...
|
||||
|
||||
# After you set all ports up.
|
||||
firewall-cmd --reload
|
||||
```
|
||||
|
||||
Notice that this will only open the access to the external client. If you want to access privileges port in your server, do this:
|
||||
|
||||
```bash
|
||||
firewall-cmd --permanent --direct --add-rule <ipv4|ipv6> nat OUTPUT 0 -p <tcp|udp> -o lo --dport <25|143|465|587|993> -j REDIRECT --to-ports <10025|10143|10465|10587|10993>
|
||||
...
|
||||
# After you set all ports up.
|
||||
firewall-cmd --reload
|
||||
```
|
||||
|
||||
Just map all the privilege port with non-privilege port you set in compose.yaml before as root user.
|
||||
|
||||
[docs::fail2ban::rootless]: ../security/fail2ban.md#rootless-container
|
||||
[rootless::podman]: https://github.com/containers/podman/blob/v3.4.1/docs/source/markdown/podman-run.1.md#--networkmode---net
|
||||
[rootless::podman::interface]: https://github.com/containers/podman/blob/v3.4.1/libpod/networking_slirp4netns.go#L264
|
||||
[firewalld-port-forwarding]: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/securing_networks/using-and-configuring-firewalld_securing-networks#port-forwarding_using-and-configuring-firewalld
|
|
@ -4,9 +4,7 @@ hide:
|
|||
- toc # Hide Table of Contents for this page
|
||||
---
|
||||
|
||||
# Auto-Discovery of Services
|
||||
|
||||
Email auto-discovery means a client email is able to automagically find out about what ports and security options to use, based on the mail server URI. It can help simplify the tedious / confusing task of adding own's email account for non-tech savvy users.
|
||||
Email auto-discovery means a client email is able to automagically find out about what ports and security options to use, based on the mail server URL. It can help simplify the tedious / confusing task of adding own's email account for non-tech savvy users.
|
||||
|
||||
Email clients will search for auto-discoverable settings and prefill almost everything when a user enters its email address :heart:
|
||||
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
---
|
||||
title: 'Best Practices | DKIM'
|
||||
---
|
||||
|
||||
DKIM is a security measure targeting email spoofing. It is greatly recommended one activates it.
|
||||
|
||||
!!! seealso
|
||||
See [the Wikipedia page](https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail) for more details on DKIM.
|
||||
|
||||
## Enabling DKIM Signature
|
||||
|
||||
To enable DKIM signature, **you must have created at least one email account**. Once its done, just run the following command to generate the signature:
|
||||
|
||||
```sh
|
||||
./setup.sh config dkim
|
||||
```
|
||||
|
||||
After generating DKIM keys, you should restart the mail server. DNS edits may take a few minutes to hours to propagate. The script assumes you're being in the directory where the `config/` directory is located. The default keysize when generating the signature is 4096 bits for now. If you need to change it (e.g. your DNS provider limits the size), then provide the size as the first parameter of the command:
|
||||
|
||||
```sh
|
||||
./setup.sh config dkim keysize <keysize>
|
||||
```
|
||||
|
||||
For LDAP systems that do not have any directly created user account you can run the following command (since `8.0.0`) to generate the signature by additionally providing the desired domain name (if you have multiple domains use the command multiple times or provide a comma-separated list of domains):
|
||||
|
||||
```sh
|
||||
./setup.sh config dkim keysize <key-size> domain <domain.tld>[,<domain2.tld>]
|
||||
```
|
||||
|
||||
Now the keys are generated, you can configure your DNS server with DKIM signature, simply by adding a TXT record. If you have direct access to your DNS zone file, then it's only a matter of pasting the content of `config/opendkim/keys/domain.tld/mail.txt` in your `domain.tld.hosts` zone.
|
||||
|
||||
```console
|
||||
$ dig mail._domainkey.domain.tld TXT
|
||||
---
|
||||
;; ANSWER SECTION
|
||||
mail._domainkey.<DOMAIN> 300 IN TXT "v=DKIM1; k=rsa; p=AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN/AZERTYUIOPQSDFGHJKLMWXCVBN"
|
||||
```
|
||||
|
||||
## Configuration using a Web Interface
|
||||
|
||||
1. Generate a new record of the type `TXT`.
|
||||
2. Paste `mail._domainkey` the `Name` txt field.
|
||||
3. In the `Target` or `Value` field fill in `v=DKIM1; k=rsa; p=AZERTYUGHJKLMWX...`.
|
||||
4. In `TTL` (time to live): Time span in seconds. How long the DNS server should cache the `TXT` record.
|
||||
5. Save.
|
||||
|
||||
!!! note
|
||||
Sometimes the key in `config/opendkim/keys/domain.tld/mail.txt` can be on multiple lines. If so then you need to concatenate the values in the TXT record:
|
||||
|
||||
```console
|
||||
$ dig mail._domainkey.domain.tld TXT
|
||||
---
|
||||
;; ANSWER SECTION
|
||||
mail._domainkey.<DOMAIN> 300 IN TXT "v=DKIM1; k=rsa; "
|
||||
"p=AZERTYUIOPQSDF..."
|
||||
"asdfQWERTYUIOPQSDF..."
|
||||
```
|
||||
|
||||
The target (or value) field must then have all the parts together: `v=DKIM1; k=rsa; p=AZERTYUIOPQSDF...asdfQWERTYUIOPQSDF...`
|
||||
|
||||
## Verify-Only
|
||||
|
||||
If you want DKIM to only _verify_ incoming emails, the following version of `/etc/opendkim.conf` may be useful (right now there is no easy mechanism for installing it other than forking the repo):
|
||||
|
||||
```conf
|
||||
# This is a simple config file verifying messages only
|
||||
|
||||
#LogWhy yes
|
||||
Syslog yes
|
||||
SyslogSuccess yes
|
||||
|
||||
Socket inet:12301@localhost
|
||||
PidFile /var/run/opendkim/opendkim.pid
|
||||
|
||||
ReportAddress postmaster@my-domain.com
|
||||
SendReports yes
|
||||
|
||||
Mode v
|
||||
```
|
||||
|
||||
## Switch Off DKIM
|
||||
|
||||
Simply remove the DKIM key by recreating (not just relaunching) the mailserver container.
|
||||
|
||||
## Debugging
|
||||
|
||||
- [DKIM-verifer](https://addons.mozilla.org/en-US/thunderbird/addon/dkim-verifier): A add-on for the mail client Thunderbird.
|
||||
- You can debug your TXT records with the `dig` tool.
|
||||
|
||||
```console
|
||||
$ dig TXT mail._domainkey.domain.tld
|
||||
---
|
||||
; <<>> DiG 9.10.3-P4-Debian <<>> TXT mail._domainkey.domain.tld
|
||||
;; global options: +cmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39669
|
||||
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
|
||||
|
||||
;; OPT PSEUDOSECTION:
|
||||
; EDNS: version: 0, flags:; udp: 512
|
||||
;; QUESTION SECTION:
|
||||
;mail._domainkey.domain.tld. IN TXT
|
||||
|
||||
;; ANSWER SECTION:
|
||||
mail._domainkey.domain.tld. 3600 IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxBSjG6RnWAdU3oOlqsdf2WC0FOUmU8uHVrzxPLW2R3yRBPGLrGO1++yy3tv6kMieWZwEBHVOdefM6uQOQsZ4brahu9lhG8sFLPX4MaKYN/NR6RK4gdjrZu+MYSdfk3THgSbNwIDAQAB"
|
||||
|
||||
;; Query time: 50 msec
|
||||
;; SERVER: 127.0.1.1#53(127.0.1.1)
|
||||
;; WHEN: Wed Sep 07 18:22:57 CEST 2016
|
||||
;; MSG SIZE rcvd: 310
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
!!! warning "Key sizes >=4096-bit"
|
||||
|
||||
Keys of 4096 bits could de denied by some mailservers. According to https://tools.ietf.org/html/rfc6376 keys are preferably between 512 and 2048 bits. See issue [#1854][github-issue-1854].
|
||||
|
||||
[github-issue-1854]: https://github.com/docker-mailserver/docker-mailserver/issues/1854
|
|
@ -1,385 +0,0 @@
|
|||
# DKIM, DMARC & SPF
|
||||
|
||||
Cloudflare has written an [article about DKIM, DMARC and SPF][cloudflare-dkim-dmarc-spf] that we highly recommend you to read to get acquainted with the topic.
|
||||
|
||||
!!! note "Rspamd vs Individual validators"
|
||||
|
||||
With v12.0.0, Rspamd was integrated into DMS. It can perform validations for DKIM, DMARC and SPF as part of the `spam-score-calculation` for an email. DMS provides individual alternatives for each validation that can be used instead of deferring to Rspamd:
|
||||
|
||||
- DKIM: `opendkim` is used as a milter (like Rspamd)
|
||||
- DMARC: `opendmarc` is used as a milter (like Rspamd)
|
||||
- SPF: `policyd-spf` is used in Postfix's `smtpd_recipient_restrictions`
|
||||
|
||||
In a future release Rspamd will become the default for these validations, with a deprecation notice issued prior to the removal of the above alternatives.
|
||||
|
||||
We encourage everyone to prefer Rspamd via `ENABLE_RSPAMD=1`.
|
||||
|
||||
!!! warning "DNS Caches & Propagation"
|
||||
|
||||
While modern DNS providers are quick, it may take minutes or even hours for new DNS records to become available / propagate.
|
||||
|
||||
## DKIM
|
||||
|
||||
!!! quote "What is DKIM"
|
||||
|
||||
DomainKeys Identified Mail (DKIM) is an email authentication method designed to detect forged sender addresses in email (email spoofing), a technique often used in phishing and email spam.
|
||||
|
||||
[Source][wikipedia-dkim]
|
||||
|
||||
When DKIM is enabled:
|
||||
|
||||
1. Inbound mail will verify any included DKIM signatures
|
||||
2. Outbound mail is signed (_when your sending domain has a configured DKIM key_)
|
||||
|
||||
DKIM requires a public/private key pair to enable **signing (_via private key_)** your outgoing mail, while the receiving end must query DNS to **verify (_via public key_)** that the signature is trustworthy.
|
||||
|
||||
??? info "Verification expiry"
|
||||
|
||||
Unlike your TLS certificate, your DKIM keypair does not have a fixed expiry associated to it.
|
||||
|
||||
|
||||
Instead, an expiry may be included in your DKIM signature for each mail sent, where a receiver will [refuse to validate the signature for an email after that expiry date][dkim-verification-expiry-refusal]. This is an added precaution to mitigate malicious activity like "DKIM replay attacks", where an already delivered email from a third-party with a trustworthy DKIM signature is leveraged by a spammer when sending mail to an MTA which verifies the DKIM signature successfully, enabling the spammer to bypass spam protections.
|
||||
|
||||
Unlike a TLS handshake where you are authenticating trust with future communications, with DKIM once the mail has been received and trust of the signature has been verified, the value of verifying the signature again at a later date is less meaningful since the signature was to ensure no tampering had occurred during delivery through the network.
|
||||
|
||||
??? tip "DKIM key rotation"
|
||||
|
||||
You can rotate your DKIM keypair by switching to a new DKIM selector (_and DNS updates_), while the previous key and selector remains valid for verification until the last mail signed with that key reaches it's expiry.
|
||||
|
||||
DMS does not provide any automation or support for key rotation, [nor is it likely to provide a notable security benefit][gh-discussion::dkim-key-rotation-expiry] to the typical small scale DMS deployment.
|
||||
|
||||
### Generating Keys
|
||||
|
||||
You'll need to repeat this process if you add any new domains.
|
||||
|
||||
You should have:
|
||||
|
||||
- At least one [email account setup][docs-accounts]
|
||||
- Attached a [volume for config][docs-volumes-config] to persist the generated files to local storage
|
||||
|
||||
!!! example "Creating DKIM Keys"
|
||||
|
||||
DKIM keys can be generated with good defaults by running:
|
||||
|
||||
```bash
|
||||
docker exec -it <CONTAINER NAME> setup config dkim
|
||||
```
|
||||
|
||||
If you need to generate your keys with different settings, check the `help` output for supported config options and examples:
|
||||
|
||||
```bash
|
||||
docker exec -it <CONTAINER NAME> setup config dkim help
|
||||
```
|
||||
|
||||
As described by the help output, you may need to use the `domain` option explicitly when you're using LDAP or Rspamd.
|
||||
|
||||
??? info "Changing the key size"
|
||||
|
||||
The keypair generated for using with DKIM presently defaults to RSA-2048. This is a good size but you can lower the security to `1024-bit`, or increase it to `4096-bit` (_discouraged as that is excessive_).
|
||||
|
||||
To generate a key with different size (_for RSA 1024-bit_) run:
|
||||
|
||||
```sh
|
||||
setup config dkim keysize 1024
|
||||
```
|
||||
|
||||
!!! warning "RSA Key Sizes >= 4096 Bit"
|
||||
|
||||
According to [RFC 8301][rfc-8301], keys are preferably between 1024 and 2048 bits. Keys of size 4096-bit or larger may not be compatible to all systems your mail is intended for.
|
||||
|
||||
You [should not need a key length beyond 2048-bit][gh-issue::dkim-length]. If 2048-bit does not meet your security needs, you may want to instead consider adopting key rotation or switching from RSA to ECC keys for DKIM.
|
||||
|
||||
??? note "You may need to specify mail domains explicitly"
|
||||
|
||||
Required when using LDAP and Rspamd.
|
||||
|
||||
`setup config dkim` will generate DKIM keys for what is assumed as the primary mail domain (_derived from the FQDN assigned to DMS, minus any subdomain_).
|
||||
|
||||
When the DMS FQDN is `mail.example.com` or `example.com`, by default this command will generate DKIM keys for `example.com` as the primary domain for your users mail accounts (eg: `hello@example.com`).
|
||||
|
||||
The DKIM generation does not have support to query LDAP for additional mail domains it should know about. If the primary mail domain is not sufficient, then you must explicitly specify any extra domains via the `domain` option:
|
||||
|
||||
```sh
|
||||
# ENABLE_OPENDKIM=1 (default):
|
||||
setup config dkim domain 'example.com,another-example.com'
|
||||
|
||||
# ENABLE_RSPAMD=1 + ENABLE_OPENDKIM=0:
|
||||
setup config dkim domain example.com
|
||||
setup config dkim domain another-example.com
|
||||
```
|
||||
|
||||
!!! info "OpenDKIM with `ACCOUNT_PROVISIONER=FILE`"
|
||||
|
||||
When DMS uses this configuration, it will by default also detect mail domains (_from accounts added via `setup email add`_), generating additional DKIM keys.
|
||||
|
||||
DKIM is currently supported by either OpenDKIM or Rspamd:
|
||||
|
||||
=== "OpenDKIM"
|
||||
|
||||
OpenDKIM is currently [enabled by default][docs-env-opendkim].
|
||||
|
||||
After running `setup config dkim`, your new DKIM key files (_and OpenDKIM config_) have been added to `/tmp/docker-mailserver/opendkim/`.
|
||||
|
||||
!!! info "Restart required"
|
||||
|
||||
After restarting DMS, outgoing mail will now be signed with your new DKIM key(s) :tada:
|
||||
|
||||
=== "Rspamd"
|
||||
|
||||
Requires opt-in via [`ENABLE_RSPAMD=1`][docs-env-rspamd] (_and disable the default OpenDKIM: `ENABLE_OPENDKIM=0`_).
|
||||
|
||||
Rspamd provides DKIM support through two separate modules:
|
||||
|
||||
1. [Verifying DKIM signatures from inbound mail][rspamd-docs-dkim-checks] is enabled by default.
|
||||
2. [Signing outbound mail with your DKIM key][rspamd-docs-dkim-signing] needs additional setup (key + dns + config).
|
||||
|
||||
??? warning "Using Multiple Domains"
|
||||
|
||||
If you have multiple domains, you need to:
|
||||
|
||||
- Create a key wth `docker exec -it <CONTAINER NAME> setup config dkim domain <DOMAIN>` for each domain DMS should sign outgoing mail for.
|
||||
- Provide a custom `dkim_signing.conf` (for which an example is shown below), as the default config only supports one domain.
|
||||
|
||||
!!! info "About the Helper Script"
|
||||
|
||||
The script will persist the keys in `/tmp/docker-mailserver/rspamd/dkim/`. Hence, if you are already using the default volume mounts, the keys are persisted in a volume. The script also restarts Rspamd directly, so changes take effect without restarting DMS.
|
||||
|
||||
The script provides you with log messages along the way of creating keys. In case you want to read the complete log, use `-v` (verbose) or `-vv` (very verbose).
|
||||
|
||||
---
|
||||
|
||||
In case you have not already provided a default DKIM signing configuration, the script will create one and write it to `/etc/rspamd/override.d/dkim_signing.conf`. If this file already exists, it will not be overwritten.
|
||||
|
||||
When you're already using [the `rspamd/override.d/` directory][docs-rspamd-config-dropin], the file is created inside your volume and therefore persisted correctly. If you are not using `rspamd/override.d/`, you will need to persist the file yourself (otherwise it is lost on container restart).
|
||||
|
||||
An example of what a default configuration file for DKIM signing looks like can be found by expanding the example below.
|
||||
|
||||
??? example "DKIM Signing Module Configuration Examples"
|
||||
|
||||
A simple configuration could look like this:
|
||||
|
||||
```cf
|
||||
# documentation: https://rspamd.com/doc/modules/dkim_signing.html
|
||||
|
||||
enabled = true;
|
||||
|
||||
sign_authenticated = true;
|
||||
sign_local = true;
|
||||
|
||||
use_domain = "header";
|
||||
use_redis = false; # don't change unless Redis also provides the DKIM keys
|
||||
use_esld = true;
|
||||
check_pubkey = true; # you want to use this in the beginning
|
||||
|
||||
selector = "mail";
|
||||
# The path location is searched for a DKIM key with these variables:
|
||||
# - `$domain` is sourced from the MIME mail message `From` header
|
||||
# - `$selector` is configured for `mail` (as a default fallback)
|
||||
path = "/tmp/docker-mailserver/dkim/keys/$domain/$selector.private";
|
||||
|
||||
# domain specific configurations can be provided below:
|
||||
domain {
|
||||
example.com {
|
||||
path = "/tmp/docker-mailserver/rspamd/dkim/mail.private";
|
||||
selector = "mail";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As shown next:
|
||||
|
||||
- You can add more domains into the `domain { ... }` section (in the following example: `example.com` and `example.org`).
|
||||
- A domain can also be configured with multiple selectors and keys within a `selectors [ ... ]` array (in the following example, this is done for `example.org`).
|
||||
|
||||
```cf
|
||||
# ...
|
||||
|
||||
domain {
|
||||
example.com {
|
||||
path = /tmp/docker-mailserver/rspamd/example.com/ed25519.private";
|
||||
selector = "dkim-ed25519";
|
||||
}
|
||||
example.org {
|
||||
selectors [
|
||||
{
|
||||
path = "/tmp/docker-mailserver/rspamd/dkim/example.org/rsa.private";
|
||||
selector = "dkim-rsa";
|
||||
},
|
||||
{
|
||||
path = "/tmp/docker-mailserver/rspamd/dkim/example.org/ed25519.private";
|
||||
selector = "dkim-ed25519";
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
??? warning "Support for DKIM Keys using ED25519"
|
||||
|
||||
This modern elliptic curve is supported by Rspamd, but support by third-parties for [verifying Ed25519 DKIM signatures is unreliable][dkim-ed25519-support].
|
||||
|
||||
If you sign your mail with this key type, you should include RSA as a fallback, like shown in the above example.
|
||||
|
||||
??? tip "Let Rspamd Check Your Keys"
|
||||
|
||||
When `check_pubkey = true;` is set, Rspamd will query the DNS record for each DKIM selector, verifying each public key matches the private key configured.
|
||||
|
||||
If there is a mismatch, a warning will be emitted to the Rspamd log `/var/log/mail/rspamd.log`.
|
||||
|
||||
### DNS Record { #dkim-dns }
|
||||
|
||||
When mail signed with your DKIM key is sent from your mail server, the receiver needs to check a DNS `TXT` record to verify the DKIM signature is trustworthy.
|
||||
|
||||
!!! example "Configuring DNS - DKIM record"
|
||||
|
||||
When you generated your key in the previous step, the DNS data was saved into a file `<selector>.txt` (default: `mail.txt`). Use this content to update your [DNS via Web Interface][dns::example-webui] or directly edit your [DNS Zone file][dns::wikipedia-zonefile]:
|
||||
|
||||
=== "Web Interface"
|
||||
|
||||
Create a new record:
|
||||
|
||||
| Field | Value |
|
||||
| ----- | ------------------------------------------------------------------------------ |
|
||||
| Type | `TXT` |
|
||||
| Name | `<selector>._domainkey` (_default: `mail._domainkey`_) |
|
||||
| TTL | Use the default (_otherwise [3600 seconds is appropriate][dns::digicert-ttl]_) |
|
||||
| Data | File content within `( ... )` (_formatted as advised below_) |
|
||||
|
||||
When using Rspamd, the helper script has already provided you with the contents (the "Data" field) of the DNS record you need to create - you can just copy-paste this text.
|
||||
|
||||
=== "DNS Zone file"
|
||||
|
||||
`<selector>.txt` is already formatted as a snippet for adding to your [DNS Zone file][dns::wikipedia-zonefile].
|
||||
|
||||
Just copy/paste the file contents into your existing DNS zone. The `TXT` value has been split into separate strings every 255 characters for compatibility.
|
||||
|
||||
??? info "`<selector>.txt` - Formatting the `TXT` record value correctly"
|
||||
|
||||
This file was generated for use within a [DNS zone file][dns::wikipedia-zonefile]. The file name uses the DKIM selector it was generated with (default DKIM selector is `mail`, which creates `mail.txt`_).
|
||||
|
||||
For your DNS setup, DKIM support needs to create a `TXT` record to store the public key for mail clients to use. `TXT` records with values that are longer than 255 characters need to be split into multiple parts. This is why the generated `<selector>.txt` file (_containing your public key for use with DKIM_) has multiple value parts wrapped within double-quotes between `(` and `)`.
|
||||
|
||||
A DNS web-interface may handle this separation internally instead, and [could expect the value provided all as a single line][dns::webui-dkim] instead of split. When that is required, you'll need to manually format the value as described below.
|
||||
|
||||
Your generated DNS record file (`<selector>.txt`) should look similar to this:
|
||||
|
||||
```txt
|
||||
mail._domainkey IN TXT ( "v=DKIM1; k=rsa; "
|
||||
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQMMqhb1S52Rg7VFS3EC6JQIMxNDdiBmOKZvY5fiVtD3Z+yd9ZV+V8e4IARVoMXWcJWSR6xkloitzfrRtJRwOYvmrcgugOalkmM0V4Gy/2aXeamuiBuUc4esDQEI3egmtAsHcVY1XCoYfs+9VqoHEq3vdr3UQ8zP/l+FP5UfcaJFCK/ZllqcO2P1GjIDVSHLdPpRHbMP/tU1a9mNZ"
|
||||
"5QMZBJ/JuJK/s+2bp8gpxKn8rh1akSQjlynlV9NI+7J3CC7CUf3bGvoXIrb37C/lpJehS39KNtcGdaRufKauSfqx/7SxA0zyZC+r13f7ASbMaQFzm+/RRusTqozY/p/MsWx8QIDAQAB"
|
||||
) ;
|
||||
```
|
||||
|
||||
Take the content between `( ... )`, and combine all the quote wrapped content and remove the double-quotes including the white-space between them. That is your `TXT` record value, the above example would become this:
|
||||
|
||||
```txt
|
||||
v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQMMqhb1S52Rg7VFS3EC6JQIMxNDdiBmOKZvY5fiVtD3Z+yd9ZV+V8e4IARVoMXWcJWSR6xkloitzfrRtJRwOYvmrcgugOalkmM0V4Gy/2aXeamuiBuUc4esDQEI3egmtAsHcVY1XCoYfs+9VqoHEq3vdr3UQ8zP/l+FP5UfcaJFCK/ZllqcO2P1GjIDVSHLdPpRHbMP/tU1a9mNZ5QMZBJ/JuJK/s+2bp8gpxKn8rh1akSQjlynlV9NI+7J3CC7CUf3bGvoXIrb37C/lpJehS39KNtcGdaRufKauSfqx/7SxA0zyZC+r13f7ASbMaQFzm+/RRusTqozY/p/MsWx8QIDAQAB
|
||||
```
|
||||
|
||||
To test that your new DKIM record is correct, query it with the `dig` command. The `TXT` value response should be a single line split into multiple parts wrapped in double-quotes:
|
||||
|
||||
```console
|
||||
$ dig +short TXT mail._domainkey.example.com
|
||||
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQMMqhb1S52Rg7VFS3EC6JQIMxNDdiBmOKZvY5fiVtD3Z+yd9ZV+V8e4IARVoMXWcJWSR6xkloitzfrRtJRwOYvmrcgugOalkmM0V4Gy/2aXeamuiBuUc4esDQEI3egmtAsHcVY1XCoYfs+9VqoHEq3vdr3UQ8zP/l+FP5UfcaJFCK/ZllqcO2P1GjIDVSHLdPpRHbMP/tU1a9mNZ5QMZBJ/JuJK/s+2bp8gpxKn8rh1akSQjlynlV9NI+7J3CC7CUf3bGvoXIrb37C/lpJehS39" "KNtcGdaRufKauSfqx/7SxA0zyZC+r13f7ASbMaQFzm+/RRusTqozY/p/MsWx8QIDAQAB"
|
||||
```
|
||||
|
||||
### Troubleshooting { #dkim-debug }
|
||||
|
||||
[MxToolbox has a DKIM Verifier][mxtoolbox-dkim-verifier] that you can use to check your DKIM DNS record(s).
|
||||
|
||||
When using Rspamd, we recommend you turn on `check_pubkey = true;` in `dkim_signing.conf`. Rspamd will then check whether your private key matches your public key, and you can check possible mismatches by looking at `/var/log/mail/rspamd.log`.
|
||||
|
||||
## DMARC
|
||||
|
||||
With DMS, DMARC is pre-configured out of the box. You may disable extra and excessive DMARC checks when using Rspamd via `ENABLE_OPENDMARC=0`.
|
||||
|
||||
The only thing you need to do in order to enable DMARC on a "DNS-level" is to add new `TXT`. In contrast to [DKIM](#dkim), DMARC DNS entries do not require any keys, but merely setting the [configuration values][dmarc-howto-configtags]. You can either handcraft the entry by yourself or use one of available generators (like [this one][dmarc-tool-gca]).
|
||||
|
||||
Typically something like this should be good to start with:
|
||||
|
||||
```txt
|
||||
_dmarc.example.com. IN TXT "v=DMARC1; p=none; sp=none; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; rua=mailto:dmarc.report@example.com; ruf=mailto:dmarc.report@example.com"
|
||||
```
|
||||
|
||||
Or a bit more strict policies (_mind `p=quarantine` and `sp=quarantine`_):
|
||||
|
||||
```txt
|
||||
_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; sp=quarantine; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; rua=mailto:dmarc.report@example.com; ruf=mailto:dmarc.report@example.com"
|
||||
```
|
||||
|
||||
The DMARC status may not be displayed instantly due to delays in DNS (caches). Dmarcian has [a few tools][dmarcian-tools] you can use to verify your DNS records.
|
||||
|
||||
## SPF
|
||||
|
||||
!!! quote "What is SPF"
|
||||
|
||||
Sender Policy Framework (SPF) is a simple email-validation system designed to detect email spoofing by providing a mechanism to allow receiving mail exchangers to check that incoming mail from a domain comes from a host authorized by that domain's administrators.
|
||||
|
||||
[Source][wikipedia-spf]
|
||||
|
||||
!!! tip "Disabling the default SPF service `policy-spf`"
|
||||
|
||||
Set [`ENABLE_POLICYD_SPF=0`][docs-env-spf-policyd] to opt-out of the default SPF service. Advised when Rspamd is configured to handle SPF instead.
|
||||
|
||||
### Adding an SPF Record
|
||||
|
||||
To add a SPF record in your DNS, insert the following line in your DNS zone:
|
||||
|
||||
```txt
|
||||
example.com. IN TXT "v=spf1 mx ~all"
|
||||
```
|
||||
|
||||
This enables the _Softfail_ mode for SPF. You could first add this SPF record with a very low TTL. _SoftFail_ is a good setting for getting started and testing, as it lets all email through, with spams tagged as such in the mailbox.
|
||||
|
||||
After verification, you _might_ want to change your SPF record to `v=spf1 mx -all` so as to enforce the _HardFail_ policy. See <http://www.open-spf.org/SPF_Record_Syntax> for more details about SPF policies.
|
||||
|
||||
In any case, increment the SPF record's TTL to its final value.
|
||||
|
||||
### Backup MX & Secondary MX for `policyd-spf`
|
||||
|
||||
For whitelisting an IP Address from the SPF test, you can create a config file (see [`policyd-spf.conf`](https://www.linuxcertif.com/man/5/policyd-spf.conf)) and mount that file into `/etc/postfix-policyd-spf-python/policyd-spf.conf`.
|
||||
|
||||
**Example:** Create and edit a `policyd-spf.conf` file at `docker-data/dms/config/postfix-policyd-spf.conf`:
|
||||
|
||||
```conf
|
||||
debugLevel = 1
|
||||
#0(only errors)-4(complete data received)
|
||||
|
||||
skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1
|
||||
|
||||
# Preferably use IP-Addresses for whitelist lookups:
|
||||
Whitelist = 192.168.0.0/31,192.168.1.0/30
|
||||
# Domain_Whitelist = mx1.not-example.com,mx2.not-example.com
|
||||
```
|
||||
|
||||
Then add this line to `compose.yaml`:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- ./docker-data/dms/config/postfix-policyd-spf.conf:/etc/postfix-policyd-spf-python/policyd-spf.conf
|
||||
```
|
||||
|
||||
[docs-accounts]: ../account-management/overview.md#accounts
|
||||
[docs-volumes-config]: ../advanced/optional-config.md#volumes-config
|
||||
[docs-env-opendkim]: ../environment.md#enable_opendkim
|
||||
[docs-env-rspamd]: ../environment.md#enable_rspamd
|
||||
[docs-env-spf-policyd]: ../environment.md#enable_policyd_spf
|
||||
[docs-rspamd-config-dropin]: ../security/rspamd.md#manually
|
||||
[cloudflare-dkim-dmarc-spf]: https://www.cloudflare.com/learning/email-security/dmarc-dkim-spf/
|
||||
[rfc-8301]: https://datatracker.ietf.org/doc/html/rfc8301#section-3.2
|
||||
[gh-discussion::dkim-key-rotation-expiry]: https://github.com/orgs/docker-mailserver/discussions/4068#discussioncomment-9784263
|
||||
[gh-issue::dkim-length]: https://github.com/docker-mailserver/docker-mailserver/issues/1854#issuecomment-806280929
|
||||
[rspamd-docs-dkim-checks]: https://www.rspamd.com/doc/modules/dkim.html
|
||||
[rspamd-docs-dkim-signing]: https://www.rspamd.com/doc/modules/dkim_signing.html
|
||||
[dns::example-webui]: https://www.vultr.com/docs/introduction-to-vultr-dns/
|
||||
[dns::digicert-ttl]: https://www.digicert.com/faq/dns/what-is-ttl
|
||||
[dns::wikipedia-zonefile]: https://en.wikipedia.org/wiki/Zone_file
|
||||
[dns::webui-dkim]: https://serverfault.com/questions/763815/route-53-doesnt-allow-adding-dkim-keys-because-length-is-too-long
|
||||
[dkim-ed25519-support]: https://serverfault.com/questions/1023674/is-ed25519-well-supported-for-the-dkim-validation/1074545#1074545
|
||||
[dkim-verification-expiry-refusal]: https://mxtoolbox.com/problem/dkim/dkim-signature-expiration
|
||||
[mxtoolbox-dkim-verifier]: https://mxtoolbox.com/dkim.aspx
|
||||
[dmarc-howto-configtags]: https://github.com/internetstandards/toolbox-wiki/blob/master/DMARC-how-to.md#overview-of-dmarc-configuration-tags
|
||||
[dmarc-tool-gca]: https://dmarcguide.globalcyberalliance.org
|
||||
[dmarcian-tools]: https://dmarcian.com/dmarc-tools/
|
||||
[wikipedia-dkim]: https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail
|
||||
[wikipedia-spf]: https://en.wikipedia.org/wiki/Sender_Policy_Framework
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
title: 'Best Practices | DMARC'
|
||||
hide:
|
||||
- toc # Hide Table of Contents for this page
|
||||
---
|
||||
|
||||
!!! seealso
|
||||
DMARC Guide: https://github.com/internetstandards/toolbox-wiki/blob/master/DMARC-how-to.md
|
||||
|
||||
## Enabling DMARC
|
||||
|
||||
In `docker-mailserver`, DMARC is pre-configured out-of the box. The only thing you need to do in order to enable it, is to add new TXT entry to your DNS.
|
||||
|
||||
In contrast with [DKIM][docs-dkim], DMARC DNS entry does not require any keys, but merely setting the [configuration values](https://github.com/internetstandards/toolbox-wiki/blob/master/DMARC-how-to.md#overview-of-dmarc-configuration-tags). You can either handcraft the entry by yourself or use one of available generators (like https://dmarcguide.globalcyberalliance.org/).
|
||||
|
||||
Typically something like this should be good to start with (don't forget to replace `@domain.com` to your actual domain)
|
||||
```
|
||||
_dmarc.domain.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc.report@domain.com; ruf=mailto:dmarc.report@domain.com; sp=none; ri=86400"
|
||||
```
|
||||
|
||||
Or a bit more strict policies (mind `p=quarantine` and `sp=quarantine`):
|
||||
```
|
||||
_dmarc IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc.report@domain.com; ruf=mailto:dmarc.report@domain.com; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; sp=quarantine"
|
||||
```
|
||||
|
||||
DMARC status is not being displayed instantly in Gmail for instance. If you want to check it directly after DNS entries, you can use some services around the Internet such as https://dmarcguide.globalcyberalliance.org/ or https://ondmarc.redsift.com/. In other case, email clients will show "DMARC: PASS" in ~1 day or so.
|
||||
|
||||
Reference: [#1511][github-issue-1511]
|
||||
|
||||
[docs-dkim]: ./dkim.md
|
||||
[github-issue-1511]: https://github.com/docker-mailserver/docker-mailserver/issues/1511
|
|
@ -1,30 +0,0 @@
|
|||
---
|
||||
title: 'Best practices | MTA-STS'
|
||||
hide:
|
||||
- toc # Hide Table of Contents for this page
|
||||
---
|
||||
|
||||
MTA-STS is an optional mechanism for a domain to signal support for STARTTLS.
|
||||
|
||||
- It can be used to prevent man-in-the-middle-attacks from hiding STARTTLS support that would force DMS to send outbound mail through an insecure connection.
|
||||
- MTA-STS is an alternative to DANE without the need of DNSSEC.
|
||||
- MTA-STS is supported by some of the biggest mail providers like Google Mail and Outlook.
|
||||
|
||||
## Supporting MTA-STS for outbound mail
|
||||
|
||||
Enable this feature via the ENV setting [`ENABLE_MTA_STS=1`](../environment.md#enable_mta_sts).
|
||||
|
||||
!!! warning "If you have configured DANE"
|
||||
|
||||
Enabling MTA-STS will by default override DANE if both are configured for a domain.
|
||||
|
||||
This can be partially addressed by configuring a dane-only policy resolver before the MTA-STS entry in `smtp_tls_policy_maps`. See the [`postfix-mta-sts-resolver` documentation][postfix-mta-sts-resolver::dane] for further details.
|
||||
|
||||
[postfix-mta-sts-resolver::dane]: https://github.com/Snawoot/postfix-mta-sts-resolver#warning-mta-sts-policy-overrides-dane-tls-authentication
|
||||
|
||||
## Supporting MTA-STS for inbound mail
|
||||
|
||||
While this feature in DMS supports ensuring STARTTLS is used when mail is sent to another mail server, you may setup similar for mail servers sending mail to DMS.
|
||||
|
||||
This requires configuring your DNS and hosting the MTA-STS policy file via a webserver. A good introduction can be found on [dmarcian.com](https://dmarcian.com/mta-sts/).
|
||||
|