Skip to content

Commit

Permalink
Merge pull request #25 from NethServer/LDAP
Browse files Browse the repository at this point in the history
Add LDAP authentication
  • Loading branch information
stephdl authored Mar 20, 2024
2 parents 689ab89 + c92ae1e commit ca6c12e
Show file tree
Hide file tree
Showing 14 changed files with 334 additions and 56 deletions.
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

0 comments on commit ca6c12e

Please sign in to comment.