Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
148f3afa84 | ||
|
|
8dc358ded5 | ||
|
|
326895dffd | ||
|
|
6b0742634c | ||
|
|
45564fa83e | ||
|
|
162951d6dd | ||
|
|
d3c54dd9bc | ||
|
|
512f07eb25 | ||
|
|
8beb4b6055 | ||
|
|
f1808995c3 | ||
|
|
fce53742db | ||
|
|
9ba26eebef | ||
|
|
c4b53401de | ||
|
|
9ac8e00d85 | ||
|
|
dd670713e6 | ||
|
|
cd57216148 | ||
|
|
301a4aae0f | ||
|
|
b258a87323 | ||
|
|
593fecbd65 |
+59
-16
@@ -366,26 +366,65 @@ root@example:/etc/ldap/certs# export LDAPTLS_CACERT=/etc/ldap/certs/ca-cert.pem
|
||||
root@example:/etc/ldap/certs# echo 'export LDAPTLS_CACERT=/etc/ldap/certs/ca-cert.pem' >> ~/.bashrc
|
||||
root@example:/etc/ldap/certs# source ~/.bashrc
|
||||
```
|
||||
\
|
||||
Check STARTTLS
|
||||
```
|
||||
root@example:/etc/ldap/certs# ldapwhoami -x -ZZ -H ldap://${LDAP_HOST}
|
||||
```
|
||||
Check SSL/ldaps
|
||||
```
|
||||
root@example:/etc/ldap/certs# ldapwhoami -x -H ldaps://${LDAP_HOST}
|
||||
```
|
||||
Both should return Anonymous.
|
||||
## _6- Connect to OpenLDAP server via StartTLS/SSL_
|
||||
|
||||
Another example to try STARTTLS/ldap it is working:
|
||||
Vital checks of different levels to test **openLDAP's StartTLS and SSL**:\
|
||||
1.Check StartTLS and SSL, both should output "anonymous"
|
||||
```
|
||||
openssl s_client -connect ${LDAP_HOST}:389 -starttls ldap -servername ${LDAP_HOST}
|
||||
root@example:/# ldapwhoami -x -ZZ -H ldap://${LDAP_HOST}
|
||||
anonymous
|
||||
root@example:/# ldapwhoami -x -H ldaps://${LDAP_HOST}
|
||||
anonymous
|
||||
```
|
||||
SSL/ldaps
|
||||
\
|
||||
2.Check direct connection via openssl to confirm certificates are working properly:
|
||||
```
|
||||
openssl s_client -connect ${LDAP_HOST}:636 -servername ${LDAP_HOST}
|
||||
root@example:/# openssl s_client -connect ${LDAP_HOST}:389 -starttls ldap -servername ${LDAP_HOST} #StartTLS
|
||||
CONNECTED(00000003)
|
||||
depth=1 CN = Example Company CA
|
||||
verify return:1
|
||||
depth=0 O = Example Company, CN = example.com
|
||||
verify return:1
|
||||
...
|
||||
SSL handshake has read 2977 bytes and written 424 bytes
|
||||
Verification: OK
|
||||
---
|
||||
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
|
||||
Server public key is 2048 bit
|
||||
Secure Renegotiation IS NOT supported
|
||||
Compression: NONE
|
||||
Expansion: NONE
|
||||
No ALPN negotiated
|
||||
Early data was not sent
|
||||
Verify return code: 0 (ok)
|
||||
root@example:/# openssl s_client -connect ${LDAP_HOST}:636 -servername ${LDAP_HOST} #SSL
|
||||
CONNECTED(00000003)
|
||||
depth=1 CN = Example Company CA
|
||||
verify return:1
|
||||
depth=0 O = Example Company, CN = example.com
|
||||
verify return:1
|
||||
...
|
||||
SSL handshake has read 2963 bytes and written 393 bytes
|
||||
Verification: OK
|
||||
---
|
||||
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
|
||||
Server public key is 2048 bit
|
||||
Secure Renegotiation IS NOT supported
|
||||
Compression: NONE
|
||||
Expansion: NONE
|
||||
No ALPN negotiated
|
||||
Early data was not sent
|
||||
Verify return code: 0 (ok)
|
||||
```
|
||||
The output of both of these commands should be similar. Also, both will show the openLDAP's server CN (example.com in this case). You can terminate the connection with Ctrl+C.
|
||||
|
||||
3.A very important check is to make sure connections as users from the OpenLDAP's tree other than admin works:
|
||||
```
|
||||
root@example:/# ldapwhoami -x -D "uid=marisa,ou=People,dc=example,dc=com" -w MarisaNewPass2025 -H ldap://127.0.0.1 #StartTLS
|
||||
dn:uid=marisa,ou=People,dc=example,dc=com
|
||||
root@example:/# ldapwhoami -x -D "uid=marisa,ou=People,dc=example,dc=com" -w MarisaNewPass2025 -H ldap://127.0.0.1 #SSL
|
||||
dn:uid=marisa,ou=People,dc=example,dc=com
|
||||
```
|
||||
Both will show the connection to the openLDAP server showing the CN(dc=example,dc=com)
|
||||
|
||||
To connect to the server via `STARTTLS`, use port 389, to connect to the server via `SSL`, use port 636, both auth method Simple.
|
||||
If asked, accept the certificate as with any certificate, or copy the CA file that resides inside ldapdock from out of the container to our host system certificate trust directory (/usr/local/share/ca-certificates/ works for any Debian based distribution):
|
||||
@@ -395,5 +434,9 @@ If asked, accept the certificate as with any certificate, or copy the CA file th
|
||||
> sudo update-ca-certificates
|
||||
```
|
||||
In both cases, providing -h ${LDAP_HOST}, by default the login "user" and password are:\
|
||||
BIND DN=cn=admin,dc=example,dc=com\
|
||||
As admin:
|
||||
BIND DN="cn=admin,dc=example,dc=com"\
|
||||
BIND password=admin
|
||||
As marisa:
|
||||
BIND DN="uid=marisa,ou=People,dc=example,dc=com"\
|
||||
BIND password=MarisaNewPass2025
|
||||
@@ -0,0 +1,29 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
ldap:
|
||||
# Tells Compose to build the image from the Dockerfile in the current directory
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
- LDAP_HOST=example.com
|
||||
image: ldapdock
|
||||
container_name: ldapdock
|
||||
hostname: example.com
|
||||
stdin_open: true # Equivalent of -i
|
||||
tty: true # Equivalent of -t
|
||||
ports:
|
||||
- "389:389"
|
||||
- "636:636"
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ldap_data:/var/lib/ldap
|
||||
- ldap_config:/etc/ldap/slapd.d
|
||||
- ldap_certs:/etc/ldap/certs
|
||||
- ./hosts-certs:/export-certs
|
||||
|
||||
volumes:
|
||||
ldap_data:
|
||||
ldap_config:
|
||||
ldap_certs:
|
||||
+39
-7
@@ -1,15 +1,40 @@
|
||||
FROM ubuntu:22.04
|
||||
FROM debian:12
|
||||
|
||||
# set container hostname
|
||||
# set container hostname and DN in case we don't set it on the docker build/run command
|
||||
ARG LDAP_HOST=example.com
|
||||
ENV LDAP_HOST=${LDAP_HOST}
|
||||
|
||||
# set non-interactive TERM for docker
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# install slapd, ldap-utils, and packages needed for ldapdock to work
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
slapd ldap-utils gnutls-bin ssl-cert ca-certificates schema2ldif vim mc && apt-get clean
|
||||
#──────────────────────────────────────────────────────────────
|
||||
# Install ALL necessary packages in a single run for minimal image size
|
||||
#──────────────────────────────────────────────────────────────
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends gnupg lsb-release ca-certificates apt-transport-https software-properties-common wget curl \
|
||||
# Add the Ondřej Surý PHP repository (modern Debian way)
|
||||
&& wget -qO- https://packages.sury.org/php/apt.gpg | gpg --dearmor -o /usr/share/keyrings/sury-php-archive-keyring.gpg \
|
||||
&& echo "deb [signed-by=/usr/share/keyrings/sury-php-archive-keyring.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list \
|
||||
# Update package lists again to include the new PHP repo
|
||||
&& apt-get update \
|
||||
# Install all required packages, using PHP 8.1 from the Surý repo
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
apt-utils \
|
||||
slapd ldap-utils gnutls-bin ssl-cert schema2ldif vim mc \
|
||||
apache2 \
|
||||
php8.1 libapache2-mod-php8.1 \
|
||||
php8.1-ldap php8.1-mbstring php8.1-xml php8.1-curl php8.1-intl \
|
||||
# Clean up APT caches to keep the image small
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Enable required Apache modules
|
||||
RUN a2enmod rewrite headers ssl
|
||||
|
||||
# Use mpm_prefork (required for PHP)
|
||||
RUN a2dismod mpm_event && a2enmod mpm_prefork
|
||||
|
||||
# Clean up default Apache site
|
||||
RUN rm -rf /var/www/html/* && \
|
||||
echo "<?php phpinfo(); ?>" > /var/www/html/info.php
|
||||
|
||||
# preconfigure slapd installation without using systemd
|
||||
RUN echo "slapd slapd/password1 password admin" | debconf-set-selections && \
|
||||
@@ -17,6 +42,7 @@ RUN echo "slapd slapd/password1 password admin" | debconf-set-selections && \
|
||||
echo "slapd slapd/domain string example.com" | debconf-set-selections && \
|
||||
echo "slapd slapd/no_configuration boolean false" | debconf-set-selections && \
|
||||
echo "slapd slapd/purge_database boolean true" | debconf-set-selections && \
|
||||
echo "slapd slapd/ldapi_tls boolean false" | debconf-set-selections && \
|
||||
echo "slapd slapd/move_old_database boolean true" | debconf-set-selections
|
||||
|
||||
# make use of debconf-set-selections
|
||||
@@ -26,21 +52,27 @@ RUN dpkg-reconfigure -f noninteractive slapd
|
||||
COPY entrypoint.sh ./entrypoint.sh
|
||||
RUN chmod +x ./entrypoint.sh
|
||||
|
||||
# open up LDAP simple port
|
||||
# open up LDAP StartTLS and SSL ports, and Apache ports
|
||||
EXPOSE 389
|
||||
EXPOSE 636
|
||||
EXPOSE 80
|
||||
EXPOSE 443
|
||||
|
||||
#──────────────────────────────────────────────────────────────
|
||||
# Create directory for exporting certs to host
|
||||
RUN mkdir -p /export-certs
|
||||
#──────────────────────────────────────────────────────────────
|
||||
|
||||
# set salvable volumes for LDAP data, configuration, certs
|
||||
VOLUME ["/var/lib/ldap", "/etc/ldap/slapd.d", "/etc/ldap/certs","/export-certs"]
|
||||
|
||||
# set correct permissions for openldap user
|
||||
RUN chown -R openldap:openldap /var/lib/ldap /etc/ldap/slapd.d
|
||||
#RUN chown -R openldap:openldap /var/lib/ldap /etc/ldap/slapd.d
|
||||
|
||||
#──────────────────────────────────────────────────────────────
|
||||
# ENTRYPOINT ensures this sh file ALWAYS runs first before any CMD or command line instruction
|
||||
ENTRYPOINT ["./entrypoint.sh"]
|
||||
#──────────────────────────────────────────────────────────────
|
||||
|
||||
# CMD provides the default command (/bin/bash) which is passed as an argument to the ENTRYPOINT script
|
||||
CMD ["/bin/bash"]
|
||||
|
||||
+275
-92
@@ -1,32 +1,65 @@
|
||||
#!/bin/bash
|
||||
# this script runs INSIDE the container
|
||||
# set -e # exit on any error?
|
||||
#set -euo pipefail
|
||||
|
||||
# Fix permissions
|
||||
chown -R openldap:openldap /var/lib/ldap /etc/ldap/slapd.d /etc/ldap/certs 2>/dev/null || true
|
||||
chmod -R 777 /export-certs 2>/dev/null || true
|
||||
|
||||
#──────────────────────────────────────────────────────────────
|
||||
# Correct base DN and hostname
|
||||
export LDAP_HOST="${LDAP_HOST:-$(hostname)}"
|
||||
export LDAP_BASE_DN=$(echo "$LDAP_HOST" | sed 's/\.\([^.]*\)/,dc=\1/g; s/^/dc=/')
|
||||
echo "--> Using LDAP base DN: ${LDAP_BASE_DN}"
|
||||
#──────────────────────────────────────────────────────────────
|
||||
|
||||
echo "--> Starting ldapdock 0.10"
|
||||
echo "--> Launching slapd (temp)..."
|
||||
|
||||
# start slapd temporarily for setup
|
||||
/usr/sbin/slapd -h "ldap:/// ldapi:///" -g openldap -u openldap &
|
||||
sleep 3
|
||||
# Temporarily "relax" strict security on start to configure stuff
|
||||
if [ -d "/etc/ldap/slapd.d" ] && ls /etc/ldap/slapd.d/* >/dev/null 2>&1; then
|
||||
echo "--> Temporarily relaxing security for init"
|
||||
slapd -h "ldap:/// ldapi:///" -u openldap -g openldap &
|
||||
sleep 6
|
||||
ldapmodify -Y EXTERNAL -H ldapi:/// >/dev/null 2>&1 <<EOF || true
|
||||
dn: cn=config
|
||||
changetype: modify
|
||||
delete: olcLocalSSF
|
||||
-
|
||||
delete: olcSecurity
|
||||
-
|
||||
EOF
|
||||
pkill slapd || true
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
# populate with user & group
|
||||
echo "--> Populating directory with users and groups..."
|
||||
cat > /tmp/add_content.ldif << EOF
|
||||
dn: ou=People,dc=${LDAP_HOST}
|
||||
# Start temporary slapd for Users and Groups addition
|
||||
echo "--> Starting temporary slapd"
|
||||
slapd -h "ldap:/// ldapi:///" -u openldap -g openldap &
|
||||
SLAPD_PID=$!
|
||||
sleep 8
|
||||
|
||||
# Full tree with root and users entries
|
||||
echo "--> Creating base.ldif with root and user entries"
|
||||
cat > /tmp/base.ldif <<EOF
|
||||
dn: ${LDAP_BASE_DN}
|
||||
objectClass: top
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
o: Example Company
|
||||
|
||||
dn: ou=People,${LDAP_BASE_DN}
|
||||
objectClass: organizationalUnit
|
||||
ou: People
|
||||
|
||||
dn: ou=Groups,dc=${LDAP_HOST}
|
||||
dn: ou=Groups,${LDAP_BASE_DN}
|
||||
objectClass: organizationalUnit
|
||||
ou: Groups
|
||||
|
||||
dn: cn=mages,ou=Groups,dc=${LDAP_HOST}
|
||||
dn: cn=mages,ou=Groups,${LDAP_BASE_DN}
|
||||
objectClass: posixGroup
|
||||
cn: mages
|
||||
gidNumber: 5000
|
||||
memberUid: marisa
|
||||
|
||||
dn: uid=marisa,ou=People,dc=${LDAP_HOST}
|
||||
dn: uid=marisa,ou=People,${LDAP_BASE_DN}
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
@@ -38,64 +71,111 @@ displayName: Marisa Kirisame
|
||||
uidNumber: 10000
|
||||
gidNumber: 5000
|
||||
userPassword: {CRYPT}x
|
||||
gecos: Marisa Kirisame
|
||||
loginShell: /bin/bash
|
||||
homeDirectory: /home/marisa
|
||||
EOF
|
||||
sleep 2
|
||||
|
||||
# add the structure — ignore "already exists" errors only here
|
||||
echo "--> Adding base structure..."
|
||||
ldapadd -x -D "cn=admin,dc=${LDAP_HOST}" -w admin -f /tmp/add_content.ldif || \
|
||||
echo "--> Some entries already exist — continuing (this is normal)"
|
||||
# setting up user marisa of group People password
|
||||
ldappasswd -x -D "cn=admin,dc=${LDAP_HOST}" -w admin -s qwerty "uid=marisa,ou=People,dc=${LDAP_HOST}"
|
||||
sleep 2
|
||||
|
||||
# load and enable policies module
|
||||
|
||||
echo "--> Loading policies module..."
|
||||
cat > modify_ppolicy_module.ldif << EOF
|
||||
dn: cn=module{0},cn=config
|
||||
changetype: modify
|
||||
add: olcModuleLoad
|
||||
olcModuleLoad: ppolicy.so
|
||||
gecos: Marisa Kirisame
|
||||
EOF
|
||||
|
||||
ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f modify_ppolicy_module.ldif
|
||||
# Create phplogin.php with dynamic base DN
|
||||
echo "--> Creating phplogin.php with full users support"
|
||||
cat > /var/www/html/phplogin.php <<'EOF'
|
||||
<?php
|
||||
// Use the same logic as entrypoint.sh, but with better localhost handling
|
||||
$raw_host = $_SERVER['HTTP_HOST'] ?? 'example.com';
|
||||
$raw_host = preg_replace('/:\d+$/', '', $raw_host); // strip port if present
|
||||
|
||||
# restarting slapd to load ppolicy.so
|
||||
if ($raw_host === 'localhost' || $raw_host === '127.0.0.1') {
|
||||
// When testing locally via http://localhost → assume default example.com
|
||||
$base_dn = 'dc=example,dc=com';
|
||||
} else {
|
||||
// Normal case: build dc=... from real hostname
|
||||
$host_parts = explode('.', $raw_host);
|
||||
$base_dn = '';
|
||||
foreach ($host_parts as $part) {
|
||||
if ($part) $base_dn .= ($base_dn ? ',' : '') . 'dc=' . $part;
|
||||
}
|
||||
if (!$base_dn) $base_dn = 'dc=example,dc=com'; // ultimate fallback
|
||||
}
|
||||
|
||||
slapd -h "ldap:/// ldapi:/// ldaps:///" -u openldap -g openldap &
|
||||
sleep 3
|
||||
$msg = '';
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
cat > enable_ppolicy.ldif << EOF
|
||||
dn: olcOverlay=ppolicy,olcDatabase={1}mdb,cn=config
|
||||
changetype: add
|
||||
objectClass: olcOverlayConfig
|
||||
objectClass: olcPPolicyConfig
|
||||
olcOverlay: ppolicy
|
||||
if ($username && $password) {
|
||||
$ldap = ldap_connect("ldap://127.0.0.1:389");
|
||||
if ($ldap) {
|
||||
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
|
||||
|
||||
if (ldap_start_tls($ldap)) {
|
||||
// First: try admin bind (no ou=People)
|
||||
$admin_dn = "cn=admin,{$base_dn}";
|
||||
if (@ldap_bind($ldap, $admin_dn, $password)) {
|
||||
$msg = "<p style='color:green;font-weight:bold'>Login successful! Welcome <strong>admin</strong> (full privileges)</p>";
|
||||
}
|
||||
// Second: if not admin, try regular user
|
||||
elseif (@ldap_bind($ldap, "uid={$username},ou=People,{$base_dn}", $password)) {
|
||||
$msg = "<p style='color:green;font-weight:bold'>Login successful! Welcome {$username}</p>";
|
||||
}
|
||||
else {
|
||||
$msg = "<p style='color:red'>Invalid credentials</p>";
|
||||
}
|
||||
} else {
|
||||
$msg = "<p style='color:red'>StartTLS failed</p>";
|
||||
}
|
||||
ldap_close($ldap);
|
||||
} else {
|
||||
$msg = "<p style='color:red'>Could not connect to LDAP server</p>";
|
||||
}
|
||||
} else {
|
||||
$msg = "<p style='color:red'>Please fill both fields</p>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>ldapdock LDAP login</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; max-width: 400px; margin: 100px auto; text-align: center; }
|
||||
input, button { padding: 10px; margin: 5px; width: 100%; font-size: 16px; box-sizing: border-box; }
|
||||
button { background: #007cba; color: white; border: none; cursor: pointer; }
|
||||
.note { font-size: 0.9em; color: #666; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>ldapdock login</h1>
|
||||
<p>Server base DN: <strong><?= htmlspecialchars($base_dn) ?></strong></p>
|
||||
<?= $msg ?>
|
||||
<form method="post">
|
||||
<input type="text" name="username" placeholder="Username (marisa or admin)" required autofocus>
|
||||
<input type="password" name="password" placeholder="Password" required>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
<hr>
|
||||
<div class="note">
|
||||
<strong>Test accounts:</strong><br>
|
||||
Regular user: <code>marisa</code> / password: <code>MarisaNewPass2025</code><br>
|
||||
Admin user: <code>admin</code> / password: <code>admin</code>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
#olcPPolicyDefault: cn=default,ou=policies,dc=${LDAP_HOST}
|
||||
|
||||
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f enable_ppolicy.ldif
|
||||
ADMIN_DN="cn=admin,${LDAP_BASE_DN}"
|
||||
ADMIN_PW="admin"
|
||||
|
||||
# display schemas loaded by default
|
||||
echo "--> Schemas loaded by default..."
|
||||
ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=schema,cn=config dn
|
||||
echo "--> Adding base structure"
|
||||
ldapadd -c -x -D "$ADMIN_DN" -w "$ADMIN_PW" -f /tmp/base.ldif || true
|
||||
|
||||
# kill temp slapd
|
||||
pkill slapd
|
||||
sleep 3
|
||||
|
||||
# === CERTIFICATES: ONLY IF NOT ALREADY EXPORTED ===
|
||||
#──────────────────────────────────────────────────────────────
|
||||
# TLS BLOCK
|
||||
#──────────────────────────────────────────────────────────────
|
||||
if [ ! -f "/export-certs/mycacert.crt" ]; then
|
||||
echo "--> No CA found in /export-certs → generating certificates..."
|
||||
|
||||
echo "--> No CA found → generating certificates..."
|
||||
mkdir -p /etc/ldap/certs
|
||||
cd /etc/ldap/certs
|
||||
|
||||
# CA
|
||||
certtool --generate-privkey --bits 4096 --outfile ca-key.pem
|
||||
cat > ca.info <<EOF
|
||||
cn = Example Company CA
|
||||
@@ -104,8 +184,6 @@ cert_signing_key
|
||||
expiration_days = 3650
|
||||
EOF
|
||||
certtool --generate-self-signed --load-privkey ca-key.pem --template ca.info --outfile ca-cert.pem
|
||||
|
||||
# server
|
||||
certtool --generate-privkey --bits 2048 --outfile ldap01_slapd_key.pem
|
||||
cat > ldap01.info <<EOF
|
||||
organization = Example Company
|
||||
@@ -121,21 +199,14 @@ EOF
|
||||
--load-ca-privkey ca-key.pem \
|
||||
--template ldap01.info \
|
||||
--outfile ldap01_slapd_cert.pem
|
||||
|
||||
# permissions
|
||||
chgrp openldap ldap01_slapd_key.pem
|
||||
chmod 640 ldap01_slapd_key.pem
|
||||
|
||||
# bundle
|
||||
cat ldap01_slapd_cert.pem ca-cert.pem > ldap01_slapd_cert_full.pem
|
||||
chown root:openldap ldap01_slapd_cert_full.pem
|
||||
chmod 640 ldap01_slapd_cert_full.pem
|
||||
|
||||
# start temp slapd to apply config
|
||||
echo "--> Starting second temporary slapd to apply TLS config"
|
||||
slapd -h "ldap:/// ldapi:///" -u openldap -g openldap &
|
||||
sleep 3
|
||||
|
||||
# apply TLS config
|
||||
sleep 4
|
||||
cat > /tmp/certinfo.ldif <<EOF
|
||||
dn: cn=config
|
||||
changetype: modify
|
||||
@@ -149,41 +220,153 @@ replace: olcTLSCertificateKeyFile
|
||||
olcTLSCertificateKeyFile: /etc/ldap/certs/ldap01_slapd_key.pem
|
||||
EOF
|
||||
ldapmodify -Y EXTERNAL -H ldapi:/// -f /tmp/certinfo.ldif
|
||||
|
||||
# trust locally
|
||||
cp /etc/ldap/certs/ca-cert.pem /usr/local/share/ca-certificates/mycacert.crt
|
||||
update-ca-certificates
|
||||
|
||||
# kill temp
|
||||
pkill slapd
|
||||
pkill slapd || true
|
||||
sleep 2
|
||||
|
||||
# === EXPORT TO HOST (always, since volume is mounted) ===
|
||||
echo "--> Exporting CA to /export-certs..."
|
||||
echo "--> Exporting certificates to host volume..."
|
||||
cp /etc/ldap/certs/ca-cert.pem /export-certs/mycacert.crt
|
||||
cp /etc/ldap/certs/ldap01_slapd_cert_full.pem /export-certs/server-cert.pem
|
||||
echo "--> Certificate READY at ./hosts-certs/mycacert.crt on host"
|
||||
else
|
||||
echo "--> CA already exists at /export-certs/mycacert.crt → skipping generation"
|
||||
echo "--> Certificates already exist — skipping generation and using existing ones"
|
||||
fi
|
||||
|
||||
# === FINAL SLAPD START ===
|
||||
echo "--> Starting final slapd with LDAPS..."
|
||||
export LDAPTLS_REQCERT=allow
|
||||
|
||||
# ←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
|
||||
# NEW: Save and restore the LDIF — no changes to TLS block
|
||||
if [ ! -f "/export-certs/certinfo.ldif" ]; then
|
||||
echo "--> Saving TLS config LDIF for future restarts"
|
||||
cp /tmp/certinfo.ldif /export-certs/certinfo.ldif
|
||||
fi
|
||||
|
||||
if [ -f "/export-certs/certinfo.ldif" ]; then
|
||||
echo "--> Restoring TLS config LDIF from persistent volume"
|
||||
cp /export-certs/certinfo.ldif /tmp/certinfo.ldif
|
||||
fi
|
||||
# ←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
|
||||
|
||||
# Set Marisa password (full LDIF — so ldapmodify knows what to modify)
|
||||
echo "--> Setting Marisa password to 'MarisaNewPass2025' using Admin Bind"
|
||||
slappasswd -h '{SSHA}' -s MarisaNewPass2025 | \
|
||||
ldapmodify -x -D "$ADMIN_DN" -w "$ADMIN_PW" <<EOF >/dev/null 2>&1
|
||||
dn: uid=marisa,ou=People,${LDAP_BASE_DN}
|
||||
changetype: modify
|
||||
replace: userPassword
|
||||
userPassword: $(< /dev/stdin)
|
||||
EOF
|
||||
|
||||
# Kill temporary slapd
|
||||
kill $SLAPD_PID 2>/dev/null || true
|
||||
wait $SLAPD_PID 2>/dev/null || true
|
||||
|
||||
# Kill any stray slapd that might be holding ports
|
||||
pkill -9 slapd 2>/dev/null || true
|
||||
sleep 2
|
||||
|
||||
# Start final OpenLDAP
|
||||
echo "--> Starting final OpenLDAP (background)"
|
||||
slapd -h "ldap:/// ldaps:/// ldapi:///" -u openldap -g openldap -d 0 &
|
||||
sleep 3
|
||||
SLAPD_PID=$!
|
||||
sleep 8
|
||||
|
||||
# === ENABLE TLS FOR ALL CLIENT TOOLS INSIDE CONTAINER ===
|
||||
export LDAPTLS_CACERT=/etc/ldap/certs/ca-cert.pem
|
||||
echo "LDAPTLS_CACERT=$LDAPTLS_CACERT (all ldap* commands now work with TLS)"
|
||||
echo 'export LDAPTLS_CACERT=/etc/ldap/certs/ca-cert.pem' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
# Apply TLS config to final slapd
|
||||
echo "--> Applying TLS config to final slapd"
|
||||
ldapmodify -Y EXTERNAL -H ldapi:/// -f /tmp/certinfo.ldif
|
||||
|
||||
echo "--> ldapdock framework ready."
|
||||
# Restart slapd to load the new TLS config (required for OpenLDAP)
|
||||
echo "--> Restarting slapd to load TLS config"
|
||||
kill $SLAPD_PID 2>/dev/null || true
|
||||
wait $SLAPD_PID 2>/dev/null || true
|
||||
slapd -h "ldap:/// ldaps:/// ldapi:///" -u openldap -g openldap -d 0 &
|
||||
SLAPD_PID=$!
|
||||
sleep 8
|
||||
|
||||
# === KEEP CONTAINER ALIVE AND CONTINUE ===
|
||||
# Make the container trust its own CA — every time
|
||||
cp /etc/ldap/certs/ca-cert.pem /usr/local/share/ca-certificates/mycacert.crt 2>/dev/null || true
|
||||
update-ca-certificates --fresh >/dev/null 2>&1 || true
|
||||
|
||||
# 'exec' replaces the script process with the command (e.g., /bin/bash),
|
||||
# ensuring the container stays alive as long as that command runs interactively.
|
||||
echo "Executing: $@"
|
||||
# Start Apache inside APACHE_PID variable in background
|
||||
echo "--> Starting Apache + PHP (background)"
|
||||
/usr/sbin/apache2ctl -D FOREGROUND &
|
||||
APACHE_PID=$!
|
||||
sleep 5
|
||||
|
||||
# HTTPS setup — using the real LDAP certificates
|
||||
echo "--> Configuring Apache for HTTPS with real certificates"
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive # Silence a2ensite prompts
|
||||
|
||||
APACHE_CERT_FILE="/etc/ldap/certs/ldap01_slapd_cert_full.pem"
|
||||
APACHE_KEY_FILE="/etc/ldap/certs/ldap01_slapd_key.pem"
|
||||
|
||||
# Enable the site silently
|
||||
a2ensite default-ssl.conf >/dev/null 2>&1
|
||||
|
||||
# Replace the snakeoil certs with your real ones
|
||||
sed -i -E "s|^\s*SSLCertificateFile\s+.*|SSLCertificateFile ${APACHE_CERT_FILE}|g" \
|
||||
/etc/apache2/sites-available/default-ssl.conf
|
||||
sed -i -E "s|^\s*SSLCertificateKeyFile\s+.*|SSLCertificateKeyFile ${APACHE_KEY_FILE}|g" \
|
||||
/etc/apache2/sites-available/default-ssl.conf
|
||||
|
||||
# Reload Apache gracefully (updates config without killing)
|
||||
apache2ctl graceful >/dev/null 2>&1
|
||||
sleep 5
|
||||
|
||||
# ──────────────────────────────
|
||||
# phpLDAPadmin — auto-installed, no rebuild, works forever
|
||||
# ──────────────────────────────
|
||||
echo "--> Installing phpLDAPadmin"
|
||||
|
||||
# Only install once — use a flag file
|
||||
if [ ! -f "/var/www/html/phpldapadmin-installed" ]; then
|
||||
cd /var/www/html
|
||||
|
||||
# Download and extract (direct tarball, no git needed)
|
||||
wget -q -O phpldapadmin.tgz \
|
||||
https://github.com/leenooks/phpLDAPadmin/archive/refs/tags/1.2.6.7.tar.gz
|
||||
tar xzf phpldapadmin.tgz
|
||||
mv phpLDAPadmin-1.2.6.7 phpldapadmin
|
||||
rm phpldapadmin.tgz
|
||||
|
||||
# Copy config and apply minimal working settings
|
||||
cp phpldapadmin/config/config.php.example phpldapadmin/config/config.php
|
||||
|
||||
cat > phpldapadmin/config/config.php <<EOF
|
||||
<?php
|
||||
\$servers = new Datastore();
|
||||
|
||||
\$servers->newServer('ldap_pla');
|
||||
\$servers->setValue('server','name','Local OpenLDAP');
|
||||
\$servers->setValue('server','host','127.0.0.1');
|
||||
\$servers->setValue('server','port',389);
|
||||
\$servers->setValue('server','base',array('${LDAP_BASE_DN}'));
|
||||
\$servers->setValue('server','tls',true);
|
||||
\$servers->setValue('login','auth_type','session');
|
||||
\$servers->setValue('login','bind_id','cn=admin,${LDAP_BASE_DN}');
|
||||
\$servers->setValue('login','bind_pass','admin');
|
||||
?>
|
||||
EOF
|
||||
|
||||
# Mark as installed
|
||||
touch /var/www/html/phpldapadmin-installed
|
||||
|
||||
echo "--> phpLDAPadmin installed → https://localhost/phpldapadmin"
|
||||
else
|
||||
echo "--> phpLDAPadmin already installed"
|
||||
fi
|
||||
|
||||
# Victory message
|
||||
echo "--> ldapdock ready — OpenLDAP + Apache + PHP running"
|
||||
echo " → LDAP: 389/636"
|
||||
echo " → PHPinfo: https://localhost/info.php"
|
||||
echo " → PHPlogin test: https://localhost/phplogin.php"
|
||||
echo " → Shell: /bin/bash"
|
||||
echo " → Exit with CTRL+D or 'exit' command"
|
||||
|
||||
# THIS IS THE MAGIC LINE THAT KILLS CHILD PROCESSES ON EXIT
|
||||
trap 'echo "Stopping services..."; kill $SLAPD_PID $APACHE_PID 2>/dev/null; wait' SIGINT SIGTERM
|
||||
|
||||
# Give you your interactive shell — forever
|
||||
exec "$@"
|
||||
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
$host = $_SERVER['HTTP_HOST']; // works for example.com or any LDAP_HOST
|
||||
$msg = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$uid = trim($_POST['uid'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if ($uid && $password) {
|
||||
$ldap = ldap_connect("ldap://127.0.0.1:389");
|
||||
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
|
||||
|
||||
// StartTLS – required because your OpenLDAP enforces it
|
||||
if (ldap_start_tls($ldap)) {
|
||||
$bind_dn = "uid=$uid,ou=People,dc=$host";
|
||||
if (@ldap_bind($ldap, $bind_dn, $password)) {
|
||||
$msg = "<p style='color:green;font-weight:bold'>Login successful! Welcome $uid</p>";
|
||||
} else {
|
||||
$msg = "<p style='color:red'>Invalid credentials</p>";
|
||||
}
|
||||
} else {
|
||||
$msg = "<p style='color:red'>TLS failure</p>";
|
||||
}
|
||||
ldap_close($ldap);
|
||||
} else {
|
||||
$msg = "<p style='color:red'>Please fill both fields</p>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>ldapdock – LDAP login</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; max-width: 400px; margin: 100px auto; text-align: center; }
|
||||
input, button { padding: 10px; margin: 5px; width: 100%; font-size: 16px; }
|
||||
button { background: #007cba; color: white; cursor: pointer; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>ldapdock login</h1>
|
||||
<p>Server: <strong><?= htmlspecialchars($host) ?></strong></p>
|
||||
<?= $msg ?>
|
||||
<form method="post">
|
||||
<input type="text" name="uid" placeholder="uid (e.g. marisa)" required autofocus><br>
|
||||
<input type="password" name="password" placeholder="password" required><br>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
<hr>
|
||||
<small>Test user: marisa / qwerty</small>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
$host = $_SERVER['HTTP_HOST']; // works for example.com or any LDAP_HOST
|
||||
$msg = '';
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$uid = trim($_POST['uid'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if ($uid && $password) {
|
||||
// DIRECT LDAPS CONNECTION — NO STARTTLS NEEDED
|
||||
$ldap = ldap_connect("ldaps://127.0.0.1:636");
|
||||
// Allow self-signed cert for ldaps://
|
||||
putenv('LDAPTLS_REQCERT=allow');
|
||||
if (!$ldap) {
|
||||
$msg = "<p style='color:red'>Cannot connect to LDAP server</p>";
|
||||
} else {
|
||||
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
|
||||
|
||||
$bind_dn = "uid=$uid,ou=People,dc=$host";
|
||||
if (@ldap_bind($ldap, $bind_dn, $password)) {
|
||||
$msg = "<p style='color:green;font-weight:bold'>Login successful! Welcome $uid 🎉</p>";
|
||||
} else {
|
||||
$msg = "<p style='color:red'>Invalid credentials</p>";
|
||||
}
|
||||
ldap_close($ldap);
|
||||
}
|
||||
} else {
|
||||
$msg = "<p style='color:red'>uid=$uid,ou=People,dc=$host Please fill both fields</p>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>ldapdock – LDAP login</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; max-width: 400px; margin: 100px auto; text-align: center; }
|
||||
input, button { padding: 10px; margin: 5px; width: 100%; font-size: 16px; }
|
||||
button { background: #007cba; color: white; cursor: pointer; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>ldapdock login</h1>
|
||||
<p>Server: <strong><?= htmlspecialchars($host) ?></strong></p>
|
||||
<?= $msg ?>
|
||||
<form method="post">
|
||||
<input type="text" name="uid" placeholder="uid (e.g. marisa)" required autofocus><br>
|
||||
<input type="password" name="password" placeholder="password" required><br>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
<hr>
|
||||
<small>Test user: marisa / q*****</small>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 238 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 366 KiB |
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
$host = $_SERVER['HTTP_HOST']; // works for example.com or any LDAP_HOST
|
||||
$msg = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$uid = trim($_POST['uid'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if ($uid && $password) {
|
||||
$ldap = ldap_connect("ldap://127.0.0.1:389");
|
||||
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
|
||||
|
||||
// StartTLS required because your OpenLDAP enforces it
|
||||
if (ldap_start_tls($ldap)) {
|
||||
$bind_dn = "$uid,dc=example,dc=com";
|
||||
if (@ldap_bind($ldap, $bind_dn, $password)) {
|
||||
$msg = "<p style='color:green;font-weight:bold'>Login successful! Welcome $uid</p>";
|
||||
} else {
|
||||
$msg = "<p style='color:red'>Invalid credentials</p>$bind_dn";
|
||||
}
|
||||
} else {
|
||||
$msg = "<p style='color:red'>TLS failure</p>";
|
||||
}
|
||||
ldap_close($ldap);
|
||||
} else {
|
||||
$msg = "<p style='color:red'>Please fill both fields</p>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>ldapdock LDAP login</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; max-width: 400px; margin: 100px auto; text-align: center; }
|
||||
input, button { padding: 10px; margin: 5px; width: 100%; font-size: 16px; }
|
||||
button { background: #007cba; color: white; cursor: pointer; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>ldapdock login</h1>
|
||||
<p>Server: <strong><?= htmlspecialchars($host) ?></strong></p>
|
||||
<?= $msg ?>
|
||||
<form method="post">
|
||||
<input type="text" name="uid" placeholder="uid (e.g. marisa)" required autofocus><br>
|
||||
<input type="password" name="password" placeholder="password" required><br>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
<hr>
|
||||
<small>Test user: uid=marisa,ou=People / MarisaNewPass2025</small>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user