From 8838d5af82bf61ec7559c1721c1ee40bf8546e37 Mon Sep 17 00:00:00 2001
From: downtownallday <downtownallday@gmail.com>
Date: Mon, 27 Jun 2022 11:00:36 -0400
Subject: [PATCH] Eliminate the manual firewall configuration step when
 connecting a remote Nextcloud's user_ldap

---
 README.md                                     |  4 -
 setup/mods.available/remote-nextcloud.sh      | 57 ++++++++++++-
 tests/system-setup/remote-nextcloud-docker.sh | 79 +++++++++++--------
 tests/system-setup/setup-defaults.sh          |  1 +
 tests/vagrant/Vagrantfile                     |  6 +-
 5 files changed, 106 insertions(+), 41 deletions(-)

diff --git a/README.md b/README.md
index d1a4c10c..27218c88 100644
--- a/README.md
+++ b/README.md
@@ -33,10 +33,6 @@ Once enabled, you'll find that Roundcube and Z-Push (ActiveSync) will use the re
 
 Copy the file `setup/mods.available/remote-nextcloud-use-miab.sh` to the Nextcloud box and run it as root. This will configure Nextcloud's "LDAP user and group backend" with the MiaB-LDAP details and ensure the contacts and calendar apps are installed. *This does not replace or alter your ability to log into Nextcloud with any existing local Nextcloud accounts. It only allows MiaB-LDAP users to log into Nextcloud using their MiaB-LDAP credentials.*
 
-**Additional Firewall Rule**
-
-On MiaB-LDAP, a one-time change must be applied manually to allow the remote Nextcloud to query the LDAP server because the default MiaB-LDAP installation doesn't allow any remote LDAP access. As root, run the following: `ufw allow proto tcp from $ip to any port ldaps`, where $ip is the ip-address of your Nextcloud server.
-
 ## Under-the-Hood
 
 **Additional directory in user-data**
diff --git a/setup/mods.available/remote-nextcloud.sh b/setup/mods.available/remote-nextcloud.sh
index ec17f949..6fc76bcf 100755
--- a/setup/mods.available/remote-nextcloud.sh
+++ b/setup/mods.available/remote-nextcloud.sh
@@ -144,7 +144,31 @@ remote_nextcloud_handler() {
     local new_url="$NC_PROTO://$NC_HOST:$NC_PORT$NC_PREFIX"
 
     if [ ! -z "$NC_HOST" ]; then
-        echo "Using Nextcloud ${new_url}"
+
+        echo ""
+        echo "Enter the source ip addresses, separated by spaces, that your remote Nextcloud uses to perform ldap queries. Include ip4 and ip6 addresses. Typically you'd leave this blank, unless you have a proxy in front of Nextcloud."
+        ans=""
+        if [ -z "${NC_HOST_SRC_IP:-}" ]; then
+            if [ -z "${NONINTERACTIVE:-}" ]; then
+                read -p "[your Nextcloud's source IP address for ldap queries] " ans
+            fi
+        else
+            if [ -z "${NONINTERACTIVE:-}" ]; then
+                read -p "[$NC_HOST_SRC_IP] " ans
+            fi
+            if [ -z "$ans" ]; then
+                ans="$NC_HOST_SRC_IP"
+            elif [ "$ans" = "none" ]; then
+                ans=""
+            fi
+        fi
+        NC_HOST_SRC_IP="$ans"
+
+        if [ -z "$NC_HOST_SRC_IP" ]; then
+            echo "Using Nextcloud ${new_url}"
+        else
+            echo "Using Nextcloud ${new_url} (but, the source ip of ldap queries will come from $NC_HOST_SRC_IP)"
+        fi
 
         # configure roundcube contacts
         configure_roundcube "$NC_HOST"
@@ -156,13 +180,42 @@ remote_nextcloud_handler() {
         # files and remove owncloud cron job
         chmod 000 /usr/local/lib/owncloud
         rm -f /etc/cron.d/mailinabox-nextcloud
+
+        # allow the remote nextcloud access to our ldap server
+        
+        # 1. remove existing firewall rules
+        local from_ips=( $(ufw status | awk '/remote_nextcloud/ {print $3}') )
+        for ip in "${from_ips[@]}"; do
+            hide_output ufw delete allow proto tcp from "$ip" to any port ldaps
+        done
+        
+        # 2. add new firewall rules
+        #
+        # if the ip address used by the Nextcloud server to contact
+        # this host with ldap queries is different than the one used
+        # by MiaB to interact with Nextcloud (eg. an nginx proxy in
+        # front of Nextcloud), then set NC_HOST_SRC_IP as an array of
+        # host or ip address expected to be used by the source
+        local ip
+        if [ ! -z "${NC_HOST_SRC_IP:-}" ]; then
+            from_ips=( $NC_HOST_SRC_IP )
+        else
+            from_ips=(
+                $(getent ahostsv4 "$NC_HOST" | head -1 | awk '{print $1}')
+                $(getent ahostsv6 "$NC_HOST" | head -1 | awk '{print $1}')
+            )
+        fi
+        for ip in "${from_ips[@]}"; do
+            hide_output ufw allow proto tcp from "$ip" to any port ldaps comment "remote_nextcloud"
+        done
     fi
     
     tools/editconf.py /etc/mailinabox_mods.conf \
                       "NC_PROTO=$NC_PROTO" \
                       "NC_HOST=$NC_HOST" \
                       "NC_PORT=$NC_PORT" \
-                      "NC_PREFIX=$NC_PREFIX"
+                      "NC_PREFIX=$NC_PREFIX" \
+                      "NC_HOST_SRC_IP='${NC_HOST_SRC_IP:-}'"
 }
 
 remote_nextcloud_handler
diff --git a/tests/system-setup/remote-nextcloud-docker.sh b/tests/system-setup/remote-nextcloud-docker.sh
index 9eea3638..50bb413b 100755
--- a/tests/system-setup/remote-nextcloud-docker.sh
+++ b/tests/system-setup/remote-nextcloud-docker.sh
@@ -47,7 +47,7 @@ fi
 init() {
     H1 "INIT"
     init_test_system
-    init_miab_testing || die "Initialization failed"
+    init_miab_testing "$@" || die "Initialization failed"
 }
 
 
@@ -88,20 +88,6 @@ install_nextcloud_docker() {
     H2 "docker: apt-get update"
     docker exec NC apt-get update || die "docker: apt-get update failed"
 
-    # allow LDAP access from docker image
-    H2 "Allow ldaps through firewall so Nextcloud can perform LDAP searches"
-    ufw allow ldaps || die "Unable to modify firewall to permit ldaps"
-
-    # add MiaB-LDAP's ca_certificate.pem to docker's trusted cert list
-    # (because setup/ssl.sh created its own self-signed ca)
-    H2 "docker: update trusted CA list"
-    docker cp \
-           $STORAGE_ROOT/ssl/ca_certificate.pem \
-           NC:/usr/local/share/ca-certificates/mailinabox.crt \
-        || die "docker: copy ca_certificate.pem failed"
-    docker exec NC update-ca-certificates \
-        || die "docker: update-ca-certificates failed"
-
     # wait for Nextcloud installation to complete
     H2 "Wait for Nextcloud installation to complete"
     wait_for_docker_nextcloud NC installed || die "Giving up"
@@ -123,12 +109,38 @@ install_nextcloud_docker() {
     docker exec -u www-data NC ./occ app:enable user_ldap \
         || die "docker: enabling user_ldap failed ($?)"
 
+    # ldap queries from the container use the container's ip address,
+    # not the exposed docker port for nextcloud. the variable
+    # NC_HOST_SRC_IP is used by the remote-nextcloud mod to configure
+    # the firewall allowing ldap queries to reach slapd from the
+    # container
+    export NC_HOST_SRC_IP=$(get_container_ip)
+    [ $? -ne 0 ] && die "Unable to get docker container IP address"
+}
+
+get_container_ip() {
+    local id
+    id=$(docker ps -aqf "name=NC")
+    [ $? -ne 0 ] && return 1
+    docker exec NC grep "$id" /etc/hosts | awk '{print $1}'
+}
+
+connect_nextcloud_to_miab() {
     #
     # integrate Nextcloud with MiaB-LDAP
     #    
-    H2 "docker: integrate Nextcloud with MiaB-LDAP"
-    
+    # add MiaB-LDAP's ca_certificate.pem to containers's trusted cert
+    # list (because setup/ssl.sh created its own self-signed ca)
+    H2 "docker: update trusted CA list"
+    docker cp \
+           $STORAGE_ROOT/ssl/ca_certificate.pem \
+           NC:/usr/local/share/ca-certificates/mailinabox.crt \
+        || die "docker: copy ca_certificate.pem failed"
+    docker exec NC update-ca-certificates \
+        || die "docker: update-ca-certificates failed"
+
     # execute the script that sets up Nextcloud
+    H2 "docker: run remote-nextcloud-use-miab.sh"
     docker cp setup/mods.available/remote-nextcloud-use-miab.sh NC:/tmp \
         || die "docker: cp remote-nextcloud-use-miab.sh failed"
     docker exec NC /tmp/remote-nextcloud-use-miab.sh \
@@ -142,12 +154,9 @@ install_nextcloud_docker() {
 
 
 
-
 do_upgrade() {
-    local populate_name="$1"
-
     # initialize test system
-    init
+    init "$@"
 
     # we install w/o remote nextcloud first so we can add
     # a user w/contacts and ensure the contact exists in the
@@ -155,20 +164,21 @@ do_upgrade() {
     disable_miab_mod "remote-nextcloud"
 
     # install w/o remote Nextcloud
-    miab_ldap_install
-    
-    # populate some data
-    [ ! -z "$populate_name" ] && populate_by_name "$populate_name"
-    
-    # install Nextcloud in a Docker container (MiaB must be available)
+    miab_ldap_install "$@"
+        
+    # install Nextcloud in a Docker container. exports NC_HOST_SRC_IP.
     install_nextcloud_docker
     
-    H1 "Enable remote-nextcloud mod"
+    H1 "Enable the remote-nextcloud mod"
     enable_miab_mod "remote-nextcloud" \
         || die "Could not enable remote-nextcloud mod"
 
-    # re-run setup to use the remote Nextcloud
+    # re-run setup (miab_ldap_install) to use the remote Nextcloud
     miab_ldap_install
+
+    # connect the remote Nextcloud to miab
+    H1 "Connect Nextcloud to MiaB-LDAP (configure user_ldap)"
+    connect_nextcloud_to_miab
 }
 
 
@@ -176,15 +186,20 @@ do_default() {
     # initialize test system
     init
 
+    # install Nextcloud in a Docker container. exports NC_HOST_SRC_IP.
+    export PRIVATE_IP=$(source setup/functions.sh; get_default_privateip 4)
+    install_nextcloud_docker
+
     H1 "Enable remote-nextcloud mod"
     enable_miab_mod "remote-nextcloud" \
         || die "Could not enable remote-nextcloud mod"
     
     # run setup to use the remote Nextcloud (doesn't need to be available)
-    miab_ldap_install
+    miab_ldap_install "$@"
 
-    # install Nextcloud in a Docker container (MiaB must be available)
-    install_nextcloud_docker    
+    # connect the remote Nextcloud to miab
+    H1 "Connect Nextcloud to MiaB-LDAP (configure user_ldap)"
+    connect_nextcloud_to_miab
 }
 
 
diff --git a/tests/system-setup/setup-defaults.sh b/tests/system-setup/setup-defaults.sh
index fabdc0c3..3d5d0e71 100755
--- a/tests/system-setup/setup-defaults.sh
+++ b/tests/system-setup/setup-defaults.sh
@@ -42,6 +42,7 @@ export NC_PROTO=${NC_PROTO:-http}
 export NC_HOST=${NC_HOST:-127.0.0.1}
 export NC_PORT=${NC_PORT:-8000}
 export NC_PREFIX=${NC_PREFIX:-/}
+export NC_HOST_SRC_IP="${NC_HOST_SRC_IP:-}"
 
 # For setup scripts that may be installing a remote Nextcloud
 export NC_ADMIN_USER="${NC_ADMIN_USER:-admin}"
diff --git a/tests/vagrant/Vagrantfile b/tests/vagrant/Vagrantfile
index be4f10fa..b8a02e29 100644
--- a/tests/vagrant/Vagrantfile
+++ b/tests/vagrant/Vagrantfile
@@ -16,7 +16,7 @@ export FEATURE_MUNIN=false
 export EHDD_KEYFILE=$HOME/keyfile
 echo -n "boo" >$EHDD_KEYFILE
 tests/system-setup/remote-nextcloud-docker.sh || exit 1
-tests/runner.sh ehdd default remote-nextcloud || exit 2
+tests/runner.sh ehdd remote-nextcloud default || exit 2
 SH
     end
   end
@@ -28,8 +28,8 @@ SH
 cd /mailinabox
 export PRIMARY_HOSTNAME=qa2.abc.com
 export FEATURE_MUNIN=false
-tests/system-setup/remote-nextcloud-docker.sh upgrade basic || exit 1
-tests/runner.sh default remote-nextcloud upgrade-basic || exit 2
+tests/system-setup/remote-nextcloud-docker.sh upgrade --populate=basic || exit 1
+tests/runner.sh remote-nextcloud upgrade-basic default || exit 2
 SH
   end