Compare commits

..

5 Commits
0.1.1 ... main

Author SHA1 Message Date
c66df72e8d
fix: include TLS config in initial cn=config entry
Some checks failed
CI Pipeline / lint (push) Successful in 20s
CI Pipeline / build (push) Successful in 1m2s
CI Pipeline / test (push) Successful in 1m2s
CI Pipeline / security-scan (push) Successful in 1m32s
CI Pipeline / autotag (push) Successful in 25s
CI Pipeline / push (push) Successful in 22s
CI Pipeline / update-cd (push) Failing after 17s
slapadd doesn't understand LDIF change records (changetype: modify).
Move TLS configuration attributes directly into the cn=config entry
instead of using a separate modify operation.
2025-12-28 02:06:15 +01:00
9b15cc31cb
feat: trigger CD pipeline after image push
Some checks failed
CI Pipeline / lint (push) Successful in 17s
CI Pipeline / build (push) Successful in 40s
CI Pipeline / test (push) Successful in 1m5s
CI Pipeline / security-scan (push) Successful in 1m22s
CI Pipeline / autotag (push) Successful in 17s
CI Pipeline / push (push) Successful in 19s
CI Pipeline / update-cd (push) Failing after 16s
Add stage 7 to trigger terraform-docker-openldap pipeline via Gitea API
after successfully pushing a new image to the registry. This enables
automatic deployment of new container versions.

Requires GITEA_TOKEN secret and GITEA_URL variable to be configured.
2025-12-28 01:12:13 +01:00
d5405f3bba
fix: handle first-time syncrepl configuration
All checks were successful
CI Pipeline / lint (push) Successful in 28s
CI Pipeline / build (push) Successful in 1m12s
CI Pipeline / test (push) Successful in 1m8s
CI Pipeline / security-scan (push) Successful in 1m38s
CI Pipeline / autotag (push) Successful in 24s
CI Pipeline / push (push) Successful in 21s
CI Pipeline / update-cd (push) Successful in 15s
Check if olcSyncRepl and olcMirrorMode attributes exist before
deciding whether to use 'add' or 'replace' operation. Previously
the script always used 'replace' which fails on first-time setup
when the attributes don't exist yet.
2025-12-28 01:00:33 +01:00
11bead045f
fix: add missing openldap-overlay-syncprov package
All checks were successful
CI Pipeline / push (push) Successful in 22s
CI Pipeline / update-cd (push) Successful in 14s
CI Pipeline / lint (push) Successful in 21s
CI Pipeline / build (push) Successful in 1m13s
CI Pipeline / test (push) Successful in 57s
CI Pipeline / security-scan (push) Successful in 1m20s
CI Pipeline / autotag (push) Successful in 22s
The syncprov.so module was being loaded in init-config.sh but the
required Alpine package was not installed, causing slapadd to fail
with "file not found" error.
2025-12-26 04:15:33 +01:00
1b24516663
feat: add multi-master replication support
Some checks failed
CI Pipeline / lint (push) Successful in 20s
CI Pipeline / build (push) Successful in 56s
CI Pipeline / test (push) Failing after 1m32s
CI Pipeline / security-scan (push) Successful in 1m21s
CI Pipeline / autotag (push) Has been skipped
CI Pipeline / push (push) Has been skipped
CI Pipeline / update-cd (push) Has been skipped
- Add syncprov module to init-config.sh
- Create init-replication.sh for configuring N-way multi-master
- Update entrypoint to handle replication configuration
- Support LDAP_REPLICATION_ENABLED, LDAP_SERVER_ID, LDAP_REPLICATION_HOSTS
- Replica servers can sync DIT from existing masters
2025-12-26 03:57:31 +01:00
5 changed files with 250 additions and 59 deletions

View File

@ -273,23 +273,36 @@ jobs:
if: always()
run: docker logout ${{ env.REGISTRY }} || true
# Stage 7: Update CD pipeline (trigger deployment)
# Stage 7: Trigger CD pipeline for terraform-docker-openldap
update-cd:
runs-on: ubuntu-latest
needs: push
if: needs.push.result == 'success'
steps:
- name: Trigger CD pipeline
- name: Trigger terraform-docker-openldap pipeline
run: |
echo "=============================================="
echo " Ready to update CD pipeline"
echo " Triggering CD pipeline"
echo "=============================================="
echo "New version: ${{ needs.push.outputs.version }}"
echo "Full image: ${{ needs.push.outputs.full_image }}"
echo ""
echo "TODO: Add step to update version in CD repository"
echo "This could be:"
echo " - Update docker-compose.yml in infra repo"
echo " - Update Helm values"
echo " - Trigger ArgoCD sync"
# Trigger the Gitea Actions workflow via repository dispatch
curl -X POST \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
-H "Content-Type: application/json" \
"${{ vars.GITEA_URL }}/api/v1/repos/wbyc/terraform-docker-openldap/actions/workflows/pipeline.yaml/dispatches" \
-d '{
"ref": "main",
"inputs": {
"image_tag": "${{ needs.push.outputs.version }}"
}
}' || {
echo "::warning::Failed to trigger CD pipeline"
exit 1
}
echo "=============================================="
echo " CD pipeline triggered successfully"
echo "=============================================="

View File

@ -12,6 +12,7 @@ RUN apk add --no-cache \
openldap-overlay-refint \
openldap-overlay-unique \
openldap-overlay-ppolicy \
openldap-overlay-syncprov \
openssl \
argon2 \
&& mkdir -p /var/lib/openldap/openldap-data \

View File

@ -44,6 +44,12 @@ export LDAP_TLS_VERIFY_CLIENT="${LDAP_TLS_VERIFY_CLIENT:-try}"
export LDAP_LOG_LEVEL="${LDAP_LOG_LEVEL:-256}"
export LDAP_READONLY="${LDAP_READONLY:-false}"
# Replication settings
export LDAP_REPLICATION_ENABLED="${LDAP_REPLICATION_ENABLED:-false}"
export LDAP_SERVER_ID="${LDAP_SERVER_ID:-1}"
export LDAP_REPLICATION_HOSTS="${LDAP_REPLICATION_HOSTS:-}"
export LDAP_BOOTSTRAP_PRIMARY="${LDAP_BOOTSTRAP_PRIMARY:-false}"
log_info "OpenLDAP Container Starting"
log_info "Domain: $LDAP_DOMAIN"
log_info "Base DN: $LDAP_BASE_DN"
@ -53,36 +59,43 @@ log_info "Organisation: $LDAP_ORGANISATION"
if [ ! -f /var/lib/openldap/openldap-data/data.mdb ]; then
log_info "First run - initializing OpenLDAP..."
# Initialize cn=config
# Initialize cn=config (always needed)
/scripts/init-config.sh
# Load schemas in order
# Load schemas in order (always needed)
/scripts/init-schemas.sh
# Configure overlays
# Configure overlays (always needed)
/scripts/init-overlays.sh
# Create base DIT
/scripts/init-dit.sh
# Determine if we should initialize DIT or replicate from peer
# If replication is enabled and we're not the bootstrap primary, skip DIT init
if [ "$LDAP_REPLICATION_ENABLED" = "true" ] && [ "$LDAP_BOOTSTRAP_PRIMARY" != "true" ]; then
log_info "Replication enabled - this server will sync DIT from peers"
log_info "Skipping local DIT initialization..."
else
# Create base DIT
/scripts/init-dit.sh
# Configure ACLs
/scripts/init-acls.sh
# Configure ACLs
/scripts/init-acls.sh
# Create service accounts if requested
if [ "$LDAP_CREATE_SERVICE_ACCOUNTS" = "true" ]; then
/scripts/init-services.sh
fi
# Create service accounts if requested
if [ "$LDAP_CREATE_SERVICE_ACCOUNTS" = "true" ]; then
/scripts/init-services.sh
fi
# Process custom LDIF files if present
if [ -d /ldif/custom ] && [ "$(ls -A /ldif/custom 2>/dev/null)" ]; then
log_info "Processing custom LDIF files..."
for ldif in /ldif/custom/*.ldif; do
if [ -f "$ldif" ]; then
log_info "Loading: $ldif"
ldapadd -x -H "$LDAPI_SOCKET" -D "cn=admin,$LDAP_BASE_DN" -w "$LDAP_ADMIN_PASSWORD" -f "$ldif" || \
log_warn "Failed to load $ldif (may already exist)"
fi
done
# Process custom LDIF files if present
if [ -d /ldif/custom ] && [ "$(ls -A /ldif/custom 2>/dev/null)" ]; then
log_info "Processing custom LDIF files..."
for ldif in /ldif/custom/*.ldif; do
if [ -f "$ldif" ]; then
log_info "Loading: $ldif"
ldapadd -x -H "$LDAPI_SOCKET" -D "cn=admin,$LDAP_BASE_DN" -w "$LDAP_ADMIN_PASSWORD" -f "$ldif" || \
log_warn "Failed to load $ldif (may already exist)"
fi
done
fi
fi
log_info "Initialization complete."
@ -108,7 +121,38 @@ fi
log_info "Starting slapd with URLs: $SLAPD_URLS"
# Start slapd
# If replication is enabled, start slapd in background first to configure replication
if [ "$LDAP_REPLICATION_ENABLED" = "true" ]; then
log_info "Starting slapd in background for replication configuration..."
/usr/sbin/slapd -h "$SLAPD_URLS" \
-F /etc/openldap/slapd.d \
-u ldap -g ldap &
SLAPD_PID=$!
# Wait for slapd to be ready
log_info "Waiting for slapd to be ready..."
sleep 2
for i in $(seq 1 30); do
if ldapsearch -x -H ldap://localhost -b "" -s base "objectClass=*" >/dev/null 2>&1; then
log_info "slapd is ready"
break
fi
sleep 1
done
# Configure replication
/scripts/init-replication.sh
# Stop background slapd gracefully
log_info "Restarting slapd in foreground mode..."
kill $SLAPD_PID 2>/dev/null || true
wait $SLAPD_PID 2>/dev/null || true
sleep 1
fi
# Start slapd in foreground (final)
exec /usr/sbin/slapd -h "$SLAPD_URLS" \
-F /etc/openldap/slapd.d \
-u ldap -g ldap \

View File

@ -13,6 +13,20 @@ export LDAP_ADMIN_PASSWORD_HASH LDAP_CONFIG_PASSWORD_HASH
# Create initial slapd.d configuration
rm -rf /etc/openldap/slapd.d/*
# Build TLS attributes if enabled
TLS_CONFIG=""
if [ "$LDAP_TLS_ENABLED" = "true" ] && [ -f "$LDAP_TLS_CERT_FILE" ] && [ -f "$LDAP_TLS_KEY_FILE" ]; then
log_info "Adding TLS configuration..."
TLS_CONFIG="olcTLSCertificateFile: ${LDAP_TLS_CERT_FILE}
olcTLSCertificateKeyFile: ${LDAP_TLS_KEY_FILE}"
if [ -f "$LDAP_TLS_CA_FILE" ]; then
TLS_CONFIG="${TLS_CONFIG}
olcTLSCACertificateFile: ${LDAP_TLS_CA_FILE}"
fi
TLS_CONFIG="${TLS_CONFIG}
olcTLSVerifyClient: ${LDAP_TLS_VERIFY_CLIENT}"
fi
# Create base cn=config LDIF
cat > /tmp/init-config.ldif << EOF
dn: cn=config
@ -21,6 +35,7 @@ cn: config
olcArgsFile: /run/openldap/slapd.args
olcPidFile: /run/openldap/slapd.pid
olcLogLevel: ${LDAP_LOG_LEVEL}
${TLS_CONFIG}
dn: cn=module{0},cn=config
objectClass: olcModuleList
@ -31,6 +46,7 @@ olcModuleLoad: memberof.so
olcModuleLoad: refint.so
olcModuleLoad: unique.so
olcModuleLoad: ppolicy.so
olcModuleLoad: syncprov.so
dn: cn=schema,cn=config
objectClass: olcSchemaConfig
@ -67,35 +83,6 @@ olcDbIndex: entryUUID eq
olcDbMaxSize: 1073741824
EOF
# Add TLS configuration if enabled and certs exist
if [ "$LDAP_TLS_ENABLED" = "true" ] && [ -f "$LDAP_TLS_CERT_FILE" ] && [ -f "$LDAP_TLS_KEY_FILE" ]; then
log_info "Adding TLS configuration..."
cat >> /tmp/init-config.ldif << EOF
dn: cn=config
changetype: modify
add: olcTLSCertificateFile
olcTLSCertificateFile: ${LDAP_TLS_CERT_FILE}
-
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: ${LDAP_TLS_KEY_FILE}
EOF
if [ -f "$LDAP_TLS_CA_FILE" ]; then
cat >> /tmp/init-config.ldif << EOF
-
add: olcTLSCACertificateFile
olcTLSCACertificateFile: ${LDAP_TLS_CA_FILE}
EOF
fi
cat >> /tmp/init-config.ldif << EOF
-
add: olcTLSVerifyClient
olcTLSVerifyClient: ${LDAP_TLS_VERIFY_CLIENT}
EOF
fi
# Import the configuration
log_info "Importing cn=config with slapadd..."
/usr/sbin/slapadd -n 0 -F /etc/openldap/slapd.d -l /tmp/init-config.ldif

146
scripts/init-replication.sh Executable file
View File

@ -0,0 +1,146 @@
#!/bin/sh
set -e
. /scripts/utils.sh
# Skip if replication not enabled
if [ "$LDAP_REPLICATION_ENABLED" != "true" ]; then
log_info "Replication not enabled, skipping..."
exit 0
fi
# Validate required variables
if [ -z "$LDAP_SERVER_ID" ]; then
log_error "LDAP_SERVER_ID is required for replication"
exit 1
fi
if [ -z "$LDAP_REPLICATION_HOSTS" ]; then
log_error "LDAP_REPLICATION_HOSTS is required for replication"
exit 1
fi
# Use admin credentials for replication if not specified
LDAP_REPLICATION_DN="${LDAP_REPLICATION_DN:-cn=admin,$LDAP_BASE_DN}"
LDAP_REPLICATION_PASSWORD="${LDAP_REPLICATION_PASSWORD:-$LDAP_ADMIN_PASSWORD}"
log_info "Configuring multi-master replication..."
log_info "Server ID: $LDAP_SERVER_ID"
log_info "Replication hosts: $LDAP_REPLICATION_HOSTS"
# Wait for slapd to be ready
wait_for_slapd 30 "$LDAPI_SOCKET"
# Configure serverID in cn=config
log_info "Setting serverID..."
cat > /tmp/repl-serverid.ldif << EOF
dn: cn=config
changetype: modify
replace: olcServerID
olcServerID: ${LDAP_SERVER_ID}
EOF
ldapmodify -Y EXTERNAL -H "$LDAPI_SOCKET" -f /tmp/repl-serverid.ldif 2>/dev/null || \
log_warn "ServerID may already be set"
# Configure syncprov overlay on the database
log_info "Configuring syncprov overlay..."
cat > /tmp/repl-syncprov.ldif << EOF
dn: olcOverlay=syncprov,olcDatabase={1}mdb,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpCheckpoint: 100 10
olcSpSessionlog: 100
EOF
ldapmodify -Y EXTERNAL -H "$LDAPI_SOCKET" -f /tmp/repl-syncprov.ldif 2>/dev/null || \
log_warn "syncprov overlay may already exist"
# Build syncrepl configuration for each peer
# Format: LDAP_REPLICATION_HOSTS="ldap://server1.example.com,ldap://server2.example.com"
REPLICA_NUM=0
SYNCREPL_CONFIG=""
# Parse the comma-separated list of hosts
OLD_IFS="$IFS"
IFS=','
for host in $LDAP_REPLICATION_HOSTS; do
REPLICA_NUM=$((REPLICA_NUM + 1))
# Trim whitespace
host=$(echo "$host" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
log_info "Adding replication peer $REPLICA_NUM: $host"
SYNCREPL_CONFIG="${SYNCREPL_CONFIG}
olcSyncRepl: rid=$(printf '%03d' $REPLICA_NUM)
provider=${host}
bindmethod=simple
binddn=\"${LDAP_REPLICATION_DN}\"
credentials=${LDAP_REPLICATION_PASSWORD}
searchbase=\"${LDAP_BASE_DN}\"
scope=sub
schemachecking=on
type=refreshAndPersist
retry=\"60 +\"
timeout=1"
done
IFS="$OLD_IFS"
# Configure syncrepl and mirrormode on the database
log_info "Configuring syncrepl and mirrormode..."
# Check if olcSyncRepl attribute already exists
if ldapsearch -Y EXTERNAL -H "$LDAPI_SOCKET" -b "olcDatabase={1}mdb,cn=config" -s base "(olcSyncRepl=*)" olcSyncRepl 2>/dev/null | grep -q "olcSyncRepl:"; then
SYNCREPL_OP="replace"
log_info "Updating existing syncrepl configuration..."
else
SYNCREPL_OP="add"
log_info "Adding new syncrepl configuration..."
fi
# Check if olcMirrorMode attribute already exists
if ldapsearch -Y EXTERNAL -H "$LDAPI_SOCKET" -b "olcDatabase={1}mdb,cn=config" -s base "(olcMirrorMode=*)" olcMirrorMode 2>/dev/null | grep -q "olcMirrorMode:"; then
MIRRORMODE_OP="replace"
else
MIRRORMODE_OP="add"
fi
cat > /tmp/repl-syncrepl.ldif << EOF
dn: olcDatabase={1}mdb,cn=config
changetype: modify
${SYNCREPL_OP}: olcSyncRepl
${SYNCREPL_CONFIG}
-
${MIRRORMODE_OP}: olcMirrorMode
olcMirrorMode: TRUE
EOF
ldapmodify -Y EXTERNAL -H "$LDAPI_SOCKET" -f /tmp/repl-syncrepl.ldif || {
log_error "Failed to configure syncrepl"
cat /tmp/repl-syncrepl.ldif
exit 1
}
# Add additional indexes for replication
log_info "Adding replication indexes..."
cat > /tmp/repl-indexes.ldif << EOF
dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: entryCSN eq
-
add: olcDbIndex
olcDbIndex: entryUUID eq
EOF
ldapmodify -Y EXTERNAL -H "$LDAPI_SOCKET" -f /tmp/repl-indexes.ldif 2>/dev/null || \
log_warn "Indexes may already exist"
# Cleanup
rm -f /tmp/repl-*.ldif
log_info "Multi-master replication configured successfully"
log_info "This server (ID=$LDAP_SERVER_ID) will replicate with $REPLICA_NUM peer(s)"