mirror of
https://github.com/funkypenguin/geek-cookbook/
synced 2025-12-13 01:36:23 +00:00
882 lines
28 KiB
HTML
882 lines
28 KiB
HTML
|
|
<!DOCTYPE html>
|
|
<html lang="en" class="no-js">
|
|
<head>
|
|
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
|
|
|
<meta name="description" content="A short description of my project">
|
|
|
|
|
|
<link rel="canonical" href="https://geeks-cookbook.funkypenguin.co.nz/recipies/mail/">
|
|
|
|
|
|
<meta name="author" content="David Young">
|
|
|
|
|
|
<link rel="shortcut icon" href="../../assets/images/favicon.png">
|
|
|
|
<meta name="generator" content="mkdocs-0.16.3, mkdocs-material-1.7.4">
|
|
|
|
|
|
|
|
<title>Mail Server - Funky Penguin's Geek's Cookbook</title>
|
|
|
|
|
|
|
|
<script src="../../assets/javascripts/modernizr-1df76c4e58.js"></script>
|
|
|
|
|
|
<link rel="stylesheet" href="../../assets/stylesheets/application-769c285a91.css">
|
|
|
|
<link rel="stylesheet" href="../../assets/stylesheets/application-02c2a4388f.palette.css">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,400i,700|Roboto+Mono">
|
|
<style>body,input{font-family:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif}code,kbd,pre{font-family:"Roboto Mono","Courier New",Courier,monospace}</style>
|
|
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
|
|
|
|
|
|
|
</head>
|
|
|
|
|
|
|
|
|
|
<body data-md-color-primary="indigo" data-md-color-accent="indigo">
|
|
|
|
<svg class="md-svg">
|
|
<defs>
|
|
|
|
|
|
</defs>
|
|
</svg>
|
|
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="drawer">
|
|
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="search">
|
|
<label class="md-overlay" data-md-component="overlay" for="drawer"></label>
|
|
|
|
<header class="md-header" data-md-component="header">
|
|
<nav class="md-header-nav md-grid">
|
|
<div class="md-flex">
|
|
<div class="md-flex__cell md-flex__cell--shrink">
|
|
|
|
<a href="https://geeks-cookbook.funkypenguin.co.nz" title="Funky Penguin's Geek's Cookbook" class="md-logo md-header-nav__button">
|
|
<img src="../../images/site-logo.png" width="24" height="24">
|
|
</a>
|
|
|
|
</div>
|
|
<div class="md-flex__cell md-flex__cell--shrink">
|
|
<label class="md-icon md-icon--menu md-header-nav__button" for="drawer"></label>
|
|
</div>
|
|
<div class="md-flex__cell md-flex__cell--stretch">
|
|
<span class="md-flex__ellipsis md-header-nav__title">
|
|
|
|
|
|
|
|
<span class="md-header-nav__parent">
|
|
Recommended
|
|
</span>
|
|
|
|
|
|
Mail Server
|
|
|
|
</span>
|
|
</div>
|
|
<div class="md-flex__cell md-flex__cell--shrink">
|
|
|
|
<label class="md-icon md-icon--search md-header-nav__button" for="search"></label>
|
|
|
|
<div class="md-search" data-md-component="search">
|
|
<label class="md-search__overlay" for="search"></label>
|
|
<div class="md-search__inner">
|
|
<form class="md-search__form" name="search">
|
|
<input type="text" class="md-search__input" name="query" required placeholder="Search" accesskey="s" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="query">
|
|
<label class="md-icon md-search__icon" for="search"></label>
|
|
<button type="reset" class="md-icon md-search__icon" data-md-component="reset">close</button>
|
|
</form>
|
|
<div class="md-search__output">
|
|
<div class="md-search__scrollwrap" data-md-scrollfix>
|
|
<div class="md-search-result" data-md-component="result" data-md-lang-search="">
|
|
<div class="md-search-result__meta" data-md-lang-result-none="No matching documents" data-md-lang-result-one="1 matching document" data-md-lang-result-other="# matching documents">
|
|
Type to start searching
|
|
</div>
|
|
<ol class="md-search-result__list"></ol>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</nav>
|
|
</header>
|
|
|
|
<div class="md-container">
|
|
|
|
|
|
<main class="md-main">
|
|
<div class="md-main__inner md-grid" data-md-component="container">
|
|
|
|
|
|
<div class="md-sidebar md-sidebar--primary" data-md-component="navigation">
|
|
<div class="md-sidebar__scrollwrap">
|
|
<div class="md-sidebar__inner">
|
|
<nav class="md-nav md-nav--primary" data-md-level="0">
|
|
<label class="md-nav__title md-nav__title--site" for="drawer">
|
|
|
|
<i class="md-logo md-nav__button">
|
|
<img src="../../images/site-logo.png">
|
|
</i>
|
|
|
|
Funky Penguin's Geek's Cookbook
|
|
</label>
|
|
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../.." title="Home" class="md-nav__link">
|
|
Home
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--nested">
|
|
|
|
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-2" type="checkbox" id="nav-2">
|
|
|
|
<label class="md-nav__link" for="nav-2">
|
|
Introduction
|
|
</label>
|
|
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
|
<label class="md-nav__title" for="nav-2">
|
|
Introduction
|
|
</label>
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../README/" title="README" class="md-nav__link">
|
|
README
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../whoami/" title="whoami" class="md-nav__link">
|
|
whoami
|
|
</a>
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--nested">
|
|
|
|
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-3" type="checkbox" id="nav-3">
|
|
|
|
<label class="md-nav__link" for="nav-3">
|
|
Essential
|
|
</label>
|
|
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
|
<label class="md-nav__title" for="nav-3">
|
|
Essential
|
|
</label>
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../ha-docker-swarm/design/" title="Design" class="md-nav__link">
|
|
Design
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../ha-docker-swarm/vms/" title="VMs" class="md-nav__link">
|
|
VMs
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../ha-docker-swarm/shared-storage-ceph/" title="Shared Storage (Ceph)" class="md-nav__link">
|
|
Shared Storage (Ceph)
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../ha-docker-swarm/shared-storage-gluster/" title="Shared Storage (GlusterFS)" class="md-nav__link">
|
|
Shared Storage (GlusterFS)
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../ha-docker-swarm/keepalived/" title="Keepalived" class="md-nav__link">
|
|
Keepalived
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../ha-docker-swarm/docker-swarm-mode/" title="Docker Swarm Mode" class="md-nav__link">
|
|
Docker Swarm Mode
|
|
</a>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="../../ha-docker-swarm/traefik/" title="Traefik" class="md-nav__link">
|
|
Traefik
|
|
</a>
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
</li>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--active md-nav__item--nested">
|
|
|
|
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-4" type="checkbox" id="nav-4" checked>
|
|
|
|
<label class="md-nav__link" for="nav-4">
|
|
Recommended
|
|
</label>
|
|
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
|
<label class="md-nav__title" for="nav-4">
|
|
Recommended
|
|
</label>
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<li class="md-nav__item md-nav__item--active">
|
|
|
|
<input class="md-toggle md-nav__toggle" data-md-toggle="toc" type="checkbox" id="toc">
|
|
|
|
|
|
|
|
|
|
<label class="md-nav__link md-nav__link--active" for="toc">
|
|
Mail Server
|
|
</label>
|
|
|
|
<a href="./" title="Mail Server" class="md-nav__link md-nav__link--active">
|
|
Mail Server
|
|
</a>
|
|
|
|
|
|
<nav class="md-nav md-nav--secondary">
|
|
|
|
|
|
|
|
|
|
|
|
<label class="md-nav__title" for="toc">Table of contents</label>
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#ingredients" title="Ingredients" class="md-nav__link">
|
|
Ingredients
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#preparation" title="Preparation" class="md-nav__link">
|
|
Preparation
|
|
</a>
|
|
|
|
<nav class="md-nav">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#setup-data-locations" title="Setup data locations" class="md-nav__link">
|
|
Setup data locations
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#get-letsencrypt-certificate" title="Get LetsEncrypt certificate" class="md-nav__link">
|
|
Get LetsEncrypt certificate
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#get-setupsh" title="Get setup.sh" class="md-nav__link">
|
|
Get setup.sh
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#create-email-accounts" title="Create email accounts" class="md-nav__link">
|
|
Create email accounts
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#create-dkim-dns-entries" title="Create DKIM DNS entries" class="md-nav__link">
|
|
Create DKIM DNS entries
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#setup-docker-swarm" title="Setup Docker Swarm" class="md-nav__link">
|
|
Setup Docker Swarm
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#serving" title="Serving" class="md-nav__link">
|
|
Serving
|
|
</a>
|
|
|
|
<nav class="md-nav">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#launch-mailserver" title="Launch mailserver" class="md-nav__link">
|
|
Launch mailserver
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#chefs-notes" title="Chef's Notes" class="md-nav__link">
|
|
Chef's Notes
|
|
</a>
|
|
|
|
</li>
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#__comments" title="Comments" class="md-nav__link md-nav__link--active">
|
|
Comments
|
|
</a>
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="md-sidebar md-sidebar--secondary" data-md-component="toc">
|
|
<div class="md-sidebar__scrollwrap">
|
|
<div class="md-sidebar__inner">
|
|
|
|
<nav class="md-nav md-nav--secondary">
|
|
|
|
|
|
|
|
|
|
|
|
<label class="md-nav__title" for="toc">Table of contents</label>
|
|
<ul class="md-nav__list" data-md-scrollfix>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#ingredients" title="Ingredients" class="md-nav__link">
|
|
Ingredients
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#preparation" title="Preparation" class="md-nav__link">
|
|
Preparation
|
|
</a>
|
|
|
|
<nav class="md-nav">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#setup-data-locations" title="Setup data locations" class="md-nav__link">
|
|
Setup data locations
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#get-letsencrypt-certificate" title="Get LetsEncrypt certificate" class="md-nav__link">
|
|
Get LetsEncrypt certificate
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#get-setupsh" title="Get setup.sh" class="md-nav__link">
|
|
Get setup.sh
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#create-email-accounts" title="Create email accounts" class="md-nav__link">
|
|
Create email accounts
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#create-dkim-dns-entries" title="Create DKIM DNS entries" class="md-nav__link">
|
|
Create DKIM DNS entries
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#setup-docker-swarm" title="Setup Docker Swarm" class="md-nav__link">
|
|
Setup Docker Swarm
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#serving" title="Serving" class="md-nav__link">
|
|
Serving
|
|
</a>
|
|
|
|
<nav class="md-nav">
|
|
<ul class="md-nav__list">
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#launch-mailserver" title="Launch mailserver" class="md-nav__link">
|
|
Launch mailserver
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
</nav>
|
|
|
|
</li>
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#chefs-notes" title="Chef's Notes" class="md-nav__link">
|
|
Chef's Notes
|
|
</a>
|
|
|
|
</li>
|
|
|
|
|
|
|
|
<li class="md-nav__item">
|
|
<a href="#__comments" title="Comments" class="md-nav__link md-nav__link--active">
|
|
Comments
|
|
</a>
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="md-content">
|
|
<article class="md-content__inner md-typeset">
|
|
|
|
|
|
|
|
<h1 id="mail-server">Mail Server<a class="headerlink" href="#mail-server" title="Permanent link">¶</a></h1>
|
|
<p>Many of the recipies that follow require email access of some kind. It's quite accepmatebl normally possible to use a hosted service such as SendGrid, or just a gmail account. If (like me) you'd like to self-host email for your stacks, then the following recipe provides a full-stack mail server running on the docker HA swarm.</p>
|
|
<p>Of value to me in choosing docker-mailserver were:</p>
|
|
<ol>
|
|
<li>Automatically renews LetsEncrypt certificates</li>
|
|
<li>Creation of email accounts across multiple domains (i.e., the same container gives me mailbox <a href="mailto:wekan@wekan.example.com">wekan@wekan.example.com</a>, and <a href="mailto:gitlab@gitlab.example.com">gitlab@gitlab.example.com</a>)</li>
|
|
<li>The entire configuration is based on flat files, so there's no database or persistence to worry about</li>
|
|
</ol>
|
|
<p>docker-mailserver doesn't include a webmail client, and one is not strictly needed. Rainloop can be added either as another service within the stack, or as a standalone service. Rainloop will be covered in a future recipe.</p>
|
|
<h2 id="ingredients">Ingredients<a class="headerlink" href="#ingredients" title="Permanent link">¶</a></h2>
|
|
<ol>
|
|
<li><a href="../../ha-docker-swarm/">Docker swarm cluster</a> with <a href="../../ha-docker-swarm/shared-storage-ceph/">persistent shared storage</a></li>
|
|
<li><a href="../../ha-docker-swarm/traefik">Traefik</a> configured per design</li>
|
|
<li>LetsEncrypt authorized email address for domain</li>
|
|
<li>Access to manage DNS records for domains</li>
|
|
</ol>
|
|
<h2 id="preparation">Preparation<a class="headerlink" href="#preparation" title="Permanent link">¶</a></h2>
|
|
<h3 id="setup-data-locations">Setup data locations<a class="headerlink" href="#setup-data-locations" title="Permanent link">¶</a></h3>
|
|
<p>We'll need several directories to bind-mount into our container, so create them in /var/data/mailserver:</p>
|
|
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
|
|
2
|
|
3
|
|
4</pre></div></td><td class="code"><div class="codehilite"><pre><span></span>cd /var/data
|
|
mkdir mailserver
|
|
cd mailserver
|
|
mkdir {maildata,mailstate,config,letsencrypt}
|
|
</pre></div>
|
|
</td></tr></table>
|
|
|
|
<h3 id="get-letsencrypt-certificate">Get LetsEncrypt certificate<a class="headerlink" href="#get-letsencrypt-certificate" title="Permanent link">¶</a></h3>
|
|
<p>Decide on the FQDN to assign to your mailserver. You can service multiple domains from a single mailserver - i.e., <a href="mailto:bob@dev.example.com">bob@dev.example.com</a> and <a href="mailto:daphne@prod.example.com">daphne@prod.example.com</a> can both be served by <strong>mail.example.com</strong>.</p>
|
|
<p>The docker-mailserver container can <em>renew</em> our LetsEncrypt certs for us, but it can't generate them. To do this, we need to run certbot (from a container) to request the initial certs and create the appropriate directory structure.</p>
|
|
<p>In the example below, since I'm already using Traefik to manage the LE certs for my web platforms, I opted to use the DNS challenge to prove my ownership of the domain. The certbot client will prompt you to add a DNS record for domain verification.</p>
|
|
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
|
|
2
|
|
3
|
|
4</pre></div></td><td class="code"><div class="codehilite"><pre><span></span>docker run -ti --rm -v \
|
|
"$(pwd)"/letsencrypt:/etc/letsencrypt certbot/certbot \
|
|
--manual --preferred-challenges dns certonly \
|
|
-d mail.example.com
|
|
</pre></div>
|
|
</td></tr></table>
|
|
|
|
<h3 id="get-setupsh">Get setup.sh<a class="headerlink" href="#get-setupsh" title="Permanent link">¶</a></h3>
|
|
<p>docker-mailserver comes with a handy bash script for managing the stack (which is just really a wrapper around the container.) It'll make our setup easier, so download it into the root of your configuration/data directory, and make it executable:</p>
|
|
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
|
|
2
|
|
3</pre></div></td><td class="code"><div class="codehilite"><pre><span></span>curl -o setup.sh \
|
|
https://raw.githubusercontent.com/tomav/docker-mailserver/master/setup.sh \
|
|
chmod a+x ./setup.sh
|
|
</pre></div>
|
|
</td></tr></table>
|
|
|
|
<h3 id="create-email-accounts">Create email accounts<a class="headerlink" href="#create-email-accounts" title="Permanent link">¶</a></h3>
|
|
<p>For every email address required, run <code class="codehilite">./setup.sh email add <email> <password></code> to create the account. The command returns no output.</p>
|
|
<p>You can run <code class="codehilite">./setup.sh email list</code> to confirm all of your addresses have been created.</p>
|
|
<h3 id="create-dkim-dns-entries">Create DKIM DNS entries<a class="headerlink" href="#create-dkim-dns-entries" title="Permanent link">¶</a></h3>
|
|
<p>Run <code class="codehilite">./setup.sh config dkim</code> to create the necessary DKIM entries. The command returns no output.</p>
|
|
<p>Examine the keys created by opendkim to identify the DNS TXT records required:</p>
|
|
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
|
|
2
|
|
3
|
|
4</pre></div></td><td class="code"><div class="codehilite"><pre><span></span>for i in `find config/opendkim/keys/ -name mail.txt`; do \
|
|
echo $i; \
|
|
cat $i; \
|
|
done
|
|
</pre></div>
|
|
</td></tr></table>
|
|
|
|
<p>You'll end up with something like this:</p>
|
|
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
|
|
2
|
|
3
|
|
4</pre></div></td><td class="code"><div class="codehilite"><pre><span></span>config/opendkim/keys/gitlab.example.com/mail.txt
|
|
mail._domainkey IN TXT ( "v=DKIM1; k=rsa; "
|
|
"p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYuQqDg2ZG8ZOfI1PvarF1Gcr5cJnCR8BeCj5HYgeRohSrxKL5utPEF/AWAxXYwnKpgYN837fu74GfqsIuOhu70lPhGV+O2gFVgpXYWHELvIiTqqO0QgarIN63WE2gzE4s0FckfLrMuxMoXr882wuzuJhXywGxOavybmjpnNHhbQIDAQAB" ) ; ----- DKIM key mail for gitlab.example.com
|
|
[root@ds1 mail]#
|
|
</pre></div>
|
|
</td></tr></table>
|
|
|
|
<p>Create the necessary DNS TXT entries for your domain(s). Note that although opendkim splits the record across two lines, the actual record should be concatenated on creation. I.e., the DNS TXT record above should read:</p>
|
|
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div></td><td class="code"><div class="codehilite"><pre><span></span>"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYuQqDg2ZG8ZOfI1PvarF1Gcr5cJnCR8BeCj5HYgeRohSrxKL5utPEF/AWAxXYwnKpgYN837fu74GfqsIuOhu70lPhGV+O2gFVgpXYWHELvIiTqqO0QgarIN63WE2gzE4s0FckfLrMuxMoXr882wuzuJhXywGxOavybmjpnNHhbQIDAQAB"
|
|
</pre></div>
|
|
</td></tr></table>
|
|
|
|
<h3 id="setup-docker-swarm">Setup Docker Swarm<a class="headerlink" href="#setup-docker-swarm" title="Permanent link">¶</a></h3>
|
|
<p>Create a docker swarm config file in docker-compose syntax (v3), something like this:</p>
|
|
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
6
|
|
7
|
|
8
|
|
9
|
|
10
|
|
11
|
|
12
|
|
13
|
|
14
|
|
15
|
|
16
|
|
17
|
|
18
|
|
19
|
|
20
|
|
21
|
|
22
|
|
23
|
|
24
|
|
25
|
|
26
|
|
27
|
|
28</pre></div></td><td class="code"><div class="codehilite"><pre><span></span>version: '3'
|
|
|
|
services:
|
|
mail:
|
|
image: tvial/docker-mailserver:latest
|
|
ports:
|
|
- "25:25"
|
|
- "587:587"
|
|
- "993:993"
|
|
volumes:
|
|
- /var/data/mail/maildata:/var/mail
|
|
- /var/data/mail/mailstate:/var/mail-state
|
|
- /var/data/mail/config:/tmp/docker-mailserver
|
|
- /var/data/mail/letsencrypt:/etc/letsencrypt
|
|
env_file: /var/data/mail/.env
|
|
networks:
|
|
- internal
|
|
deploy:
|
|
replicas: 1
|
|
|
|
networks:
|
|
traefik:
|
|
external: true
|
|
internal:
|
|
driver: overlay
|
|
ipam:
|
|
config:
|
|
- subnet: 172.16.2.0/24
|
|
</pre></div>
|
|
</td></tr></table>
|
|
|
|
<div class="admonition tip">
|
|
<p class="admonition-title">Tip</p>
|
|
<p>Setup unique static subnets for every stack you deploy. This avoids IP/gateway conflicts which can otherwise occur when you're creating/removing stacks a lot.</p>
|
|
</div>
|
|
<p>A sample .env file looks like this:</p>
|
|
<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
6
|
|
7
|
|
8
|
|
9</pre></div></td><td class="code"><div class="codehilite"><pre><span></span>ENABLE_SPAMASSASSIN=1
|
|
ENABLE_CLAMAV=1
|
|
ENABLE_POSTGREY=1
|
|
ONE_DIR=1
|
|
OVERRIDE_HOSTNAME=mail.example.com
|
|
OVERRIDE_DOMAINNAME=mail.example.com
|
|
POSTMASTER_ADDRESS=admin@example.com
|
|
PERMIT_DOCKER=network
|
|
SSL_TYPE=letsencrypt
|
|
</pre></div>
|
|
</td></tr></table>
|
|
|
|
<h2 id="serving">Serving<a class="headerlink" href="#serving" title="Permanent link">¶</a></h2>
|
|
<h3 id="launch-mailserver">Launch mailserver<a class="headerlink" href="#launch-mailserver" title="Permanent link">¶</a></h3>
|
|
<p>Launch the mail server stack by running <code class="codehilite">docker stack deploy mailserver -c <path -to-docker-compose.yml></code></p>
|
|
<h2 id="chefs-notes">Chef's Notes<a class="headerlink" href="#chefs-notes" title="Permanent link">¶</a></h2>
|
|
<ol>
|
|
<li>One of the elements of this design which I didn't appreciate at first is that since the config is entirely file-based, <strong>setup.sh</strong> can be run on any container host, provided it has the shared data mounted. This means that even though docker-mailserver was not designed with docker swarm in mind, it works perfectl with swarm. I.e., from any node, regardless of where the container is actually running, you're able to add/delete email addresses, view logs, etc.</li>
|
|
</ol>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<h2 id="__comments">Comments</h2>
|
|
<div id="disqus_thread"></div>
|
|
<script>
|
|
var disqus_config = function () {
|
|
this.page.url = "https://geeks-cookbook.funkypenguin.co.nz/recipies/mail/";
|
|
this.page.identifier =
|
|
"/recipies/mail/";
|
|
};
|
|
(function() {
|
|
var d = document, s = d.createElement("script");
|
|
s.src = "//geeks-cookbook.disqus.com/embed.js";
|
|
s.setAttribute("data-timestamp", +new Date());
|
|
(d.head || d.body).appendChild(s);
|
|
})();
|
|
</script>
|
|
|
|
|
|
</article>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
|
|
<footer class="md-footer">
|
|
|
|
<div class="md-footer-nav">
|
|
<nav class="md-footer-nav__inner md-grid">
|
|
|
|
<a href="../../ha-docker-swarm/traefik/" title="Traefik" class="md-flex md-footer-nav__link md-footer-nav__link--prev" rel="prev">
|
|
<div class="md-flex__cell md-flex__cell--shrink">
|
|
<i class="md-icon md-icon--arrow-back md-footer-nav__button"></i>
|
|
</div>
|
|
<div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title">
|
|
<span class="md-flex__ellipsis">
|
|
<span class="md-footer-nav__direction">
|
|
Previous
|
|
</span>
|
|
Traefik
|
|
</span>
|
|
</div>
|
|
</a>
|
|
|
|
|
|
</nav>
|
|
</div>
|
|
|
|
<div class="md-footer-meta md-typeset">
|
|
<div class="md-footer-meta__inner md-grid">
|
|
<div class="md-footer-copyright">
|
|
|
|
<div class="md-footer-copyright__highlight">
|
|
Copyright © 2016 - 2017 David Young
|
|
</div>
|
|
|
|
powered by
|
|
<a href="http://www.mkdocs.org" title="MkDocs">MkDocs</a>
|
|
and
|
|
<a href="http://squidfunk.github.io/mkdocs-material/" title="Material for MkDocs">
|
|
Material for MkDocs</a>
|
|
</div>
|
|
|
|
|
|
<div class="md-footer-social">
|
|
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
|
|
|
<a href="https://github.com/funkypenguin" class="md-footer-social__link fa fa-github"></a>
|
|
|
|
<a href="https://twitter.com/funkypenguin" class="md-footer-social__link fa fa-twitter"></a>
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
|
|
</div>
|
|
|
|
<script src="../../assets/javascripts/application-c35428f87f.js"></script>
|
|
|
|
|
|
<script>app.initialize({url:{base:"../.."}})</script>
|
|
|
|
<script src="../../extras/javascript/piwik.js"></script>
|
|
|
|
|
|
|
|
|
|
<script>!function(e,t,a,n,o,c,i){e.GoogleAnalyticsObject=o,e[o]=e[o]||function(){(e[o].q=e[o].q||[]).push(arguments)},e[o].l=1*new Date,c=t.createElement(a),i=t.getElementsByTagName(a)[0],c.async=1,c.src=n,i.parentNode.insertBefore(c,i)}(window,document,"script","https://www.google-analytics.com/analytics.js","ga"),ga("create","UA-139253-18","auto"),ga("set","anonymizeIp",!0),ga("send","pageview");var links=document.getElementsByTagName("a");Array.prototype.map.call(links,function(e){e.host!=document.location.host&&e.addEventListener("click",function(){var t=e.getAttribute("data-md-action")||"follow";ga("send","event","outbound",t,e.href)})});var query=document.forms.search.query;query.addEventListener("blur",function(){if(this.value){var e=document.location.pathname;ga("send","pageview",e+"?q="+this.value)}})</script>
|
|
|
|
|
|
</body>
|
|
</html> |