about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEfraim Flashner2020-04-28 05:44:02 -0500
committerEfraim Flashner2020-04-28 05:44:02 -0500
commit55ed6aaaac53876c0c398a13aa45b1e8a0530bbd (patch)
tree48ba6306d8be955e07def87a28e00a861fb11f3c
parent3b5182bdd5d9a24cbc15c47df6acbfdbc1c80d3a (diff)
downloadguix-bioinformatics-wip-mailman.tar.gz
WIP mailman service wip-mailman
-rw-r--r--gn/services/mailman-container.scm94
-rw-r--r--gn/services/mailman.scm352
2 files changed, 446 insertions, 0 deletions
diff --git a/gn/services/mailman-container.scm b/gn/services/mailman-container.scm
new file mode 100644
index 0000000..1afa08a
--- /dev/null
+++ b/gn/services/mailman-container.scm
@@ -0,0 +1,94 @@
+(define-module (gn services mailman-container))
+
+(use-modules (gnu)
+             (gn packages mailman)
+             (guix records)
+             (ice-9 match))
+(use-service-modules base mcron networking shepherd)
+
+(define-record-type* <mailman-configuration>
+  mailman-configuration
+  make-mailman-configuration
+  mailman-configuration?
+  (package          mailman-configuration-package     ; package
+                    (default mailman))
+  )
+
+(define mailman-cronjobs
+  (match-lambda
+    (($ <mailman-configuration> package)
+     (list
+       #~(job '(next-hour '(0))
+              (string-append #$package "/bin/mailman digests --periodic"))
+       #~(job '(next-hour '(8))
+              (string-append #$package "/bin/mailman notify"))))))
+
+(define mailman-activation
+  (match-lambda
+    (($ <mailman-configuration> package)
+     #~(begin
+         (use-modules (guix build utils))
+         (let ((%user (getpwnam "mailman")))
+           ;; Prepare the environment for mailman:
+           ;; https://docs.mailman.io/en-us/install-from-binary/
+           (unless (directory-exists? #$work-dir)
+             (mkdir-p #$work-dir)
+             ;; These two are supposed to be recursive.
+             (chown #$work-dir (passwd:uid %user) (passwd:gid %user))
+             (chmod #$work-dir #o750)))))))
+
+(define mailman-config-file
+  (plain-file "mailman.cfg"
+              "[mailman]
+layout: local
+\n"))
+
+(define mailman-shepherd-service
+  (match-lambda
+    (($ <mailman-configuration> package)
+     (list (shepherd-service
+             (documentation "Run the Mailman server.")
+             (requirement '(networking))
+             (provision '(mailman))
+             (start #~(make-forkexec-constructor
+                        (list
+                          #$(file-append package "/bin/mailman")
+                          "start")
+                        #:environment-variables
+                        (list (string-append "MAILMAN_CONFIG_FILE" mailman-config-file)
+                        )
+                        ))
+             (stop #~(make-kill-destructor
+                       (list
+                         #$(file-append package "/bin/mailman")
+                         "stop"))))))))
+
+(define mailman-service-type
+  (service-type
+    (name 'mailman)
+    (extensions
+      (list (service-extension shepherd-root-service-type
+                               mailman-shepherd-service)
+            (service-extension activation-service-type
+                               mailman-activation)
+            (service-extension mcron-service-type
+                               mailman-cronjobs)))
+    (description
+     "Run a Mailman server.")
+    (default-value (mailman-configuration))))
+
+
+(operating-system
+  (host-name "mailman")
+  (timezone "Etc/UTC")
+  (locale "en_US.utf8")
+
+  (bootloader (bootloader-configuration
+               (bootloader grub-bootloader)
+               (target "does-not-matter")))
+  (file-systems %base-file-systems)
+  ;; No firmware for VMs or containers.
+  (firmware '())
+
+  (services (list (service dhcp-client-service-type)
+                  (service mailman-service-type))))
diff --git a/gn/services/mailman.scm b/gn/services/mailman.scm
new file mode 100644
index 0000000..d68924f
--- /dev/null
+++ b/gn/services/mailman.scm
@@ -0,0 +1,352 @@
+(define-module (gn services mailman)
+  #:use-module (guix gexp)
+  #:use-module (guix records)
+  #:use-module (gnu services)
+  #:use-module (gnu services base)
+  #:use-module (gnu services mcron)
+  #:use-module (gnu services networking)
+  #:use-module (gnu services shepherd)
+  #:use-module (gnu services web)
+  #:use-module (gn packages mailman)
+  #:use-module (ice-9 match)
+  #:export (mailman-service-type))
+
+(define-record-type* <mailman-database-configuration>
+  mailman-database-configuration
+  make-mailman-database-configuration
+  mailman-database-configuration?
+  (engine          mailman-database-configuration-engine
+                   (default "django.db.backends.postgresql_psycopg2"))
+  (name            mailman-database-configuration-name
+                   (default "mailman"))
+  (user            mailman-database-configuration-user
+                   (default "mailman"))
+  (password        mailman-database-configuration-password
+                   (default ""))
+  (host            mailman-database-configuration-host
+                   (default ""))
+  (port            mailman-database-configuration-port
+                   (default "")))
+
+(define-record-type* <mailman-settings-module>
+  mailman-settings-module make-mailman-settings-module
+  mailman-settings-module?
+  (database-configuration    mailman-settings-module-database-configuration
+                             (default (mailman-database-configuration)))
+  (secret-key-file           mailman-settings-module-secret-key-file
+                             (default "/etc/mailman/django-secret-key"))
+  (allowed-hosts             mailman-settings-module-allowed-hosts)
+  (default-from-email        mailman-settings-module-default-from-email)
+  (static-url                mailman-settings-module-static-url
+                             (default "/static/"))
+  (admins                    mailman-settings-module-admins
+                             (default '()))
+  (debug?                    mailman-settings-module-debug?
+                             (default #f))
+  (enable-rest-api?          mailman-settings-module-enable-rest-api?
+                             (default #t))
+  (enable-xmlrpc?            mailman-settings-module-enable-xmlrpc?
+                             (default #t))
+  (force-https-links?        mailman-settings-module-force-https-links?
+                             (default #t))
+  (extra-settings            mailman-settings-module-extra-settings
+                             (default "")))
+
+(define-record-type* <mailman-rest-api-configuration>
+  mailman-rest-api-configuration
+  make-mailman-rest-api-configuration
+  mailman-rest-api-configuration?
+  (url          mailman-rest-api-configuration-url
+                (default "http://localhost:8001"))
+  (user         mailman-rest-api-configuration-user
+                (default "restadmin"))
+  (pass         mailman-rest-api-configuration-pass
+                (default "restpass"))
+  (archiver-key mailman-rest-api-configuration-archiver-key
+                (default "SecretArchiverAPIKey"))
+  )
+  ;;MAILMAN_REST_API_URL = 'http://localhost:8001' MAILMAN_REST_API_USER = 'restadmin' MAILMAN_REST_API_PASS = 'restpass' MAILMAN_ARCHIVER_KEY = 'SecretArchiverAPIKey' MAILMAN_ARCHIVER_FROM = ('127.0.0.1', '::1')
+
+(define-record-type* <mailman-configuration>
+  mailman-configuration
+  make-mailman-configuration
+  mailman-configuration?
+  (package          mailman-configuration-package       ; package
+                    (default mailman))
+  (database         mailman-configuration-database)
+  (settings-module  mailman-configuration-settings-module)
+  (static-path      mailman-configuration-static-url
+                    (default "/static/"))
+  )
+
+(define mailman-cronjobs
+  (match-lambda
+    (($ <mailman-configuration> package)
+     (list
+       #~(job '(next-hour '(0))
+              (string-append #$package "/bin/mailman digests --periodic"))
+       #~(job '(next-hour '(8))
+              (string-append #$package "/bin/mailman notify"))))))
+
+;(define mailman-activation
+;  (match-lambda
+;    (($ <mailman-configuration> package postgresql user)
+;     #~(begin
+;         (use-modules (guix build utils))
+;         (let ((%user (getpwnam "mailman")))
+;           ;; Prepare the environment for mailman:
+;           ;; https://docs.mailman.io/en-us/install-from-binary/
+;           (unless (directory-exists? #$work-dir)
+;             (mkdir-p #$work-dir)
+;             ;; These two are supposed to be recursive.
+;             (chown #$work-dir (passwd:uid %user) (passwd:gid %user))
+;             (chmod #$work-dir #o750)))))))
+
+;; Django uses a Python module for configuration, so this compiler generates a
+;; Python module from the configuration record.
+(define-gexp-compiler (mailman-settings-module-compiler
+                        (file <mailman-settings-module>) system target)
+  (match file
+    (($ <mailman-settings-module> database-configuration secret-key-file
+        allowed-hosts default-from-email
+        static-url admins debug? enable-rest-api?
+        enable-xmlrpc? force-https-links?
+        extra-configuration)
+     (gexp->derivation
+       "mailman-settings"
+       (with-imported-modules '((guix build utils))
+         #~(let ((output #$output))
+             (define (create-__init__.py filename)
+               (call-with-output-file filename
+                                      (lambda (port) (display "" port))))
+
+             (use-modules (guix build utils)
+                          (srfi srfi-1))
+
+             (mkdir-p (string-append output "/guix/mailman"))
+             (create-__init__.py
+               (string-append output "/guix/__init__.py"))
+             (create-__init__.py
+               (string-append output "/guix/mailman/__init__.py"))
+
+             (call-with-output-file
+               (string-append output "/guix/mailman/settings.py")
+               (lambda (port)
+                 (display
+                   (string-append "import os
+BASE_DIR = os.path.dirname(os.path.abspath(__file__))
+
+# Configuration from Guix
+with open('" #$secret-key-file "') as f:
+    SECRET_KEY = f.read().strip()
+
+DEBUG = " #$(if debug? "True" "False") "
+
+
+ADMINS = [
+" #$(string-concatenate
+     (map (match-lambda
+            ((name email-address)
+             (string-append
+              "('" name "','" email-address "'),")))
+          admins))
+"]
+
+SITE_ID = 1
+
+ALLOWED_HOSTS = [
+" #$(string-concatenate
+     (map (lambda (allowed-host)
+            (string-append "  '" allowed-host "'\n"))
+          allowed-hosts))
+"]
+
+# Mailman API credentials
+" #$(if enable-rest-api?
+        (match rest-api-configuration
+          (($ <mailman-rest-api-configuration>
+              url user pass archiver-key)
+           (string-append
+"MAILMAN_REST_API_URL = '"url"'\n"
+"MAILMAN_REST_API_USER = '"user"'\n"
+"MAILMAN_REST_API_PASS = '"pass"'\n"
+"MAILMAN_ARCHIVER_KEY = '"archiver-key"'\n"
+"MAILMAN_ARCHIVER_FROM = ('127.0.0.1', '::1')\n")))
+        "") "
+
+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'
+
+DATABASES = {
+    'default': {
+" #$(match database-configuration
+      (($ <mailman-database-configuration>
+          engine name user password host port)
+       (string-append
+        "        'ENGINE': '" engine "',\n"
+        "        'NAME': '" name "',\n"
+        "        'USER': '" user "',\n"
+        "        'PASSWORD': '" password "',\n"
+        "        'HOST': '" host "',\n"
+        "        'PORT': '" port "',\n"))) "
+    },
+}
+
+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',     }, ]
+
+LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True
+
+" #$(if debug?
+        #~(string-append "STATIC_ROOT = '"
+                                                  #$(file-append patchwork "/share/patchwork/htdocs")
+                                                                           "'")
+                #~(string-append "STATIC_URL = '" #$static-url "'")) "
+
+STATICFILES_FINDERS = (     'django.contrib.staticfiles.finders.FileSystemFinder',
+                            'django.contrib.staticfiles.finders.AppDirectoriesFinder',    
+                            # 'django.contrib.staticfiles.finders.DefaultStorageFinder',
+                            'compressor.finders.CompressorFinder', )
+# 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'
+
+DEFAULT_FROM_EMAIL = 'postorius@localhost.local' 
+SERVER_EMAIL = 'root@localhost.local' 
+# Change this when you have a real email backend
+EMAIL_BACKEND = 'django.core.mail.backends.console.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\"
+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',     }, }
+
+COMPRESS_PRECOMPILERS = (    ('text/less', 'lessc {infile} {outfile}'),    ('text/x-scss', 'sassc -t compressed {infile} {outfile}'),    ('text/x-sass', 'sassc -t compressed {infile} {outfile}'), )
+HAYSTACK_CONNECTIONS = {     'default': {         'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',         'PATH': os.path.join(BASE_DIR, \"fulltext_index\"), }, }
+
+Q_CLUSTER = {     'timeout': 300,     'save_limit': 100,     'orm': 'default', } 
+
+LOGGING = {     'version': 1,     'disable_existing_loggers': False,     'filters': {         'require_debug_false': {             '()': 'django.utils.log.RequireDebugFalse'         }     },     'handlers': {         'mail_admins': {             'level': 'ERROR',             'filters': ['require_debug_false'],             'class': 'django.utils.log.AdminEmailHandler'         },         'file':{             'level': 'INFO',             #'class': 'logging.handlers.RotatingFileHandler',             'class': 'logging.handlers.WatchedFileHandler',             'filename': os.path.join(BASE_DIR, 'logs', 'mailmansuite.log'),             'formatter': 'verbose',         },         'console': {             'class': 'logging.StreamHandler',             'formatter': 'simple',         },     },     'loggers': {         'django.request': {             'handlers': ['mail_admins', 'file'],             'level': 'ERROR',             'propagate': True,         },         'django': {             'handlers': ['file'],             'level': 'ERROR',             'propagate': True,         },         'hyperkitty': {             'handlers': ['file'],             'level': 'DEBUG',             'propagate': True,         },         'postorius': {             'handlers': ['console', 'file'],             'level': 'INFO',         },     },     'formatters': {         'verbose': {             'format': '%(levelname)s %(asctime)s %(process)d %(name)s %(message)s'         },         'simple': {             'format': '%(levelname)s %(message)s'         },     },     #'root': {     #    'handlers': ['file'],     #    'level': 'INFO',     #}, } 
+
+if DEBUG == True:     EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'     EMAIL_FILE_PATH = os.path.join(BASE_DIR, 'emails') 
+FILTER_VHOST = False
+POSTORIUS_TEMPLATE_BASE_URL = 'http://localhost:8000'
+try:     from settings_local import * except ImportError:     pass
+
+"))))))))))
+
+(define mailman-config-file
+  (plain-file "mailman.cfg"
+              "[mailman]
+layout: local
+[mta]
+# https://mailman.readthedocs.io/en/latest/src/mailman/docs/mta.html#exim
+# For all Exim4 installations.
+incoming: mailman.mta.exim4.LMTP
+outgoing: mailman.mta.deliver.deliver
+# Typical single host with MTA and Mailman configuration.
+# Adjust to your system's configuration.
+# Exim happily works with the \"localhost\" alias rather than IP address.
+lmtp_host: localhost
+smtp_host: localhost
+# Mailman should not be run as root.
+# Use any convenient port > 1024.  8024 is a convention, but can be
+# changed if there is a conflict with other software using that port.
+lmtp_port: 8024
+# smtp_port rarely needs to be set.
+smtp_port: 25
+# Exim4-specific configuration parameter defaults.  Currently empty.
+configuration: python:mailman.config.exim4
+\n"))
+
+;; https://mailman.readthedocs.io/en/latest/src/mailman/docs/mta.html#exim4-configuration
+(define exim-config-file
+  (plain-file "exim.conf" "# /etc/exim4/conf.d/main/25_mm3_macros
+# The colon-separated list of domains served by Mailman.
+domainlist mm_domains=list.example.net
+
+MM3_LMTP_PORT=8024
+
+# MM3_HOME must be set to mailman's var directory, wherever it is
+# according to your installation.
+MM3_HOME=/opt/mailman/var
+MM3_UID=list
+MM3_GID=list
+
+################################################################
+# The configuration below is boilerplate:
+# you should not need to change it.
+
+# The path to the list receipt (used as the required file when
+# matching list addresses)
+MM3_LISTCHK=MM3_HOME/lists/${local_part}.${domain}
+
+# /etc/exim4/conf.d/router/455_mm3_router
+mailman3_router:
+driver = accept
+domains = +mm_domains
+require_files = MM3_LISTCHK
+local_part_suffix_optional
+local_part_suffix = \
+-bounces   : -bounces+* : \
+-confirm   : -confirm+* : \
+-join      : -leave     : \
+-owner     : -request   : \
+-subscribe : -unsubscribe
+transport = mailman3_transport
+
+# /etc/exim4/conf.d/transport/55_mm3_transport
+mailman3_transport:
+driver = smtp
+protocol = lmtp
+allow_localhost
+hosts = localhost
+port = MM3_LMTP_PORT
+rcpt_include_affixes = true
+\n")
+
+(define mailman-shepherd-service
+  (match-lambda
+    (($ <mailman-configuration> package)
+     (list (shepherd-service
+             (documentation "Run the Mailman server.")
+             (requirement '(networking))
+             (provision '(mailman))
+             (start #~(make-forkexec-constructor
+                        (list
+                          #$(file-append package "/bin/mailman")
+                          "start")
+                        #:environment-variables
+                        (list (string-append "MAILMAN_CONFIG_FILE" mailman-config-file)
+                        )
+                        ))
+             (stop #~(make-kill-destructor
+                       (list
+                         #$(file-append package "/bin/mailman")
+                         "stop"))))))))
+
+(define mailman-service-type
+  (service-type
+    (name 'mailman)
+    (extensions
+      (list (service-extension shepherd-root-service-type
+                               mailman-shepherd-service)
+            (service-extension activation-service-type
+                               mailman-activation)
+            (service-extension mcron-service-type
+                               mailman-cronjobs)))
+    (description
+     "Run a Mailman server.")
+    (default-value (mailman-configuration))))
+