diff --git a/.github/workflows/build_test_deploy.yml b/.github/workflows/build_test_deploy.yml index 6512ccd5..4d10e4ca 100644 --- a/.github/workflows/build_test_deploy.yml +++ b/.github/workflows/build_test_deploy.yml @@ -340,7 +340,7 @@ jobs: strategy: fail-fast: false matrix: - target: ["core", "fetchmail", "filters", "snappymail", "roundcube", "webdav"] + target: ["core", "fetchmail", "filters", "webmail", "webdav"] time: ["2"] include: - target: "filters" @@ -394,7 +394,7 @@ jobs: strategy: fail-fast: false matrix: - target: ["setup", "docs", "fetchmail", "roundcube", "admin", "traefik-certdumper", "radicale", "clamav", "rspamd", "postfix", "dovecot", "unbound", "nginx", "snappymail"] + target: ["setup", "docs", "fetchmail", "webmail", "admin", "traefik-certdumper", "radicale", "clamav", "rspamd", "postfix", "dovecot", "unbound", "nginx"] steps: - uses: actions/checkout@v3 - name: Retrieve global variables @@ -439,7 +439,7 @@ jobs: strategy: fail-fast: false matrix: - target: ["setup", "docs", "fetchmail", "roundcube", "admin", "traefik-certdumper", "radicale", "clamav", "rspamd", "postfix", "dovecot", "unbound", "nginx", "snappymail"] + target: ["setup", "docs", "fetchmail", "webmail", "admin", "traefik-certdumper", "radicale", "clamav", "rspamd", "postfix", "dovecot", "unbound", "nginx"] steps: - uses: actions/checkout@v3 - name: Retrieve global variables diff --git a/core/admin/mailu/internal/views/fetch.py b/core/admin/mailu/internal/views/fetch.py index 1945b9c7..e813c33b 100644 --- a/core/admin/mailu/internal/views/fetch.py +++ b/core/admin/mailu/internal/views/fetch.py @@ -12,10 +12,12 @@ def fetch_list(): "id": fetch.id, "tls": fetch.tls, "keep": fetch.keep, + "scan": fetch.scan, "user_email": fetch.user_email, "protocol": fetch.protocol, "host": fetch.host, "port": fetch.port, + "folders": fetch.folders, "username": fetch.username, "password": fetch.password } for fetch in models.Fetch.query.all() diff --git a/core/admin/mailu/models.py b/core/admin/mailu/models.py index 48ce8b33..4b048c45 100644 --- a/core/admin/mailu/models.py +++ b/core/admin/mailu/models.py @@ -771,6 +771,8 @@ class Fetch(Base): username = db.Column(db.String(255), nullable=False) password = db.Column(db.String(255), nullable=False) keep = db.Column(db.Boolean, nullable=False, default=False) + scan = db.Column(db.Boolean, nullable=False, default=False) + folders = db.Column(CommaSeparatedList, nullable=True, default=list) last_check = db.Column(db.DateTime, nullable=True) error = db.Column(db.String(1023), nullable=True) diff --git a/core/admin/mailu/sso/views/languages.py b/core/admin/mailu/sso/views/languages.py index ff65af45..19764519 100644 --- a/core/admin/mailu/sso/views/languages.py +++ b/core/admin/mailu/sso/views/languages.py @@ -1,7 +1,7 @@ from mailu.sso import sso import flask -@sso.route('/language/', methods=['POST']) +@sso.route('/language/', methods=['GET','POST']) def set_language(language=None): if language: flask.session['language'] = language diff --git a/core/admin/mailu/ui/forms.py b/core/admin/mailu/ui/forms.py index beb44092..ec19bb0b 100644 --- a/core/admin/mailu/ui/forms.py +++ b/core/admin/mailu/ui/forms.py @@ -41,6 +41,16 @@ class MultipleEmailAddressesVerify(object): if not pattern.match(field.data.replace(" ", "")): raise validators.ValidationError(self.message) +class MultipleFoldersVerify(object): + """ Ensure that we have CSV formated data """ + def __init__(self,message=_('Invalid list of folders.')): + self.message = message + + def __call__(self, form, field): + pattern = re.compile(r'^\w+(\s*,\s*\w+)*$') + if not pattern.match(field.data.replace(" ", "")): + raise validators.ValidationError(self.message) + class ConfirmationForm(flask_wtf.FlaskForm): submit = fields.SubmitField(_('Confirm')) @@ -164,11 +174,13 @@ class FetchForm(flask_wtf.FlaskForm): ('imap', 'IMAP'), ('pop3', 'POP3') ]) host = fields.StringField(_('Hostname or IP'), [validators.DataRequired()]) - port = fields.IntegerField(_('TCP port'), [validators.DataRequired(), validators.NumberRange(min=0, max=65535)]) - tls = fields.BooleanField(_('Enable TLS')) + port = fields.IntegerField(_('TCP port'), [validators.DataRequired(), validators.NumberRange(min=0, max=65535)], default=993) + tls = fields.BooleanField(_('Enable TLS'), default=True) username = fields.StringField(_('Username'), [validators.DataRequired()]) password = fields.PasswordField(_('Password')) keep = fields.BooleanField(_('Keep emails on the server')) + scan = fields.BooleanField(_('Rescan emails locally')) + folders = fields.StringField(_('Folders to fetch on the server'), [validators.Optional(), MultipleFoldersVerify()], default='INBOX,Junk') submit = fields.SubmitField(_('Submit')) diff --git a/core/admin/mailu/ui/templates/fetch/create.html b/core/admin/mailu/ui/templates/fetch/create.html index 00698329..69584d15 100644 --- a/core/admin/mailu/ui/templates/fetch/create.html +++ b/core/admin/mailu/ui/templates/fetch/create.html @@ -24,6 +24,8 @@ {%- call macros.card(title="Settings") %} {{ macros.form_field(form.keep) }} + {{ macros.form_field(form.scan) }} + {{ macros.form_field(form.folders) }} {%- endcall %} {{ macros.form_field(form.submit) }} diff --git a/core/admin/mailu/ui/templates/fetch/list.html b/core/admin/mailu/ui/templates/fetch/list.html index 7a527ce8..74d3a02f 100644 --- a/core/admin/mailu/ui/templates/fetch/list.html +++ b/core/admin/mailu/ui/templates/fetch/list.html @@ -20,6 +20,8 @@ {% trans %}Endpoint{% endtrans %} {% trans %}Username{% endtrans %} {% trans %}Keep emails{% endtrans %} + {% trans %}Rescan emails{% endtrans %} + {% trans %}Folders{% endtrans %} {% trans %}Last check{% endtrans %} {% trans %}Status{% endtrans %} {% trans %}Created{% endtrans %} @@ -36,6 +38,8 @@ {{ fetch.protocol }}{{ 's' if fetch.tls else '' }}://{{ fetch.host }}:{{ fetch.port }} {{ fetch.username }} {% if fetch.keep %}{% trans %}yes{% endtrans %}{% else %}{% trans %}no{% endtrans %}{% endif %} + {% if fetch.scan %}{% trans %}yes{% endtrans %}{% else %}{% trans %}no{% endtrans %}{% endif %} + {{ fetch.folders | join(',') }} {{ fetch.last_check | format_datetime or '-' }} {{ fetch.error or '-' }} {{ fetch.created_at | format_date }} diff --git a/core/admin/mailu/ui/views/fetches.py b/core/admin/mailu/ui/views/fetches.py index ca837a8e..3c4d629d 100644 --- a/core/admin/mailu/ui/views/fetches.py +++ b/core/admin/mailu/ui/views/fetches.py @@ -1,4 +1,4 @@ -from mailu import models +from mailu import models, utils from mailu.ui import ui, forms, access from flask import current_app as app @@ -28,9 +28,12 @@ def fetch_create(user_email): user = models.User.query.get(user_email) or flask.abort(404) form = forms.FetchForm() form.password.validators = [wtforms.validators.DataRequired()] + utils.formatCSVField(form.folders) if form.validate_on_submit(): fetch = models.Fetch(user=user) form.populate_obj(fetch) + if form.folders.data: + fetch.folders = form.folders.data.replace(' ','').split(',') models.db.session.add(fetch) models.db.session.commit() flask.flash('Fetch configuration created') @@ -46,10 +49,13 @@ def fetch_edit(fetch_id): flask.abort(404) fetch = models.Fetch.query.get(fetch_id) or flask.abort(404) form = forms.FetchForm(obj=fetch) + utils.formatCSVField(form.folders) if form.validate_on_submit(): if not form.password.data: form.password.data = fetch.password form.populate_obj(fetch) + if form.folders.data: + fetch.folders = form.folders.data.replace(' ','').split(',') models.db.session.commit() flask.flash('Fetch configuration updated') return flask.redirect( diff --git a/core/admin/mailu/ui/views/users.py b/core/admin/mailu/ui/views/users.py index 85a5c2db..c7d252a9 100644 --- a/core/admin/mailu/ui/views/users.py +++ b/core/admin/mailu/ui/views/users.py @@ -64,10 +64,11 @@ def user_edit(user_email): form.quota_bytes.validators = [ wtforms.validators.NumberRange(max=max_quota_bytes)] if form.validate_on_submit(): - if msg := utils.isBadOrPwned(form): - flask.flash(msg, "error") - return flask.render_template('user/edit.html', form=form, user=user, - domain=user.domain, max_quota_bytes=max_quota_bytes) + if form.pw.data: + if msg := utils.isBadOrPwned(form): + flask.flash(msg, "error") + return flask.render_template('user/edit.html', form=form, user=user, + domain=user.domain, max_quota_bytes=max_quota_bytes) form.populate_obj(user) if form.pw.data: user.set_password(form.pw.data) @@ -99,11 +100,7 @@ def user_settings(user_email): user_email_or_current = user_email or flask_login.current_user.email user = models.User.query.get(user_email_or_current) or flask.abort(404) form = forms.UserSettingsForm(obj=user) - if isinstance(form.forward_destination.data,str): - data = form.forward_destination.data.replace(" ","").split(",") - else: - data = form.forward_destination.data - form.forward_destination.data = ", ".join(data) + utils.formatCSVField(form.forward_destination) if form.validate_on_submit(): form.forward_destination.data = form.forward_destination.data.replace(" ","").split(",") form.populate_obj(user) diff --git a/core/admin/mailu/utils.py b/core/admin/mailu/utils.py index f160fe3f..b432192d 100644 --- a/core/admin/mailu/utils.py +++ b/core/admin/mailu/utils.py @@ -518,3 +518,10 @@ def isBadOrPwned(form): if breaches > 0: return f"This password appears in {breaches} data breaches! It is not unique; please change it." return None + +def formatCSVField(field): + if isinstance(field.data,str): + data = field.data.replace(" ","").split(",") + else: + data = field.data + field.data = ", ".join(data) diff --git a/core/admin/migrations/versions/f4f0f89e0047_.py b/core/admin/migrations/versions/f4f0f89e0047_.py new file mode 100644 index 00000000..8d20063c --- /dev/null +++ b/core/admin/migrations/versions/f4f0f89e0047_.py @@ -0,0 +1,25 @@ +""" Add fetch.scan and fetch.folders + +Revision ID: f4f0f89e0047 +Revises: 8f9ea78776f4 +Create Date: 2022-11-13 16:29:01.246509 + +""" + +# revision identifiers, used by Alembic. +revision = 'f4f0f89e0047' +down_revision = '8f9ea78776f4' + +from alembic import op +import sqlalchemy as sa +import mailu + +def upgrade(): + with op.batch_alter_table('fetch') as batch: + batch.add_column(sa.Column('scan', sa.Boolean(), nullable=False, server_default=sa.sql.expression.false())) + batch.add_column(sa.Column('folders', mailu.models.CommaSeparatedList(), nullable=True)) + +def downgrade(): + with op.batch_alter_table('fetch') as batch: + batch.drop_column('fetch', 'folders') + batch.drop_column('fetch', 'scan') diff --git a/core/base/Dockerfile b/core/base/Dockerfile index 814a946a..20e8b055 100644 --- a/core/base/Dockerfile +++ b/core/base/Dockerfile @@ -8,14 +8,13 @@ ENV TZ=Etc/UTC LANG=C.UTF-8 ARG MAILU_UID=1000 ARG MAILU_GID=1000 -ARG TARGETPLATFORM RUN set -euxo pipefail \ ; addgroup -Sg ${MAILU_GID} mailu \ ; adduser -Sg ${MAILU_UID} -G mailu -h /app -g "mailu app" -s /bin/bash mailu \ ; apk add --no-cache bash ca-certificates curl python3 tzdata libcap \ ; machine="$(uname -m)" \ - ; ! [[ "${TARGETPLATFORM}" != linux/arm/v7 && \( "${machine}" == x86_64 || "${machine}" == armv8* || "${machine}" == aarch64 \) ]] \ + ; ! [[ "${machine}" == x86_64 ]] \ || apk add --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing hardened-malloc ENV LD_PRELOAD=/usr/lib/libhardened_malloc.so diff --git a/docs/webadministration.rst b/docs/webadministration.rst index e17d12f0..fde4a271 100644 --- a/docs/webadministration.rst +++ b/docs/webadministration.rst @@ -157,7 +157,11 @@ You can add a fetched account by clicking on the `Add an account` button on the * Keep emails on the server. When ticked, retains the email message in the email account after retrieving it. -Click the submit button to apply settings. With the default polling interval, fetchmail will start polling the email account after 10 minutes. +* Scan emails. When ticked, all the fetched emails will go through the local filters (rspamd, clamav, ...). + +* Folders. A comma separated list of folders to fetch from the server. This is optional, by default only the INBOX will be pulled. + +Click the submit button to apply settings. With the default polling interval, fetchmail will start polling the email account after ``FETCHMAIL_DELAY``. Authentication tokens diff --git a/optional/fetchmail/fetchmail.py b/optional/fetchmail/fetchmail.py index 3a82a124..62bd7124 100755 --- a/optional/fetchmail/fetchmail.py +++ b/optional/fetchmail/fetchmail.py @@ -2,11 +2,14 @@ import time import os +from pathlib import Path +from pwd import getpwnam import tempfile import shlex import subprocess import re import requests +from socrate import system import sys import traceback @@ -14,6 +17,7 @@ import traceback FETCHMAIL = """ fetchmail -N \ --idfile /data/fetchids --uidl \ + --pidfile /dev/shm/fetchmail.pid \ --sslcertck --sslcertpath /etc/ssl/certs \ -f {} """ @@ -24,7 +28,9 @@ poll "{host}" proto {protocol} port {port} user "{username}" password "{password}" is "{user_email}" smtphost "{smtphost}" + {folders} {options} + {lmtp} """ @@ -48,26 +54,37 @@ def fetchmail(fetchmailrc): def run(debug): try: - fetches = requests.get("http://" + os.environ.get("HOST_ADMIN", "admin") + "/internal/fetch").json() - smtphost, smtpport = extract_host_port(os.environ.get("HOST_SMTP", "smtp"), None) + os.environ["SMTP_ADDRESS"] = system.get_host_address_from_environment("SMTP", "smtp") + os.environ["ADMIN_ADDRESS"] = system.get_host_address_from_environment("ADMIN", "admin") + fetches = requests.get(f"http://{os.environ['ADMIN_ADDRESS']}/internal/fetch").json() + smtphost, smtpport = extract_host_port(os.environ["SMTP_ADDRESS"], None) if smtpport is None: smtphostport = smtphost else: smtphostport = "%s/%d" % (smtphost, smtpport) + os.environ["LMTP_ADDRESS"] = system.get_host_address_from_environment("LMTP", "imap:2525") + lmtphost, lmtpport = extract_host_port(os.environ["LMTP_ADDRESS"], None) + if lmtpport is None: + lmtphostport = lmtphost + else: + lmtphostport = "%s/%d" % (lmtphost, lmtpport) for fetch in fetches: fetchmailrc = "" options = "options antispam 501, 504, 550, 553, 554" options += " ssl" if fetch["tls"] else "" options += " keep" if fetch["keep"] else " fetchall" + folders = "folders %s" % ((','.join('"' + item + '"' for item in fetch['folders'])) if fetch['folders'] else '"INBOX"') fetchmailrc += RC_LINE.format( user_email=escape_rc_string(fetch["user_email"]), protocol=fetch["protocol"], host=escape_rc_string(fetch["host"]), port=fetch["port"], - smtphost=smtphostport, + smtphost=smtphostport if fetch['scan'] else lmtphostport, username=escape_rc_string(fetch["username"]), password=escape_rc_string(fetch["password"]), - options=options + options=options, + folders=folders, + lmtp='' if fetch['scan'] else 'lmtp', ) if debug: print(fetchmailrc) @@ -86,14 +103,21 @@ def run(debug): user_info in error_message): print(error_message) finally: - requests.post("http://" + os.environ.get("HOST_ADMIN", "admin") + "/internal/fetch/{}".format(fetch["id"]), - json=error_message.split("\n")[0] + requests.post("http://{}/internal/fetch/{}".format(os.environ['ADMIN_ADDRESS'],fetch['id']), + json=error_message.split('\n')[0] ) except Exception: traceback.print_exc() if __name__ == "__main__": + id_fetchmail = getpwnam('fetchmail') + Path('/data/fetchids').touch() + os.chown("/data/fetchids", id_fetchmail.pw_uid, id_fetchmail.pw_gid) + os.chown("/data/", id_fetchmail.pw_uid, id_fetchmail.pw_gid) + os.chmod("/data/fetchids", 0o700) + os.setgid(id_fetchmail.pw_gid) + os.setuid(id_fetchmail.pw_uid) while True: delay = int(os.environ.get("FETCHMAIL_DELAY", 60)) print("Sleeping for {} seconds".format(delay)) diff --git a/setup/flavors/compose/docker-compose.yml b/setup/flavors/compose/docker-compose.yml index 6dac166b..b6c99ca5 100644 --- a/setup/flavors/compose/docker-compose.yml +++ b/setup/flavors/compose/docker-compose.yml @@ -157,8 +157,11 @@ services: env_file: {{ env }} volumes: - "{{ root }}/data/fetchmail:/data" - {% if resolver_enabled %} depends_on: + - admin + - smtp + - imap + {% if resolver_enabled %} - resolver dns: - {{ dns }} @@ -168,7 +171,7 @@ services: # Webmail {% if webmail_type != 'none' %} webmail: - image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}{{ webmail_type }}:${MAILU_VERSION:-{{ version }}} + image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}webmail:${MAILU_VERSION:-{{ version }}} restart: always env_file: {{ env }} volumes: diff --git a/setup/flavors/stack/docker-compose.yml b/setup/flavors/stack/docker-compose.yml index 89da923c..809362df 100644 --- a/setup/flavors/stack/docker-compose.yml +++ b/setup/flavors/stack/docker-compose.yml @@ -119,7 +119,7 @@ services: {% if webmail_type != 'none' %} webmail: - image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}{{ webmail_type }}:${MAILU_VERSION:-{{ version }}} + image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}webmail:${MAILU_VERSION:-{{ version }}} env_file: {{ env }} volumes: - "{{ root }}/webmail:/data" diff --git a/tests/build.hcl b/tests/build.hcl index 0f6226c8..d657cbb7 100644 --- a/tests/build.hcl +++ b/tests/build.hcl @@ -36,8 +36,7 @@ group "default" { "imap", "smtp", - "snappymail", - "roundcube", + "webmail", "antivirus", "fetchmail", @@ -172,24 +171,15 @@ target "smtp" { } # ----------------------------------------------------------------------------------------- -# Webmail images +# Webmail image # ----------------------------------------------------------------------------------------- -target "snappymail" { +target "webmail" { inherits = ["defaults"] - context = "webmails/snappymail/" + context = "webmails/" contexts = { base = "target:base" } - tags = tag("snappymail") -} - -target "roundcube" { - inherits = ["defaults"] - context = "webmails/roundcube/" - contexts = { - base = "target:base" - } - tags = tag("roundcube") + tags = tag("webmail") } # ----------------------------------------------------------------------------------------- diff --git a/tests/compose/snappymail/docker-compose.yml b/tests/compose/snappymail/docker-compose.yml deleted file mode 100644 index b9df7332..00000000 --- a/tests/compose/snappymail/docker-compose.yml +++ /dev/null @@ -1,106 +0,0 @@ -# This file is auto-generated by the Mailu configuration wizard. -# Please read the documentation before attempting any change. -# Generated for compose flavor - -version: '3.6' - -services: - - # External dependencies - redis: - image: redis:alpine - restart: always - volumes: - - "/mailu/redis:/data" - - # Core services - front: - image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}nginx:${MAILU_VERSION:-local} - restart: always - env_file: mailu.env - logging: - driver: json-file - ports: - - "127.0.0.1:80:80" - - "127.0.0.1:443:443" - - "127.0.0.1:25:25" - - "127.0.0.1:465:465" - - "127.0.0.1:587:587" - - "127.0.0.1:110:110" - - "127.0.0.1:995:995" - - "127.0.0.1:143:143" - - "127.0.0.1:993:993" - volumes: - - "/mailu/certs:/certs" - - admin: - image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}admin:${MAILU_VERSION:-local} - restart: always - env_file: mailu.env - volumes: - - "/mailu/data:/data" - - "/mailu/dkim:/dkim" - depends_on: - - redis - - resolver - dns: - - 192.168.203.254 - - imap: - image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}dovecot:${MAILU_VERSION:-local} - restart: always - env_file: mailu.env - volumes: - - "/mailu/mail:/mail" - - "/mailu/overrides:/overrides" - depends_on: - - front - - smtp: - image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}postfix:${MAILU_VERSION:-local} - restart: always - env_file: mailu.env - volumes: - - "/mailu/overrides:/overrides" - depends_on: - - front - - antispam: - image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}rspamd:${MAILU_VERSION:-local} - restart: always - env_file: mailu.env - volumes: - - "/mailu/filter:/var/lib/rspamd" - - "/mailu/dkim:/dkim" - - "/mailu/overrides/rspamd:/etc/rspamd/override.d" - depends_on: - - front - - # Optional services - - resolver: - image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}unbound:${MAILU_VERSION:-local} - env_file: mailu.env - restart: always - networks: - default: - ipv4_address: 192.168.203.254 - - # Webmail - webmail: - image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}snappymail:${MAILU_VERSION:-local} - restart: always - env_file: mailu.env - volumes: - - "/mailu/webmail:/data" - depends_on: - - imap - - -networks: - default: - driver: bridge - ipam: - driver: default - config: - - subnet: 192.168.203.0/24 diff --git a/tests/compose/snappymail/mailu.env b/tests/compose/snappymail/mailu.env deleted file mode 100644 index 50271fc7..00000000 --- a/tests/compose/snappymail/mailu.env +++ /dev/null @@ -1,138 +0,0 @@ -# Mailu main configuration file -# -# Generated for compose flavor -# -# This file is autogenerated by the configuration management wizard. -# For a detailed list of configuration variables, see the documentation at -# https://mailu.io - -################################### -# Common configuration variables -################################### - -# Set this to the path where Mailu data and configuration is stored -# This variable is now set directly in `docker-compose.yml by the setup utility -# ROOT=/mailu - -# Mailu version to run (1.0, 1.1, etc. or master) -#VERSION=master - -# Set to a randomly generated 16 bytes string -SECRET_KEY=V5J4SHRYVW9PZIQU - -# Address where listening ports should bind -# This variables are now set directly in `docker-compose.yml by the setup utility -# PUBLIC_IPV4= 127.0.0.1 (default: 127.0.0.1) -# PUBLIC_IPV6= (default: ::1) - -# Subnet of the docker network. This should not conflict with any networks to which your system is connected. (Internal and external!) -SUBNET=192.168.203.0/24 - -# Main mail domain -DOMAIN=mailu.io - -# Hostnames for this server, separated with comas -HOSTNAMES=localhost - -# Postmaster local part (will append the main mail domain) -POSTMASTER=admin - -# Choose how secure connections will behave (value: letsencrypt, cert, notls, mail, mail-letsencrypt) -TLS_FLAVOR=cert - -# Authentication rate limit (per source IP address) -AUTH_RATELIMIT=10/minute;1000/hour - -# Opt-out of statistics, replace with "True" to opt out -DISABLE_STATISTICS=False - -################################### -# Optional features -################################### - -# Expose the admin interface (value: true, false) -ADMIN=false - -# Choose which webmail to run if any (values: roundcube, snappymail, none) -WEBMAIL=snappymail - -# Dav server implementation (value: radicale, none) -WEBDAV=none - -# Antivirus solution (value: clamav, none) -#ANTIVIRUS=none - -#Antispam solution -ANTISPAM=none - -################################### -# Mail settings -################################### - -# Message size limit in bytes -# Default: accept messages up to 50MB -MESSAGE_SIZE_LIMIT=50000000 - -# Networks granted relay permissions -# Use this with care, all hosts in this networks will be able to send mail without authentication! -RELAYNETS= - -# Will relay all outgoing mails if configured -RELAYHOST= - -# Fetchmail delay -FETCHMAIL_DELAY=600 - -# Recipient delimiter, character used to delimiter localpart from custom address part -RECIPIENT_DELIMITER=+ - -# DMARC rua and ruf email -DMARC_RUA=admin -DMARC_RUF=admin - - -# Maildir Compression -# choose compression-method, default: none (value: gz, bz2, lz4, zstd) -COMPRESSION= -# change compression-level, default: 6 (value: 1-9) -COMPRESSION_LEVEL= - -################################### -# Web settings -################################### - -# Path to the admin interface if enabled -WEB_ADMIN=/admin - -# Path to the webmail if enabled -WEB_WEBMAIL=/webmail - -# Website name -SITENAME=Mailu - -# Linked Website URL -WEBSITE=https://mailu.io - - - -################################### -# Advanced settings -################################### - -# Log driver for front service. Possible values: -# json-file (default) -# journald (On systemd platforms, useful for Fail2Ban integration) -# syslog (Non systemd platforms, Fail2Ban integration. Disables `docker-compose log` for front!) -# LOG_DRIVER=json-file - -# Docker-compose project name, this will prepended to containers names. -COMPOSE_PROJECT_NAME=mailu - -# Header to take the real ip from -REAL_IP_HEADER= - -# IPs for nginx set_real_ip_from (CIDR list separated by commas) -REAL_IP_FROM= - -# choose wether mailu bounces (no) or rejects (yes) mail when recipient is unknown (value: yes, no) -REJECT_UNLISTED_RECIPIENT= diff --git a/tests/compose/webmail/01_ensure_admin_unreachable.sh b/tests/compose/webmail/01_ensure_admin_unreachable.sh new file mode 100755 index 00000000..4fd78a1b --- /dev/null +++ b/tests/compose/webmail/01_ensure_admin_unreachable.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +IP="$(docker inspect webmail_webmail_1|jq -r '.[0].NetworkSettings.Networks.webmail_default.IPAddress')" + +MAIN_RETURN_CODE=$(curl -I -so /dev/null -w "%{http_code}" http://$IP/) +[[ $MAIN_RETURN_CODE -ne 200 && $MAIN_RETURN_CODE -ne 302 ]] && echo "The default page of snappymail hasn't returned 200 but $MAIN_RETURN_CODE!" >>/dev/stderr && exit 1 +[[ $(curl -I -so /dev/null -w "%{http_code}" http://$IP/?admin) -ne 403 ]] && echo "The admin of snappymail is not disabled!" >>/dev/stderr && exit 1 +echo "Everything OK" >/dev/stderr + +exit 0 diff --git a/tests/compose/roundcube/docker-compose.yml b/tests/compose/webmail/docker-compose.yml similarity index 96% rename from tests/compose/roundcube/docker-compose.yml rename to tests/compose/webmail/docker-compose.yml index f2c43686..14d1dae9 100644 --- a/tests/compose/roundcube/docker-compose.yml +++ b/tests/compose/webmail/docker-compose.yml @@ -88,7 +88,7 @@ services: # Webmail webmail: - image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}roundcube:${MAILU_VERSION:-local} + image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}webmail:${MAILU_VERSION:-local} restart: always env_file: mailu.env volumes: diff --git a/tests/compose/roundcube/mailu.env b/tests/compose/webmail/mailu.env similarity index 99% rename from tests/compose/roundcube/mailu.env rename to tests/compose/webmail/mailu.env index 7f000f2c..f87f3262 100644 --- a/tests/compose/roundcube/mailu.env +++ b/tests/compose/webmail/mailu.env @@ -54,7 +54,7 @@ DISABLE_STATISTICS=False ADMIN=false # Choose which webmail to run if any (values: roundcube, snappymail, none) -WEBMAIL=roundcube +WEBMAIL=snappymail # Dav server implementation (value: radicale, none) WEBDAV=none diff --git a/towncrier/newsfragments/1231.bugfix b/towncrier/newsfragments/1231.bugfix new file mode 100644 index 00000000..333ae35f --- /dev/null +++ b/towncrier/newsfragments/1231.bugfix @@ -0,0 +1 @@ +Add an option so that emails fetched with fetchmail don't go through the filters (closes #1231) diff --git a/towncrier/newsfragments/2246.bugfix b/towncrier/newsfragments/2246.bugfix new file mode 100644 index 00000000..92e90ac6 --- /dev/null +++ b/towncrier/newsfragments/2246.bugfix @@ -0,0 +1 @@ +Fetchmail: Missing support for '*_ADDRESS' env vars diff --git a/towncrier/newsfragments/2526.misc b/towncrier/newsfragments/2526.misc new file mode 100644 index 00000000..9425e88a --- /dev/null +++ b/towncrier/newsfragments/2526.misc @@ -0,0 +1 @@ +Upgrade Snappymail to 2.21 and merge the webmail containers diff --git a/towncrier/newsfragments/711.feature b/towncrier/newsfragments/711.feature new file mode 100644 index 00000000..aa605aa2 --- /dev/null +++ b/towncrier/newsfragments/711.feature @@ -0,0 +1 @@ +Allow other folders to be synced by fetchmail diff --git a/webmails/Dockerfile b/webmails/Dockerfile new file mode 100644 index 00000000..5d0bc23b --- /dev/null +++ b/webmails/Dockerfile @@ -0,0 +1,93 @@ +# syntax=docker/dockerfile-upstream:1.4.3 + +FROM base + +ARG VERSION +LABEL version=$VERSION + +COPY snappymail/pubkey.asc /tmp/snappymail.asc +COPY roundcube/pubkey.asc /tmp/roundcube.asc + +RUN set -euxo pipefail \ + ; apk add --no-cache \ + nginx gpg gpg-agent \ + php81 php81-fpm php81-mbstring php81-zip php81-xml php81-simplexml php81-pecl-apcu \ + php81-dom php81-curl php81-exif gd php81-gd php81-iconv php81-intl php81-openssl \ + php81-pdo_sqlite php81-pdo_mysql php81-pdo_pgsql php81-pdo php81-sodium libsodium php81-tidy php81-pecl-uuid \ + php81-pspell php81-pecl-imagick php81-opcache php81-session php81-sockets php81-fileinfo \ + aspell-uk aspell-ru aspell-fr aspell-de aspell-en \ + ; rm /etc/nginx/http.d/default.conf \ + ; rm /etc/php81/php-fpm.d/www.conf \ + ; ln -s /usr/bin/php81 /usr/bin/php \ + ; gpg --import /tmp/snappymail.asc \ + ; gpg --import /tmp/roundcube.asc \ + ; mkdir -p /run/nginx \ + ; mkdir -p /conf + +# roundcube +ENV ROUNDCUBE_URL https://github.com/roundcube/roundcubemail/releases/download/1.5.3/roundcubemail-1.5.3-complete.tar.gz +ENV CARDDAV_URL https://github.com/mstilkerich/rcmcarddav/releases/download/v4.4.3/carddav-v4.4.3.tar.gz + +RUN set -euxo pipefail \ + ; cd /var/www \ + ; curl -sLo /dev/shm/roundcube.tgz ${ROUNDCUBE_URL} \ + ; curl -sLo /dev/shm/roundcube.tgz.asc ${ROUNDCUBE_URL}.asc \ + ; gpg --status-fd 1 --verify /dev/shm/roundcube.tgz.asc \ + ; tar xzf /dev/shm/roundcube.tgz \ + ; curl -sL ${CARDDAV_URL} | tar xz \ + ; mv roundcubemail-* roundcube \ + ; mkdir -p /var/www/roundcube/config \ + ; mv carddav roundcube/plugins/ \ + ; cd roundcube \ + ; rm -rf CHANGELOG.md SECURITY.md INSTALL LICENSE README.md UPGRADING composer.json-dist installer composer.* \ + ; ln -sf index.php /var/www/roundcube/public_html/sso.php \ + ; rm -rf plugins/{autologon,example_addressbook,http_authentication,krb_authentication,new_user_identity,password,redundant_attachments,squirrelmail_usercopy,userinfo,virtuser_file,virtuser_query} + +COPY roundcube/config/config.inc.php /conf/ +COPY roundcube/login/mailu.php /var/www/roundcube/plugins/mailu/ +COPY roundcube/config/config.inc.carddav.php /var/www/roundcube/plugins/carddav/config.inc.php + +# snappymail + +ENV SNAPPYMAIL_URL https://github.com/the-djmaze/snappymail/releases/download/v2.21.3/snappymail-2.21.3.tar.gz + +RUN set -euxo pipefail \ + ; mkdir /var/www/snappymail \ + ; cd /var/www/snappymail \ + ; curl -sLo /dev/shm/snappymail.tgz ${SNAPPYMAIL_URL} \ + ; curl -sLo /dev/shm/snappymail.tgz.asc ${SNAPPYMAIL_URL}.asc \ + ; gpg --status-fd 1 --verify /dev/shm/snappymail.tgz.asc \ + ; tar xzf /dev/shm/snappymail.tgz + +# SnappyMail login +COPY snappymail/login/include.php /var/www/snappymail/ +COPY snappymail/login/sso.php /var/www/snappymail/ + +# Parsed and moved at startup +COPY snappymail/defaults/application.ini /defaults/ +COPY snappymail/defaults/default.json /defaults/ + +# set perms +RUN set -euxo pipefail \ + ; chmod -R a+rX /var/www/snappymail \ + ; chown -R root:root /var/www/snappymail \ + ; chown -R mailu:mailu /var/www/snappymail/data \ + ; chown -R root:root /var/www/roundcube/ \ + ; chown -R mailu:mailu /var/www/roundcube/temp /var/www/roundcube/logs \ + ; chmod -R a+rX /var/www/roundcube + +# common +COPY start.py / +COPY php.ini /defaults/ +COPY php-webmail.conf /etc/php81/php-fpm.d/ +COPY nginx-webmail.conf /conf/ + +EXPOSE 80/tcp +VOLUME /data +VOLUME /overrides + +CMD /start.py + +HEALTHCHECK CMD curl -f -L http://localhost/ping || exit 1 + +RUN echo $VERSION >> /version diff --git a/webmails/snappymail/config/nginx-snappymail.conf b/webmails/nginx-webmail.conf similarity index 76% rename from webmails/snappymail/config/nginx-snappymail.conf rename to webmails/nginx-webmail.conf index 80268340..1794a635 100644 --- a/webmails/snappymail/config/nginx-snappymail.conf +++ b/webmails/nginx-webmail.conf @@ -2,7 +2,11 @@ server { listen 80 default_server; listen [::]:80 default_server; - root /var/www/webmail; +{% if WEBMAIL == 'roundcube' %} + root /var/www/{{ WEBMAIL }}/public_html; +{% else %} + root /var/www/{{ WEBMAIL }}; +{% endif %} include /etc/nginx/mime.types; @@ -16,6 +20,11 @@ server { # set maximum body size to configured limit client_max_body_size {{ MESSAGE_SIZE_LIMIT|int + 8388608 }}; + fastcgi_hide_header X-Powered-By; + add_header X-Download-Options "noopen" always; + add_header X-Robots-Tag "none" always; + add_header X-Permitted-Cross-Domain-Policies "none" always; + add_header Referrer-Policy "no-referrer" always; location / { try_files $uri $uri/ /index.php$args; @@ -42,11 +51,11 @@ server { {% endif %} } - location ~ /\. { + location ~ (^|/)\. { deny all; } - location ^~ /data { + location ~* /(config|temp|logs|data) { deny all; } diff --git a/webmails/roundcube/config/php-roundcube.conf b/webmails/php-webmail.conf similarity index 98% rename from webmails/roundcube/config/php-roundcube.conf rename to webmails/php-webmail.conf index ac0c3375..18a1f66e 100644 --- a/webmails/roundcube/config/php-roundcube.conf +++ b/webmails/php-webmail.conf @@ -1,7 +1,7 @@ -; Start a new pool named 'roundcube'. +; Start a new pool named 'php'. ; the variable $pool can be used in any directive and will be replaced by the -; pool name ('roundcube' here) -[roundcube] +; pool name ('php' here) +[php] ; Redirect worker stdout and stderr into main error log. If not set, stdout and ; stderr will be redirected to /dev/null according to FastCGI specs. @@ -11,8 +11,8 @@ catch_workers_output = 1 ; Unix user/group of processes ; Note: The user is mandatory. If the group is not set, the default user's group ; will be used. -user = nginx -group = nginx +user = mailu +group = mailu ; The address on which to accept FastCGI requests. ; Valid syntaxes are: diff --git a/webmails/roundcube/config/php.ini b/webmails/php.ini similarity index 51% rename from webmails/roundcube/config/php.ini rename to webmails/php.ini index 9f45dc80..d9ba892c 100644 --- a/webmails/roundcube/config/php.ini +++ b/webmails/php.ini @@ -2,7 +2,12 @@ expose_php=Off date.timezone={{ TZ }} upload_max_filesize = {{ MAX_FILESIZE }}M post_max_size = {{ MAX_FILESIZE }}M -suhosin.session.encrypt=Off session.auto_start=Off mbstring.func_overload=Off file_uploads=On +error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE +display_errors=Off +log_errors=On +zlib.output_compression=Off +access.log = /dev/fd/2 +error_log = /dev/fd/2 diff --git a/webmails/roundcube/Dockerfile b/webmails/roundcube/Dockerfile deleted file mode 100644 index 8db6f984..00000000 --- a/webmails/roundcube/Dockerfile +++ /dev/null @@ -1,58 +0,0 @@ -# syntax=docker/dockerfile-upstream:1.4.3 - -#roundcube image -FROM base - -ARG VERSION -LABEL version=$VERSION - -RUN set -euxo pipefail \ - ; apk add --no-cache \ - nginx gpg gpg-agent \ - php81 php81-fpm php81-mbstring php81-zip php81-xml php81-simplexml \ - php81-dom php81-curl php81-exif gd php81-gd php81-iconv php81-intl php81-openssl \ - php81-pdo_sqlite php81-pdo_mysql php81-pdo_pgsql php81-pdo php81-sodium libsodium php81-tidy php81-pecl-uuid \ - php81-pspell php81-pecl-imagick php81-opcache php81-session php81-sockets php81-fileinfo \ - ; rm /etc/nginx/http.d/default.conf \ - ; rm /etc/php81/php-fpm.d/www.conf \ - ; ln -s /usr/bin/php81 /usr/bin/php \ - ; mkdir -p /run/nginx \ - ; mkdir -p /conf - -ENV ROUNDCUBE_URL https://github.com/roundcube/roundcubemail/releases/download/1.5.3/roundcubemail-1.5.3-complete.tar.gz -ENV CARDDAV_URL https://github.com/mstilkerich/rcmcarddav/releases/download/v4.4.3/carddav-v4.4.3.tar.gz - -RUN set -euxo pipefail \ - ; cd /var/www \ - ; curl -sL ${ROUNDCUBE_URL} | tar xz \ - ; curl -sL ${CARDDAV_URL} | tar xz \ - ; mv roundcubemail-* webmail \ - ; mkdir -p /var/www/webmail/config \ - ; mv carddav webmail/plugins/ \ - ; cd webmail \ - ; rm -rf CHANGELOG.md SECURITY.md INSTALL LICENSE README.md UPGRADING composer.json-dist installer composer.* \ - ; ln -sf index.php /var/www/webmail/sso.php \ - ; chmod -R u+w,a+rX /var/www/webmail \ - ; chown -R nginx:nginx /var/www/webmail \ - ; rm -rf plugins/{autologon,example_addressbook,http_authentication,krb_authentication,new_user_identity,password,redundant_attachments,squirrelmail_usercopy,userinfo,virtuser_file,virtuser_query} - - -# nginx / PHP config files -COPY config/nginx-roundcube.conf /conf/ -COPY config/php-roundcube.conf /etc/php81/php-fpm.d/roundcube.conf -COPY config/php.ini /conf/ -COPY config/config.inc.php /conf/ -COPY login/mailu.php /var/www/webmail/plugins/mailu/ -COPY config/config.inc.carddav.php /var/www/webmail/plugins/carddav/config.inc.php - -COPY start.py / - -EXPOSE 80/tcp -VOLUME /data -VOLUME /overrides - -CMD /start.py - -HEALTHCHECK CMD curl -f -L http://localhost/ping || exit 1 - -RUN echo $VERSION >> /version diff --git a/webmails/roundcube/config/config.inc.php b/webmails/roundcube/config/config.inc.php index d5213b32..6e5ea0bd 100644 --- a/webmails/roundcube/config/config.inc.php +++ b/webmails/roundcube/config/config.inc.php @@ -4,7 +4,7 @@ $config = array(); // Generals $config['db_dsnw'] = '{{ DB_DSNW }}'; -$config['temp_dir'] = '/tmp/'; +$config['temp_dir'] = '/dev/shm/'; $config['des_key'] = '{{ SECRET_KEY }}'; $config['cipher_method'] = 'AES-256-CBC'; $config['identities_level'] = 0; diff --git a/webmails/roundcube/config/nginx-roundcube.conf b/webmails/roundcube/config/nginx-roundcube.conf deleted file mode 100644 index 80268340..00000000 --- a/webmails/roundcube/config/nginx-roundcube.conf +++ /dev/null @@ -1,63 +0,0 @@ -server { - listen 80 default_server; - listen [::]:80 default_server; - - root /var/www/webmail; - - include /etc/nginx/mime.types; - - # /dev/stdout (Default), , off - access_log off; - - # /dev/stderr (Default), , debug, info, notice, warn, error, crit, alert, emerg - error_log /dev/stderr notice; - - index index.php; - - # set maximum body size to configured limit - client_max_body_size {{ MESSAGE_SIZE_LIMIT|int + 8388608 }}; - - location / { - try_files $uri $uri/ /index.php$args; - } - - location ~ \.php$ { - fastcgi_split_path_info ^(.+?\.php)(/.*)$; - if (!-f $document_root$fastcgi_script_name) { - return 404; - } - include /etc/nginx/fastcgi_params; - - fastcgi_intercept_errors on; - fastcgi_index index.php; - - fastcgi_keep_conn on; - - fastcgi_pass unix:/var/run/php8-fpm.sock; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - {% if WEB_WEBMAIL == '/' %} - fastcgi_param SCRIPT_NAME $fastcgi_script_name; - {% else %} - fastcgi_param SCRIPT_NAME {{WEB_WEBMAIL}}/$fastcgi_script_name; - {% endif %} - } - - location ~ /\. { - deny all; - } - - location ^~ /data { - deny all; - } - - location = /ping { - allow 127.0.0.1; - allow ::1; - deny all; - - include /etc/nginx/fastcgi_params; - fastcgi_index index.php; - fastcgi_pass unix:/var/run/php8-fpm.sock; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - } -} diff --git a/webmails/roundcube/login/mailu.php b/webmails/roundcube/login/mailu.php index 0596ca9d..86de6562 100644 --- a/webmails/roundcube/login/mailu.php +++ b/webmails/roundcube/login/mailu.php @@ -18,13 +18,6 @@ class mailu extends rcube_plugin $args['action'] = 'login'; } - $ua = $_SERVER['HTTP_USER_AGENT']; - $ra = $_SERVER['REMOTE_ADDR']; - if ($ua == 'health' and ($ra == '127.0.0.1' or $ra == '::1')) { - print('OK'); - exit(); - } - return $args; } @@ -35,7 +28,7 @@ class mailu extends rcube_plugin header('HTTP/1.0 403 Forbidden'); print('mailu sso failure'); } else { - header('Location: sso.php'); + header('Location: sso.php', 302); } exit(); } @@ -54,19 +47,19 @@ class mailu extends rcube_plugin { $this->load_config(); $sso_logout_url = rcmail::get_instance()->config->get('sso_logout_url'); - header('Location: ' . $sso_logout_url, true); + header('Location: ' . $sso_logout_url, true, 302); exit(); } function login($args) { - header('Location: index.php'); + header('Location: index.php', 302); exit(); } function login_failed($args) { - header('Location: sso.php'); + header('Location: sso.php', 302); exit(); } diff --git a/webmails/roundcube/pubkey.asc b/webmails/roundcube/pubkey.asc new file mode 100644 index 00000000..3d4449c9 --- /dev/null +++ b/webmails/roundcube/pubkey.asc @@ -0,0 +1,102 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +0dXJnhEe7eXkwBbfEzk= +=lBKd +-----END PGP PUBLIC KEY BLOCK----- diff --git a/webmails/snappymail/Dockerfile b/webmails/snappymail/Dockerfile deleted file mode 100644 index 3bc4ef53..00000000 --- a/webmails/snappymail/Dockerfile +++ /dev/null @@ -1,54 +0,0 @@ -# syntax=docker/dockerfile-upstream:1.4.3 - -#snappymail image -FROM base - -ARG VERSION -LABEL version=$VERSION - -RUN set -euxo pipefail \ - ; apk add --no-cache \ - nginx curl \ - php81 php81-fpm php81-mbstring php81-zip php81-xml php81-simplexml \ - php81-dom php81-curl php81-exif gd php81-gd php81-iconv php81-intl php81-openssl \ - php81-pdo_sqlite php81-pdo php81-sodium libsodium php81-tidy php81-pecl-uuid \ - ; ln -s /usr/bin/php81 /usr/bin/php \ - ; rm /etc/nginx/http.d/default.conf \ - ; rm /etc/php81/php-fpm.d/www.conf \ - ; mkdir -p /run/nginx \ - ; mkdir -p /var/www/webmail \ - ; mkdir -p /config - -# nginx / PHP config files -COPY config/nginx-snappymail.conf /config/ -COPY config/php-snappymail.conf /etc/php81/php-fpm.d/snappymail.conf - -# Parsed and moved at startup -COPY defaults/php.ini /defaults/ -COPY defaults/application.ini /defaults/ -COPY defaults/default.ini /defaults/ - -# Install Snappymail from source -ENV SNAPPYMAIL_URL https://github.com/the-djmaze/snappymail/releases/download/v2.19.4/snappymail-2.19.4.tar.gz -# Note. This is the last working snappymail version. 2.19.6 up to 2.20.6 do not work. - -RUN set -euxo pipefail \ - ; cd /var/www/webmail \ - ; curl -sL ${SNAPPYMAIL_URL} | tar xz \ - ; chmod -R u+w,a+rX /var/www/webmail \ - ; chown -R nginx:nginx /var/www/webmail - -# SnappyMail login -COPY login/include.php /var/www/webmail/ -COPY login/sso.php /var/www/webmail/ - -COPY start.py / -COPY config.py / - -EXPOSE 80/tcp -VOLUME ["/data"] - -CMD /start.py - -HEALTHCHECK CMD curl -f -L http://localhost/ping || exit 1 -RUN echo $VERSION >> /version diff --git a/webmails/snappymail/config.py b/webmails/snappymail/config.py deleted file mode 100755 index f9fa363c..00000000 --- a/webmails/snappymail/config.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python3 - -import os -import logging as log -import sys - -from socrate import system, conf - -args = os.environ.copy() - -log.basicConfig(stream=sys.stderr, level=args.get("LOG_LEVEL", "WARNING")) - -# Build final configuration paths -conf.jinja("/config/nginx-snappymail.conf", args, "/etc/nginx/http.d/snappymail.conf") -if os.path.exists("/var/run/nginx.pid"): - os.system("nginx -s reload") diff --git a/webmails/snappymail/config/php-snappymail.conf b/webmails/snappymail/config/php-snappymail.conf deleted file mode 100644 index 74b1889f..00000000 --- a/webmails/snappymail/config/php-snappymail.conf +++ /dev/null @@ -1,118 +0,0 @@ -; Start a new pool named 'snappymail'. -; the variable $pool can be used in any directive and will be replaced by the -; pool name ('snappymail' here) -[snappymail] - -; Redirect worker stdout and stderr into main error log. If not set, stdout and -; stderr will be redirected to /dev/null according to FastCGI specs. -; Default value: no. -catch_workers_output = 1 - -; Unix user/group of processes -; Note: The user is mandatory. If the group is not set, the default user's group -; will be used. -user = nginx -group = nginx - -; The address on which to accept FastCGI requests. -; Valid syntaxes are: -; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on -; a specific port; -; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on -; a specific port; -; 'port' - to listen on a TCP socket to all addresses -; (IPv6 and IPv4-mapped) on a specific port; -; '/path/to/unix/socket' - to listen on a unix socket. -; Note: This value is mandatory. -listen = /var/run/php8-fpm.sock - -; Set permissions for unix socket, if one is used. In Linux, read/write -; permissions must be set in order to allow connections from a web server. Many -; BSD-derived systems allow connections regardless of permissions. -; Default Values: user and group are set as the running user -; mode is set to 0660 -listen.owner = nginx -listen.group = nginx -listen.mode = 0660 - -; Choose how the process manager will control the number of child processes. -; Possible Values: -; static - a fixed number (pm.max_children) of child processes; -; dynamic - the number of child processes are set dynamically based on the -; following directives. With this process management, there will be -; always at least 1 children. -; pm.max_children - the maximum number of children that can -; be alive at the same time. -; pm.start_servers - the number of children created on startup. -; pm.min_spare_servers - the minimum number of children in 'idle' -; state (waiting to process). If the number -; of 'idle' processes is less than this -; number then some children will be created. -; pm.max_spare_servers - the maximum number of children in 'idle' -; state (waiting to process). If the number -; of 'idle' processes is greater than this -; number then some children will be killed. -; ondemand - no children are created at startup. Children will be forked when -; new requests will connect. The following parameter are used: -; pm.max_children - the maximum number of children that -; can be alive at the same time. -; pm.process_idle_timeout - The number of seconds after which -; an idle process will be killed. -; Note: This value is mandatory. -pm = ondemand - -; The number of child processes to be created when pm is set to 'static' and the -; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. -; This value sets the limit on the number of simultaneous requests that will be -; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. -; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP -; CGI. The below defaults are based on a server without much resources. Don't -; forget to tweak pm.* to fit your needs. -; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' -; Note: This value is mandatory. -pm.max_children = 5 - -; The number of child processes created on startup. -; Note: Used only when pm is set to 'dynamic' -; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 -; pm.start_servers = 2 - -; The desired minimum number of idle server processes. -; Note: Used only when pm is set to 'dynamic' -; Note: Mandatory when pm is set to 'dynamic' -; pm.min_spare_servers = 1 - -; The desired maximum number of idle server processes. -; Note: Used only when pm is set to 'dynamic' -; Note: Mandatory when pm is set to 'dynamic' -; pm.max_spare_servers = 3 - -; This sets the maximum time in seconds a script is allowed to run before it is -; terminated by the parser. This helps prevent poorly written scripts from tying up -; the server. The default setting is 30s. -; Note: Used only when pm is set to 'ondemand' -pm.process_idle_timeout = 10s - -; The number of requests each child process should execute before respawning. -; This can be useful to work around memory leaks in 3rd party libraries. For endless -; request processing specify '0'. -; Equivalent to PHP_FCGI_MAX_REQUESTS. Default value: 0. -; Noted: Used only when pm is set to 'ondemand' -pm.max_requests = 200 - -; The ping URI to call the monitoring page of FPM. If this value is not set, no -; URI will be recognized as a ping page. This could be used to test from outside -; that FPM is alive and responding, or to -; - create a graph of FPM availability (rrd or such); -; - remove a server from a group if it is not responding (load balancing); -; - trigger alerts for the operating team (24/7). -; Note: The value must start with a leading slash (/). The value can be -; anything, but it may not be a good idea to use the .php extension or it -; may conflict with a real PHP file. -; Default Value: not set -ping.path = /ping - -; This directive may be used to customize the response of a ping request. The -; response is formatted as text/plain with a 200 response code. -; Default Value: pong -;ping.response = pong diff --git a/webmails/snappymail/defaults/application.ini b/webmails/snappymail/defaults/application.ini index 71a19f35..bcf544c5 100644 --- a/webmails/snappymail/defaults/application.ini +++ b/webmails/snappymail/defaults/application.ini @@ -5,15 +5,14 @@ attachment_size_limit = {{ MAX_FILESIZE }} [security] allow_admin_panel = Off +openpgp = On [labs] allow_gravatar = Off -{% if WEB_WEBMAIL == '/' %} -custom_login_link='sso.php' -{% else %} -custom_login_link='{{ WEB_WEBMAIL }}/sso.php' -{% endif %} -custom_logout_link='/sso/logout' +image_exif_auto_rotate = On +try_to_detect_hidden_images = On +{% if WEB_WEBMAIL == '/' %}custom_login_link = "sso.php"{% else %}custom_login_link = "{{ WEB_WEBMAIL }}/sso.php"{% endif %} +custom_logout_link = "/sso/logout" [contacts] enable = On @@ -21,3 +20,10 @@ allow_sync = On [defaults] contacts_autosave = On + +[cache] +enable = On +fast_cache_driver = "APCU" + +[imap] +use_move = On diff --git a/webmails/snappymail/defaults/default.ini b/webmails/snappymail/defaults/default.ini deleted file mode 100644 index be9a0969..00000000 --- a/webmails/snappymail/defaults/default.ini +++ /dev/null @@ -1,15 +0,0 @@ -imap_host = "{{ FRONT_ADDRESS }}" -imap_port = 10143 -imap_secure = "None" -imap_short_login = Off -sieve_use = On -sieve_allow_raw = Off -sieve_host = "{{ IMAP_ADDRESS }}" -sieve_port = 4190 -sieve_secure = "None" -smtp_host = "{{ FRONT_ADDRESS }}" -smtp_port = 10025 -smtp_secure = "None" -smtp_short_login = Off -smtp_auth = On -smtp_php_mail = Off diff --git a/webmails/snappymail/defaults/default.json b/webmails/snappymail/defaults/default.json new file mode 100644 index 00000000..ecbf116c --- /dev/null +++ b/webmails/snappymail/defaults/default.json @@ -0,0 +1,50 @@ +{ + "name": "*", + "IMAP": { + "host": "{{ FRONT_ADDRESS }}", + "port": 10143, + "secure": 0, + "shortLogin": false, + "ssl": { + "verify_peer": false, + "verify_peer_name": false, + "allow_self_signed": false, + "SNI_enabled": true, + "disable_compression": true, + "security_level": 1 + } + }, + "SMTP": { + "host": "{{ FRONT_ADDRESS }}", + "port": 10025, + "secure": 0, + "shortLogin": false, + "ssl": { + "verify_peer": false, + "verify_peer_name": false, + "allow_self_signed": false, + "SNI_enabled": true, + "disable_compression": true, + "security_level": 1 + }, + "useAuth": true, + "setSender": false, + "usePhpMail": false + }, + "Sieve": { + "host": "{{ IMAP_ADDRESS }}", + "port": 4190, + "secure": 0, + "shortLogin": false, + "ssl": { + "verify_peer": false, + "verify_peer_name": false, + "allow_self_signed": false, + "SNI_enabled": true, + "disable_compression": true, + "security_level": 1 + }, + "enabled": true + }, + "whiteList": "" +} diff --git a/webmails/snappymail/defaults/php.ini b/webmails/snappymail/defaults/php.ini deleted file mode 100644 index d3d4d9f1..00000000 --- a/webmails/snappymail/defaults/php.ini +++ /dev/null @@ -1,5 +0,0 @@ -expose_php=Off -date.timezone={{ TZ }} -upload_max_filesize = {{ MAX_FILESIZE }}M -post_max_size = {{ MAX_FILESIZE }}M - diff --git a/webmails/snappymail/login/sso.php b/webmails/snappymail/login/sso.php index e3d04824..254bb151 100644 --- a/webmails/snappymail/login/sso.php +++ b/webmails/snappymail/login/sso.php @@ -9,9 +9,9 @@ if (isset($_SERVER['HTTP_X_REMOTE_USER']) && isset($_SERVER['HTTP_X_REMOTE_USER_ $ssoHash = \RainLoop\Api::CreateUserSsoHash($email, $password); // redirect to webmail sso url - header('Location: index.php?sso&hash='.$ssoHash); + header('Location: index.php?sso&hash='.$ssoHash, 302); } else { - header('HTTP/1.0 403 Forbidden'); + header('HTTP/1.0 403 Forbidden', 403); } -?> \ No newline at end of file +?> diff --git a/webmails/snappymail/pubkey.asc b/webmails/snappymail/pubkey.asc new file mode 100644 index 00000000..9f295b79 --- /dev/null +++ b/webmails/snappymail/pubkey.asc @@ -0,0 +1,11 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Hostname: +Version: Hockeypuck 2.1.0-184-g50f1108 + +xjMEYg0atBYJKwYBBAHaRw8BAQdA2S2tvGavChACjtBastsKRThD3rsBW1LUZLmN +Zbs4uaHNI1NuYXBweU1haWwgPHJlbGVhc2VzQHNuYXBweW1haWwuZXU+wpQEExYK +ADwWIQQQFuRweRRVQvi6EzVIIIuhMpDz6wUCYg0atAIbAwULCQgHAgMiAgEGFQoJ +CAsCBBYCAwECHgcCF4AACgkQSCCLoTKQ8+u9SAD/Q/IoAwjUkKDJBPq0RGwCFnl6 +FG/VHB97CvBSpGOxtIsBAMCwMhWlsaBHAEqbzxiN+cdlMYwV23+SWLUJ/XMFgukE +=vC/h +-----END PGP PUBLIC KEY BLOCK----- diff --git a/webmails/snappymail/start.py b/webmails/snappymail/start.py deleted file mode 100755 index 5307f23b..00000000 --- a/webmails/snappymail/start.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python3 - -import os -import shutil -import logging as log -import sys -import subprocess - -from socrate import system, conf - -log.basicConfig(stream=sys.stderr, level=os.environ.get("LOG_LEVEL", "WARNING")) - -# Actual startup script -os.environ["FRONT_ADDRESS"] = system.resolve_address(os.environ.get("HOST_FRONT", "front")) -os.environ["IMAP_ADDRESS"] = system.resolve_address(os.environ.get("HOST_IMAP", "imap")) - -os.environ["MAX_FILESIZE"] = str(int(int(os.environ.get("MESSAGE_SIZE_LIMIT"))*0.66/1048576)) - -base = "/data/_data_/_default_/" -shutil.rmtree(base + "domains/", ignore_errors=True) -os.makedirs(base + "domains", exist_ok=True) -os.makedirs(base + "configs", exist_ok=True) - -conf.jinja("/defaults/default.ini", os.environ, "/data/_data_/_default_/domains/default.ini") -conf.jinja("/defaults/application.ini", os.environ, "/data/_data_/_default_/configs/application.ini") -conf.jinja("/defaults/php.ini", os.environ, "/etc/php81/php.ini") -# Start the fastcgi process manager now that config files have been adjusted -os.system("php-fpm81") - -os.system("chown -R nginx:nginx /data") -os.system("chmod -R a+rX /var/www/webmail/") - -subprocess.call(["/config.py"]) -os.execv("/usr/sbin/nginx", ["nginx", "-g", "daemon off;"]) diff --git a/webmails/roundcube/start.py b/webmails/start.py similarity index 72% rename from webmails/roundcube/start.py rename to webmails/start.py index b5a4dca5..06b90351 100755 --- a/webmails/roundcube/start.py +++ b/webmails/start.py @@ -4,9 +4,10 @@ import os import logging import sys import subprocess +import shutil import hmac -from socrate import conf +from socrate import conf, system env = os.environ @@ -17,6 +18,8 @@ context = {} context.update(env) context["MAX_FILESIZE"] = str(int(int(env.get("MESSAGE_SIZE_LIMIT", "50000000")) * 0.66 / 1048576)) +context["FRONT_ADDRESS"] = system.get_host_address_from_environment("FRONT", "front") +context["IMAP_ADDRESS"] = system.get_host_address_from_environment("IMAP", "imap") db_flavor = env.get("ROUNDCUBE_DB_FLAVOR", "sqlite") if db_flavor == "sqlite": @@ -52,7 +55,7 @@ context['SECRET_KEY'] = hmac.new(bytearray(secret_key, 'utf-8'), bytearray('ROUN # roundcube plugins # (using "dict" because it is ordered and "set" is not) -plugins = dict((p, None) for p in env.get("ROUNDCUBE_PLUGINS", "").replace(" ", "").split(",") if p and os.path.isdir(os.path.join("/var/www/webmail/plugins", p))) +plugins = dict((p, None) for p in env.get("ROUNDCUBE_PLUGINS", "").replace(" ", "").split(",") if p and os.path.isdir(os.path.join("/var/www/roundcube/plugins", p))) if plugins: plugins["mailu"] = None else: @@ -67,15 +70,14 @@ context["INCLUDES"] = sorted(inc for inc in os.listdir("/overrides") if inc.ends context["SESSION_TIMEOUT_MINUTES"] = max(int(env.get("SESSION_TIMEOUT", "3600")) // 60, 1) # create config files -conf.jinja("/conf/php.ini", context, "/etc/php81/php.ini") -conf.jinja("/conf/config.inc.php", context, "/var/www/webmail/config/config.inc.php") +conf.jinja("/conf/config.inc.php", context, "/var/www/roundcube/config/config.inc.php") # create dirs os.system("mkdir -p /data/gpg") print("Initializing database") try: - result = subprocess.check_output(["/var/www/webmail/bin/initdb.sh", "--dir", "/var/www/webmail/SQL"], + result = subprocess.check_output(["/var/www/roundcube/bin/initdb.sh", "--dir", "/var/www/roundcube/SQL"], stderr=subprocess.STDOUT) print(result.decode()) except subprocess.CalledProcessError as exc: @@ -88,22 +90,30 @@ except subprocess.CalledProcessError as exc: print("Upgrading database") try: - subprocess.check_call(["/var/www/webmail/bin/update.sh", "--version=?", "-y"], stderr=subprocess.STDOUT) + subprocess.check_call(["/var/www/roundcube/bin/update.sh", "--version=?", "-y"], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as exc: exit(4) else: print("Cleaning database") try: - subprocess.check_call(["/var/www/webmail/bin/cleandb.sh"], stderr=subprocess.STDOUT) + subprocess.check_call(["/var/www/roundcube/bin/cleandb.sh"], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as exc: exit(5) +base = "/data/_data_/_default_/" +shutil.rmtree(base + "domains/", ignore_errors=True) +os.makedirs(base + "domains", exist_ok=True) +os.makedirs(base + "configs", exist_ok=True) + +conf.jinja("/defaults/default.json", context, "/data/_data_/_default_/domains/default.json") +conf.jinja("/defaults/application.ini", context, "/data/_data_/_default_/configs/application.ini") +conf.jinja("/defaults/php.ini", context, "/etc/php81/php.ini") + # setup permissions -os.system("chown -R nginx:nginx /data") -os.system("chmod -R a+rX /var/www/webmail/") +os.system("chown -R mailu:mailu /data") # Configure nginx -conf.jinja("/conf/nginx-roundcube.conf", context, "/etc/nginx/http.d/roundcube.conf") +conf.jinja("/conf/nginx-webmail.conf", context, "/etc/nginx/http.d/webmail.conf") if os.path.exists("/var/run/nginx.pid"): os.system("nginx -s reload")