Add multi-site support

This adds optional support for creating multiple sites which
appear as independent mailman installations, though they all
rely on the underlying operating-system provided install.

Story: 2001382
Task: 6091
Depends-On: Ic92726dc341af5802ad803d239bd547ef5068043
Change-Id: I3a31465882ec95d822d590045216ec751c7cd22e
This commit is contained in:
James E. Blair 2017-12-12 10:48:30 -08:00
parent 2704eca15a
commit 0778735e66
8 changed files with 637 additions and 34 deletions

View File

@ -0,0 +1,132 @@
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
# Copyright (C) 2017 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Puppet maillist provider for mailman mailing lists.
# Based on the 'mailman' package provider in puppet 2.7, except this one
# does not muck with aliases.
require 'puppet/provider/parsedfile'
Puppet::Type.type(:mailman_list).provide(:mailman) do
defaultfor :kernel => 'Linux'
if [ "CentOS", "RedHat", "Fedora" ].any? { |os| Facter.value(:operatingsystem) == os }
commands :list_lists => "/usr/lib/mailman/bin/list_lists", :rmlist => "/usr/lib/mailman/bin/rmlist", :newlist => "/usr/lib/mailman/bin/newlist"
commands :mailman => "/usr/lib/mailman/mail/mailman"
else
# This probably won't work for non-Debian installs, but this path is sure not to be in the PATH.
commands :list_lists => "list_lists", :rmlist => "rmlist", :newlist => "newlist"
commands :mailman => "/var/lib/mailman/mail/mailman"
end
mk_resource_methods
# Return a list of existing mailman instances.
def self.instances
ret = []
Dir.entries('/srv/mailman').each do |entry|
if (entry == '.' || entry == '..') then next end
path = File.join('/srv/mailman', entry)
if !File.directory?(path) then next end
if !File.exists?(File.join(path, 'lists')) then next end
ENV['MAILMAN_SITE_DIR'] = path
list_lists('--bare').split("\n").each do |line|
ret << new(:ensure => :present, :name => line.strip+'@'+entry)
end
end
return ret
end
def self.prefetch(lists)
instances.each do |prov|
if list = lists[prov.name] || lists[prov.name.downcase]
list.provider = prov
end
end
end
def setenv
r = self.name.split('@')
ENV['MAILMAN_SITE_DIR'] = File.join('/srv/mailman', r[1])
print "Mailman install dir", ENV['MAILMAN_SITE_DIR'], "\n"
return r[0]
end
def create
print "create ", self.name, "\n"
name = setenv
args = []
if val = @resource[:mailserver]
args << "--emailhost" << val
end
if val = @resource[:webserver]
args << "--urlhost" << val
end
args << name
if val = @resource[:admin]
args << val
else
raise ArgumentError, "Mailman lists require an administrator email address"
end
if val = @resource[:password]
args << val
else
raise ArgumentError, "Mailman lists require an administrator password"
end
newlist(*args)
puts "done"
end
def destroy(purge = false)
puts "destroy", self.name
name = setenv
args = []
args << "--archives" if purge
args << name
rmlist(*args)
end
def exists?
properties[:ensure] != :absent
end
def flush
@property_hash.clear
end
def properties
if @property_hash.empty?
@property_hash = query || {:ensure => :absent}
@property_hash[:ensure] = :absent if @property_hash.empty?
end
@property_hash.dup
end
def purge
destroy(true)
end
def query
self.class.instances.each do |list|
if list.name == self.name or list.name.downcase == self.name
return list.properties
end
end
nil
end
end

View File

@ -0,0 +1,30 @@
require 'puppet/provider/parsedfile'
Puppet::Type.newtype(:mailman_list) do
ensurable
newparam(:name) do
desc "The name of the mailing list."
end
newparam(:install) do
desc "The mailmain installation to use."
end
newparam(:admin) do
desc "The email address of the administrator."
end
newparam(:description) do
desc "The description of the mailing list."
end
newparam(:mailserver) do
desc "The FQDN of the mailing list host."
end
newparam(:password) do
desc "The admin password for the list."
end
newparam(:provider) do
desc "The backend to use for this mailman_list."
end
newparam(:webserver) do
desc "The FQDN of the host providing web archives."
end
end

View File

@ -2,7 +2,7 @@
#
class mailman(
$vhost_name = $::fqdn,
$multihost = false,
) {
include ::httpd
@ -11,49 +11,51 @@ class mailman(
ensure => installed,
}
::httpd::vhost { $vhost_name:
port => 80,
docroot => '/var/www/',
priority => '50',
template => 'mailman/mailman.vhost.erb',
if ($multihost) {
file { '/etc/mailman/mm_cfg.py':
ensure => present,
owner => 'root',
group => 'root',
mode => '0444',
content => template('mailman/mm_cfg_multihost.py.erb'),
replace => true,
require => Package['mailman'],
}
} else {
::httpd::vhost { $vhost_name:
port => 80,
docroot => '/var/www/',
priority => '50',
template => 'mailman/mailman.vhost.erb',
}
file { '/etc/mailman/mm_cfg.py':
ensure => present,
owner => 'root',
group => 'root',
mode => '0444',
content => template('mailman/mm_cfg.py.erb'),
replace => true,
require => Package['mailman'],
}
service { 'mailman':
ensure => running,
hasrestart => true,
hasstatus => false,
subscribe => File['/etc/mailman/mm_cfg.py'],
require => Package['mailman'],
}
}
httpd_mod { 'rewrite':
ensure => present,
before => Service['httpd'],
}
httpd_mod { 'cgid':
ensure => present,
before => Service['httpd'],
}
file { '/var/www/index.html':
ensure => present,
source => 'puppet:///modules/mailman/index.html',
owner => 'root',
group => 'root',
replace => true,
mode => '0444',
require => Httpd::Vhost[$vhost_name],
}
file { '/etc/mailman/mm_cfg.py':
ensure => present,
owner => 'root',
group => 'root',
mode => '0444',
content => template('mailman/mm_cfg.py.erb'),
replace => true,
require => Package['mailman'],
}
service { 'mailman':
ensure => running,
hasrestart => true,
hasstatus => false,
subscribe => File['/etc/mailman/mm_cfg.py'],
require => Package['mailman'],
}
file { '/etc/mailman/en':
ensure => directory,
owner => 'root',
@ -63,4 +65,13 @@ class mailman(
require => Package['mailman'],
source => 'puppet:///modules/mailman/html-templates-en',
}
file { '/var/www/index.html':
ensure => present,
source => 'puppet:///modules/mailman/index.html',
owner => 'root',
group => 'root',
replace => true,
mode => '0444',
}
}

93
manifests/site.pp Normal file
View File

@ -0,0 +1,93 @@
# Copyright (C) 2017 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
define mailman::site ($default_email_host, $default_url_host)
{
include ::httpd
$root = "/srv/mailman/${name}"
$dirs = [
"${root}/",
"${root}/etc",
"${root}/lists",
"${root}/logs",
"${root}/locks",
"${root}/data",
"${root}/spam",
"${root}/mail",
"${root}/run",
"${root}/archives",
"${root}/archives/public",
"${root}/archives/private",
"${root}/qfiles",
"${root}/qfiles/in",
"${root}/qfiles/out",
"${root}/qfiles/commands",
"${root}/qfiles/bounces",
"${root}/qfiles/news",
"${root}/qfiles/archive",
"${root}/qfiles/shunt",
"${root}/qfiles/virgin",
"${root}/qfiles/bad",
"${root}/qfiles/retry",
"${root}/qfiles/maildir",
]
file { $dirs:
ensure => directory,
owner => 'list',
group => 'list',
mode => '2775',
}
file { "/srv/mailman/${name}/etc/mm_cfg_local.py":
ensure => present,
content => template('mailman/mm_site_cfg.py.erb'),
}
if ! defined(File['/etc/mailman/sites']) {
file { '/etc/mailman/sites':
ensure => present,
}
}
file_line { "mailman_site_file_${name}":
require => File['/etc/mailman/sites'],
path => '/etc/mailman/sites',
line => "${default_email_host}: /srv/mailman/${name}",
}
::httpd::vhost { $default_url_host:
port => 80,
docroot => '/var/www/',
priority => '50',
template => 'mailman/mailman_multihost.vhost.erb',
}
file { "/etc/init.d/mailman-${name}":
ensure => present,
content => template('mailman/mailman.init.erb'),
owner => 'root',
group => 'root',
mode => '0755',
}
service { "mailman-${name}":
enable => true,
hasrestart => true,
hasstatus => false,
require => File["/etc/init.d/mailman-${name}"],
}
}

116
templates/mailman.init.erb Normal file
View File

@ -0,0 +1,116 @@
#! /bin/sh
#
# mailman-<%= @name %> starts up the master queue runner for mailman
#
# Based on skeleton originally by Miquel van Smoorenburg and Ian Murdock,
# customisations by Tollef Fog Heen and Thijs Kinkhorst for Debian.
#
### BEGIN INIT INFO
# Provides: mailman-qrunner-<%= @name %>
# Required-Start: $syslog $local_fs $remote_fs $named $network
# Required-Stop: $syslog $local_fs $remote_fs $named $network
# Should-Start:
# Should-Stop:
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: Mailman Master Queue Runner
# Description: Starts and stops the Mailman queue runners, used to
# manage the various message queues within the Mailman
# mailing list manager.
### END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/lib/mailman/bin/mailmanctl
MAILMAN_SITE_DIR=/srv/mailman/<%= @name %>
PIDFILE=$MAILMAN_SITE_DIR/run/mailman.pid
test -x $DAEMON || exit 0
set -e
if ! [ -d /var/run/mailman ]; then
install -d -o list -g list /var/run/mailman
fi
if ! [ -d /var/lock/mailman ]; then
install -d -o root -g list -m 2775 /var/lock/mailman
fi
. /lib/lsb/init-functions
# In rare upgrading cycles python might not be available at some point.
# Do not break the upgrade in that case.
if ! [ -x /usr/bin/python ]; then
log_warning_msg "Python interpreter not available, exiting."
exit 0;
fi
# Just a newline.
nl='
'
case "$1" in
start)
SITE_LIST=$( sed -rne "s/^[[:space:]]*MAILMAN_SITE_LIST[[:space:]]*=[[:space:]]*(['\"])([^'\"]+)\\1/\\2/p" /etc/mailman/mm_cfg.py )
[ -n "$SITE_LIST" ] || SITE_LIST='mailman'
case "$nl$(/var/lib/mailman/bin/list_lists -b)$nl" in
(*$nl$SITE_LIST$nl*) ;;
(*)
log_warning_msg "Site list for mailman missing (looking for list named '${SITE_LIST}')."
log_warning_msg "Please create it; until then, mailman will refuse to start."
exit 0 ;;
esac
log_daemon_msg "Starting Mailman master qrunner" "mailmanctl"
if $DAEMON -s -q start; then
log_end_msg 0
else
log_end_msg 1
fi
;;
stop)
log_daemon_msg "Stopping Mailman master qrunner" "mailmanctl"
if $DAEMON -q stop; then
rm -f $PIDFILE
log_end_msg 0
else
log_end_msg 1
fi
;;
reload)
log_begin_msg "Reloading Mailman master qrunner configuration"
if $DAEMON -q restart; then
log_end_msg 0
else
log_end_msg 1
fi
;;
restart|force-reload)
PID=`cat $PIDFILE 2>/dev/null` || true
log_daemon_msg "Restarting Mailman master qrunner" "mailmanctl"
$DAEMON -q stop
if test -n "$PID" && kill -0 $PID 2>/dev/null ; then
log_action_begin_msg "Waiting"
for cnt in `seq 1 5`; do
sleep 1
kill -0 $PID 2>/dev/null || break
done;
if kill -0 $PID 2>/dev/null ; then
log_action_end_msg 1
else
log_action_end_msg 0
fi
fi
if $DAEMON -q start; then
log_end_msg 0
else
log_end_msg 1
fi
;;
*)
echo "Usage: /etc/init.d/mailman {start|stop|restart|reload|force-reload}" >&2
exit 1
;;
esac
exit 0

View File

@ -0,0 +1,62 @@
<VirtualHost *:80>
ServerName <%= @default_url_host %>
ErrorLog ${APACHE_LOG_DIR}/<%= @default_url_host %>-error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/<%= @default_url_host %>-access.log combined
DocumentRoot /var/www
RewriteEngine on
RewriteRule ^/$ /cgi-bin/mailman/listinfo [R]
# We can find mailman here:
ScriptAlias /cgi-bin/mailman/ /usr/lib/cgi-bin/mailman/
# And the public archives:
Alias /pipermail/ /var/lib/mailman/archives/public/
# Logos:
Alias /images/mailman/ /usr/share/images/mailman/
# Use this if you don't want the "cgi-bin" component in your URL:
# In case you want to access mailman through a shorter URL you should enable
# this:
#ScriptAlias /mailman/ /usr/lib/cgi-bin/mailman/
# In this case you need to set the DEFAULT_URL_PATTERN in
# /etc/mailman/mm_cfg.py to http://%s/mailman/ for the cookie
# authentication code to work. Note that you need to change the base
# URL for all the already-created lists as well.
<Directory /usr/lib/cgi-bin/mailman/>
AllowOverride None
Options ExecCGI
AddHandler cgi-script .cgi
SetEnv MAILMAN_SITE_DIR /srv/mailman/<%= @name %>
Order allow,deny
Allow from all
<IfVersion >= 2.4>
Require all granted
</IfVersion>
</Directory>
<Directory /var/lib/mailman/archives/public/>
Options FollowSymlinks
AllowOverride None
Order allow,deny
Allow from all
<IfVersion >= 2.4>
Require all granted
</IfVersion>
</Directory>
<Directory /usr/share/images/mailman/>
AllowOverride None
Order allow,deny
Allow from all
<IfVersion >= 2.4>
Require all granted
</IfVersion>
</Directory>
</VirtualHost>

View File

@ -0,0 +1,40 @@
import os
import sys
sys.path.insert(0, os.path.join(os.environ['MAILMAN_SITE_DIR'], 'etc'))
from mm_cfg_local import *
VAR_PREFIX = os.environ['MAILMAN_SITE_DIR']
# Useful directories
LIST_DATA_DIR = os.path.join(VAR_PREFIX, 'lists')
LOG_DIR = os.path.join(VAR_PREFIX, 'logs')
LOCK_DIR = os.path.join(VAR_PREFIX, 'locks')
DATA_DIR = os.path.join(VAR_PREFIX, 'data')
SPAM_DIR = os.path.join(VAR_PREFIX, 'spam')
WRAPPER_DIR = os.path.join(EXEC_PREFIX, 'mail')
BIN_DIR = os.path.join(PREFIX, 'bin')
SCRIPTS_DIR = os.path.join(PREFIX, 'scripts')
TEMPLATE_DIR = os.path.join(PREFIX, 'templates')
MESSAGES_DIR = os.path.join(PREFIX, 'messages')
PUBLIC_ARCHIVE_FILE_DIR = os.path.join(VAR_PREFIX, 'archives', 'public')
PRIVATE_ARCHIVE_FILE_DIR = os.path.join(VAR_PREFIX, 'archives', 'private')
# Directories used by the qrunner subsystem
QUEUE_DIR = os.path.join(VAR_PREFIX, 'qfiles')
INQUEUE_DIR = os.path.join(QUEUE_DIR, 'in')
OUTQUEUE_DIR = os.path.join(QUEUE_DIR, 'out')
CMDQUEUE_DIR = os.path.join(QUEUE_DIR, 'commands')
BOUNCEQUEUE_DIR = os.path.join(QUEUE_DIR, 'bounces')
NEWSQUEUE_DIR = os.path.join(QUEUE_DIR, 'news')
ARCHQUEUE_DIR = os.path.join(QUEUE_DIR, 'archive')
SHUNTQUEUE_DIR = os.path.join(QUEUE_DIR, 'shunt')
VIRGINQUEUE_DIR = os.path.join(QUEUE_DIR, 'virgin')
BADQUEUE_DIR = os.path.join(QUEUE_DIR, 'bad')
RETRYQUEUE_DIR = os.path.join(QUEUE_DIR, 'retry')
MAILDIR_DIR = os.path.join(QUEUE_DIR, 'maildir')
# Other useful files
PIDFILE = os.path.join(VAR_PREFIX, 'run', 'mailman.pid')
SITE_PW_FILE = os.path.join(DATA_DIR, 'adm.pw')
LISTCREATOR_PW_FILE = os.path.join(DATA_DIR, 'creator.pw')

View File

@ -0,0 +1,119 @@
# -*- 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 Mailman.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/cgi-bin/mailman/'
PRIVATE_ARCHIVE_URL = '/cgi-bin/mailman/private'
IMAGE_LOGOS = '/images/mailman/'
#-------------------------------------------------------------
# Default domain for email addresses of newly created MLs
DEFAULT_EMAIL_HOST = '<%= @default_email_host %>'
#-------------------------------------------------------------
# Default host for web interface of newly created MLs
DEFAULT_URL_HOST = '<%= @default_url_host %>'
#-------------------------------------------------------------
# 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'
#-------------------------------------------------------------
# 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.
# Enable VERP, but let Exim create the VERP addresses since it's
# more efficient. --jeblair
VERP_PASSWORD_REMINDERS = 1
VERP_PERSONALIZED_DELIVERIES = 1
VERP_CONFIRMATIONS = 1
VERP_DELIVERY_INTERVAL = 0
# Make membership viewable by admin only by default (lp bug 1021493)
# Private_roster == 0: anyone can see, 1: members only, 2: admin only.
DEFAULT_PRIVATE_ROSTER = 2