✨ Adds MariaDB image with direct LDAP PAM authentication
- Introduced a new Containerfile to build a MariaDB image with direct LDAP PAM support. - Implemented a custom PAM module (`pam_ldap_direct.so`) built from the PADL source to avoid dependencies on `libpam-ldap` and `nslcd`. - Updated the entrypoint script to dynamically generate the PAM configuration file (`/etc/pam_ldap.conf`) based on environment variables. - Added a README.md to document the image usage, configuration, and build process. - Created a test suite to validate the image build and entrypoint behavior, ensuring proper handling of environment variables and configuration generation. - Configured health checks and ensured the stock MariaDB entrypoint and lifecycle are preserved.
This commit is contained in:
11 files changed
+519
-282
No files matched your search
@@ -1,26 +1,64 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
set -f
|
||||
|
||||
# Render pam_ldap's config from the environment so the AD bind credentials are
|
||||
# never baked into the image. pam_ldap performs a search+bind against AD: bind
|
||||
# as the service account, find the user by ${LDAP_LOGIN_ATTR}, then re-bind as
|
||||
# that DN with the supplied password to verify it — the same shape as pgpool's
|
||||
# `ldap` method (see pool_hba.conf).
|
||||
: "${LDAPURI:?LDAPURI is required (e.g. ldaps://hecate.ad.kritikos.io)}"
|
||||
: "${LDAP_BASE:?LDAP_BASE is required (e.g. DC=ad,DC=kritikos,DC=io)}"
|
||||
: "${LDAPBINDDN:?LDAPBINDDN is required (AD service/bind account)}"
|
||||
: "${LDAPPASSWORD:?LDAPPASSWORD is required}"
|
||||
: "${LDAP_LOGIN_ATTR:?LDAP_LOGIN_ATTR is required (e.g. sAMAccountName)}"
|
||||
# demand = validate the AD cert against the trust store; relax to 'allow' only
|
||||
# for testing, or add your internal CA to the image and keep 'demand'.
|
||||
: "${LDAP_TLS_REQCERT:?LDAP_TLS_REQCERT is required (demand|allow|never)}"
|
||||
: "${LDAP_TLS_REQCERT:?LDAP_TLS_REQCERT is required (demand or hard)}"
|
||||
: "${CERTS_GID:?CERTS_GID is required (GID owning the host TLS key)}"
|
||||
|
||||
fail() {
|
||||
printf 'ldap-entrypoint: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
for setting in LDAPURI LDAP_BASE LDAPBINDDN LDAPPASSWORD LDAP_LOGIN_ATTR LDAP_TLS_REQCERT CERTS_GID; do
|
||||
[[ ${!setting} != *$'\n'* && ${!setting} != *$'\r'* ]] \
|
||||
|| fail "$setting must be a single line"
|
||||
done
|
||||
|
||||
case "${LDAP_TLS_REQCERT,,}" in
|
||||
demand|hard) ;;
|
||||
*) fail "LDAP_TLS_REQCERT must be demand or hard; LDAP certificate verification cannot be disabled" ;;
|
||||
esac
|
||||
|
||||
[[ $LDAP_LOGIN_ATTR =~ ^[A-Za-z][A-Za-z0-9-]*$ ]] \
|
||||
|| fail "LDAP_LOGIN_ATTR must be an LDAP attribute name"
|
||||
[[ $CERTS_GID =~ ^[0-9]+$ ]] || fail "CERTS_GID must be numeric"
|
||||
|
||||
tls_mode=''
|
||||
for uri in $LDAPURI; do
|
||||
case "$uri" in
|
||||
ldaps://*) mode=on ;;
|
||||
ldap://*) mode=start_tls ;;
|
||||
*) fail "LDAPURI must use ldaps:// or ldap:// with StartTLS" ;;
|
||||
esac
|
||||
[[ -z $tls_mode || $tls_mode == "$mode" ]] \
|
||||
|| fail "LDAPURI cannot mix LDAPS and StartTLS endpoints"
|
||||
tls_mode=$mode
|
||||
done
|
||||
[[ -n $tls_mode ]] || fail "LDAPURI must contain an endpoint"
|
||||
|
||||
module=/usr/local/lib/security/pam_ldap_direct.so
|
||||
helper=/usr/lib/mysql/plugin/auth_pam_tool_dir/auth_pam_tool
|
||||
ca_file=/etc/ssl/certs/ca-certificates.crt
|
||||
[[ -r $module ]] || fail "direct LDAP PAM module is missing: $module"
|
||||
[[ -x $helper && -u $helper ]] || fail "MariaDB's setuid PAM helper is unavailable"
|
||||
[[ -r $ca_file ]] || fail "system CA bundle is missing: $ca_file"
|
||||
privileges=$(LC_ALL=C setpriv --dump) \
|
||||
|| fail "cannot inspect the PAM helper privilege constraints"
|
||||
no_new_privs=$(printf '%s\n' "$privileges" | sed -n 's/^no_new_privs:[[:space:]]*//p')
|
||||
case "$no_new_privs" in
|
||||
0) ;;
|
||||
1) fail "no-new-privileges prevents MariaDB's PAM helper from reading its root-only configuration" ;;
|
||||
*) fail "cannot determine the PAM helper privilege constraints" ;;
|
||||
esac
|
||||
|
||||
umask 077
|
||||
# pam_ldap.so on this image reads /etc/ldap.conf (the PADL default path), and
|
||||
# libpam-ldap ships a placeholder there (base dc=example,dc=net) that otherwise
|
||||
# shadows our config and makes every login fail. Render both paths.
|
||||
for conf in /etc/ldap.conf /etc/pam_ldap.conf; do
|
||||
cat > "$conf" <<EOF
|
||||
cat > /etc/pam_ldap.conf <<EOF
|
||||
uri ${LDAPURI}
|
||||
base ${LDAP_BASE}
|
||||
binddn ${LDAPBINDDN}
|
||||
@@ -29,19 +67,12 @@ scope sub
|
||||
referrals no
|
||||
pam_login_attribute ${LDAP_LOGIN_ATTR}
|
||||
pam_filter objectclass=User
|
||||
tls_cacertdir /etc/ssl/certs
|
||||
tls_reqcert ${LDAP_TLS_REQCERT}
|
||||
ssl ${tls_mode}
|
||||
tls_cacertfile ${ca_file}
|
||||
tls_checkpeer yes
|
||||
EOF
|
||||
chmod 600 "$conf"
|
||||
done
|
||||
chmod 600 /etc/pam_ldap.conf
|
||||
|
||||
# Grant mysql read access to the host TLS key (mode 0640, owned by GID
|
||||
# ${CERTS_GID}). compose exposes that GID via `group_add`, but the stock
|
||||
# entrypoint drops root -> mysql with gosu, which rebuilds mysql's groups from
|
||||
# /etc/group and discards the compose-added GID — mysql then can't read ssl_key
|
||||
# and MariaDB aborts with "Unable to get private key". While still root, put
|
||||
# mysql in a group with that GID so it survives the privilege drop.
|
||||
: "${CERTS_GID:?CERTS_GID is required (GID owning the host TLS key)}"
|
||||
if [ "$(id -u)" = '0' ]; then
|
||||
if getent group "${CERTS_GID}" >/dev/null; then
|
||||
certgroup="$(getent group "${CERTS_GID}" | cut -d: -f1)"
|
||||
|
||||
Reference in new issue
Block a user