Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a FQDN for LDAP domain and migration scripts instead of directory.nh #90

Merged
merged 10 commits into from
Nov 25, 2024
12 changes: 10 additions & 2 deletions api/connection/read
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,25 @@
import sys
import subprocess
import simplejson

import os

def get_config():
# 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 = {"ns8": ns8_config, "slapd": {"props":{"status": "disabled"}}}

return {"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["slapd"] = slapd_config

return props

try:
config = get_config()
Expand Down
6 changes: 4 additions & 2 deletions api/connection/update
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
26 changes: 26 additions & 0 deletions api/connection/validate
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand All @@ -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

Expand Down Expand Up @@ -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

Expand Down
9 changes: 7 additions & 2 deletions root/usr/sbin/ns8-join
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ 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="")
parser.add_argument('--no-tlsverify', dest='tlsverify', action='store_false', default=True)

args = parser.parse_args()
Expand Down Expand Up @@ -248,8 +250,11 @@ 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)
if not args.user_domain:
print("ns8-join: user_domain is required for OpenLDAP account provider", file=sys.stderr)
sys.exit(1)
account_provider_domain = args.user_domain.lower()
account_provider_external = ""
add_external_domain_request = {
"domain": account_provider_domain,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -32,8 +39,8 @@ ldapservice_password=$(< /var/lib/nethserver/secrets/ldapservice)
cat - >import.env <<EOF
LDAP_SVCUSER=ldapservice
LDAP_SVCPASS=${ldapservice_password}
LDAP_DOMAIN=directory.nh
LDAP_SUFFIX=dc=directory,dc=nh
LDAP_DOMAIN=${fqdn}
LDAP_SUFFIX=${ldap_suffix}
EOF

# Generate .ldif data dump
Expand All @@ -43,6 +50,10 @@ EOF
# NS8 apps require a displayName attribute is set. Copy gecos attribute
# value to displayName, assuming it is not already present.
sed -i '/^gecos: / { h ; s/^gecos:/displayName:/ ; G }' dump-mdb0.ldif
# replace dc=directory,dc=nh by ldap_suffix
sed -i "s/dc=directory,dc=nh/${ldap_suffix}/g" dump-mdb0.ldif
# replace dc: directory by dc: host
sed -i "s/^dc: directory/dc: $host/" dump-mdb0.ldif

# Send import.env
rsync -i import.env "${RSYNC_ENDPOINT:?}"/data/state/import.env
Expand All @@ -58,7 +69,7 @@ if [[ "${MIGRATE_ACTION}" != "finish" ]]; then
fi

# Remove temporary external user domain
ns8-action --attach cluster remove-external-domain "$(printf '{"domain":"%s"}' "directory.nh")" || :
ns8-action --attach cluster remove-external-domain "$(printf '{"domain":"%s"}' $fqdn)" || :

# Commit DC migration
rsync -v "${RSYNC_ENDPOINT}"/terminate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Host=$(/sbin/e-smith/config get DomainName)
# Search for Samba or LDAP domain
domain=$(/sbin/e-smith/config getprop sssd Realm | tr '[:upper:]' '[:lower:]')
if [ -z "$domain" ]; then
domain="directory.nh"
domain=${USER_DOMAIN:?}
fi

# we find admin users from jabberadmins group
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ ns8-action --attach wait "${IMPORT_TASK_ID}"
# Search for Samba or LDAP domain
domain=$(/sbin/e-smith/config getprop sssd Realm | tr '[:upper:]' '[:lower:]')
if [ -z "$domain" ]; then
domain="directory.nh"
domain=${USER_DOMAIN:?}
fi

if [ -z "${host}" ]; then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ ns8-action --attach wait "${IMPORT_TASK_ID}"
# Search for Samba or LDAP domain
domain=$(/sbin/e-smith/config getprop sssd Realm | tr '[:upper:]' '[:lower:]')
if [ -z "$domain" ]; then
domain="directory.nh"
domain=${USER_DOMAIN:?}
fi

# we find admin users
Expand Down
8 changes: 6 additions & 2 deletions ui/public/i18n/language.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@
"no_skip": "Enable migration",
"error_on_skip": "There was an error when changing the skip flag.",
"app_migrated_with_ad": "This app is migrated together with Local Active Directory app",
"enable_forge_sogo": "To successfully migrate SOGo, ensure NethForge repository is enabled under NS8 Settings"
"enable_forge_sogo": "To successfully migrate SOGo, ensure NethForge repository is enabled under NS8 Settings",
"ldap_user_domain_description": "This machine uses a local OpenLDAP account provider. You must choose a new name for the domain, something like ldap.domain.tld. The name must be unique inside the NethServer 8 cluster",
"ldap_user_domain": "LDAP user domain"
},
"validation": {
"leader_node_empty": "Leader node is required",
Expand All @@ -109,7 +111,9 @@
"virtual_host_empty": "Virtual host is required",
"virtualhost_cannot_be_the_same": "Virtual host cannot be the same",
"ad_ip_address_empty": "Active Directory IP address is required",
"admin_password_not_allowed":"The `|` character is not allowed"
"admin_password_not_allowed":"The `|` character is not allowed",
"ldap_user_domain_empty": "LDAP user domain is required",
"ldap_user_domain_invalid": "Invalid LDAP user domain"
},
"docs": {},
"are_you_sure": "Are you sure",
Expand Down
55 changes: 48 additions & 7 deletions ui/src/views/Dashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,35 @@
/>
</div>
</div>
<!-- ldap_userdomain -->
<div v-if="isLdapEnabled && accountProviderConfig.location == 'local' && accountProviderConfig.type == 'ldap'">
<div
class="page-description"
>
{{
$t("dashboard.ldap_user_domain_description")
}}
</div>
</div>
<div v-if="isLdapEnabled && accountProviderConfig.location == 'local' && accountProviderConfig.type == 'ldap'">
<div :class="['form-group', { 'has-error': error.ldapUserDomain }]">
<label class="col-sm-2 control-label" for="ldap-userdomain">{{
$t("dashboard.ldap_user_domain")
}}</label>
<div class="col-sm-5">
<input
type="text"
v-model="config.ldapUserDomain"
id="ldap-userdomain"
ref="ldapUserDomain"
class="form-control"
/>
<span v-if="error.ldapUserDomain" class="help-block">{{
$t("validation.ldap_user_domain_" + error.ldapUserDomain)
}}</span>
</div>
</div>
</div>
<!-- connect button -->
<div class="form-group">
<label class="col-sm-2 control-label">
Expand Down Expand Up @@ -1088,8 +1117,10 @@ export default {
tlsVerify: false,
leaderNode: "",
adminUsername: "",
adminPassword: ""
adminPassword: "",
ldapUserDomain: "",
},
isLdapEnabled: false,
installedApps: [],
apps: [],
currentApp: null,
Expand Down Expand Up @@ -1143,7 +1174,8 @@ export default {
ctiVirtualHost: "",
sogoVirtualHost: "",
webtopVirtualHost: "",
userDomains: ""
userDomains: "",
ldapUserDomain: "",
}
};
},
Expand Down Expand Up @@ -1201,7 +1233,9 @@ export default {
}
},
mounted() {
this.connectionRead();
// get connection info we need to retrieve the account provider info first
// to check if it's local or remote and ldap or ad
this.getAccountProviderInfo();
},
methods: {
togglePassword() {
Expand Down Expand Up @@ -1442,6 +1476,7 @@ export default {
},
connectionRead() {
const context = this;
context.config.ldapUserDomain = "";
context.loading.connectionRead = true;
nethserver.exec(
["nethserver-ns8-migration/connection/read"],
Expand All @@ -1463,15 +1498,16 @@ export default {
},
connectionReadSuccess(output) {
const ns8Config = output.configuration.ns8.props;
const slapd = output.configuration.slapd.props;
this.config.isConnected = ns8Config.Host != "";
this.config.leaderNode = ns8Config.Host;
this.config.adminUsername = ns8Config.User;
this.config.adminPassword = ns8Config.Password;
this.config.tlsVerify = ns8Config.TLSVerify == "enabled";
this.loading.connectionRead = false;

this.isLdapEnabled = slapd.status === "enabled";
if (this.config.isConnected) {
this.getAccountProviderInfo();
this.listApplications();
} else {
this.$nextTick(() => {
this.$refs.leaderNode.focus();
Expand All @@ -1482,6 +1518,7 @@ export default {
this.error.leaderNode = "";
this.error.adminUsername = "";
this.error.adminPassword = "";
this.error.ldapUserDomain = "";
this.error.leaderNode = "";
this.loading.connectionUpdate = true;
this.error.connectionUpdate = "";
Expand All @@ -1492,7 +1529,8 @@ export default {
Host: this.config.leaderNode,
User: this.config.adminUsername,
Password: this.config.adminPassword,
TLSVerify: this.config.tlsVerify ? "enabled" : "disabled"
TLSVerify: this.config.tlsVerify ? "enabled" : "disabled",
LdapUserDomain: this.isLdapEnabled ? this.config.ldapUserDomain : "ad.provider.is.used" // dummy value to validate, not used with ad
gsanchietti marked this conversation as resolved.
Show resolved Hide resolved
};

const context = this;
Expand Down Expand Up @@ -1530,6 +1568,9 @@ export default {
} else if (param === "Password") {
this.error.adminPassword = attr.error;
this.$refs.adminPassword.focus();
} else if (param === "LdapUserDomain") {
this.error.ldapUserDomain = attr.error;
this.$refs.ldapUserDomain.focus();
}
}
},
Expand Down Expand Up @@ -1856,7 +1897,7 @@ export default {
accountProviderConfig.location = location;
context.accountProviderConfig = accountProviderConfig;
context.loading.accountProviderInfo = false;
context.listApplications();
context.connectionRead();
},
function (error) {
const errorMessage = context.$i18n.t(
Expand Down
Loading