From 3ce17c4c6b28372a2645030751df2c162153362d Mon Sep 17 00:00:00 2001 From: Stephane de Labrusse Date: Wed, 20 Nov 2024 14:19:08 +0100 Subject: [PATCH 01/10] feat (migrate) : use dynamic env USER_DOMAIN --- .../apps/account-provider/ldap/migrate | 17 ++++++++++++++--- .../apps/nethserver-ejabberd/migrate | 2 +- .../apps/nethserver-nextcloud/migrate | 2 +- .../apps/nethserver-sogo/migrate | 2 +- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/root/usr/share/nethesis/nethserver-ns8-migration/apps/account-provider/ldap/migrate b/root/usr/share/nethesis/nethserver-ns8-migration/apps/account-provider/ldap/migrate index e2edbf64..5d247f65 100755 --- a/root/usr/share/nethesis/nethserver-ns8-migration/apps/account-provider/ldap/migrate +++ b/root/usr/share/nethesis/nethserver-ns8-migration/apps/account-provider/ldap/migrate @@ -22,6 +22,13 @@ set -e +fqdn=${USER_DOMAIN:?} +ldap_suffix=dc=$(echo "$fqdn" | sed 's/\./,dc=/g') +# Extract the hostname (part before the first dot) +host=${USER_DOMAIN%%.*} +# Extract the domain (part after the first dot) +domain=${USER_DOMAIN#*.} + ldapservice_password=$(< /var/lib/nethserver/secrets/ldapservice) ( umask 077 @@ -32,8 +39,8 @@ ldapservice_password=$(< /var/lib/nethserver/secrets/ldapservice) cat - >import.env < Date: Wed, 20 Nov 2024 14:22:49 +0100 Subject: [PATCH 02/10] feat(ns8-join): add dynamic baseDN for openldap --- root/usr/sbin/ns8-join | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/root/usr/sbin/ns8-join b/root/usr/sbin/ns8-join index c444a297..20351caa 100755 --- a/root/usr/sbin/ns8-join +++ b/root/usr/sbin/ns8-join @@ -75,11 +75,15 @@ def call(api_endpoint, action, token, data, tlsverify): return None +# default value, must be unique per cluster +ldap_user_domain = 'defaultLdap-' + subprocess.check_output(['/usr/bin/hostname', '-f'], encoding='utf-8').strip() parser = argparse.ArgumentParser() parser.add_argument('host') parser.add_argument('username', default="admin") parser.add_argument('password', default="Nethesis,1234") +# user domain used to rename the directory.nh to another baseDN +parser.add_argument('user_domain', default=ldap_user_domain) parser.add_argument('--no-tlsverify', dest='tlsverify', action='store_false', default=True) args = parser.parse_args() @@ -248,8 +252,8 @@ if account_provider_config['isAD'] == '1': subprocess.run(['/usr/sbin/ns8-leave']) sys.exit(1) elif account_provider_config['isLdap'] == '1' and '127.0.0.1' in account_provider_config['LdapURI']: - # Configure OpenLDAP as account provider of an external user domain: - account_provider_domain = "directory.nh" + # Configure OpenLDAP as account provider of an external user domain: (retrieve the baseDN from the UI, directory.nh is obsoleted) + account_provider_domain = args.user_domain.lower() account_provider_external = "" add_external_domain_request = { "domain": account_provider_domain, From 717e2e8805872b74b59f540afc30b2093ece56aa Mon Sep 17 00:00:00 2001 From: Stephane de Labrusse Date: Wed, 20 Nov 2024 14:23:04 +0100 Subject: [PATCH 03/10] feat(connection): add LdapUserDomain support for ns8-join and validation --- api/connection/read | 18 ++++++++++++++++-- api/connection/update | 6 ++++-- api/connection/validate | 26 ++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/api/connection/read b/api/connection/read index 19393869..0293455a 100755 --- a/api/connection/read +++ b/api/connection/read @@ -23,16 +23,30 @@ import sys import subprocess import simplejson - +import os def get_config(): + props = {} + # ns8 config bash_command = "/sbin/e-smith/config getjson ns8" process = subprocess.Popen(bash_command.split(), stdout=subprocess.PIPE) output, error = process.communicate() ns8_config = simplejson.loads(output) + props.update({"ns8": ns8_config}) + + # slapd config + if os.path.isfile('/etc/e-smith/db/configuration/defaults/slapd/type'): + bash_command = "/sbin/e-smith/config getjson slapd" + process = subprocess.Popen(bash_command.split(), stdout=subprocess.PIPE) + output, error = process.communicate() + slapd_config = simplejson.loads(output) + props.update({"slapd": slapd_config}) + else : + props.update({"slapd": {}}) + + return props - return {"ns8": ns8_config} try: diff --git a/api/connection/update b/api/connection/update index f27df033..9bd0a5e9 100755 --- a/api/connection/update +++ b/api/connection/update @@ -30,6 +30,8 @@ host=$(echo $data | jq -r '.Host') user=$(echo $data | jq -r '.User') password=$(echo $data | jq -r '.Password') tls_verify=$(echo $data | jq -r '.TLSVerify') +# user domain used to rename the directory.nh to another baseDN +ldap_user_domain=$(echo $data | jq -r '.LdapUserDomain') if [[ "$action" == "login" ]]; then # execute ns8-join @@ -38,9 +40,9 @@ if [[ "$action" == "login" ]]; then trap 'rm -f $tmp_output' EXIT echo "=========== Join cluster" $(date -R) >>/var/log/ns8-migration.log if [ "$tls_verify" = "disabled" ]; then - /usr/sbin/ns8-join --no-tlsverify "$host" "$user" "$password" &>"${tmp_output}" + /usr/sbin/ns8-join --no-tlsverify "$host" "$user" "$password" "$ldap_user_domain" &>"${tmp_output}" else - /usr/sbin/ns8-join "$host" "$user" "$password" &>"${tmp_output}" + /usr/sbin/ns8-join "$host" "$user" "$password" "$ldap_user_domain" &>"${tmp_output}" fi if [ "$?" -gt 0 ]; then diff --git a/api/connection/validate b/api/connection/validate index c62742f5..58140963 100755 --- a/api/connection/validate +++ b/api/connection/validate @@ -22,11 +22,25 @@ import sys import simplejson +import re def invalid_attribute(parameter, error): return {"parameter": parameter, "error": error, "value": ""} +def is_valid_fqdn(domain): + # Regex breakdown: + # - ^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+ # Domain labels + # - [a-zA-Z0-9]{2,63}$ # TLD with 2-63 characters + fqdn_pattern = r'^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z0-9]{2,63}$' + + # Check overall domain length + if not domain or len(domain) > 255: + return False + + # Validate using regex + return re.match(fqdn_pattern, domain) is not None + input_json = simplejson.load(sys.stdin) invalid_attributes = [] @@ -36,11 +50,13 @@ host_p = 'Host' user_p = 'User' password_p = 'Password' tls_verify_p = 'TLSVerify' +ldap_user_domain_p = 'LdapUserDomain' host = '' user = '' password = '' tls_verify = '' +ldap_user_domain = '' # action @@ -76,6 +92,16 @@ else: if tls_verify not in ['enabled', 'disabled']: invalid_attributes.append(invalid_attribute(tls_verify_p, "invalid")) +# ldap user domain +if (ldap_user_domain_p not in input_json) or (not input_json[ldap_user_domain_p]): + invalid_attributes.append(invalid_attribute(ldap_user_domain_p, "empty")) +else: + ldap_user_domain = input_json[ldap_user_domain_p] + + # check if the domain is a valid domain + if not is_valid_fqdn(ldap_user_domain): + invalid_attributes.append(invalid_attribute(ldap_user_domain_p, "invalid")) + # output success = len(invalid_attributes) == 0 From 54973270e20db43c9c12a84598412264994c4506 Mon Sep 17 00:00:00 2001 From: Stephane de Labrusse Date: Wed, 20 Nov 2024 14:48:50 +0100 Subject: [PATCH 04/10] feat(dashboard): add ldapUserDomain input and validation for local LDAP configuration --- ui/src/views/Dashboard.vue | 55 +++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/ui/src/views/Dashboard.vue b/ui/src/views/Dashboard.vue index 2b7d52db..99499cee 100644 --- a/ui/src/views/Dashboard.vue +++ b/ui/src/views/Dashboard.vue @@ -140,6 +140,35 @@ /> + +
+
+ {{ + $t("dashboard.ldap_user_domain_description") + }} +
+
+
+
+ +
+ + {{ + $t("validation.ldap_user_domain_" + error.ldapUserDomain) + }} +
+
+