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

Add LDAP authentication #25

Merged
merged 24 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b015d17
Update org.nethserver.authorizations label in build-images.sh
stephdl Mar 15, 2024
8fd4e1d
Add discover-ldap script for LDAP service configuration
stephdl Mar 15, 2024
616e76d
Add wait-after-boot script for Dokuwiki container initialization
stephdl Mar 15, 2024
e5a93e7
Add script to write LDAP configuration file
stephdl Mar 15, 2024
201a82d
Update dokuwiki.service with additional environment files and commands
stephdl Mar 15, 2024
2956b71
Add LDAP domain configuration
stephdl Mar 15, 2024
a6df6f8
Add LDAP domain list to configuration
stephdl Mar 15, 2024
0a4de47
Add LDAP domain selection for user authentication
stephdl Mar 15, 2024
e73b388
Update @nethserver/ns8-ui-lib version to 0.1.32
stephdl Mar 15, 2024
c764b47
Refactor Settings.vue to conditionally render LDAP domain input
stephdl Mar 15, 2024
661c045
Merge branch 'main' into LDAP
stephdl Mar 15, 2024
92615fa
Update Dokuwiki configuration options in translation.json
stephdl Mar 15, 2024
14a0aba
Refactor Settings.vue component to disable input fields based on load…
stephdl Mar 15, 2024
4c36ee4
Add script to configure LDAP and restart DokuWiki service
stephdl Mar 18, 2024
b366435
Add "no_user_domain" option to LDAP domain list
stephdl Mar 18, 2024
c8ce306
Fix LDAP domain value in Settings.vue
stephdl Mar 18, 2024
01867a6
Refactor ldap_domain_list initialization in Settings.vue
stephdl Mar 18, 2024
d34f7cd
Enable authad plugin and update ldap_domain_list in Settings.vue
stephdl Mar 18, 2024
cdd5397
Translation review of Andrea
stephdl Mar 20, 2024
088bb7e
code review of Andrea Leardini
stephdl Mar 20, 2024
f9783cd
Add LDAP domain binding in configure-module script and remove unneces…
stephdl Mar 20, 2024
e8b24ed
Merge branch 'LDAP' of https://github.com/NethServer/ns8-dokuwiki int…
stephdl Mar 20, 2024
2821f0f
Update LDAP authentication message
stephdl Mar 20, 2024
c92ae1e
Refactor Settings.vue component to improve loading and input handling
stephdl Mar 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build-images.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ buildah add "${container}" ui/dist /ui
buildah config --entrypoint=/ \
--label="org.nethserver.tcp-ports-demand=1" \
--label="org.nethserver.rootfull=0" \
--label="org.nethserver.authorizations=traefik@any:routeadm" \
--label="org.nethserver.authorizations=traefik@any:routeadm cluster:accountconsumer" \
--label="org.nethserver.images=docker.io/bitnami/dokuwiki:20240206-debian-12" \
"${container}"
# Commit everything
Expand Down
10 changes: 10 additions & 0 deletions imageroot/actions/configure-module/20configure
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ username = data.get("username","admin")
password = data.get("password","admin")
email = data.get("email", "[email protected]")
full_name = data.get("user_full_name","Administrator")
ldap_domain = data.get("ldap_domain", "")
# bind user to the domain
if ldap_domain:
agent.bind_user_domains([ldap_domain])
else:
agent.bind_user_domains([])


# Talk with agent using file descriptor.
# Setup configuration from user input.
Expand All @@ -53,6 +60,9 @@ agent.set_env("DOKUWIKI_FULL_NAME", full_name)
agent.set_env("PHP_ENABLE_OPCACHE", "1")
agent.set_env("PHP_MEMORY_LIMIT", "512M")

# Setup LDAP domain
agent.set_env("LDAP_DOMAIN", ldap_domain)

# Make sure everything is saved inside the environment file
# just before starting systemd unit
agent.dump_env()
7 changes: 6 additions & 1 deletion imageroot/actions/configure-module/validate-input.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"email",
"host",
"http2https",
"lets_encrypt"
"lets_encrypt",
"ldap_domain"
],
"properties": {
"wiki_name": {
Expand Down Expand Up @@ -61,6 +62,10 @@
"type": "boolean",
"title": "HTTP to HTTPS redirection",
"description": "Redirect all the HTTP requests to HTTPS"
},
"ldap_domain": {
"type": "string",
"description": "LDAP domain name"
}
}
}
12 changes: 12 additions & 0 deletions imageroot/actions/get-configuration/20read
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import os
import sys
import json
import agent
from agent.ldapproxy import Ldapproxy

# Prepare return variable
config = {}
Expand All @@ -43,6 +44,17 @@ config["user_full_name"] = rdb.hget(env, "DOKUWIKI_FULL_NAME");
config["host"] = rdb.hget(env, "TRAEFIK_HOST");
config["http2https"] = rdb.hget(env, "TRAEFIK_HTTP2HTTPS") == "True";
config["lets_encrypt"] = rdb.hget(env, "TRAEFIK_LETS_ENCRYPT") == "True";
# retrieve LDAP domains list
lp = Ldapproxy()
domains = []
for key in lp.get_domains_list():
domains.append({
"name": key,
"label": key,
"value": key,
})

config['ldap_domain_list'] = domains
config['ldap_domain'] = os.environ.get("LDAP_DOMAIN",'')
# Dump the configuratio to stdou
json.dump(config, fp=sys.stdout)
7 changes: 6 additions & 1 deletion imageroot/actions/get-configuration/validate-output.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"email",
"host",
"http2https",
"lets_encrypt"
"lets_encrypt",
"ldap_domain"
],
"properties": {
"wiki_name": {
Expand Down Expand Up @@ -59,6 +60,10 @@
"type": "boolean",
"title": "HTTP to HTTPS redirection",
"description": "Redirect all the HTTP requests to HTTPS"
},
"ldap_domain": {
"type": "string",
"description": "The LDAP domain name"
}
}
}
Expand Down
45 changes: 45 additions & 0 deletions imageroot/bin/discover-ldap
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env python3

#
# Copyright (C) 2024 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-3.0-or-later
#

#
# Find settings for LDAP service
#

import os
import agent
from agent.ldapproxy import Ldapproxy

udomname = os.environ.get('LDAP_DOMAIN','')

try:
odom = Ldapproxy().get_domain(udomname)
'host' in odom # Throw exception if odom is None
except:
# During restore the domain could be unavailable. Use a fallback
# configuration, pointing to nowhere, just to set the variables.
# Once the domain becomes available, the event will fix them.
odom = {
'host': '127.0.0.1',
'port': 20000,
'schema': 'rfc2307',
'location': 'internal',
'base_dn': 'dc=dokuwiki,dc=invalid',
'bind_dn': 'cn=example,dc=dokuwiki,dc=invalid',
'bind_password': 'invalid',
}

tmpfile = "discover.env." + str(os.getpid())

with open(tmpfile, "w") as denv:
print('LDAP_PORT=' + str(odom['port']), file=denv)
print('LDAP_USER=' + odom['bind_dn'], file=denv)
print('LDAP_HOST=' + odom['host'], file=denv)
print('LDAP_PASS=' + odom['bind_password'], file=denv)
print('LDAP_SCHEMA=' + odom['schema'], file=denv)
print('LDAP_BASE=' + odom['base_dn'], file=denv)

os.replace(tmpfile, "discovery_ldap.env")
22 changes: 22 additions & 0 deletions imageroot/bin/wait-after-boot
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash

#
# Copyright (C) 2024 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-3.0-or-later
#

# specific to dokuwiki, it creates its own configuration file after a long time boot

count=0
while [[ $count -lt 60 ]]; do
if podman exec -ti dokuwiki ls /bitnami/dokuwiki/conf/local.php.dist >/dev/null 2>&1; then
echo "First Configuration done. We push custom configuration files."
exit 0
fi
((count++))
echo "Waiting for dokuwiki container to be ready..."
sleep 1
done

echo "Dokuwiki container is not ready after 60s. Exiting..."
exit 1
78 changes: 78 additions & 0 deletions imageroot/bin/write-ldap-conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/bin/bash

#
# Copyright (C) 2024 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-3.0-or-later
#

# Retrieving environment variables
LDAP_DOMAIN=${LDAP_DOMAIN}
LDAP_PORT=${LDAP_PORT}
LDAP_USER=${LDAP_USER}
LDAP_HOST=${LDAP_HOST}
LDAP_PASS=${LDAP_PASS}
LDAP_SCHEMA=${LDAP_SCHEMA}
LDAP_BASE=${LDAP_BASE}

mkdir -vp dokuwiki-config
cat <<EOF > dokuwiki-config/local.protected.php
<?php
/**
* this is file is generated by the dokuwki container automatically
* do not edit it manually
*/
EOF
# Check the value of $LDAP_DOMAIN
if [[ "$LDAP_DOMAIN" == "" ]]; then
cat <<EOF >> dokuwiki-config/local.protected.php
\$conf['authtype'] = 'authplain';
EOF

elif [[ "$LDAP_DOMAIN" != "" ]]; then
if [[ "$LDAP_SCHEMA" == "rfc2307" ]]; then
cat <<EOF >> dokuwiki-config/local.protected.php
\$conf['authtype'] = 'authldap';
\$conf['plugin'][\$conf['authtype']]['server'] = "ldap://accountprovider:${LDAP_PORT}";
\$conf['plugin'][\$conf['authtype']]['version'] = '3';
\$conf['plugin'][\$conf['authtype']]['usertree'] = "ou=People,${LDAP_BASE}";
\$conf['plugin'][\$conf['authtype']]['grouptree'] = "ou=Groups,${LDAP_BASE}";
\$conf['plugin'][\$conf['authtype']]['userfilter'] = '(|(uid=%{user})(mail=%{user}))';
\$conf['plugin']['authldap']['groupfilter'] = '(memberUid=%{uid})';
\$conf['plugin'][\$conf['authtype']]['groupkey'] = 'cn';
\$conf['plugin']['authldap']['binddn'] = "${LDAP_USER}";
\$conf['plugin']['authldap']['bindpw'] = "${LDAP_PASS}";
\$conf['plugin']['authldap']['starttls'] = 0;
\$conf['plugin']['authldap']['modPass'] = 0;
EOF
elif [[ "$LDAP_SCHEMA" == "ad" ]]; then
cat <<EOF >> dokuwiki-config/local.protected.php
\$conf['authtype'] = 'authad';
\$conf['plugin']['authad']['account_suffix'] = '@${LDAP_DOMAIN}';
\$conf['plugin']['authad']['base_dn'] = '${LDAP_BASE}';
\$conf['plugin']['authad']['domain_controllers'] = 'ldap://accountprovider:${LDAP_PORT}'; //multiple can be given
\$conf['plugin']['authad']['use_tls'] = 0;
EOF

fi
fi
cat <<EOF >> dokuwiki-config/local.protected.php
\$conf['useacl'] = 1;
\$conf['superuser'] = 'admin,admin@${LDAP_DOMAIN},administrator,administrator@${LDAP_DOMAIN}';
EOF

echo "Configuration written to dokuwiki-config/local.protected.php"

cat <<EOF > dokuwiki-config/plugins.local.php
<?php
/*
* Local plugin enable/disable settings
*
* Auto-generated by install s
*/

\$plugins['authad'] = 1;
\$plugins['authldap'] = 1;
\$plugins['authmysql'] = 0;
\$plugins['authpgsql'] = 0;
EOF
echo "Configuration written to dokuwiki-config/plugins.local.php"
21 changes: 21 additions & 0 deletions imageroot/events/user-domain-changed/20configure-ldap
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env python3

#
# Copyright (C) 2024 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-3.0-or-later
#

import json
import sys
import agent
import os

event = json.load(sys.stdin)

if event.get('domain') != os.getenv('LDAP_DOMAIN'):
exit(0)

if 'node' in event and str(event['node']) != os.getenv('NODE_ID'):
exit(0) # ignore event if the source is not in our node

agent.run_helper('systemctl', '--user', '-T', 'try-restart', 'dokuwiki.service').check_returncode()
17 changes: 16 additions & 1 deletion imageroot/systemd/user/dokuwiki.service
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,27 @@ Environment=PODMAN_SYSTEMD_UNIT=%n
# - DOKUWIKI_IMAGE: the podman/docker image to rung
# - TCP_PORT, TCP_PORTS: the random ports assigned by system
EnvironmentFile=%S/state/environment
EnvironmentFile=-%S/state/discovery_ldap.env
WorkingDirectory=%S/state
Restart=always
ExecStartPre=/usr/local/bin/runagent discover-ldap
ExecStartPre=/usr/local/bin/runagent write-ldap-conf
ExecStartPre=/bin/rm -f %t/dokuwiki.pid %t/dokuwiki.ctr-id
# Podman should bind only to 127.0.0.1:TCP_PORT
# Data are persistend inside dokuwiki-data volume
ExecStart=/usr/bin/podman run --conmon-pidfile %t/dokuwiki.pid --cidfile %t/dokuwiki.ctr-id --cgroups=no-conmon --replace --name dokuwiki -d --env-file=%S/state/environment -p 127.0.0.1:${TCP_PORT}:8080 -v dokuwiki-data:/bitnami/dokuwiki:z ${DOKUWIKI_IMAGE}
ExecStart=/usr/bin/podman run --conmon-pidfile %t/dokuwiki.pid \
--cidfile %t/dokuwiki.ctr-id \
--cgroups=no-conmon \
--replace --name dokuwiki -d \
--env-file=%S/state/environment \
--publish 127.0.0.1:${TCP_PORT}:8080 \
--volume dokuwiki-data:/bitnami/dokuwiki:z \
--network=slirp4netns:allow_host_loopback=true \
--add-host=accountprovider:10.0.2.2 \
${DOKUWIKI_IMAGE}
ExecStartPost=/usr/local/bin/runagent wait-after-boot
ExecStartPost=podman cp ./dokuwiki-config/local.protected.php dokuwiki:/bitnami/dokuwiki/conf/local.protected.php
ExecStartPost=podman cp ./dokuwiki-config/plugins.local.php dokuwiki:/bitnami/dokuwiki/conf/plugins.local.php
ExecStop=/usr/bin/podman stop --ignore --cidfile %t/dokuwiki.ctr-id -t 10
ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/dokuwiki.ctr-id
PIDFile=%t/dokuwiki.pid
Expand Down
2 changes: 1 addition & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"dependencies": {
"@carbon/icons-vue": "^10.37.0",
"@carbon/vue": "^2.40.0",
"@nethserver/ns8-ui-lib": "^0.0.91",
"@nethserver/ns8-ui-lib": "^0.1.32",
"await-to-js": "^3.0.0",
"axios": "^0.21.2",
"carbon-components": "^10.41.0",
Expand Down
9 changes: 6 additions & 3 deletions ui/public/i18n/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,14 @@
"instance_configuration": "Configure {instance}",
"configuring": "Configuring...",
"dokuwiki_note": "Note",
"must_be_configured_inside_dokuwiki": "You can configure only Dokuwiki FQDN in this page. To configure other setting go to Dokuwiki webapp",
"must_be_configured_inside_dokuwiki": "You can configure only Dokuwiki FQDN and authentication in this page. To configure other setting go to Dokuwiki webapp",
"go_to_dokuwiki": "Go to Dokuwiki",
"email_format": "Invalid email address",
"host_pattern": "Must be a valid fully qualified domain name",
"host_format": "Must be a valid fully qualified domain name"
"ldap_domain": "LDAP domain",
"host_format": "Must be a valid fully qualified domain name",
"choose_the_ldap_domain_to_authenticate_users": "Choose the LDAP domain to authenticate users, or use the internal authentication",
"choose_ldap_domain": "Choose LDAP domain",
"internal_authentication": "Internal authentication"
},
"about": {
"title": "About"
Expand Down
Loading
Loading