From ae35908e7857aa925ed3ff7343855a39c1eedaf1 Mon Sep 17 00:00:00 2001 From: Antonio Terceiro Date: Thu, 12 Feb 2015 17:33:02 -0200 Subject: [PATCH] mailman setup (mail setup part) --- config/roles/integration_server.rb | 4 ++-- cookbooks/mailman/files/centos/postfix-to-mailman-centos.py | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ cookbooks/mailman/recipes/default.rb | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ cookbooks/mailman/templates/centos/mm_cfg.py.erb | 27 +++++++++++++++++++++++++++ docs/install.rst | 7 ++++++- docs/postfix-to-mailman-centos.py | 133 ------------------------------------------------------------------------------------------------------------------------------------- docs/postfix-to-mailman-centos.py | 1 + nodes.yaml | 2 ++ test/mailman_test.sh | 30 ++++++++++++++++++++++++++++++ test/test_helper.sh | 16 ++++++++++++++++ 10 files changed, 291 insertions(+), 136 deletions(-) create mode 100755 cookbooks/mailman/files/centos/postfix-to-mailman-centos.py create mode 100644 cookbooks/mailman/recipes/default.rb create mode 100644 cookbooks/mailman/templates/centos/mm_cfg.py.erb delete mode 100755 docs/postfix-to-mailman-centos.py create mode 120000 docs/postfix-to-mailman-centos.py create mode 100644 test/mailman_test.sh diff --git a/config/roles/integration_server.rb b/config/roles/integration_server.rb index e286841..dd2a3a8 100644 --- a/config/roles/integration_server.rb +++ b/config/roles/integration_server.rb @@ -1,6 +1,6 @@ name "integration_server" -description "Application that manages user authentication, visual integration and gamification" +description "Server that runs COLAB (user authentication, visual integration and gamification), mailman (mailing lists), and Gitlab (git repositories)" # TODO colab and mailman-api should be able to run in separate hosts at some # point in the future -run_list 'recipe[mailman-api]', 'recipe[colab]', 'recipe[basics::nginx]', 'recipe[colab::nginx]' +run_list 'recipe[mailman-api]', 'recipe[mailman]', 'recipe[colab]', 'recipe[basics::nginx]', 'recipe[colab::nginx]' diff --git a/cookbooks/mailman/files/centos/postfix-to-mailman-centos.py b/cookbooks/mailman/files/centos/postfix-to-mailman-centos.py new file mode 100755 index 0000000..6629d02 --- /dev/null +++ b/cookbooks/mailman/files/centos/postfix-to-mailman-centos.py @@ -0,0 +1,133 @@ +#! /usr/bin/env python + +# Configuration variables - Change these for your site if necessary. +MailmanHome = "/var/lib/mailman"; # Mailman home directory. +MailmanOwner = "postmaster@example.com"; # Postmaster and abuse mail recipient. +MailmanScripts = "/usr/lib/mailman"; # Where mailman scripts reside + +# End of configuration variables. + +# postfix-to-mailman-2.1.py (to be installed as postfix-to-mailman.py) +# +# Interface mailman to a postfix with a mailman transport. Does not require +# the creation of _any_ aliases to connect lists to your mail system. +# +# Dax Kelson, dkelson@gurulabs.com, Sept 2002. +# coverted from qmail to postfix interface +# Jan 2003: Fixes for Mailman 2.1 +# Thanks to Simen E. Sandberg +# Feb 2003: Change the suggested postfix transport to support VERP +# Thanks to Henrique de Moraes Holschuh +# +# This script was originally qmail-to-mailman.py by: +# Bruce Perens, bruce@perens.com, March 1999. +# This is free software under the GNU General Public License. +# +# This script is meant to be called from ~mailman/postfix-to-mailman.py. +# It catches all mail to a virtual domain, eg "lists.example.com". +# It looks at the recipient for each mail message and decides if the mail is +# addressed to a valid list or not, and bounces the message with a helpful +# suggestion if it's not addressed to a list. It decides if it is a posting, +# a list command, or mail to the list administrator, by checking for the +# -admin, -owner, and -request addresses. It will recognize a list as soon +# as the list is created, there is no need to add _any_ aliases for any list. +# It recognizes mail to postmaster, mailman-owner, abuse, mailer-daemon, root, +# and owner, and routes those mails to MailmanOwner as defined in the +# configuration variables, above. +# +# INSTALLATION: +# +# Install this file as ~mailman/postfix-to-mailman.py +# +# To configure a virtual domain to connect to mailman, edit Postfix thusly: +# +# /etc/postfix/main.cf: +# relay_domains = ... lists.example.com +# transport_maps = hash:/etc/postfix/transport +# mailman_destination_recipient_limit = 1 +# +# /etc/postfix/transport: +# lists.example.com mailman: +# +# /etc/postfix/master.cf +# mailman unix - n n - - pipe +# flags=FR user=mailman:mailman +# argv=/var/mailman/postfix-to-mailman.py ${nexthop} ${user} +# +# +# Replace list.example.com above with the name of the domain to be connected +# to Mailman. Note that _all_ mail to that domain will go to Mailman, so you +# don't want to put the name of your main domain here. Typically a virtual +# domain lists.domain.com is used for Mailman, and domain.com for regular +# email. +# + +import sys, os, re, string + +def main(): + os.nice(5) # Handle mailing lists at non-interactive priority. + # delete this if you wish + + os.chdir(MailmanHome + "/lists") + + try: + local = sys.argv[2] + except: + # This might happen if we're not using Postfix + sys.stderr.write("LOCAL not set?\n") + sys.exit(1) + + local = string.lower(local) + local = re.sub("^mailman-","",local) + + names = ("root", "postmaster", "mailer-daemon", "mailman-owner", "owner", + "abuse") + for i in names: + if i == local: + os.execv("/usr/sbin/sendmail", + ("/usr/sbin/sendmail", MailmanOwner)) + sys.exit(0) + + type = "post" + types = (("-admin$", "admin"), + ("-owner$", "owner"), + ("-request$", "request"), + ("-bounces$", "bounces"), + ("-confirm$", "confirm"), + ("-join$", "join"), + ("-leave$", "leave"), + ("-subscribe$", "subscribe"), + ("-unsubscribe$", "unsubscribe")) + + for i in types: + if re.search(i[0],local): + type = i[1] + local = re.sub(i[0],"",local) + + if os.path.exists(local): + os.execv(MailmanScripts + "/mail/mailman", + (MailmanScripts + "/mail/mailman", type, local)) + else: + bounce() + sys.exit(75) + +def bounce(): + bounce_message = """\ +TO ACCESS THE MAILING LIST SYSTEM: Start your web browser on +http://%s/ +That web page will help you subscribe or unsubscribe, and will +give you directions on how to post to each mailing list.\n""" + sys.stderr.write(bounce_message % (sys.argv[1])) + sys.exit(1) + +try: + sys.exit(main()) +except SystemExit, argument: + sys.exit(argument) + +except Exception, argument: + info = sys.exc_info() + trace = info[2] + sys.stderr.write("%s %s\n" % (sys.exc_type, argument)) + sys.stderr.write("Line %d\n" % (trace.tb_lineno)) + sys.exit(75) # Soft failure, try again later. diff --git a/cookbooks/mailman/recipes/default.rb b/cookbooks/mailman/recipes/default.rb new file mode 100644 index 0000000..1386833 --- /dev/null +++ b/cookbooks/mailman/recipes/default.rb @@ -0,0 +1,74 @@ +package 'mailman' + +template '/etc/mailman/mm_cfg.py' do + owner 'root' + group 'mailman' + mode 0644 + notifies :restart, 'service[mailman]' +end + +execute 'create-meta-list' do + admin = node['config']['lists_admin'] + password = SecureRandom.random_number.to_s + + command "sudo -u mailman /usr/lib/mailman/bin/newlist --quiet mailman #{admin} $(openssl rand -hex 6)" + + not_if { File.exists?('/var/lib/mailman/lists/mailman') } + notifies :restart, 'service[mailman]' +end + +service 'mailman' do + action :enable + supports :restart => true +end + +package 'postfix' +package 'mailx' # for testing, etc + +execute 'postfix-config' do + command [ + "postconf relay_domains=#{node['config']['lists_hostname']}", + "postconf transport_maps=hash:/etc/postfix/transport", + ].join(' && ') + only_if { !system('grep', node['config']['lists_hostname'], '/etc/postfix/main.cf') } + notifies :restart, 'service[postfix]' +end + +file '/etc/postfix/transport' do + owner 'root' + group 'root' + mode 0644 + content "#{node['config']['lists_hostname']} mailman:\n" + notifies :run, 'execute[compile-postfix-transport]' +end + +execute 'compile-postfix-transport' do + command 'postmap /etc/postfix/transport' + action :nothing +end + +cookbook_file '/etc/postfix/postfix-to-mailman-centos.py' do + owner 'root' + group 'root' + mode 0755 +end + +ruby_block 'configure-mailman-transport' do + block do + lines = [ + 'mailman unix - n n - - pipe', + ' flags=FR user=mailman:mailman', + ' argv=/etc/postfix/postfix-to-mailman-centos.py ${nexthop} ${user}', + ] + File.open('/etc/postfix/master.cf', 'a') do |f| + lines.each do |line| + f.puts line + end + end + end + only_if { !system('grep', '^mailman', '/etc/postfix/master.cf')} +end + +service 'postfix' do + action [:enable, :reload] +end diff --git a/cookbooks/mailman/templates/centos/mm_cfg.py.erb b/cookbooks/mailman/templates/centos/mm_cfg.py.erb new file mode 100644 index 0000000..789e9b5 --- /dev/null +++ b/cookbooks/mailman/templates/centos/mm_cfg.py.erb @@ -0,0 +1,27 @@ +############################################################### +# copied from default mailman config file +############################################################### +from Defaults import * +import pwd, grp +MAILMAN_UID = pwd.getpwnam('mailman')[2] +MAILMAN_GID = grp.getgrnam('mailman')[2] +from socket import * +try: + fqdn = getfqdn() +except: + fqdn = 'mm_cfg_has_unknown_host_domains' + + +############################################################### +# site-specific items +############################################################### + +DEFAULT_URL_HOST = '<%= node['config']['external_hostname'] %>' +DEFAULT_EMAIL_HOST = '<%= node['config']['lists_hostname'] %>' +MTA = None +POSTFIX_STYLE_VIRTUAL_DOMAINS = ['<%= node['config']['lists_hostname']%>'] + +############################################################### +# copied from default mailman config file +############################################################### +add_virtualhost(DEFAULT_URL_HOST, DEFAULT_EMAIL_HOST) diff --git a/docs/install.rst b/docs/install.rst index b4da73c..59af6db 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -357,7 +357,12 @@ Crie/edite ``/etc/postfix/transport`` com o seguinte conteúdo: Faça o download do arquivo :download:`postfix-to-mailman-centos.py` e salve no -diretório ``/etc/postfix``. +diretório ``/etc/postfix``, e altere as permissões para tornar o arquivo +executável: + +.. code-block:: sh + + $ sudo chmod +x /etc/postfix/postfix-to-mailman-centos.py Adicione o seguinte conteúdo no final do arquivo ``/etc/postfix/master.cf``: diff --git a/docs/postfix-to-mailman-centos.py b/docs/postfix-to-mailman-centos.py deleted file mode 100755 index 6629d02..0000000 --- a/docs/postfix-to-mailman-centos.py +++ /dev/null @@ -1,133 +0,0 @@ -#! /usr/bin/env python - -# Configuration variables - Change these for your site if necessary. -MailmanHome = "/var/lib/mailman"; # Mailman home directory. -MailmanOwner = "postmaster@example.com"; # Postmaster and abuse mail recipient. -MailmanScripts = "/usr/lib/mailman"; # Where mailman scripts reside - -# End of configuration variables. - -# postfix-to-mailman-2.1.py (to be installed as postfix-to-mailman.py) -# -# Interface mailman to a postfix with a mailman transport. Does not require -# the creation of _any_ aliases to connect lists to your mail system. -# -# Dax Kelson, dkelson@gurulabs.com, Sept 2002. -# coverted from qmail to postfix interface -# Jan 2003: Fixes for Mailman 2.1 -# Thanks to Simen E. Sandberg -# Feb 2003: Change the suggested postfix transport to support VERP -# Thanks to Henrique de Moraes Holschuh -# -# This script was originally qmail-to-mailman.py by: -# Bruce Perens, bruce@perens.com, March 1999. -# This is free software under the GNU General Public License. -# -# This script is meant to be called from ~mailman/postfix-to-mailman.py. -# It catches all mail to a virtual domain, eg "lists.example.com". -# It looks at the recipient for each mail message and decides if the mail is -# addressed to a valid list or not, and bounces the message with a helpful -# suggestion if it's not addressed to a list. It decides if it is a posting, -# a list command, or mail to the list administrator, by checking for the -# -admin, -owner, and -request addresses. It will recognize a list as soon -# as the list is created, there is no need to add _any_ aliases for any list. -# It recognizes mail to postmaster, mailman-owner, abuse, mailer-daemon, root, -# and owner, and routes those mails to MailmanOwner as defined in the -# configuration variables, above. -# -# INSTALLATION: -# -# Install this file as ~mailman/postfix-to-mailman.py -# -# To configure a virtual domain to connect to mailman, edit Postfix thusly: -# -# /etc/postfix/main.cf: -# relay_domains = ... lists.example.com -# transport_maps = hash:/etc/postfix/transport -# mailman_destination_recipient_limit = 1 -# -# /etc/postfix/transport: -# lists.example.com mailman: -# -# /etc/postfix/master.cf -# mailman unix - n n - - pipe -# flags=FR user=mailman:mailman -# argv=/var/mailman/postfix-to-mailman.py ${nexthop} ${user} -# -# -# Replace list.example.com above with the name of the domain to be connected -# to Mailman. Note that _all_ mail to that domain will go to Mailman, so you -# don't want to put the name of your main domain here. Typically a virtual -# domain lists.domain.com is used for Mailman, and domain.com for regular -# email. -# - -import sys, os, re, string - -def main(): - os.nice(5) # Handle mailing lists at non-interactive priority. - # delete this if you wish - - os.chdir(MailmanHome + "/lists") - - try: - local = sys.argv[2] - except: - # This might happen if we're not using Postfix - sys.stderr.write("LOCAL not set?\n") - sys.exit(1) - - local = string.lower(local) - local = re.sub("^mailman-","",local) - - names = ("root", "postmaster", "mailer-daemon", "mailman-owner", "owner", - "abuse") - for i in names: - if i == local: - os.execv("/usr/sbin/sendmail", - ("/usr/sbin/sendmail", MailmanOwner)) - sys.exit(0) - - type = "post" - types = (("-admin$", "admin"), - ("-owner$", "owner"), - ("-request$", "request"), - ("-bounces$", "bounces"), - ("-confirm$", "confirm"), - ("-join$", "join"), - ("-leave$", "leave"), - ("-subscribe$", "subscribe"), - ("-unsubscribe$", "unsubscribe")) - - for i in types: - if re.search(i[0],local): - type = i[1] - local = re.sub(i[0],"",local) - - if os.path.exists(local): - os.execv(MailmanScripts + "/mail/mailman", - (MailmanScripts + "/mail/mailman", type, local)) - else: - bounce() - sys.exit(75) - -def bounce(): - bounce_message = """\ -TO ACCESS THE MAILING LIST SYSTEM: Start your web browser on -http://%s/ -That web page will help you subscribe or unsubscribe, and will -give you directions on how to post to each mailing list.\n""" - sys.stderr.write(bounce_message % (sys.argv[1])) - sys.exit(1) - -try: - sys.exit(main()) -except SystemExit, argument: - sys.exit(argument) - -except Exception, argument: - info = sys.exc_info() - trace = info[2] - sys.stderr.write("%s %s\n" % (sys.exc_type, argument)) - sys.stderr.write("Line %d\n" % (trace.tb_lineno)) - sys.exit(75) # Soft failure, try again later. diff --git a/docs/postfix-to-mailman-centos.py b/docs/postfix-to-mailman-centos.py new file mode 120000 index 0000000..5e03894 --- /dev/null +++ b/docs/postfix-to-mailman-centos.py @@ -0,0 +1 @@ +../cookbooks/mailman/files/centos/postfix-to-mailman-centos.py \ No newline at end of file diff --git a/nodes.yaml b/nodes.yaml index d6d13df..eb5e75d 100644 --- a/nodes.yaml +++ b/nodes.yaml @@ -12,6 +12,8 @@ vagrant@integration: colab_from_address: '"Portal do Software Publico" ' server_email: '"Portal do Software Publico" ' email_subject_prefix: '[spb]' + lists_hostname: listas.softwarepublico.dev + lists_admin: paulo@softwarelivre.org peers: &PEERS integration: 10.10.10.2 email: 10.10.10.3 diff --git a/test/mailman_test.sh b/test/mailman_test.sh new file mode 100644 index 0000000..176a827 --- /dev/null +++ b/test/mailman_test.sh @@ -0,0 +1,30 @@ +. $(dirname $0)/test_helper.sh + +test_mailman_running() { + assertTrue 'mailman running' 'run_on integration pgrep -fa mailmanctl' +} + +test_mailman_delivery() { + # create list + run_on integration sudo -u mailman /usr/lib/mailman/bin/newlist --quiet foobar foobar@example.com foobar + # subscribe us + echo 'foobar@example.com' | run_on integration sudo -u mailman /usr/lib/mailman/bin/add_members -r - --welcome-msg=n foobar > /dev/null + + # send message + date | run_on integration mail -r foobar@example.com -s test foobar@listas.softwarepublico.dev + + # wait for message to arrive at the list mailbox + mbox=/var/lib/mailman/archives/private/foobar.mbox/foobar.mbox + if wait_for integration $mbox; then + messages=$(run_on integration sudo grep -i -c ^Message-ID: $mbox) + else + messages=0 + fi + + # remove list + run_on integration sudo -u mailman /usr/lib/mailman/bin/rmlist -a foobar > /dev/null + + assertEquals 'Message arrives at mailbox' "1" "$messages" +} + +. shunit2 diff --git a/test/test_helper.sh b/test/test_helper.sh index b60fbe4..7d26132 100644 --- a/test/test_helper.sh +++ b/test/test_helper.sh @@ -4,6 +4,22 @@ run_on() { vagrant ssh "$vm" -- "$@" } +# waits until a file exists +wait_for() { + local machine="$1" + local file="$2" + local total=0 + while [ "$total" -lt 10 ]; do + if run_on "$machine" sudo test -f "$file"; then + return 0 + fi + sleep 1 + total=$(($total + 1)) + done + return 1 +} + + curl=/vagrant/test/bin/curl # make IP addresses avaliable at the environment so we can refer to hosts by -- libgit2 0.21.2