2035: updated roundcube to 1.5.1 and carddav to 4.3.0 r=mergify[bot] a=ghostwheel42

## What type of PR?

enhancement

## What does this PR do?

updated roundcube to 1.5 and carddav to 4.2.2
also runs cleanup cronjob _once_ at startup

### Related issue(s)

- closes #2031 
- runs cleanup job mentioned in #1702 at startup

## Prerequisites
Before we can consider review and merge, please make sure the following list is done and checked.
If an entry in not applicable, you can check it or remove it from the list.

- [X] In case of feature or enhancement: documentation updated accordingly
- [X] Unless it's docs or a minor change: add [changelog](https://mailu.io/master/contributors/workflow.html#changelog) entry file.


Co-authored-by: Alexander Graf <ghostwheel42@users.noreply.github.com>
master
bors[bot] 3 years ago committed by GitHub
commit c5966b29db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -100,6 +100,7 @@ services:
env_file: .env env_file: .env
volumes: volumes:
- "$ROOT/webmail:/data" - "$ROOT/webmail:/data"
- "$ROOT/overrides/$WEBMAIL:/overrides:ro"
depends_on: depends_on:
- imap - imap

@ -128,6 +128,7 @@ services:
env_file: .env env_file: .env
volumes: volumes:
- "$ROOT/webmail:/data" - "$ROOT/webmail:/data"
- "$ROOT/overrides/$WEBMAIL:/overrides:ro"
depends_on: depends_on:
- imap - imap

@ -263,6 +263,50 @@ The roundcube service stores configurations in a database.
- ``ROUNDCUBE_DB_USER``: the database user for roundcube service. (when not ``sqlite``) - ``ROUNDCUBE_DB_USER``: the database user for roundcube service. (when not ``sqlite``)
- ``ROUNDCUBE_DB_NAME``: the database name for roundcube service. (when not ``sqlite``) - ``ROUNDCUBE_DB_NAME``: the database name for roundcube service. (when not ``sqlite``)
Webmail settings
----------------
When using roundcube it is possible to select the plugins to be enabled by setting ``ROUNDCUBE_PLUGINS`` to
a comma separated list of plugin-names. Included plugins are:
- acl (needs configuration)
- additional_message_headers (needs configuration)
- archive
- attachment_reminder
- carddav
- database_attachmentsi
- debug_logger
- emoticons
- enigma
- help
- hide_blockquote
- identicon
- identity_select
- jqueryui
- mailu
- managesieve
- markasjunk
- new_user_dialog
- newmail_notifier
- reconnect
- show_additional_headers (needs configuration)
- subscriptions_option
- vcard_attachments
- zipdownload
If ``ROUNDCUBE_PLUGINS`` is not set the following plugins are enabled by default:
- archive
- carddav
- enigma
- mailu
- managesieve
- markasjunk
- zipdownload
To disable all plugins just set ``ROUNDCUBE_PLUGINS`` to ``mailu``.
To configure a plugin add php files named ``*.inc`` to roundcube's :ref:`override section <override-label>`.
Mail log settings Mail log settings
----------------- -----------------

@ -147,6 +147,7 @@ services:
env_file: {{ env }} env_file: {{ env }}
volumes: volumes:
- "{{ root }}/webmail:/data" - "{{ root }}/webmail:/data"
- "{{ root }}/overrides/{{ webmail_type }}:/overrides:ro"
depends_on: depends_on:
- imap - imap
{% endif %} {% endif %}

@ -123,6 +123,7 @@ services:
env_file: {{ env }} env_file: {{ env }}
volumes: volumes:
- "{{ root }}/webmail:/data" - "{{ root }}/webmail:/data"
- "{{ root }}/overrides/{{ webmail_type }}:/overrides:ro"
deploy: deploy:
replicas: 1 replicas: 1
healthcheck: healthcheck:

@ -0,0 +1 @@
updated roundcube to 1.5 and carddav to 4.2.2 using php8

@ -1,10 +1,10 @@
# NOTE: only add file if building for arm # NOTE: only add qemu-arm-static if building for arm
ARG ARCH="" ARG ARCH=""
ARG QEMU=other ARG QEMU=other
FROM ${ARCH}php:7.4-apache as build_arm FROM ${ARCH}php:8.0-apache as build_arm
ONBUILD COPY --from=balenalib/rpi-alpine:3.14 /usr/bin/qemu-arm-static /usr/bin/qemu-arm-static ONBUILD COPY --from=balenalib/rpi-alpine:3.14 /usr/bin/qemu-arm-static /usr/bin/qemu-arm-static
FROM ${ARCH}php:8.0-apache as build_other
FROM ${ARCH}php:7.4-apache as build_other
FROM build_${QEMU} FROM build_${QEMU}
ARG VERSION ARG VERSION
@ -12,25 +12,37 @@ ENV TZ Etc/UTC
LABEL version=$VERSION LABEL version=$VERSION
#Shared layer between rainloop and roundcube RUN set -eu \
RUN apt-get update && apt-get install -y \ && apt update \
python3 curl python3-pip git python3-multidict tzdata \ && echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections \
&& rm -rf /var/lib/apt/lists \ && apt install -y --no-install-recommends \
&& echo "ServerSignature Off\nServerName roundcube" >> /etc/apache2/apache2.conf \ python3 curl python3-pip git python3-multidict \
&& sed -i 's,CustomLog.*combined$,\0 "'"expr=!(%{HTTP_USER_AGENT}=='health'\&\&(-R '127.0.0.1/8' || -R '::1'))"'",' /etc/apache2/sites-available/000-default.conf python3-jinja2 gpg tzdata \
&& pip3 install socrate \
&& echo date.timezone=UTC > /usr/local/etc/php/conf.d/timezone.ini \
&& echo "ServerSignature Off\nServerName roundcube" >> /etc/apache2/apache2.conf \
&& sed -i 's,CustomLog.*combined$,\0 "'"expr=!(%{HTTP_USER_AGENT}=='health'\&\&(-R '127.0.0.1/8' || -R '::1'))"'",' /etc/apache2/sites-available/000-default.conf \
\
&& mark="$(apt-mark showmanual)" \
&& apt install -y --no-install-recommends \
libfreetype6-dev libicu-dev libjpeg62-turbo-dev libldap2-dev libmagickwand-dev \
libpng-dev libpq-dev libsqlite3-dev libzip-dev libpspell-dev libonig-dev \
&& ln -s php.ini-production /usr/local/etc/php/php.ini \
&& docker-php-ext-configure gd --with-jpeg --with-freetype \
&& docker-php-ext-install exif gd intl zip pspell pdo_mysql pdo_pgsql pdo_sqlite \
&& pecl install imagick \
&& docker-php-ext-enable imagick opcache \
&& apt-mark auto '.*' >/dev/null \
&& apt-mark manual ${mark} >/dev/null \
&& ldd "$(php -r 'echo ini_get("extension_dir");')"/*.so | awk '/=>/ { print $3 }' | sort -u | \
xargs -r dpkg-query -S | cut -d: -f1 | sort -u | xargs -r apt-mark manual >/dev/null \
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
&& rm -rf /var/lib/apt/lists
# Shared layer between nginx, dovecot, postfix, postgresql, rspamd, unbound, rainloop, roundcube ENV ROUNDCUBE_URL https://github.com/roundcube/roundcubemail/releases/download/1.5.1/roundcubemail-1.5.1-complete.tar.gz
RUN pip3 install socrate ENV CARDDAV_URL https://github.com/mstilkerich/rcmcarddav/releases/download/v4.3.0/carddav-v4.3.0.tar.gz
ENV ROUNDCUBE_URL https://github.com/roundcube/roundcubemail/releases/download/1.4.11/roundcubemail-1.4.11-complete.tar.gz RUN set -eu \
ENV CARDDAV_URL https://github.com/mstilkerich/rcmcarddav/releases/download/v4.1.2/carddav-v4.1.2.tar.gz
RUN apt-get update && apt-get install -y \
zlib1g-dev libzip4 libzip-dev libpq-dev \
python3-jinja2 \
gpg \
&& docker-php-ext-install zip pdo_mysql pdo_pgsql \
&& rm -rf /var/www/html/ \ && rm -rf /var/www/html/ \
&& cd /var/www \ && cd /var/www \
&& curl -sL ${ROUNDCUBE_URL} | tar xz \ && curl -sL ${ROUNDCUBE_URL} | tar xz \
@ -38,27 +50,29 @@ RUN apt-get update && apt-get install -y \
&& mv roundcubemail-* html \ && mv roundcubemail-* html \
&& mv carddav html/plugins/ \ && mv carddav html/plugins/ \
&& cd html \ && cd html \
&& rm -rf CHANGELOG INSTALL LICENSE README.md UPGRADING composer.json-dist installer composer.* \ && rm -rf CHANGELOG.md SECURITY.md INSTALL LICENSE README.md UPGRADING composer.json-dist installer composer.* \
&& sed -i 's,mod_php5.c,mod_php7.c,g' .htaccess \
&& sed -i 's,^php_value.*post_max_size,#&,g' .htaccess \
&& sed -i 's,^php_value.*upload_max_filesize,#&,g' .htaccess \
&& ln -sf index.php /var/www/html/sso.php \ && ln -sf index.php /var/www/html/sso.php \
&& ln -sf /dev/stderr /var/www/html/logs/errors.log \ && ln -sf /dev/stderr /var/www/html/logs/errors.log \
&& chown -R root:root . \ && chown -R root:root . \
&& chown www-data:www-data logs temp \ && chown www-data:www-data logs temp \
&& chmod -R a+rX . \ && chmod -R a+rX . \
&& rm -rf /var/lib/apt/lists \ && a2enmod rewrite deflate expires headers \
&& a2enmod rewrite deflate expires headers && echo date.timezone=${TZ} > /usr/local/etc/php/conf.d/timezone.ini \
&& rm -rf plugins/{autologon,example_addressbook,http_authentication,krb_authentication,new_user_identity,password,redundant_attachments,squirrelmail_usercopy,userinfo,virtuser_file,virtuser_query}
# enable database_attachments (and memcache?)
COPY php.ini /php.ini
COPY config.inc.php /var/www/html/config/
COPY mailu.php /var/www/html/plugins/mailu/mailu.php COPY mailu.php /var/www/html/plugins/mailu/mailu.php
COPY start.py /start.py COPY php.ini /
COPY config.inc.php /
COPY start.py /
EXPOSE 80/tcp EXPOSE 80/tcp
VOLUME ["/data"] VOLUME /data
VOLUME /overrides
CMD /start.py CMD /start.py
HEALTHCHECK CMD curl -f -L -H 'User-Agent: health' http://localhost/ || exit 1 HEALTHCHECK CMD curl -f -L -H 'User-Agent: health' http://localhost/ || exit 1
RUN echo $VERSION >> /version RUN echo $VERSION >> /version

@ -3,46 +3,29 @@
$config = array(); $config = array();
// Generals // Generals
$config['db_dsnw'] = getenv('DB_DSNW');; $config['db_dsnw'] = '{{ DB_DSNW }}';
$config['temp_dir'] = '/tmp/'; $config['temp_dir'] = '/tmp/';
$config['des_key'] = getenv('SECRET_KEY') ? getenv('SECRET_KEY') : trim(file_get_contents(getenv('SECRET_KEY_FILE'))); $config['des_key'] = '{{ SECRET_KEY }}';
$config['cipher_method'] = 'AES-256-CBC'; $config['cipher_method'] = 'AES-256-CBC';
$config['identities_level'] = 0; $config['identities_level'] = 0;
$config['reply_all_mode'] = 1; $config['reply_all_mode'] = 1;
$config['log_driver'] = 'stdout';
// List of active plugins (in plugins/ directory) $config['zipdownload_selection'] = true;
$config['plugins'] = array( $config['enable_spellcheck'] = true;
'archive', $config['spellcheck_engine'] = 'pspell';
'zipdownload',
'markasjunk',
'managesieve',
'enigma',
'carddav'
);
$front = getenv('FRONT_ADDRESS') ? getenv('FRONT_ADDRESS') : 'front';
$imap = getenv('IMAP_ADDRESS') ? getenv('IMAP_ADDRESS') : 'imap';
// Mail servers // Mail servers
$config['default_host'] = $front; $config['default_host'] = '{{ FRONT_ADDRESS or "front" }}';
$config['default_port'] = 10143; $config['default_port'] = 10143;
$config['smtp_server'] = $front; $config['smtp_server'] = '{{ FRONT_ADDRESS or "front" }}';
$config['smtp_port'] = 10025; $config['smtp_port'] = 10025;
$config['smtp_user'] = '%u'; $config['smtp_user'] = '%u';
$config['smtp_pass'] = '%p'; $config['smtp_pass'] = '%p';
// Sieve script management // Sieve script management
$config['managesieve_host'] = $imap; $config['managesieve_host'] = '{{ IMAP_ADDRESS or "imap" }}';
$config['managesieve_usetls'] = false; $config['managesieve_usetls'] = false;
// Customization settings
if (filter_var(getenv('ADMIN'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
$config['support_url'] = getenv('WEB_ADMIN') ? '../..' . getenv('WEB_ADMIN') : '';
}
$config['product_name'] = 'Mailu Webmail';
array_push($config['plugins'], 'mailu');
$config['sso_logout_url'] = '/sso/logout';
// We access the IMAP and SMTP servers locally with internal names, SSL // We access the IMAP and SMTP servers locally with internal names, SSL
// will obviously fail but this sounds better than allowing insecure login // will obviously fail but this sounds better than allowing insecure login
// from the outter world // from the outter world
@ -56,11 +39,27 @@ $config['imap_conn_options'] = $ssl_no_check;
$config['smtp_conn_options'] = $ssl_no_check; $config['smtp_conn_options'] = $ssl_no_check;
$config['managesieve_conn_options'] = $ssl_no_check; $config['managesieve_conn_options'] = $ssl_no_check;
// roundcube customization
$config['product_name'] = 'Mailu Webmail';
{%- if ADMIN and WEB_ADMIN %}
$config['support_url'] = '../..{{ WEB_ADMIN }}';
{%- endif %}
$config['plugins'] = array({{ PLUGINS }});
// skin name: folder from skins/ // skin name: folder from skins/
$config['skin'] = 'elastic'; $config['skin'] = 'elastic';
// Enigma gpg plugin // configure mailu sso plugin
$config['sso_logout_url'] = '/sso/logout';
// configure enigma gpg plugin
$config['enigma_pgp_homedir'] = '/data/gpg'; $config['enigma_pgp_homedir'] = '/data/gpg';
// Set From header for DKIM signed message delivery reports // set From header for DKIM signed message delivery reports
$config['mdn_use_from'] = true; $config['mdn_use_from'] = true;
// includes
{%- for inc in INCLUDES %}
include('{{ inc }}');
{%- endfor %}

@ -0,0 +1,2 @@
#!/bin/sh
SCRIPT_NAME=/ping SCRIPT_FILENAME=/ping REQUEST_METHOD=GET cgi-fcgi -bind -connect 127.0.0.1:9000 2>/dev/null | grep -qFx pong

@ -15,7 +15,14 @@ class mailu extends rcube_plugin
function startup($args) function startup($args)
{ {
if (empty($_SESSION['user_id'])) { if (empty($_SESSION['user_id'])) {
$args['action'] = 'login'; $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; return $args;
@ -23,10 +30,16 @@ class mailu extends rcube_plugin
function authenticate($args) function authenticate($args)
{ {
if (!in_array('HTTP_X_REMOTE_USER', $_SERVER) || !in_array('HTTP_X_REMOTE_USER_TOKEN', $_SERVER)) { if (!array_key_exists('HTTP_X_REMOTE_USER', $_SERVER) or !array_key_exists('HTTP_X_REMOTE_USER_TOKEN', $_SERVER)) {
if ($_SERVER['PHP_SELF'] == '/sso.php') {
header('HTTP/1.0 403 Forbidden'); header('HTTP/1.0 403 Forbidden');
die(); print('mailu sso failure');
} else {
header('Location: sso.php');
}
exit();
} }
$args['user'] = $_SERVER['HTTP_X_REMOTE_USER']; $args['user'] = $_SERVER['HTTP_X_REMOTE_USER'];
$args['pass'] = $_SERVER['HTTP_X_REMOTE_USER_TOKEN']; $args['pass'] = $_SERVER['HTTP_X_REMOTE_USER_TOKEN'];
@ -36,30 +49,25 @@ class mailu extends rcube_plugin
return $args; return $args;
} }
function logout($args) { // Redirect to global SSO logout path.
// Redirect to global SSO logout path. function logout($args)
{
$this->load_config(); $this->load_config();
$sso_logout_url = rcmail::get_instance()->config->get('sso_logout_url'); $sso_logout_url = rcmail::get_instance()->config->get('sso_logout_url');
header("Location: " . $sso_logout_url, true); header('Location: ' . $sso_logout_url, true);
exit; exit();
} }
function login($args) function login($args)
{ {
header('Location: index.php'); header('Location: index.php');
exit(); exit();
} }
function login_failed($args) function login_failed($args)
{ {
$ua = $_SERVER['HTTP_USER_AGENT']; header('Location: sso.php');
$ra = $_SERVER['REMOTE_ADDR']; exit();
if ($ua == 'health' and ($ra == '127.0.0.1' or $ra == '::1')) {
echo "OK";
exit;
}
header('Location: sso.php');
exit();
} }
} }

@ -1,61 +1,105 @@
#!/usr/bin/python3 #!/usr/bin/python3
import os import os
import logging as log import logging
import sys import sys
from socrate import conf from socrate import conf
import subprocess import subprocess
import hmac
log.basicConfig(stream=sys.stderr, level=os.environ.get("LOG_LEVEL", "WARNING")) env = os.environ
os.environ["MAX_FILESIZE"] = str(int(int(os.environ.get("MESSAGE_SIZE_LIMIT")) * 0.66 / 1048576)) logging.basicConfig(stream=sys.stderr, level=env.get("LOG_LEVEL", "WARNING"))
db_flavor = os.environ.get("ROUNDCUBE_DB_FLAVOR", "sqlite") # jinja context
context = {}
context.update(env)
context["MAX_FILESIZE"] = str(int(int(env.get("MESSAGE_SIZE_LIMIT", "50000000")) * 0.66 / 1048576))
db_flavor = env.get("ROUNDCUBE_DB_FLAVOR", "sqlite")
if db_flavor == "sqlite": if db_flavor == "sqlite":
os.environ["DB_DSNW"] = "sqlite:////data/roundcube.db" context["DB_DSNW"] = "sqlite:////data/roundcube.db"
elif db_flavor == "mysql": elif db_flavor == "mysql":
os.environ["DB_DSNW"] = "mysql://%s:%s@%s/%s" % ( context["DB_DSNW"] = "mysql://%s:%s@%s/%s" % (
os.environ.get("ROUNDCUBE_DB_USER", "roundcube"), env.get("ROUNDCUBE_DB_USER", "roundcube"),
os.environ.get("ROUNDCUBE_DB_PW"), env.get("ROUNDCUBE_DB_PW"),
os.environ.get("ROUNDCUBE_DB_HOST", "database"), env.get("ROUNDCUBE_DB_HOST", "database"),
os.environ.get("ROUNDCUBE_DB_NAME", "roundcube") env.get("ROUNDCUBE_DB_NAME", "roundcube")
) )
elif db_flavor == "postgresql": elif db_flavor == "postgresql":
os.environ["DB_DSNW"] = "pgsql://%s:%s@%s/%s" % ( context["DB_DSNW"] = "pgsql://%s:%s@%s/%s" % (
os.environ.get("ROUNDCUBE_DB_USER", "roundcube"), env.get("ROUNDCUBE_DB_USER", "roundcube"),
os.environ.get("ROUNDCUBE_DB_PW"), env.get("ROUNDCUBE_DB_PW"),
os.environ.get("ROUNDCUBE_DB_HOST", "database"), env.get("ROUNDCUBE_DB_HOST", "database"),
os.environ.get("ROUNDCUBE_DB_NAME", "roundcube") env.get("ROUNDCUBE_DB_NAME", "roundcube")
) )
else: else:
print("Unknown ROUNDCUBE_DB_FLAVOR: %s", db_flavor) print(f"Unknown ROUNDCUBE_DB_FLAVOR: {db_flavor}", file=sys.stderr)
exit(1) exit(1)
conf.jinja("/php.ini", os.environ, "/usr/local/etc/php/conf.d/roundcube.ini") # derive roundcube secret key
secret_key = env.get("SECRET_KEY")
if not secret_key:
try:
secret_key = open(env.get("SECRET_KEY_FILE"), "r").read().strip()
except Exception as exc:
print(f"Can't read SECRET_KEY from file: {exc}", file=sys.stderr)
exit(2)
# Create dirs, setup permissions context['SECRET_KEY'] = hmac.new(bytearray(secret_key, 'utf-8'), bytearray('ROUNDCUBE_KEY', 'utf-8'), 'sha256').hexdigest()
# 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/plugins", p)))
if plugins:
plugins["mailu"] = None
else:
plugins = dict((k, None) for k in ["archive", "zipdownload", "markasjunk", "managesieve", "enigma", "carddav", "mailu"])
context["PLUGINS"] = ",".join(f"'{p}'" for p in plugins)
# add overrides
context["INCLUDES"] = sorted(inc for inc in os.listdir("/overrides") if inc.endswith(".inc")) if os.path.isdir("/overrides") else []
# create config files
conf.jinja("/php.ini", context, "/usr/local/etc/php/conf.d/roundcube.ini")
conf.jinja("/config.inc.php", context, "/var/www/html/config/config.inc.php")
# create dirs
os.system("mkdir -p /data/gpg") os.system("mkdir -p /data/gpg")
print("Initializing database")
try: try:
print("Initializing database")
result = subprocess.check_output(["/var/www/html/bin/initdb.sh", "--dir", "/var/www/html/SQL"], result = subprocess.check_output(["/var/www/html/bin/initdb.sh", "--dir", "/var/www/html/SQL"],
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
print(result.decode()) print(result.decode())
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as exc:
if "already exists" in e.stdout.decode(): err = exc.stdout.decode()
if "already exists" in err:
print("Already initialzed") print("Already initialzed")
else: else:
print(e.stdout.decode()) print(err)
quit(1) exit(3)
print("Upgrading database")
try: try:
print("Upgrading database")
subprocess.check_call(["/var/www/html/bin/update.sh", "--version=?", "-y"], stderr=subprocess.STDOUT) subprocess.check_call(["/var/www/html/bin/update.sh", "--version=?", "-y"], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as exc:
quit(1) exit(4)
else:
print("Cleaning database")
try:
subprocess.check_call(["/var/www/html/bin/cleandb.sh"], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as exc:
exit(5)
# Setup database permissions # setup permissions
os.system("chown -R www-data:www-data /data") os.system("chown -R www-data:www-data /data")
# Run apache # clean env
os.execv("/usr/local/bin/apache2-foreground", ["apache2-foreground"]) [env.pop(key, None) for key in env.keys() if key == "SECRET_KEY" or key.startswith("ROUNDCUBE_")]
# run apache
os.execve("/usr/local/bin/apache2-foreground", ["apache2-foreground"], env)

Loading…
Cancel
Save