first commit

This commit is contained in:
Max Erenberg 2021-04-29 21:13:38 +00:00
commit a534a91985
44 changed files with 2500 additions and 0 deletions

82
README.md Normal file
View File

@ -0,0 +1,82 @@
# syscom-playground
The objective of this repo is to allow syscom members to create a local
development environment which is reasonably close to the services which
run on the CSC servers. The idea is to encourage experimentation without
breaking the real services and causing outages.
## Prerequisites
This repo consists of several Ansible playbooks which will automate tasks
in LXC containers. I strongly recommend creating a VM and running the
containers inside the VM to avoid screwing up the network interfaces
on your real computer. I am using KVM + QEMU, but VirtualBox should
theoretically also work. The VM should be running some reasonably
recent version of Debian or Ubuntu. 2 CPU cores and 2 GB of RAM
should be sufficient.
The VM should be attached to a bridge interface with NAT forwarding.
QEMU should create a default interface like this called 'virbr0'.
For this tutorial, I am assuming that the interface subnet is
192.168.122.0/24, and the bridge IP address on the host is 192.168.122.1.
If you decide to use a different subnet, make sure to update the `hosts`
file accordingly. If you need to edit the subnet which QEMU uses,
do this via virsh or virt-manager; do not modify the subnet manually
using iproute2. The reason for this is because libvirt needs to know
what the subnet is to setup dnsmasq and iptables properly.
Once the VM is up and running, you will need to create a shared bridge
interface. First, disable the default bridge:
```
systemctl stop lxc-net
systemctl mask lxc-net
```
Then paste the following into /etc/network/interfaces:
```
iface enp1s0 inet manual
auto lxcbr0
iface lxcbr0 inet dhcp
bridge_ports enp1s0
bridge_fd 0
bridge_maxwait 0
```
Replace enp1s0 by the name of the default interface in the VM.
Then, restart the VM.
Once you have restarted the VM, take note of its IP address on lxcbr0,
and write it to the variable `host_ipv4_addr` in the `hosts` file.
## Creating the LXC containers
Install the lxc-utils package if you have not done so already:
```
apt install lxc-utils
```
For the time being, it is necessary to manually create each container and to
install python3 in it before running the corresponding playbooks. For example,
to setup the DNS container:
```
lxc-create -t download -n dns -- -d debian -r buster -a amd64
lxc-start dns
lxc-attach dns
apt update
apt install python3
exit
```
The containers should be privileged since the CSC currently uses privileged
LXC containers. If we switch to unprivileged containers in the future, this
repo should be correspondingly updated.
It is also necessary to have Ansible and the Python LXC driver installed
on the host where the LXC containers are running. e.g. for Debian:
```
apt install ansible python3-lxc
``
Now we are ready to run the playbook:
```
ansible-playbook dns/main.yml
```
If you see a whole bunch of errors like
```
RuntimeError: cannot release un-acquired lock
```
it is safe to ignore those. [Here](https://github.com/lxc/python3-lxc/issues/11)
is the GitHub issue if you are interested.

2
ansible.cfg Normal file
View File

@ -0,0 +1,2 @@
[defaults]
inventory = hosts

7
coffee/README.md Normal file
View File

@ -0,0 +1,7 @@
# Coffee container
This container just hosts the databases for other apps to use.
## Installation
```
ansible-playbook main.yml
```

27
coffee/main.yml Normal file
View File

@ -0,0 +1,27 @@
---
- hosts: coffee
roles:
- role: ../roles/network_setup
vars:
ipv4_addr: "{{ coffee_ipv4_addr }}"
tasks:
- name: install MariaDB
apt:
name: default-mysql-server
state: present
- name: override systemd services
import_role:
name: ../../roles/systemd_workarounds
vars:
services: [ "mariadb" ]
- name: allow remote connections to MariaDB
lineinfile:
path: /etc/mysql/mariadb.conf.d/50-server.cnf
regexp: "^bind-address\\s+=\\s+127.0.0.1$"
line: "#bind-address = 127.0.0.1"
notify: restart MariaDB
handlers:
- name: restart MariaDB
systemd:
name: mariadb
state: restarted

9
dns/README.md Normal file
View File

@ -0,0 +1,9 @@
## DNS container setup
This doesn't actually reflect the way the CSC manages its nameservers,
but the other containers in this repo need some sort of DNS server
to work properly.
## Instructions
```
ansible-playbook main.yml
```

28
dns/main.yml Normal file
View File

@ -0,0 +1,28 @@
---
- hosts: dns
roles:
- role: ../roles/network_setup
vars:
ipv4_addr: "{{ dns_ipv4_addr }}"
tasks:
- name: install dnsmasq
apt:
name: dnsmasq
state: present
update_cache: true
- name: add hosts file for CNAME
copy:
content: |
{{ mail_ipv4_addr }} mail.{{ base_domain }}
dest: /etc/dnsmasq_hosts
notify: restart dnsmasq
- name: add dnsmasq config
template:
src: templates/dnsmasq.conf.j2
dest: /etc/dnsmasq.d/dnsmasq.conf
notify: restart dnsmasq
handlers:
- name: restart dnsmasq
systemd:
name: dnsmasq
state: restarted

View File

@ -0,0 +1,10 @@
no-hosts
no-resolv
server={{ upstream_dns }}
interface=eth0
addn-hosts=/etc/dnsmasq_hosts
address=/dns.{{ base_domain }}/{{ dns_ipv4_addr }}
address=/mail.{{ base_domain }}/{{ mail_ipv4_addr }}
cname=mailman.{{ base_domain }},mail.{{ base_domain }}
address=/coffee.{{ base_domain }}/{{ coffee_ipv4_addr }}
mx-host={{ base_domain }},mail.{{ base_domain }},50

18
hosts Normal file
View File

@ -0,0 +1,18 @@
[containers]
dns ansible_lxc_host=dns
mail ansible_lxc_host=mail
coffee ansible_lxc_host=coffee
outsider ansible_lxc_host=outsider
[containers:vars]
ansible_connection = lxc
ansible_python_interpreter = python3
base_domain = csclub.internal
ipv4_subnet = 192.168.122.0/24
ipv4_gateway = 192.168.122.1
upstream_dns = 192.168.122.1
host_ipv4_addr = 192.168.122.226
outsider_ipv4_addr = 192.168.125.2
dns_ipv4_addr = 192.168.122.4
mail_ipv4_addr = 192.168.122.52
coffee_ipv4_addr = 192.168.122.20

108
mail/README.md Normal file
View File

@ -0,0 +1,108 @@
# Mail container setup
This is one of the trickier ones.
## Instructions
First, get the email server up and running:
```
ansible-playbook main.yml
```
At this point, it would be a good idea to send a few emails back
and forth between the dummy users to make sure everything's working
(their usernames are alice, bob, and eve). [Mutt](http://www.mutt.org/)
has already been setup for each user.
```
lxc-attach mail
su - alice
mutt
(send an email to bob@csclub.internal)
exit
su - bob
mutt
(check that alice's email got sent)
```
## Installing Mailman 2
```
ansible-playbook mailman2/mailman2.yml
```
Attach to the mail container and create a new list, e.g. syscom:
```
cd /var/lib/mailman
bin/newlist -a syscom root@csclub.internal mailman
```
Now on your **real** computer (i.e. not the VM), you are going to visit
the web interface for Mailman to adjust some settings and subscribe
some new users.
First, open `/etc/hosts` on your computer and add the following entry:
```
192.168.122.52 mailman.csclub.internal
```
Now visit http://mailman.csclub.internal/admin/syscom in your browser.
The admin password is 'mailman' (no quotes).
I suggest going over each setting in the Privacy section and reading it
carefully. Under 'Sender filters', I suggest setting 'generic_nonmember_action'
to 'Accept' rather than 'Hold'. Under 'Recipient filters', I suggest setting
'require_explicit_destination' to 'No'.
If you are feeling adventurous, add the following to the `/etc/aliases` file
in the mail container:
```
www-data: root
root: syscom
```
Then run `postalias /etc/aliases` and `postfix reload`.
This is actually what the CSC uses on the real mail container (on xylitol).
Make sure not to create mailing loops, though, especially when you make one
mailing list the owner of another mailing list.
You should also subscribe some members to the list and make sure that
messages get sent to them properly. Go to http://mailman.csclub.internal/listinfo/syscom
and subscribe both alice@csclub.internal and bob@csclub.internal. They will
get confirmation messages; go back to the mail container and use Mutt
as each user to confirm their subscriptions. Then, as either bob or alice,
send a message to the syscom list, and make sure that they both received
the message. You should also be able to see the message in Pipermail by
going to http://mailman.csclub.internal/pipermail/syscom/.
## Installing Mailman 3
Make sure you have installed Mailman 2 first, since we are going to need
Pipermail.
We are also going to need the MariaDB database on coffee, so make sure the
coffee container has been setup.
Run the playbook for Mailman 3:
```
ansible-playbook mailman3/mailman3.yml
```
Run the database migration and collect static files:
```
cd /opt/mailman3
su -s /bin/bash www-data
source bin/activate
mailman-web migrate
mailman-web collectstatic
mailman-web compress
exit
systemctl restart mailman3-web
```
You will also want to create a superuser account:
```
cd /opt/mailman3
su -s /bin/bash www-data
source bin/activate
mailman-web createsuperuser --username bob --email bob@csclub.internal
exit
```
Now open http://mailman.csclub.internal in your browser, login as bob,
and start doing things. See the official [Mailman 3 documentation](https://docs.mailman3.org)
and [our wiki](https://wiki.csclub.uwaterloo.ca/Mailing_Lists#Mailman_3)
for information on how to create lists, import lists from Mailman 2,
etc. Make sure to send some messages to the lists which you create
to verify that everything's working.

10
mail/common.yml Normal file
View File

@ -0,0 +1,10 @@
- name: reload Postfix
command: postfix reload
- name: reload Apache
systemd:
name: apache2
state: reloaded
- name: restart Apache
systemd:
name: apache2
state: restarted

115
mail/dovecot/dovecot.conf Normal file
View File

@ -0,0 +1,115 @@
## Dovecot configuration file
# If you're in a hurry, see http://wiki2.dovecot.org/QuickConfiguration
# "doveconf -n" command gives a clean output of the changed settings. Use it
# instead of copy&pasting files when posting to the Dovecot mailing list.
# '#' character and everything after it is treated as comments. Extra spaces
# and tabs are ignored. If you want to use either of these explicitly, put the
# value inside quotes, eg.: key = "# char and trailing whitespace "
# Most (but not all) settings can be overridden by different protocols and/or
# source/destination IPs by placing the settings inside sections, for example:
# protocol imap { }, local 127.0.0.1 { }, remote 10.0.0.0/8 { }
# Default values are shown for each setting, it's not required to uncomment
# those. These are exceptions to this though: No sections (e.g. namespace {})
# or plugin settings are added by default, they're listed only as examples.
# Paths are also just examples with the real defaults being based on configure
# options. The paths listed here are for configure --prefix=/usr
# --sysconfdir=/etc --localstatedir=/var
# Enable installed protocols
!include_try /usr/share/dovecot/protocols.d/*.protocol
# A comma separated list of IPs or hosts where to listen in for connections.
# "*" listens in all IPv4 interfaces, "::" listens in all IPv6 interfaces.
# If you want to specify non-default ports or anything more complex,
# edit conf.d/master.conf.
#listen = *, ::
# Base directory where to store runtime data.
#base_dir = /var/run/dovecot/
# Name of this instance. In multi-instance setup doveadm and other commands
# can use -i <instance_name> to select which instance is used (an alternative
# to -c <config_path>). The instance name is also added to Dovecot processes
# in ps output.
#instance_name = dovecot
# Greeting message for clients.
#login_greeting = Dovecot ready.
# Space separated list of trusted network ranges. Connections from these
# IPs are allowed to override their IP addresses and ports (for logging and
# for authentication checks). disable_plaintext_auth is also ignored for
# these networks. Typically you'd specify your IMAP proxy servers here.
#login_trusted_networks =
# Space separated list of login access check sockets (e.g. tcpwrap)
#login_access_sockets =
# With proxy_maybe=yes if proxy destination matches any of these IPs, don't do
# proxying. This isn't necessary normally, but may be useful if the destination
# IP is e.g. a load balancer's IP.
#auth_proxy_self =
# Show more verbose process titles (in ps). Currently shows user name and
# IP address. Useful for seeing who are actually using the IMAP processes
# (eg. shared mailboxes or if same uid is used for multiple accounts).
#verbose_proctitle = no
# Should all processes be killed when Dovecot master process shuts down.
# Setting this to "no" means that Dovecot can be upgraded without
# forcing existing client connections to close (although that could also be
# a problem if the upgrade is e.g. because of a security fix).
#shutdown_clients = yes
# If non-zero, run mail commands via this many connections to doveadm server,
# instead of running them directly in the same process.
#doveadm_worker_count = 0
# UNIX socket or host:port used for connecting to doveadm server
#doveadm_socket_path = doveadm-server
# Space separated list of environment variables that are preserved on Dovecot
# startup and passed down to all of its child processes. You can also give
# key=value pairs to always set specific settings.
#import_environment = TZ
##
## Dictionary server settings
##
# Dictionary can be used to store key=value lists. This is used by several
# plugins. The dictionary can be accessed either directly or though a
# dictionary server. The following dict block maps dictionary names to URIs
# when the server is used. These can then be referenced using URIs in format
# "proxy::<name>".
dict {
#quota = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext
#expire = sqlite:/etc/dovecot/dovecot-dict-sql.conf.ext
}
# Most of the actual configuration gets included below. The filenames are
# first sorted by their ASCII value and parsed in that order. The 00-prefixes
# in filenames are intended to make it easier to understand the ordering.
!include conf.d/*.conf
# A config file can also tried to be included without giving an error if
# it's not found:
!include_try local.conf
mail_location = maildir:~/.maildir
disable_plaintext_auth = no
# Authentication service for Postfix
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0666
user = postfix
group = postfix
}
}

View File

@ -0,0 +1,65 @@
<VirtualHost *:80>
ServerName mailman.{{ base_domain }}
ServerAlias mailman
ServerAdmin root@{{ base_domain }}
DocumentRoot /usr/lib/cgi-bin/mailman
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
RedirectMatch ^/$ /listinfo
Alias /pipermail/ /var/lib/mailman/archives/public/
Alias /images/mailman/ /usr/share/images/mailman/
ScriptAlias /admin /usr/lib/cgi-bin/mailman/admin
ScriptAlias /admindb /usr/lib/cgi-bin/mailman/admindb
ScriptAlias /confirm /usr/lib/cgi-bin/mailman/confirm
ScriptAlias /create /usr/lib/cgi-bin/mailman/create
ScriptAlias /edithtml /usr/lib/cgi-bin/mailman/edithtml
ScriptAlias /listinfo /usr/lib/cgi-bin/mailman/listinfo
ScriptAlias /options /usr/lib/cgi-bin/mailman/options
ScriptAlias /private /usr/lib/cgi-bin/mailman/private
ScriptAlias /rmlist /usr/lib/cgi-bin/mailman/rmlist
ScriptAlias /roster /usr/lib/cgi-bin/mailman/roster
ScriptAlias /subscribe /usr/lib/cgi-bin/mailman/subscribe
ScriptAlias /mailman/ /usr/lib/cgi-bin/mailman/
ScriptAlias /cgi-bin/mailman/ /usr/lib/cgi-bin/mailman/
<Directory /usr/lib/cgi-bin/mailman/>
Options ExecCGI
SetHandler cgi-script
AllowOverride None
Order allow,deny
Allow from all
</Directory>
<Directory /var/lib/mailman/>
Options FollowSymlinks
AllowOverride None
#Order allow,deny
#Allow from all
Require all granted
</Directory>
<Directory /usr/share/images/mailman/>
Options FollowSymlinks
AllowOverride None
Order allow,deny
Allow from all
</Directory>
<Directory /var/lib/mailman/archives/public/>
Options Indexes FollowSymlinks
AllowOverride None
#Order allow,deny
#Allow from all
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/mailman-error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/mailman-access.log combined
</VirtualHost>

View File

@ -0,0 +1,64 @@
---
- hosts: mail
vars:
# use this password for all mailing lists
list_password: mailman
tasks:
- name: install packages for Mailman 2
apt:
name: "{{ item }}"
state: present
loop:
- mailman
- apache2
- name: override systemd services
import_role:
name: ../../roles/systemd_workarounds
vars:
services: [ "apache2" ]
- name: add Mailman config
template:
src: mm_cfg.py.j2
dest: /etc/mailman/mm_cfg.py
- name: create Mailman aliases file
command:
chdir: /var/lib/mailman
cmd: bin/genaliases
creates: /var/lib/mailman/data/aliases
- name: create initial list
shell:
chdir: /var/lib/mailman
cmd: "bin/newlist -a mailman root@{{ base_domain }} {{ list_password }} || true"
- name: restart Mailman
systemd:
name: mailman
state: restarted
- name: add Mailman aliases to Postfix config
lineinfile:
path: /etc/postfix/main.cf
regexp: "^alias_maps = .*$"
line: "alias_maps = hash:/etc/aliases, hash:/var/lib/mailman/data/aliases"
notify: reload Postfix
- name: add Apache config
template:
src: mailman.conf.j2
dest: /etc/apache2/sites-available/mailman.conf
notify: reload Apache
- name: enable Mailman site
command:
cmd: a2ensite mailman.conf
creates: /etc/apache2/sites-enabled/mailman.conf
notify: reload Apache
- name: disable default site
command:
cmd: a2dissite 000-default.conf
removes: /etc/apache2/sites-enabled/000-default.conf
notify: reload Apache
- name: enable CGI on Apache
command:
cmd: a2enmod cgid
creates: /etc/apache2/mods-enabled/cgid.load
notify: restart Apache
handlers:
- name: _imports
import_tasks: ../common.yml

112
mail/mailman2/mm_cfg.py.j2 Normal file
View File

@ -0,0 +1,112 @@
# -*- python -*-
# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
"""This is the module which takes your site-specific settings.
From a raw distribution it should be copied to mm_cfg.py. If you
already have an mm_cfg.py, be careful to add in only the new settings
you want. The complete set of distributed defaults, with annotation,
are in ./Defaults. In mm_cfg, override only those you want to
change, after the
from Defaults import *
line (see below).
Note that these are just default settings - many can be overridden via the
admin and user interfaces on a per-list or per-user basis.
Note also that some of the settings are resolved against the active list
setting by using the value as a format string against the
list-instance-object's dictionary - see the distributed value of
DEFAULT_MSG_FOOTER for an example."""
#######################################################
# Here's where we get the distributed defaults. #
from Defaults import *
##############################################################
# Put YOUR site-specific configuration below, in mm_cfg.py . #
# See Defaults.py for explanations of the values. #
#-------------------------------------------------------------
# The name of the list Mailman uses to send password reminders
# and similar. Don't change if you want mailman-owner to be
# a valid local part.
MAILMAN_SITE_LIST = 'mailman'
#-------------------------------------------------------------
# If you change these, you have to configure your http server
# accordingly (Alias and ScriptAlias directives in most httpds)
DEFAULT_URL_PATTERN = 'http://%s/'
PRIVATE_ARCHIVE_URL = '/private'
IMAGE_LOGOS = '/images/mailman/'
#-------------------------------------------------------------
# Default domain for email addresses of newly created MLs
DEFAULT_EMAIL_HOST = '{{ base_domain }}'
#-------------------------------------------------------------
# Default host for web interface of newly created MLs
DEFAULT_URL_HOST = 'mailman.{{ base_domain }}'
#-------------------------------------------------------------
# Required when setting any of its arguments.
add_virtualhost(DEFAULT_URL_HOST, DEFAULT_EMAIL_HOST)
#-------------------------------------------------------------
# The default language for this server.
DEFAULT_SERVER_LANGUAGE = 'en'
#-------------------------------------------------------------
# Iirc this was used in pre 2.1, leave it for now
USE_ENVELOPE_SENDER = 0 # Still used?
#-------------------------------------------------------------
# Unset send_reminders on newly created lists
DEFAULT_SEND_REMINDERS = 0
#-------------------------------------------------------------
# Uncomment this if you configured your MTA such that it
# automatically recognizes newly created lists.
# (see /usr/share/doc/mailman/README.Exim4.Debian or
# /usr/share/mailman/postfix-to-mailman.py)
# MTA=None # Misnomer, suppresses alias output on newlist
#-------------------------------------------------------------
# Uncomment if you use Postfix virtual domains (but not
# postfix-to-mailman.py), but be sure to see
# /usr/share/doc/mailman/README.Debian first.
MTA='Postfix'
DELIVERY_MODULE = 'SMTPDirect'
SMTPHOST = '127.0.0.1'
SMTPPORT = 10025
#-------------------------------------------------------------
# Uncomment if you want to filter mail with SpamAssassin. For
# more information please visit this website:
# http://www.jamesh.id.au/articles/mailman-spamassassin/
# GLOBAL_PIPELINE.insert(1, 'SpamAssassin')
# Note - if you're looking for something that is imported from mm_cfg, but you
# didn't find it above, it's probably in /usr/lib/mailman/Mailman/Defaults.py.
POSTFIX_STYLE_VIRTUAL_DOMAINS = []

View File

@ -0,0 +1,20 @@
# This is the mailman extension configuration file to enable HyperKitty as an
# archiver. Remember to add the following lines in the mailman.cfg file:
#
# [archiver.hyperkitty]
# class: mailman_hyperkitty.Archiver
# enable: yes
# configuration: /etc/mailman3/mailman-hyperkitty.cfg
#
[general]
# This is your HyperKitty installation, preferably on the localhost. This
# address will be used by Mailman to forward incoming emails to HyperKitty
# for archiving. It does not need to be publicly available, in fact it's
# better if it is not.
base_url: http://mailman.{{ base_domain }}/hyperkitty/
# Shared API key, must be the identical to the value in HyperKitty's
# settings.
api_key: mailman3

View File

@ -0,0 +1,282 @@
# Copyright (C) 2008-2017 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
# GNU Mailman is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
# This file contains the Debian configuration for mailman. It uses ini-style
# formats under the lazr.config regime to define all system configuration
# options. See <https://launchpad.net/lazr.config> for details.
[mailman]
# This address is the "site owner" address. Certain messages which must be
# delivered to a human, but which can't be delivered to a list owner (e.g. a
# bounce from a list owner), will be sent to this address. It should point to
# a human.
site_owner: root@{{ base_domain }}
# This is the local-part of an email address used in the From field whenever a
# message comes from some entity to which there is no natural reply recipient.
# Mailman will append '@' and the host name of the list involved. This
# address must not bounce and it must not point to a Mailman process.
noreply_address: noreply
# The default language for this server.
default_language: en
# Membership tests for posting purposes are usually performed by looking at a
# set of headers, passing the test if any of their values match a member of
# the list. Headers are checked in the order given in this variable. The
# value From_ means to use the envelope sender. Field names are case
# insensitive. This is a space separate list of headers.
sender_headers: from from_ reply-to sender
# Mail command processor will ignore mail command lines after designated max.
email_commands_max_lines: 10
# Default length of time a pending request is live before it is evicted from
# the pending database.
pending_request_life: 3d
# How long should files be saved before they are evicted from the cache?
cache_life: 7d
# A callable to run with no arguments early in the initialization process.
# This runs before database initialization.
pre_hook:
# A callable to run with no arguments late in the initialization process.
# This runs after adapters are initialized.
post_hook:
# Which paths.* file system layout to use.
# You should not change this variable.
layout: venv
# Can MIME filtered messages be preserved by list owners?
filtered_messages_are_preservable: no
# How should text/html parts be converted to text/plain when the mailing list
# is set to convert HTML to plaintext? This names a command to be called,
# where the substitution variable $filename is filled in by Mailman, and
# contains the path to the temporary file that the command should read from.
# The command should print the converted text to stdout.
html_to_plain_text_command: /usr/bin/lynx -dump $filename
# Specify what characters are allowed in list names. Characters outside of
# the class [-_.+=!$*{}~0-9a-z] matched case insensitively are never allowed,
# but this specifies a subset as the only allowable characters. This must be
# a valid character class regexp or the effect on list creation is
# unpredictable.
listname_chars: [-_.0-9a-z]
[shell]
# `mailman shell` (also `withlist`) gives you an interactive prompt that you
# can use to interact with an initialized and configured Mailman system. Use
# --help for more information. This section allows you to configure certain
# aspects of this interactive shell.
# Customize the interpreter prompt.
prompt: >>>
# Banner to show on startup.
banner: Welcome to the GNU Mailman shell
# Use IPython as the shell, which must be found on the system. Valid values
# are `no`, `yes`, and `debug` where the latter is equivalent to `yes` except
# that any import errors will be displayed to stderr.
use_ipython: no
# Set this to allow for command line history if readline is available. This
# can be as simple as $var_dir/history.py to put the file in the var directory.
history_file:
[paths.venv]
# Important directories for Mailman operation. These are defined here so that
# different layouts can be supported. For example, a developer layout would
# be different from a FHS layout. Most paths are based off the var_dir, and
# often just setting that will do the right thing for all the other paths.
# You might also have to set spool_dir though.
#
# Substitutions are allowed, but must be of the form $var where 'var' names a
# configuration variable in the paths.* section. Substitutions are expanded
# recursively until no more $-variables are present. Beware of infinite
# expansion loops!
#
# This is the root of the directory structure that Mailman will use to store
# its run-time data.
var_dir: /opt/mailman3
# This is where the Mailman queue files directories will be created.
queue_dir: $var_dir/queue
# This is the directory containing the Mailman 'runner' and 'master' commands
# if set to the string '$argv', it will be taken as the directory containing
# the 'mailman' command.
bin_dir: /opt/mailman3/bin
# All list-specific data.
list_data_dir: $var_dir/lists
# Directory where log files go.
log_dir: /var/log/mailman3
# Directory for system-wide locks.
lock_dir: $var_dir/locks
# Directory for system-wide data.
data_dir: $var_dir/data
# Cache files.
cache_dir: $var_dir/cache
# Directory for configuration files and such.
etc_dir: /etc/mailman3
# Directory containing Mailman plugins.
ext_dir: $var_dir/ext
# Directory where the default IMessageStore puts its messages.
messages_dir: $var_dir/messages
# Directory for archive backends to store their messages in. Archivers should
# create a subdirectory in here to store their files.
archive_dir: $var_dir/archives
# Root directory for site-specific template override files.
template_dir: $var_dir/templates
# There are also a number of paths to specific file locations that can be
# defined. For these, the directory containing the file must already exist,
# or be one of the directories created by Mailman as per above.
#
# This is where PID file for the master runner is stored.
pid_file: /run/mailman3/master.pid
# Lock file.
lock_file: $lock_dir/master.lck
[database]
# The class implementing the IDatabase.
#class: mailman.database.sqlite.SQLiteDatabase
class: mailman.database.mysql.MySQLDatabase
#class: mailman.database.postgresql.PostgreSQLDatabase
# Use this to set the Storm database engine URL. You generally have one
# primary database connection for all of Mailman. List data and most rosters
# will store their data in this database, although external rosters may access
# other databases in their own way. This string supports standard
# 'configuration' substitutions.
#url: sqlite:///$DATA_DIR/mailman.db
url: mysql+mysqldb://mailman3:mailman3@coffee/mailman3?charset=utf8&use_unicode=1
#url: postgres://mailman3:mmpass@localhost/mailman3
debug: no
[logging.debian]
# This defines various log settings. The options available are:
#
# - level -- Overrides the default level; this may be any of the
# standard Python logging levels, case insensitive.
# - format -- Overrides the default format string
# - datefmt -- Overrides the default date format string
# - path -- Overrides the default logger path. This may be a relative
# path name, in which case it is relative to Mailman's LOG_DIR,
# or it may be an absolute path name. You cannot change the
# handler class that will be used.
# - propagate -- Boolean specifying whether to propagate log message from this
# logger to the root "mailman" logger. You cannot override
# settings for the root logger.
#
# In this section, you can define defaults for all loggers, which will be
# prefixed by 'mailman.'. Use subsections to override settings for specific
# loggers. The names of the available loggers are:
#
# - archiver -- All archiver output
# - bounce -- All bounce processing logs go here
# - config -- Configuration issues
# - database -- Database logging (SQLAlchemy and Alembic)
# - debug -- Only used for development
# - error -- All exceptions go to this log
# - fromusenet -- Information related to the Usenet to Mailman gateway
# - http -- Internal wsgi-based web interface
# - locks -- Lock state changes
# - mischief -- Various types of hostile activity
# - runner -- Runner process start/stops
# - smtp -- Successful SMTP activity
# - smtp-failure -- Unsuccessful SMTP activity
# - subscribe -- Information about leaves/joins
# - vette -- Message vetting information
format: %(asctime)s (%(process)d) %(message)s
datefmt: %b %d %H:%M:%S %Y
propagate: no
level: info
path: mailman.log
[webservice]
# The hostname at which admin web service resources are exposed.
hostname: localhost
# The port at which the admin web service resources are exposed.
port: 8001
# Whether or not requests to the web service are secured through SSL.
use_https: no
# Whether or not to show tracebacks in an HTTP response for a request that
# raised an exception.
show_tracebacks: yes
# The API version number for the current (highest) API.
api_version: 3.1
# The administrative username.
admin_user: restadmin
# The administrative password.
admin_pass: mailman3
[mta]
# The class defining the interface to the incoming mail transport agent.
#incoming: mailman.mta.exim4.LMTP
incoming: mailman.mta.postfix.LMTP
# The callable implementing delivery to the outgoing mail transport agent.
# This must accept three arguments, the mailing list, the message, and the
# message metadata dictionary.
outgoing: mailman.mta.deliver.deliver
# How to connect to the outgoing MTA. If smtp_user and smtp_pass is given,
# then Mailman will attempt to log into the MTA when making a new connection.
smtp_host: localhost
smtp_port: 10025
smtp_user:
smtp_pass:
# Where the LMTP server listens for connections. Use 127.0.0.1 instead of
# localhost for Postfix integration, because Postfix only consults DNS
# (e.g. not /etc/hosts).
lmtp_host: 127.0.0.1
lmtp_port: 8024
# Where can we find the mail server specific configuration file? The path can
# be either a file system path or a Python import path. If the value starts
# with python: then it is a Python import path, otherwise it is a file system
# path. File system paths must be absolute since no guarantees are made about
# the current working directory. Python paths should not include the trailing
# .cfg, which the file must end with.
#configuration: python:mailman.config.exim4
configuration: python:mailman.config.postfix
# The following lines are specific to mailing lists archiving using
# HyperKitty. They require 'python3-mailman-hyperkitty' to be installed
# and will produce errors otherwise.
#
# If you don't want to use HyperKitty, please comment them out.
[archiver.hyperkitty]
class: mailman_hyperkitty.Archiver
enable: yes
configuration: /etc/mailman3/mailman-hyperkitty.cfg

View File

@ -0,0 +1,82 @@
<VirtualHost *:80>
ServerName mailman.{{ base_domain }}
ServerAlias mailman
ServerAdmin root@{{ base_domain }}
# MAIMLAN 3 CONFIG
Alias /favicon.ico /opt/mailman3/web/static/postorius/img/favicon.ico
Alias /static /opt/mailman3/web/static
<Directory "/opt/mailman3/web/static">
Require all granted
</Directory>
<IfModule mod_proxy_uwsgi.c>
ProxyPass /pipermail !
ProxyPass /images/mailman !
ProxyPass /favicon.ico !
ProxyPass /static !
ProxyPass / unix:/run/mailman3-web/uwsgi.sock|uwsgi://localhost/
</IfModule>
# MAILMAN 2 CONFIG
#DocumentRoot /usr/lib/cgi-bin/mailman
#<Directory />
# Options FollowSymLinks
# AllowOverride None
#</Directory>
#
#RedirectMatch ^/$ /listinfo
#
Alias /pipermail/ /var/lib/mailman/archives/public/
Alias /images/mailman/ /usr/share/images/mailman/
#ScriptAlias /admin /usr/lib/cgi-bin/mailman/admin
#ScriptAlias /admindb /usr/lib/cgi-bin/mailman/admindb
#ScriptAlias /confirm /usr/lib/cgi-bin/mailman/confirm
#ScriptAlias /create /usr/lib/cgi-bin/mailman/create
#ScriptAlias /edithtml /usr/lib/cgi-bin/mailman/edithtml
#ScriptAlias /listinfo /usr/lib/cgi-bin/mailman/listinfo
#ScriptAlias /options /usr/lib/cgi-bin/mailman/options
#ScriptAlias /private /usr/lib/cgi-bin/mailman/private
#ScriptAlias /rmlist /usr/lib/cgi-bin/mailman/rmlist
#ScriptAlias /roster /usr/lib/cgi-bin/mailman/roster
#ScriptAlias /subscribe /usr/lib/cgi-bin/mailman/subscribe
#ScriptAlias /mailman/ /usr/lib/cgi-bin/mailman/
#ScriptAlias /cgi-bin/mailman/ /usr/lib/cgi-bin/mailman/
#
#<Directory /usr/lib/cgi-bin/mailman/>
# Options ExecCGI
# SetHandler cgi-script
# AllowOverride None
# Order allow,deny
# Allow from all
#</Directory>
#<Directory /var/lib/mailman/>
# Options FollowSymlinks
# AllowOverride None
# #Order allow,deny
# #Allow from all
# Require all granted
#</Directory>
<Directory /usr/share/images/mailman/>
Options FollowSymlinks
AllowOverride None
Order allow,deny
Allow from all
</Directory>
<Directory /var/lib/mailman/archives/public/>
Options Indexes FollowSymlinks
AllowOverride None
#Order allow,deny
#Allow from all
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/mailman-error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/mailman-access.log combined
</VirtualHost>

View File

@ -0,0 +1,7 @@
*/15 * * * * www-data [ -f /opt/mailman3/bin/django-admin ] && flock -n /run/mailman3-web/cron.minutely /opt/mailman3/bin/mailman-web runjobs minutely
2,17,32,47 * * * * www-data [ -f /opt/mailman3/bin/django-admin ] && flock -n /run/mailman3-web/cron.quarter_hourly /opt/mailman3/bin/mailman-web runjobs quarter_hourly
@hourly www-data [ -f /opt/mailman3/bin/django-admin ] && flock -n /run/mailman3-web/cron.hourly /opt/mailman3/bin/mailman-web runjobs hourly
@daily www-data [ -f /opt/mailman3/bin/django-admin ] && flock -n /run/mailman3-web/cron.daily /opt/mailman3/bin/mailman-web runjobs daily
@weekly www-data [ -f /opt/mailman3/bin/django-admin ] && flock -n /run/mailman3-web/cron.weekly /opt/mailman3/bin/mailman-web runjobs weekly
@monthly www-data [ -f /opt/mailman3/bin/django-admin ] && flock -n /run/mailman3-web/cron.monthly /opt/mailman3/bin/mailman-web runjobs monthly
@yearly www-data [ -f /opt/mailman3/bin/django-admin ] && flock -n /run/mailman3-web/cron.yearly /opt/mailman3/bin/mailman-web runjobs yearly

View File

@ -0,0 +1,16 @@
[Unit]
Description=Mailman3-web uWSGI service
After=network.target
[Service]
ExecStart=/usr/bin/uwsgi --ini /etc/mailman3/uwsgi.ini
Restart=on-failure
KillSignal=SIGQUIT
Type=notify
StandardError=syslog
NotifyAccess=all
User=root
Group=root
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,4 @@
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 0 * * * list if [ -x /opt/mailman3/bin/mailman ]; then /opt/mailman3/bin/mailman digests --send; fi

View File

@ -0,0 +1,18 @@
[Unit]
Description=Mailman3 server
Documentation=man:mailman(1)
Documentation=https://mailman.readthedocs.io/
ConditionPathExists=/etc/mailman3/mailman.cfg
[Service]
ExecStart=/opt/mailman3/bin/mailman -C /etc/mailman3/mailman.cfg start --force
ExecReload=/opt/mailman3/bin/mailman -C /etc/mailman3/mailman.cfg restart
ExecStop=/opt/mailman3/bin/mailman -C /etc/mailman3/mailman.cfg stop
Type=forking
PIDFile=/run/mailman3/master.pid
SyslogIdentifier=mailman3
User=list
Group=list
[Install]
WantedBy=multi-user.target

173
mail/mailman3/mailman3.yml Normal file
View File

@ -0,0 +1,173 @@
- hosts: coffee
tasks:
- name: setup the database on coffee
command:
cmd: mysql
stdin: |
CREATE DATABASE IF NOT EXISTS {{ item }};
CREATE USER IF NOT EXISTS {{ item }} IDENTIFIED BY '{{ item }}';
GRANT ALL PRIVILEGES ON {{ item }}.* TO {{ item }};
loop:
- mailman3
- mailman3web
- hosts: mail
tasks:
- name: install Mailman 3 prerequisites
apt:
name: "{{ item }}"
loop:
- python3-pip
- python3-dev
- python3-xapian
- virtualenv
- uwsgi
- uwsgi-plugin-python3
- default-libmysqlclient-dev
- sassc
- lynx
- git
- memcached
- name: override systemd services
import_role:
name: ../../roles/systemd_workarounds
vars:
services: [ "memcached" ]
- name: upgrade pip
pip:
executable: pip3
name: pip
extra_args: --upgrade
- name: create mailman3 directory
file:
path: /opt/mailman3
state: directory
owner: list
group: list
mode: '2755'
- name: create mailman3-web directory
file:
path: /opt/mailman3/web
state: directory
owner: www-data
group: www-data
- name: create mailman3-web run directory
file:
path: /run/mailman3-web
state: directory
owner: www-data
group: www-data
- name: install pip packages
become_user: list
pip:
virtualenv: /opt/mailman3
virtualenv_python: python3
name: "{{ item }}"
loop:
- mysqlclient
- pylibmc
- git+https://github.com/notanumber/xapian-haystack.git
- mailman
- mailman-web
- mailman-hyperkitty
- name: create mailman3 folder
file:
path: /etc/mailman3
state: directory
mode: 0755
- name: add Mailman 3 configs
template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
group: "{{ item.group }}"
mode: 0640
loop:
- src: mailman.cfg.j2
dest: /etc/mailman3/mailman.cfg
group: list
- src: mailman-hyperkitty.cfg.j2
dest: /etc/mailman3/mailman-hyperkitty.cfg
group: list
- src: settings.py.j2
dest: /etc/mailman3/settings.py
group: www-data
- src: urls.py
dest: /etc/mailman3/urls.py
group: www-data
- src: uwsgi.ini
dest: /etc/mailman3/uwsgi.ini
group: www-data
- name: update cron log level
lineinfile:
path: /etc/default/cron
line: 'EXTRA_OPTS="-L 4"'
notify: restart cron
- name: add new services
copy:
src: "{{ item }}.service"
dest: "/etc/systemd/system/{{ item }}.service"
loop:
- mailman3
- mailman3-web
notify: reload systemd
- name: enable and start new services
systemd:
name: "{{ item }}"
enabled: true
state: started
loop:
- mailman3
- mailman3-web
- name: add cron jobs
copy:
src: "{{ item }}.cron"
dest: "/etc/cron.d/{{ item }}"
loop:
- mailman3
- mailman3-web
- name: enable mod_proxy_uwsgi
command:
cmd: a2enmod proxy_uwsgi
creates: /etc/apache2/mods-enabled/proxy_uwsgi.load
notify: restart Apache
- name: update Apache config
template:
src: mailman.conf.j2
dest: /etc/apache2/sites-available/mailman.conf
notify: reload Apache
- name: update Postfix config
blockinfile:
path: /etc/postfix/main.cf
block: |
owner_request_special = no
transport_maps = hash:/opt/mailman3/data/postfix_lmtp
local_recipient_maps =
proxy:unix:passwd.byname,
$alias_maps,
hash:/opt/mailman3/data/postfix_lmtp
notify: reload Postfix
- name: disable Mailman 2 in Postfix main.cf
lineinfile:
path: /etc/postfix/main.cf
regexp: "^alias_maps = .*$"
line: "alias_maps = hash:/etc/aliases"
notify: reload Postfix
- name: disable Mailman 2 in Postfix master.cf
copy:
src: master.cf
dest: /etc/postfix/master.cf
notify: reload Postfix
- name: disable Mailman 2 cron jobs
replace:
path: /etc/cron.d/mailman
regexp: "^([*\\d@].*)$"
replace: "### \\1"
handlers:
- name: _imports
import_tasks: ../common.yml
- name: reload systemd
systemd:
daemon_reload: true
- name: restart cron
systemd:
name: cron
state: restarted

131
mail/mailman3/master.cf Normal file
View File

@ -0,0 +1,131 @@
#
# Postfix master process configuration file. For details on the format
# of the file, see the master(5) manual page (command: "man 5 master" or
# on-line: http://www.postfix.org/master.5.html).
#
# Do not forget to execute "postfix reload" after editing this file.
#
# ==========================================================================
# service type private unpriv chroot wakeup maxproc command + args
# (yes) (yes) (no) (never) (100)
# ==========================================================================
smtp inet n - n - - smtpd
#smtp inet n - y - 1 postscreen
#smtpd pass - - y - - smtpd
#dnsblog unix - - y - 0 dnsblog
#tlsproxy unix - - y - 0 tlsproxy
submission inet n - n - - smtpd
-o smtpd_sasl_auth_enable=yes
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
-o smtpd_helo_restrictions=reject_invalid_helo_hostname,permit
-o milter_macro_daemon_name=ORIGINATING
#smtps inet n - y - - smtpd
# -o syslog_name=postfix/smtps
# -o smtpd_tls_wrappermode=yes
# -o smtpd_sasl_auth_enable=yes
# -o smtpd_reject_unlisted_recipient=no
# -o smtpd_client_restrictions=$mua_client_restrictions
# -o smtpd_helo_restrictions=$mua_helo_restrictions
# -o smtpd_sender_restrictions=$mua_sender_restrictions
# -o smtpd_recipient_restrictions=
# -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
# -o milter_macro_daemon_name=ORIGINATING
#628 inet n - y - - qmqpd
pickup unix n - y 60 1 pickup
cleanup unix n - y - 0 cleanup
qmgr unix n - n 300 1 qmgr
#qmgr unix n - n 300 1 oqmgr
tlsmgr unix - - y 1000? 1 tlsmgr
rewrite unix - - y - - trivial-rewrite
bounce unix - - y - 0 bounce
defer unix - - y - 0 bounce
trace unix - - y - 0 bounce
verify unix - - y - 1 verify
flush unix n - y 1000? 0 flush
proxymap unix - - n - - proxymap
proxywrite unix - - n - 1 proxymap
smtp unix - - y - - smtp
relay unix - - y - - smtp
-o syslog_name=postfix/$service_name
# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
showq unix n - y - - showq
error unix - - y - - error
retry unix - - y - - error
discard unix - - y - - discard
local unix - n n - - local
virtual unix - n n - - virtual
lmtp unix - - y - - lmtp
anvil unix - - y - 1 anvil
scache unix - - y - 1 scache
postlog unix-dgram n - n - 1 postlogd
#
# ====================================================================
# Interfaces to non-Postfix software. Be sure to examine the manual
# pages of the non-Postfix software to find out what options it wants.
#
# Many of the following services use the Postfix pipe(8) delivery
# agent. See the pipe(8) man page for information about ${recipient}
# and other message envelope options.
# ====================================================================
#
# maildrop. See the Postfix MAILDROP_README file for details.
# Also specify in main.cf: maildrop_destination_recipient_limit=1
#
maildrop unix - n n - - pipe
flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
#
# ====================================================================
#
# Recent Cyrus versions can use the existing "lmtp" master.cf entry.
#
# Specify in cyrus.conf:
# lmtp cmd="lmtpd -a" listen="localhost:lmtp" proto=tcp4
#
# Specify in main.cf one or more of the following:
# mailbox_transport = lmtp:inet:localhost
# virtual_transport = lmtp:inet:localhost
#
# ====================================================================
#
# Cyrus 2.1.5 (Amos Gouaux)
# Also specify in main.cf: cyrus_destination_recipient_limit=1
#
#cyrus unix - n n - - pipe
# user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}
#
# ====================================================================
# Old example of delivery via Cyrus.
#
#old-cyrus unix - n n - - pipe
# flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}
#
# ====================================================================
#
# See the Postfix UUCP_README file for configuration details.
#
uucp unix - n n - - pipe
flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
#
# Other external delivery methods.
#
ifmail unix - n n - - pipe
flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
bsmtp unix - n n - - pipe
flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
scalemail-backend unix - n n - 2 pipe
flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
#mailman unix - n n - - pipe
# flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
# ${nexthop} ${user}
# Mailman
127.0.0.1:10025 inet n - n - 16 smtpd
-o content_filter=
-o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
-o smtpd_helo_restrictions=permit_mynetworks,reject
-o smtpd_client_restrictions=permit_mynetworks,reject
-o smtpd_sender_restrictions=permit_mynetworks,reject
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o mynetworks_style=host
-o smtpd_authorized_xforward_hosts=127.0.0.0/8

View File

@ -0,0 +1,429 @@
"""
Django Settings for Mailman Suite (hyperkitty + postorius)
For more information on this file, see
https://docs.djangoproject.com/en/1.8/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.8/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'very_very_secret'
ADMINS = (
('Mailman Suite Admin', 'root@localhost'),
)
SITE_ID = 1
# Hosts/domain names that are valid for this site; required if DEBUG is False
# See https://docs.djangoproject.com/en/1.8/ref/settings/#allowed-hosts
# Set to '*' per default in the Deian package to allow all hostnames. Mailman3
# is meant to run behind a webserver reverse proxy anyway.
ALLOWED_HOSTS = [
#"localhost", # Archiving API from Mailman, keep it.
# "lists.your-domain.org",
# Add here all production URLs you may have.
'*'
]
# Mailman API credentials
MAILMAN_REST_API_URL = 'http://localhost:8001'
MAILMAN_REST_API_USER = 'restadmin'
MAILMAN_REST_API_PASS = 'mailman3'
MAILMAN_ARCHIVER_KEY = 'mailman3'
MAILMAN_ARCHIVER_FROM = (
'127.0.0.1',
'::1',
# IP addresses for this machine
'{{ mail_ipv4_addr }}',
)
# Application definition
INSTALLED_APPS = (
'hyperkitty',
'postorius',
'django_mailman3',
# Uncomment the next line to enable the admin:
'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'django_gravatar',
'compressor',
'haystack',
'django_extensions',
'django_q',
'allauth',
'allauth.account',
'allauth.socialaccount',
#'django_mailman3.lib.auth.fedora',
#'allauth.socialaccount.providers.openid',
#'allauth.socialaccount.providers.github',
#'allauth.socialaccount.providers.gitlab',
#'allauth.socialaccount.providers.google',
#'allauth.socialaccount.providers.facebook',
#'allauth.socialaccount.providers.twitter',
#'allauth.socialaccount.providers.stackexchange',
)
MIDDLEWARE = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django_mailman3.middleware.TimezoneMiddleware',
'postorius.middleware.PostoriusMiddleware',
)
ROOT_URLCONF = 'urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.template.context_processors.csrf',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'django_mailman3.context_processors.common',
'hyperkitty.context_processors.common',
'postorius.context_processors.postorius',
],
},
},
]
WSGI_APPLICATION = 'wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
DATABASES = {
'default': {
# Use 'sqlite3', 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
#'ENGINE': 'django.db.backends.sqlite3',
#'ENGINE': 'django.db.backends.postgresql_psycopg2',
'ENGINE': 'django.db.backends.mysql',
# DB name or path to database file if using sqlite3.
'NAME': 'mailman3web',
# The following settings are not used with sqlite3:
'USER': 'mailman3web',
'PASSWORD': 'mailman3web',
# HOST: empty for localhost through domain sockets or '127.0.0.1' for
# localhost through TCP.
'HOST': 'coffee',
# PORT: set to empty string for default.
'PORT': '',
# OPTIONS: Extra parameters to use when connecting to the database.
'OPTIONS': {
# Set sql_mode to 'STRICT_TRANS_TABLES' for MySQL. See
# https://docs.djangoproject.com/en/1.11/ref/
# databases/#setting-sql-mode
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
'charset': 'utf8mb4',
},
}
}
# Full-text search engine
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'xapian_backend.XapianEngine',
'PATH': '/opt/mailman3/web/xapian_index',
}
}
# Django Q
Q_CLUSTER = {
'timeout': 300,
'retry': 305,
'save_limit': 100,
'orm': 'default',
'poll': 5,
'workers': 2,
}
# Cache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '127.0.0.1:11211',
}
}
# If you're behind a proxy, use the X-Forwarded-Host header
# See https://docs.djangoproject.com/en/1.8/ref/settings/#use-x-forwarded-host
USE_X_FORWARDED_HOST = True
# And if your proxy does your SSL encoding for you, set SECURE_PROXY_SSL_HEADER
# https://docs.djangoproject.com/en/1.8/ref/settings/#secure-proxy-ssl-header
# SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_SCHEME', 'https')
# Other security settings
# SECURE_SSL_REDIRECT = True
# If you set SECURE_SSL_REDIRECT to True, make sure the SECURE_REDIRECT_EXEMPT
# contains at least this line:
# SECURE_REDIRECT_EXEMPT = [
# "archives/api/mailman/.*", # Request from Mailman.
# ]
# SESSION_COOKIE_SECURE = True
# SECURE_CONTENT_TYPE_NOSNIFF = True
# SECURE_BROWSER_XSS_FILTER = True
# CSRF_COOKIE_SECURE = True
# CSRF_COOKIE_HTTPONLY = True
# X_FRAME_OPTIONS = 'DENY'
# Password validation
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME':
'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME':
'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME':
'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME':
'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'America/Toronto'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Django 1.6+ defaults to a JSON serializer, but it won't work with
# django-openid, see
# https://bugs.launchpad.net/django-openid-auth/+bug/1252826
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
LOGIN_URL = 'account_login'
LOGIN_REDIRECT_URL = 'list_index'
LOGOUT_URL = 'account_logout'
# List of finder classes that know how to find static files in
# various locations.
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
'compressor.finders.CompressorFinder',
)
STATIC_ROOT = '/opt/mailman3/web/static'
STATIC_URL = '/static/'
HOSTNAME = 'mailman.{{ base_domain }}'
# Set default domain for email addresses.
EMAILNAME = '{{ base_domain }}'
# If you enable internal authentication, this is the address that the emails
# will appear to be coming from. Make sure you set a valid domain name,
# otherwise the emails may get rejected.
# https://docs.djangoproject.com/en/1.8/ref/settings/#default-from-email
# DEFAULT_FROM_EMAIL = "mailing-lists@you-domain.org"
DEFAULT_FROM_EMAIL = 'postorius@{}'.format(EMAILNAME)
# If you enable email reporting for error messages, this is where those emails
# will appear to be coming from. Make sure you set a valid domain name,
# otherwise the emails may get rejected.
# https://docs.djangoproject.com/en/1.8/ref/settings/#std:setting-SERVER_EMAIL
# SERVER_EMAIL = 'root@your-domain.org'
SERVER_EMAIL = 'root@{}'.format(EMAILNAME)
# Change this when you have a real email backend
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# Compatibility with Bootstrap 3
from django.contrib.messages import constants as messages # flake8: noqa
MESSAGE_TAGS = {
messages.ERROR: 'danger'
}
#
# Social auth
#
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend',
)
# Django Allauth
ACCOUNT_AUTHENTICATION_METHOD = "username_email"
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
# You probably want https in production, but this is a dev setup file
ACCOUNT_DEFAULT_HTTP_PROTOCOL = "http"
ACCOUNT_UNIQUE_EMAIL = True
SOCIALACCOUNT_PROVIDERS = {
#'openid': {
# 'SERVERS': [
# dict(id='yahoo',
# name='Yahoo',
# openid_url='http://me.yahoo.com'),
# ],
#},
#'google': {
# 'SCOPE': ['profile', 'email'],
# 'AUTH_PARAMS': {'access_type': 'online'},
#},
#'facebook': {
# 'METHOD': 'oauth2',
# 'SCOPE': ['email'],
# 'FIELDS': [
# 'email',
# 'name',
# 'first_name',
# 'last_name',
# 'locale',
# 'timezone',
# ],
# 'VERSION': 'v2.4',
#},
}
#
# Gravatar
# https://github.com/twaddington/django-gravatar
#
# Gravatar base url.
# GRAVATAR_URL = 'http://cdn.libravatar.org/'
# Gravatar base secure https url.
# GRAVATAR_SECURE_URL = 'https://seccdn.libravatar.org/'
# Gravatar size in pixels.
# GRAVATAR_DEFAULT_SIZE = '80'
# An image url or one of the following: 'mm', 'identicon', 'monsterid',
# 'wavatar', 'retro'.
# GRAVATAR_DEFAULT_IMAGE = 'mm'
# One of the following: 'g', 'pg', 'r', 'x'.
# GRAVATAR_DEFAULT_RATING = 'g'
# True to use https by default, False for plain http.
# GRAVATAR_DEFAULT_SECURE = True
#
# django-compressor
# https://pypi.python.org/pypi/django_compressor
#
COMPRESS_PRECOMPILERS = (
('text/less', 'lessc {infile} {outfile}'),
('text/x-scss', 'sassc -t compressed {infile} {outfile}'),
('text/x-sass', 'sassc -t compressed {infile} {outfile}'),
)
# On a production setup, setting COMPRESS_OFFLINE to True will bring a
# significant performance improvement, as CSS files will not need to be
# recompiled on each requests. It means running an additional "compress"
# management command after each code upgrade.
# http://django-compressor.readthedocs.io/en/latest/usage/#offline-compression
COMPRESS_OFFLINE = True
POSTORIUS_TEMPLATE_BASE_URL = 'http://mailman.{{ base_domain }}/'
# Custom logging. Disable sending log emails because this floods our inbox.
# See https://lincolnloop.com/blog/disabling-error-emails-django/.
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse'
}
},
'handlers': {
'file':{
'level': 'WARNING',
'class': 'logging.handlers.RotatingFileHandler',
#'class': 'logging.handlers.WatchedFileHandler',
'filename': '/var/log/mailman3/web/mailman-web.log',
'formatter': 'verbose',
},
'console': {
'class': 'logging.StreamHandler',
'formatter': 'simple',
},
},
'loggers': {
'django.request': {
'handlers': ['file'],
'level': 'WARNING',
'propagate': True,
},
'django': {
'handlers': ['file'],
'level': 'WARNING',
'propagate': True,
},
'hyperkitty': {
'handlers': ['file'],
'level': 'WARNING',
'propagate': True,
},
'postorius': {
'handlers': ['file'],
'level': 'WARNING',
'propagate': True,
},
},
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(process)d %(name)s %(message)s'
},
'simple': {
'format': '%(levelname)s %(message)s'
},
},
#'root': {
# 'handlers': ['file'],
# 'level': 'INFO',
#},
}
#
# HyperKitty-specific
#
# Only display mailing-lists from the same virtual host as the webserver
FILTER_VHOST = False

35
mail/mailman3/urls.py Normal file
View File

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Copyright (C) 1998-2016 by the Free Software Foundation, Inc.
#
# This file is part of Postorius.
#
# Postorius is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Postorius is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# Postorius. If not, see <http://www.gnu.org/licenses/>.
from django.conf.urls import include, url
from django.contrib import admin
from django.urls import reverse_lazy
from django.views.generic import RedirectView
urlpatterns = [
url(r'^$', RedirectView.as_view(
url=reverse_lazy('list_index'),
permanent=True)),
url(r'^postorius/', include('postorius.urls')),
url(r'^hyperkitty/', include('hyperkitty.urls')),
url(r'', include('django_mailman3.urls')),
url(r'^accounts/', include('allauth.urls')),
# Django admin
url(r'^admin/', admin.site.urls),
]

55
mail/mailman3/uwsgi.ini Normal file
View File

@ -0,0 +1,55 @@
[uwsgi]
# Port on which uwsgi will be listening.
uwsgi-socket = /run/mailman3-web/uwsgi.sock
virtualenv = /opt/mailman3
#Enable threading for python
enable-threads = true
# Move to the directory wher the django files are.
chdir = /opt/mailman3/web
module = mailman_web.wsgi:application
env = PYTHONPATH=/etc/mailman3
env = DJANGO_SETTINGS_MODULE=settings
# Setup default number of processes and threads per process.
master = true
process = 2
threads = 2
# Drop privielges and don't run as root.
uid = www-data
gid = www-data
plugins = python3
# Setup the django_q related worker processes.
attach-daemon = /opt/mailman3/bin/mailman-web qcluster
# Increase the buffer size limit (default is 4096)
buffer-size = 32768
# Setup hyperkitty's cron jobs.
#unique-cron = -1 -1 -1 -1 -1 ./manage.py runjobs minutely
#unique-cron = -15 -1 -1 -1 -1 ./manage.py runjobs quarter_hourly
#unique-cron = 0 -1 -1 -1 -1 ./manage.py runjobs hourly
#unique-cron = 0 0 -1 -1 -1 ./manage.py runjobs daily
#unique-cron = 0 0 1 -1 -1 ./manage.py runjobs monthly
#unique-cron = 0 0 -1 -1 0 ./manage.py runjobs weekly
#unique-cron = 0 0 1 1 -1 ./manage.py runjobs yearly
# Setup the request log.
#req-logger = file:/var/log/mailman3/web/mailman-web.log
# Log cron seperately.
#logger = cron file:/var/log/mailman3/web/mailman-web-cron.log
#log-route = cron uwsgi-cron
# Log qcluster commands seperately.
#logger = qcluster file:/var/log/mailman3/web/mailman-web-qcluster.log
#log-route = qcluster uwsgi-daemons
# Last log and it logs the rest of the stuff.
#logger = file:/var/log/mailman3/web/mailman-web-error.log
#logto = /var/log/mailman3/web/mailman-web.log

74
mail/main.yml Normal file
View File

@ -0,0 +1,74 @@
---
- hosts: mail
roles:
- role: ../roles/network_setup
vars:
ipv4_addr: "{{ mail_ipv4_addr }}"
tasks:
- name: install packages for email server
apt:
name: "{{ item }}"
state: present
update_cache: true
loop:
- postfix
- postfix-pcre
- dovecot-imapd
- spamassassin
- spamass-milter
- procmail
- name: override systemd services
import_role:
name: ../roles/systemd_workarounds
vars:
services: [ "dovecot" ]
- name: enable and start SpamAssassin
systemd:
name: spamassassin
enabled: true
state: started
- name: add Dovecot config
copy:
src: dovecot/dovecot.conf
dest: /etc/dovecot/dovecot.conf
notify: restart Dovecot
- name: add Postfix config
template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
loop:
- src: postfix/main.cf.j2
dest: /etc/postfix/main.cf
- src: postfix/master.cf
dest: /etc/postfix/master.cf
- src: postfix/login_maps.pcre.j2
dest: /etc/postfix/login_maps.pcre
notify: reload Postfix
- name: add Procmail config
copy:
src: procmail/procmailrc
dest: /etc/procmailrc
- name: add SpamAssassin config
template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
loop:
- src: spamassassin/local.cf.j2
dest: /etc/spamassassin/local.cf
- src: spamassassin/spamc.conf
dest: /etc/spamassassin/spamc.conf
notify: restart SpamAssassin
- name: add local users
import_role:
name: ../roles/local_users
handlers:
- name: _imports
import_tasks: common.yml
- name: restart Dovecot
systemd:
name: dovecot
state: restarted
- name: restart SpamAssassin
systemd:
name: spamassassin
state: restarted

View File

@ -0,0 +1 @@
/^(.*)@{{ base_domain | replace('.', '\\.') }}$/ ${1}

100
mail/postfix/main.cf.j2 Normal file
View File

@ -0,0 +1,100 @@
# See /usr/share/postfix/main.cf.dist for a commented, more complete version
# Debian specific: Specifying a file name will cause the first
# line of that file to be used as the name. The Debian default
# is /etc/mailname.
#myorigin = /etc/mailname
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no
# appending .domain is the MUA's job.
append_dot_mydomain = no
# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h
readme_directory = no
# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 2 on
# fresh installs.
compatibility_level = 2
mailbox_size_limit = 0
myhostname = mail.{{ base_domain }}
mydomain = {{ base_domain }}
alias_maps = hash:/etc/aliases
myorigin = $mydomain
mydestination = $myhostname, $mydomain, localhost.$mydomain, localhost
relayhost =
relay_domains =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 {{ ipv4_subnet }}
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = {{ mail_ipv4_addr }}, 127.0.0.1
inet_protocols = ipv4, ipv6
require_home_directory = yes
# Procmail
mailbox_command = procmail -a "$EXTENSION"
# rewrite @foo.csclub.internal to @csclub.internal for locally generated mail
masquerade_domains = $mydomain
masquerade_classes = envelope_sender, envelope_recipient, header_sender, header_recipient
local_header_rewrite_clients = permit_inet_interfaces, permit_mynetworks
# SpamAssassin
milter_default_action = accept
milter_connect_macros = j {daemon_name} v {if_name} _
non_smtpd_milters = $smtpd_milters
smtpd_milters = unix:/var/spool/postfix/spamass/spamass.sock
# TLS parameters
#smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
#smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
#smtpd_use_tls=yes
#smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
#smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
# information on enabling SSL in the smtp client.
# HELO restrictions
smtpd_helo_required = yes
smtpd_helo_restrictions =
permit_mynetworks,
reject_invalid_helo_hostname,
#reject_non_fqdn_helo_hostname,
permit
# Sender (MAIL FROM) restrictions
smtpd_sender_login_maps = pcre:/etc/postfix/login_maps.pcre
smtpd_sender_restrictions =
permit_mynetworks,
reject_sender_login_mismatch,
reject_non_fqdn_sender
#reject_unknown_sender_domain
# Relay restrictions
smtpd_relay_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_unauth_destination
# Recipient (RCPT TO) restrictions
smtpd_recipient_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_unauth_destination,
reject_non_fqdn_recipient,
#reject_unknown_reverse_client_hostname,
reject_unknown_recipient_domain
# SASL
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
broken_sasl_auth_clients = yes

131
mail/postfix/master.cf Normal file
View File

@ -0,0 +1,131 @@
#
# Postfix master process configuration file. For details on the format
# of the file, see the master(5) manual page (command: "man 5 master" or
# on-line: http://www.postfix.org/master.5.html).
#
# Do not forget to execute "postfix reload" after editing this file.
#
# ==========================================================================
# service type private unpriv chroot wakeup maxproc command + args
# (yes) (yes) (no) (never) (100)
# ==========================================================================
smtp inet n - n - - smtpd
#smtp inet n - y - 1 postscreen
#smtpd pass - - y - - smtpd
#dnsblog unix - - y - 0 dnsblog
#tlsproxy unix - - y - 0 tlsproxy
submission inet n - n - - smtpd
-o smtpd_sasl_auth_enable=yes
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
-o smtpd_helo_restrictions=reject_invalid_helo_hostname,permit
-o milter_macro_daemon_name=ORIGINATING
#smtps inet n - y - - smtpd
# -o syslog_name=postfix/smtps
# -o smtpd_tls_wrappermode=yes
# -o smtpd_sasl_auth_enable=yes
# -o smtpd_reject_unlisted_recipient=no
# -o smtpd_client_restrictions=$mua_client_restrictions
# -o smtpd_helo_restrictions=$mua_helo_restrictions
# -o smtpd_sender_restrictions=$mua_sender_restrictions
# -o smtpd_recipient_restrictions=
# -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
# -o milter_macro_daemon_name=ORIGINATING
#628 inet n - y - - qmqpd
pickup unix n - y 60 1 pickup
cleanup unix n - y - 0 cleanup
qmgr unix n - n 300 1 qmgr
#qmgr unix n - n 300 1 oqmgr
tlsmgr unix - - y 1000? 1 tlsmgr
rewrite unix - - y - - trivial-rewrite
bounce unix - - y - 0 bounce
defer unix - - y - 0 bounce
trace unix - - y - 0 bounce
verify unix - - y - 1 verify
flush unix n - y 1000? 0 flush
proxymap unix - - n - - proxymap
proxywrite unix - - n - 1 proxymap
smtp unix - - y - - smtp
relay unix - - y - - smtp
-o syslog_name=postfix/$service_name
# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
showq unix n - y - - showq
error unix - - y - - error
retry unix - - y - - error
discard unix - - y - - discard
local unix - n n - - local
virtual unix - n n - - virtual
lmtp unix - - y - - lmtp
anvil unix - - y - 1 anvil
scache unix - - y - 1 scache
postlog unix-dgram n - n - 1 postlogd
#
# ====================================================================
# Interfaces to non-Postfix software. Be sure to examine the manual
# pages of the non-Postfix software to find out what options it wants.
#
# Many of the following services use the Postfix pipe(8) delivery
# agent. See the pipe(8) man page for information about ${recipient}
# and other message envelope options.
# ====================================================================
#
# maildrop. See the Postfix MAILDROP_README file for details.
# Also specify in main.cf: maildrop_destination_recipient_limit=1
#
maildrop unix - n n - - pipe
flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
#
# ====================================================================
#
# Recent Cyrus versions can use the existing "lmtp" master.cf entry.
#
# Specify in cyrus.conf:
# lmtp cmd="lmtpd -a" listen="localhost:lmtp" proto=tcp4
#
# Specify in main.cf one or more of the following:
# mailbox_transport = lmtp:inet:localhost
# virtual_transport = lmtp:inet:localhost
#
# ====================================================================
#
# Cyrus 2.1.5 (Amos Gouaux)
# Also specify in main.cf: cyrus_destination_recipient_limit=1
#
#cyrus unix - n n - - pipe
# user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}
#
# ====================================================================
# Old example of delivery via Cyrus.
#
#old-cyrus unix - n n - - pipe
# flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}
#
# ====================================================================
#
# See the Postfix UUCP_README file for configuration details.
#
uucp unix - n n - - pipe
flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
#
# Other external delivery methods.
#
ifmail unix - n n - - pipe
flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
bsmtp unix - n n - - pipe
flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
scalemail-backend unix - n n - 2 pipe
flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
mailman unix - n n - - pipe
flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
${nexthop} ${user}
# Mailman
127.0.0.1:10025 inet n - n - 16 smtpd
-o content_filter=
-o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
-o smtpd_helo_restrictions=permit_mynetworks,reject
-o smtpd_client_restrictions=permit_mynetworks,reject
-o smtpd_sender_restrictions=permit_mynetworks,reject
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o mynetworks_style=host
-o smtpd_authorized_xforward_hosts=127.0.0.0/8

1
mail/procmail/procmailrc Normal file
View File

@ -0,0 +1 @@
DEFAULT=$HOME/.maildir/

View File

@ -0,0 +1,109 @@
# This is the right place to customize your installation of SpamAssassin.
#
# See 'perldoc Mail::SpamAssassin::Conf' for details of what can be
# tweaked.
#
# Only a small subset of options are listed below
#
###########################################################################
# Add *****SPAM***** to the Subject header of spam e-mails
#
# rewrite_header Subject *****SPAM*****
# Add X-Spam-Flag header to all emails, not just spam ones
add_header all Flag _YESNOCAPS_
# Save spam messages as a message/rfc822 MIME attachment instead of
# modifying the original message (0: off, 2: use text/plain instead)
#
# report_safe 1
report_safe 0
# Use site-wide Bayesian learner instead of per-user
bayes_path /var/lib/spamassassin/.spamassassin/bayes
bayes_file_mode 0775
# Don't use autolearning; we are using supervised learning
bayes_auto_learn 0
# Default DB file size is 150000 tokens, which is roughly equivalent to 8Mb
bayes_expiry_max_db_size 600000
# Whitelist ourselves
whitelist_from_rcvd *@{{ base_domain }} {{ base_domain }}
whitelist_from_rcvd *@*.{{ base_domain }} {{ base_domain }}
whitelist_from_rcvd *@{{ base_domain }} localhost
# Custom Bayes scores
score BAYES_95 0 0 4.0 4.0
score BAYES_99 0 0 7.0 7.0
# Custom rules
header TO_UNDISCLOSED_RECIPIENTS To =~ /\bundisclosed-recipients\b/i
describe TO_UNDISCLOSED_RECIPIENTS undisclosed-recipients in To: header
score TO_UNDISCLOSED_RECIPIENTS 4.0
# Set which networks or hosts are considered 'trusted' by your mail
# server (i.e. not spammers)
#
# trusted_networks 212.17.35.
# Set file-locking method (flock is not safe over NFS, but is faster)
#
# lock_method flock
# Set the threshold at which a message is considered spam (default: 5.0)
#
# required_score 5.0
# Use Bayesian classifier (default: 1)
#
# use_bayes 1
# Set headers which may provide inappropriate cues to the Bayesian
# classifier
#
# bayes_ignore_header X-Bogosity
# bayes_ignore_header X-Spam-Flag
# bayes_ignore_header X-Spam-Status
# Whether to decode non- UTF-8 and non-ASCII textual parts and recode
# them to UTF-8 before the text is given over to rules processing.
#
# normalize_charset 1
# Some shortcircuiting, if the plugin is enabled
#
ifplugin Mail::SpamAssassin::Plugin::Shortcircuit
#
# default: strongly-whitelisted mails are *really* whitelisted now, if the
# shortcircuiting plugin is active, causing early exit to save CPU load.
# Uncomment to turn this on
#
# shortcircuit USER_IN_WHITELIST on
# shortcircuit USER_IN_DEF_WHITELIST on
# shortcircuit USER_IN_ALL_SPAM_TO on
# shortcircuit SUBJECT_IN_WHITELIST on
# the opposite; blacklisted mails can also save CPU
#
# shortcircuit USER_IN_BLACKLIST on
# shortcircuit USER_IN_BLACKLIST_TO on
# shortcircuit SUBJECT_IN_BLACKLIST on
# if you have taken the time to correctly specify your "trusted_networks",
# this is another good way to save CPU
#
# shortcircuit ALL_TRUSTED on
# and a well-trained bayes DB can save running rules, too
#
# shortcircuit BAYES_99 spam
# shortcircuit BAYES_00 ham
endif # Mail::SpamAssassin::Plugin::Shortcircuit

View File

@ -0,0 +1,2 @@
# same value as message_size_limit in /etc/postfix/main.cf
-s 20971520

37
outsider/README.md Normal file
View File

@ -0,0 +1,37 @@
# Outsider container
So this container's a bit special - it represents a host which is **not**
on the UW network. The motivation is to test software which have different
privilege settings for people outside of the local network, e.g. Postfix.
The idea is to route packets from the 'outsider' container to the LXC host
(i.e. the VM), and the VM will then route them to the other containers.
We could've also created an extra container to act as the router, but
that seemed kind of wasteful.
## Installation
Once you have created the container, add the following iptables rules on
the VM:
```
iptables -t nat -A POSTROUTING -s 192.168.125.0/24 -d 192.168.122.1 -j MASQUERADE
iptables -t nat -A POSTROUTING -s 192.168.125.0/24 ! -d 192.168.122.0/24 -j MASQUERADE
```
I also strongly suggest installing iptables-persistent so that these rules
persist on the next reboot:
```
apt install iptables-persistent
```
The idea here is that packets from the 'outsider' container should only be
**forwarded**, not masqueraded, to the other containers (to preserve its IP
address), unless if it needs to communicate with the outside world (e.g. to
download Debian packages), in which case we need to use NAT because the
iptables rules which libvirt created on your real computer don't take that
subnet into account (run `iptables -t nat -L -v` on your real computer
to see what I mean). 192.168.122.1, which is your real computer, is a special
case because your host does not have a routing table entry for that
subnet, so it wouldn't be able to reply.
As usual, create the container, start it, and install python3.
Now detach and run the playbook:
```
ansible-playbook main.yml
```

10
outsider/main.yml Normal file
View File

@ -0,0 +1,10 @@
---
- hosts: outsider
roles:
- role: ../roles/network_setup
vars:
ipv4_addr: "{{ outsider_ipv4_addr }}"
tasks:
- name: add local users
import_role:
name: ../roles/local_users

View File

@ -0,0 +1,29 @@
- name: create users
user:
name: "{{ item }}"
# password is dovecotpostfix
# This hash was generated with the following command:
# python3 -c "import crypt; import getpass; print(crypt.crypt(getpass.getpass()))"
password: $6$StYWEDOVhv1TnjaM$xq9mmGWY8VDGNGavODuVbCYs56keLivi9GUB3W.NCTNusQaFZsYllSVwCMGXef0.P3vlHL.GsHuD1LCngv2G7/
shell: /bin/bash
loop: "{{ local_users }}"
- name: install Mutt
apt:
name: mutt
state: present
- name: create Mutt cache directory
file:
path: "/home/{{ item }}/.mutt/cache"
state: directory
owner: "{{ item }}"
group: "{{ item }}"
loop: "{{ local_users }}"
- name: add muttrc for each user
template:
src: templates/muttrc.j2
dest: "/home/{{ item }}/.muttrc"
owner: "{{ item }}"
group: "{{ item }}"
vars:
username: "{{ item }}"
loop: "{{ local_users }}"

View File

@ -0,0 +1,28 @@
# SMTP
set realname = "{{ username.capitalize() }} Smith"
set from = "{{ username }}@{{ base_domain }}"
set smtp_url = "smtp://{{ username }}@mail.{{ base_domain }}:587"
set smtp_pass = "dovecotpostfix"
unset record
# IMAP
set folder = "imap://mail.{{ base_domain }}"
set imap_user = "{{ username }}"
set imap_pass = "dovecotpostfix"
set imap_authenticators = "plain"
set spoolfile = "+Inbox"
set sort = reverse-date-received
set header_cache = "~/.mutt/cache/headers"
set message_cachedir = "~/.mutt/cache/bodies"
# TLS
set ssl_starttls = no
set ssl_force_tls = no
# Misc
bind index G imap-fetch-mail
set pager_stop=yes
bind pager <up> previous-line
bind pager <down> next-line
set mail_check=60
set imap_keepalive=900

View File

@ -0,0 +1,4 @@
local_users:
- alice
- bob
- eve

View File

@ -0,0 +1,4 @@
- name: restart networking
systemd:
name: networking
state: restarted

View File

@ -0,0 +1,17 @@
- name: setup /etc/network/interfaces
template:
src: templates/interfaces.j2
dest: /etc/network/interfaces
notify:
- restart networking
- name: setup /etc/hosts
template:
src: templates/hosts.j2
dest: /etc/hosts
- name: setup /etc/resolv.conf
copy:
content: |
search {{ base_domain }}
nameserver {{ dns_ipv4_addr }}
dest: /etc/resolv.conf
when: ansible_host != 'dns'

View File

@ -0,0 +1,6 @@
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
{{ ipv4_addr }} {{ ansible_hostname }}.{{ base_domain }} {{ ansible_hostname }}

View File

@ -0,0 +1,10 @@
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address {{ ipv4_addr }}
netmask 255.255.255.0
gateway {{ host_ipv4_addr }}
source /etc/network/interfaces.d/*

View File

@ -0,0 +1,8 @@
- name: reload systemd
systemd:
daemon_reload: true
- name: restart service
systemd:
name: "{{ item.dest | regex_replace('^/etc/systemd/system/(.*)\\.service\\.d/override\\.conf$', '\\1') }}"
state: restarted
loop: "{{ service_overrides.results }}"

View File

@ -0,0 +1,20 @@
- name: create override directory
file:
path: "/etc/systemd/system/{{ item }}.service.d"
mode: 0755
state: directory
loop: "{{ services }}"
- name: disable mount namespaces
copy:
content: |
[Service]
ProtectSystem=false
PrivateTmp=false
PrivateDevices=false
ProtectHome=false
dest: "/etc/systemd/system/{{ item }}.service.d/override.conf"
loop: "{{ services }}"
register: service_overrides
notify:
- reload systemd
- restart service