1
0
mirror of https://github.com/mail-in-a-box/mailinabox.git synced 2025-04-04 00:17:06 +00:00

Initial remote Nextcloud integration support

This commit is contained in:
downtownallday 2020-06-06 14:06:15 -04:00
parent a8b1e0c5b7
commit 81950592a7
7 changed files with 533 additions and 1 deletions

View File

@ -0,0 +1,16 @@
<?php
/***********************************************
* File : config.php
* Project : Z-Push
* Descr : CalDAV backend configuration file
************************************************/
define('CALDAV_PROTOCOL', 'https');
define('CALDAV_SERVER', '127.0.0.1');
define('CALDAV_PORT', '443');
define('CALDAV_PATH', 'NC_PREFIX/remote.php/dav/calendars/%u/');
define('CALDAV_PERSONAL', 'PRINCIPAL');
define('CALDAV_SUPPORTS_SYNC', false);
define('CALDAV_MAX_SYNC_PERIOD', 2147483647);
?>

View File

@ -0,0 +1,31 @@
<?php
/***********************************************
* File : config.php
* Project : Z-Push
* Descr : CardDAV backend configuration file
************************************************/
define('CARDDAV_PROTOCOL', 'https'); /* http or https */
define('CARDDAV_SERVER', '127.0.0.1');
define('CARDDAV_PORT', '443');
define('CARDDAV_PATH', 'NC_PREFIX/remote.php/dav/addressbooks/users/%u/');
define('CARDDAV_DEFAULT_PATH', 'NC_PREFIX/remote.php/dav/addressbooks/users/%u/contacts/'); /* subdirectory of the main path */
define('CARDDAV_GAL_PATH', ''); /* readonly, searchable, not syncd */
define('CARDDAV_GAL_MIN_LENGTH', 5);
define('CARDDAV_CONTACTS_FOLDER_NAME', '%u Addressbook');
define('CARDDAV_SUPPORTS_SYNC', false);
// If the CardDAV server supports the FN attribute for searches
// DAViCal supports it, but SabreDav, Nextcloud and SOGo don't
// Setting this to true will search by FN. If false will search by sn, givenName and email
// It's safe to leave it as false
define('CARDDAV_SUPPORTS_FN_SEARCH', false);
// If your carddav server needs to use file extension to recover a vcard.
// Davical needs it
// SOGo official demo online needs it, but some SOGo installation don't need it, so test it
define('CARDDAV_URL_VCARD_EXTENSION', '.vcf');
?>

View File

@ -0,0 +1,347 @@
#!/bin/bash
#
# Run this script on your remote Nextcloud to configure it to use
# Mail-in-a-Box-LDAP.
#
# The script will:
# 1. enable the "LDAP user and group backend" in Nextcloud
# 2. configure Nextcloud to access MiaB-LDAP for users and groups
# 3. optionally install and configure ssmtp so system mail is
# sent to MiaB-LDAP
#
VERBOSE=0
usage() {
cat <<EOF
Usage: $0 <NCDIR> <NC_ADMIN_USER> <NC_ADMIN_PASSWORD> <MIAB_HOSTNAME> <LDAP_NEXTCLOUD_PASS> [ <SSMTP_ALERTS_EMAIL> <SSMTP_AUTH_USER> <SSMTP_AUTH_PASS> ]
Configure Nextcloud to use MiaB-LDAP for users and groups
Optionally configure a mail relay to MiaB-LDAP
Arguments:
NCDIR
the path to the local Nextcloud installation directory
NC_ADMIN_USER
a current Nextcloud username that has ADMIN rights
NC_ADMIN_PASSWORD
the password for NC_ADMIN
MIAB_HOSTNAME
the fully-qualified host name of MiaB-LDAP
LDAP_NEXTCLOUD_PASS
supply the password for the LDAP service account Nextcloud
uses to locate and enumerate users and groups. A MiaB-LDAP
installation automatically creates this limited-access service
account with a long random password. Open
/home/user-data/ldap/miab_ldap.conf on your MiaB-LDAP box,
then paste the password for "$LDAP_NEXTCLOUD_DN" as a script
argument. It will be the value of the LDAP_NEXTCLOUD_PASSWORD
key.
SSMTP_ALERTS_EMAIL / SSMTP_AUTH_USER / SSMTP_AUTH_PASS
OPTIONAL. Supplying these arguments will setup ssmtp on your
system and configure it to use MiaB-LDAP as its mail relay.
Email sent with sendmail or ssmtp will be relayed to
MiaB-LDAP. SSMTP_ALERTS_EMAIL is the email address that will
receive messages for all userids less than
1000. SSMTP_AUTH_USER / SSMTP_AUTH_PASS is the email address
that will be used to authenticate with MiaB-LDAP (the sender
or envelope FROM address). You probably want a new/dedicated
email address for this - create on in the MiaB-LDAP admin
interface. More information on ssmtp is available at
https://help.ubuntu.com/community/EmailAlerts.
The script must be run as root.
EOF
exit 1
}
if [ "$1" == "-v" ]; then
VERBOSE=1
shift
fi
# Directory where Nextcloud is installed (must contain occ)
NCDIR="$1"
# Nextcloud admin credentials for making user-ldap API calls via curl
NC_ADMIN_USER="$2"
NC_ADMIN_PASSWORD="$3"
# Hostname of the remote MiaB-LDAP
MAILINABOX_HOSTNAME="$4"
# LDAP service account Nextcloud uses to perform ldap searches.
# Values are found in mailinabox:/home/user-data/ldap/miab_ldap.conf
LDAP_NEXTCLOUD_DN="cn=nextcloud,ou=Services,dc=mailinabox"
LDAP_NEXTCLOUD_PASSWORD="$5"
# ssmtp: the person who gets all emails for userids < 1000
SSMTP_ALERTS_EMAIL="$6"
SSMTP_AUTH_USER="$7"
SSMTP_AUTH_PASS="$8"
#
# validate arguments
#
if [ -z "$NCDIR" -o "$1" == "-h" -o "$1" == "--help" ]
then
usage
fi
if [ -z "$NCDIR" -o ! -d "$NCDIR" ]
then
echo "Invalid directory: $NCDIR" 1>&2
exit 1
fi
if [ ! -e "$NCDIR/occ" ]; then
echo "OCC not found at: $NCDIR/occ !" 1>&2
exit 1
fi
if [ -z "$NC_ADMIN_USER" -o \
-z "$MAILINABOX_HOSTNAME" -o \
-z "$LDAP_NEXTCLOUD_PASSWORD" ]
then
usage
fi
if [ ! -z "$SSMTP_ALERTS_EMAIL" ]; then
if [ -z "$(awk -F@ '{print $2}' <<< "$SSMTP_ALERTS_EMAIL")" ]; then
echo "Invalid email address: $SSMTP_ALERTS_EMAIL" 1>&2
exit 1
fi
if [ -z "$(awk -F@ '{print $2}' <<< "$SSMTP_AUTH_USER")" ]; then
echo "Invalid email address: $SSMTP_AUTH_USER" 1>&2
exit 1
fi
fi
if [ -s /etc/mailinabox.conf ]; then
echo "Run on your remote Nextcloud, not on Mail-in-a-Box !!" 1>&2
exit 1
fi
if [ "$EUID" != "0" ]; then
echo "The script must be run as root (sudo)" 1>&2
exit 1
fi
#
# other constants
#
LDAP_URL="ldaps://$MAILINABOX_HOSTNAME"
LDAP_SERVER="$MAILINABOX_HOSTNAME"
LDAP_SERVER_PORT="636"
LDAP_SERVER_STARTTLS="no"
LDAP_BASE="dc=mailinabox"
LDAP_USERS_BASE="ou=Users,dc=mailinabox"
PRIMARY_HOSTNAME="$(hostname --fqdn)"
say() {
echo "$@"
}
say_verbose() {
if [ $VERBOSE -gt 0 ]; then
echo "$@"
fi
}
die() {
echo "$@" 1>&2
exit 2
}
#
# configure nextcloud's user-ldap to access the local ldap server
#
# See: https://docs.nextcloud.com/server/17/admin_manual/configuration_user/user_auth_ldap_api.html
#
config_user_ldap() {
local id="${1:-s01}"
local first_call="${2:-yes}"
local starttls=0
[ "$LDAP_SERVER_STARTTLS" == "yes" ] && starttls=1
local c=(
"--data-urlencode configData[ldapHost]=$LDAP_URL"
"--data-urlencode configData[ldapPort]=$LDAP_SERVER_PORT"
"--data-urlencode configData[ldapBase]=$LDAP_USERS_BASE"
"--data-urlencode configData[ldapTLS]=$starttls"
"--data-urlencode configData[ldapAgentName]=$LDAP_NEXTCLOUD_DN"
"--data-urlencode configData[ldapAgentPassword]=$LDAP_NEXTCLOUD_PASSWORD"
"--data-urlencode configData[ldapUserDisplayName]=cn"
"--data-urlencode configData[ldapUserDisplayName2]="
"--data-urlencode configData[ldapUserFilter]=(&(objectClass=inetOrgPerson)(objectClass=mailUser))"
"--data-urlencode configData[ldapLoginFilter]=(&(objectClass=inetOrgPerson)(objectClass=mailUser)(|(mail=%uid)(uid=%uid)))"
"--data-urlencode configData[ldapEmailAttribute]=mail"
"--data-urlencode configData[ldapGroupFilter]=(objectClass=mailGroup)"
"--data-urlencode configData[ldapGroupMemberAssocAttr]=member"
"--data-urlencode configData[ldapGroupDisplayName]=mail"
"--data-urlencode configData[ldapNestedGroups]=1"
"--data-urlencode configData[turnOnPasswordChange]=1"
"--data-urlencode configData[ldapExpertUsernameAttr]=maildrop"
"--data-urlencode configData[ldapExpertUUIDUserAttr]=uid"
"--data-urlencode configData[ldapExpertUUIDGroupAttr]=entryUUID"
"--data-urlencode configData[ldapConfigurationActive]=1"
)
# apply the settings - note: we can't use localhost because nginx
# will route to the wrong virtual host
local xml
say_verbose "curl \"https://${NC_ADMIN_USER}:${NC_ADMIN_PASSWORD}@$PRIMARY_HOSTNAME/ocs/v2.php/apps/user_ldap/api/v1/config/$id\""
xml="$(curl -s -S --insecure -X PUT "https://${NC_ADMIN_USER}:${NC_ADMIN_PASSWORD}@$PRIMARY_HOSTNAME/ocs/v2.php/apps/user_ldap/api/v1/config/$id" -H "OCS-APIREQUEST: true" ${c[@]})"
[ $? -ne 0 ] &&
die "Unable to issue a REST call as $NC_ADMIN_USER to nextcloud"
# did it work?
local statuscode
statuscode=$(python3 -c "import xml.etree.ElementTree as ET; print(ET.fromstring('''$xml''').findall('meta')[0].findall('statuscode')[0].text)")
if [ "$statuscode" == "404" -a "$first_call" == "yes" ]; then
# got a 404 so maybe this is the first time -- we have to create
# an initial blank ldap configuration and try again
xml="$(curl -s -S --insecure -X POST "https://${NC_ADMIN_USER}:${NC_ADMIN_PASSWORD}@$PRIMARY_HOSTNAME/ocs/v2.php/apps/user_ldap/api/v1/config" -H "OCS-APIREQUEST: true")"
[ $? -ne 0 ] &&
die "Unable to issue a REST call as $NC_ADMIN_USER to nextcloud: $xml"
statuscode=$(python3 -c "import xml.etree.ElementTree as ET; print(ET.fromstring('''$xml''').findall('meta')[0].findall('statuscode')[0].text)")
[ $? -ne 0 -o "$statuscode" != "200" ] &&
die "Error creating initial ldap configuration: $xml"
id=$(python3 -c "import xml.etree.ElementTree as ET; print(ET.fromstring('''$xml''').findall('data')[0].findall('configID')[0].text)" 2>/dev/null)
[ $? -ne 0 ] &&
die "Error creating initial ldap configuration: $xml"
config_user_ldap "$id" no
elif [ "$statuscode" == "997" -a "$first_call" == "yes" ]; then
# could not log in
die "Could not authenticate as $NC_ADMIN_USER to perform user-ldap API call. statuscode=$statuscode: $xml"
elif [ "$statuscode" != "200" ]; then
die "Unable to apply ldap configuration to nextcloud: id=$id first_call=$first_call statuscode=$statuscode: $xml"
fi
return 0
}
enable_user_ldap() {
# install prerequisite package
say_verbose "Installing system package php-ldap"
apt-get install -y -qq php-ldap || die "Could not install php-ldap package"
#restart_service php7.2-fpm
# enable user_ldap
sudo -E -u www-data php $NCDIR/occ app:enable user_ldap -q
[ $? -ne 0 ] && die "Unable to enable user_ldap nextcloud app"
}
install_app() {
local app="$1"
if [ -z "$(sudo -E -u www-data php $NCDIR/occ app:list | grep $app)" ]; then
sudo -E -u www-data php $NCDIR/occ app:install $app
[ $? -ne 0 ] && die "Unable to install Nextcloud app '$app'"
fi
}
setup_ssmtp() {
# sendmail-like mailer with a mailhub to remote mail-in-a-box
# see: https://help.ubuntu.com/community/EmailAlerts
say_verbose "Installing system package ssmtp"
apt-get install -y -qq ssmtp
if [ ! -e /etc/ssmtp/ssmtp.conf.orig ]; then
cp /etc/ssmtp/ssmtp.conf /etc/ssmtp/ssmtp.conf.orig
fi
cat <<EOF >/etc/ssmtp/ssmtp.conf
# Generated by MiaB-LDAP integration script on $(date)
# The person who gets all mail for userids < 1000
root=${SSMTP_ALERTS_EMAIL}
# The place where mail goes
mailhub=${MAILINABOX_HOSTNAME}:587
AuthUser=${SSMTP_AUTH_USER}
AuthPass=${SSMTP_AUTH_PASS}
UseTLS=YES
UseSTARTTLS=YES
# The full hostname
hostname=${PRIMARY_HOSTNAME}
# Are users allowed to set their own From address?
FromLineOverride=YES
EOF
}
remote_mailinabox_handler() {
say_verbose "Installing system package ldap-utils"
apt-get install -y -qq ldap-utils || die "Could not install ldap-utils package"
while /bin/true; do
# ensure we can search
local output
say ""
say "Testing MiaB-LDAP connection..."
output="$(ldapsearch -H $LDAP_URL -x -D "$LDAP_NEXTCLOUD_DN" -w "$LDAP_NEXTCLOUD_PASSWORD" -b "$LDAP_BASE" -s base 2>&1)"
local code=$?
if [ $code -ne 0 ]; then
say "Unable to contact $LDAP_URL"
say " base=$LDAP_BASE"
say " user=$LDAP_NEXTCLOUD_DN"
say " error code=$code"
say " msg= $output"
say ""
say "You may need to permit access to the ldap server running on $LDAP_SERVER"
say "On $LDAP_SERVER execute:"
local ip
for ip in $(hostname -I); do
say " \$ ufw allow proto tcp from $ip to any port ldaps"
done
say ""
read -p "Press [enter] when ready, or \"no\" to quit: " ans
[ "$ans" == "no" ] && die "Quit"
else
break
fi
done
enable_user_ldap
config_user_ldap
return 0
}
echo "Integrating Nextcloud with Mail-in-a-box LDAP"
remote_mailinabox_handler
# contacts and calendar are required for Roundcube and Z-Push
install_app "calendar"
install_app "contacts"
if [ ! -z "${SSMTP_ALERTS_EMAIL}" ]; then
setup_ssmtp
fi
say ""
say "Done!"

View File

@ -0,0 +1,123 @@
#!/bin/bash
source setup/functions.sh # load our functions
source /etc/mailinabox.conf # load global vars
# maintain a separate conf file because setup rewrites mailinabox.conf
touch /etc/mailinabox_mods.conf
. /etc/mailinabox_mods.conf
# where webmail.sh installs roundcube
RCM_DIR=/usr/local/lib/roundcubemail
RCM_PLUGIN_DIR=${RCM_DIR}/plugins
# where zpush.sh installs z-push
ZPUSH_DIR=/usr/local/lib/z-push
configure_zpush() {
# have zpush use the remote nextcloud for carddav/caldav
# instead of the nextcloud that comes with mail-in-a-box
local nc_host="$1"
local nc_prefix="$2"
[ "$nc_prefix" == "/" ] && nc_prefix=""
# Configure CardDav
if [ ! -z "$nc_host" ]
then
cp setup/mods.available/conf/zpush/backend_carddav.php $ZPUSH_DIR/backend/carddav/config.php
cp setup/mods.available/conf/zpush/backend_caldav.php $ZPUSH_DIR/backend/caldav/config.php
sed -i "s/127\.0\.0\.1/$nc_host/g" $ZPUSH_DIR/backend/carddav/config.php
sed -i "s^NC_PREFIX^$nc_prefix^g" $ZPUSH_DIR/backend/carddav/config.php
sed -i "s/127\.0\.0\.1/$nc_host/g" $ZPUSH_DIR/backend/caldav/config.php
sed -i "s^NC_PREFIX^$nc_prefix^g" $ZPUSH_DIR/backend/caldav/config.php
fi
}
configure_roundcube() {
# replace the plugin configuration from the default Mail-In-A-Box
local name="$1"
local nc_host="$2"
local nc_prefix="$3"
[ "$nc_prefix" == "/" ] && nc_prefix=""
# Configure CardDav
cat > ${RCM_PLUGIN_DIR}/carddav/config.inc.php <<EOF
<?php
/* Do not edit. Written by Mail-in-a-Box-LDAP mods. Regenerated on updates. */
\$prefs['_GLOBAL']['hide_preferences'] = true;
\$prefs['_GLOBAL']['suppress_version_warning'] = true;
\$prefs['cloud'] = array(
'name' => '$name',
'username' => '%u', // login username
'password' => '%p', // login password
'url' => 'https://${nc_host}${nc_prefix}/remote.php/carddav/addressbooks/%u/contacts',
'active' => true,
'readonly' => false,
'refresh_time' => '02:00:00',
'fixed' => array('username','password'),
'preemptive_auth' => '1',
'hide' => false,
);
?>
EOF
}
remote_nextcloud_handler() {
echo "Configure a remote Nextcloud"
echo "============================"
echo 'Enter the hostname and web prefix of your remote Nextcloud'
echo 'For example:'
echo ' "cloud.mydomain.com/" - Nextcloud server with no prefix'
echo ' "cloud.mydomain.com" - same as above'
echo ' "www.mydomain.com/cloud" - a Nextcloud server having a prefix /cloud'
echo ''
local ans_hostname
local ans_prefix
if [ -z "${NC_HOST:-}" ]; then
if [ -z "${NONINTERACTIVE:-}" ]; then
read -p "[your Nextcloud's hostname/prefix] " ans_hostname
fi
[ -z "$ans_hostname" ] && return 0
else
if [ -z "${NONINTERACTIVE:-}" ]; then
read -p "[$NC_HOST/$NC_PREFIX] " ans_hostname
if [ -z "$ans_hostname" ]; then
ans_hostname="$NC_HOST/$NC_PREFIX"
elif [ "$ans_hostname" == "none" ]; then
ans_hostname=""
fi
else
ans_hostname="${NC_HOST}${NC_PREFIX}"
fi
fi
ans_prefix="/$(awk -F/ '{print substr($0,length($1)+2)}' <<< "$ans_hostname")"
ans_hostname="$(awk -F/ '{print $1}' <<< "$ans_hostname")"
if [ ! -z "$ans_hostname" ]; then
echo "Using Nextcloud ${ans_hostname}${ans_prefix}"
# configure roundcube contacts
configure_roundcube "$ans_hostname" "$ans_hostname" "$ans_prefix"
# configure zpush (which links to contacts & calendar)
configure_zpush "$ans_hostname" "$ans_prefix"
# prevent nginx from serving any miab-installed nextcloud files
chmod 000 /usr/local/lib/owncloud
fi
tools/editconf.py /etc/mailinabox_mods.conf \
"NC_HOST=$ans_hostname" \
"NC_PREFIX=$ans_prefix"
}
remote_nextcloud_handler

2
setup/mods.d/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -123,15 +123,21 @@ if [ ! -f $STORAGE_ROOT/ssl/ca_certificate.pem ]; then
-passin 'pass:SECRET-PASSWORD' \ -passin 'pass:SECRET-PASSWORD' \
-out $CERT \ -out $CERT \
-subj '/CN=Temporary-Mail-In-A-Box-CA' -subj '/CN=Temporary-Mail-In-A-Box-CA'
fi
# add the certificate to the system's trusted root ca list if [ ! -e /usr/local/share/ca-certificates/mailinabox.crt ]; then
# add the CA certificate to the system's trusted root ca list
# this is required for openldap's TLS implementation # this is required for openldap's TLS implementation
# do this as a separate step in case a CA certificate is manually
# copied onto the machine for QA/test
CERT=$STORAGE_ROOT/ssl/ca_certificate.pem
hide_output \ hide_output \
cp $CERT /usr/local/share/ca-certificates/mailinabox.crt cp $CERT /usr/local/share/ca-certificates/mailinabox.crt
hide_output \ hide_output \
update-ca-certificates update-ca-certificates
fi fi
# Generate a signed SSL certificate because things like nginx, dovecot, # Generate a signed SSL certificate because things like nginx, dovecot,
# etc. won't even start without some certificate in place, and we need nginx # etc. won't even start without some certificate in place, and we need nginx
# so we can offer the user a control panel to install a better certificate. # so we can offer the user a control panel to install a better certificate.

View File

@ -147,6 +147,13 @@ echo
certbot register --register-unsafely-without-email --agree-tos --config-dir $STORAGE_ROOT/ssl/lets_encrypt certbot register --register-unsafely-without-email --agree-tos --config-dir $STORAGE_ROOT/ssl/lets_encrypt
fi fi
#
# Run setup mods
#
for mod in $(ls setup/mods.d | grep -v '~$'); do
setup/mods.d/$mod
done
# Done. # Done.
echo echo
echo "-----------------------------------------------" echo "-----------------------------------------------"