Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .github/workflows/build-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ jobs:
fetch-depth: 0
persist-credentials: false

- name: Test git-identity profile script
run: bash images/base/test-git-identity.sh

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

Expand Down
101 changes: 58 additions & 43 deletions images/base/git-identity.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,76 @@
# profile on first interactive login to a container. Subsequent logins skip
# this entirely once git config is set.
#
# The name is read from the NSS gecos field (mapped from LDAP cn via sssd.conf).
# The email is read from LDAP via an anonymous ldapsearch (REQUIRE_AUTH_FOR_SEARCH
# is disabled on the internal ldap-gateway, so no bind credentials are needed).
# The name is read from the NSS gecos field (mapped from LDAP via sssd.conf).
# The email is looked up with ldapsearch, driven by the same SSSD_* environment
# variables that render /etc/sssd/sssd.conf (see sssd.conf.template), so the
# query targets the same directory — and binds with the same credentials — that
# sssd authenticates against.

# Only run for interactive shells
[[ $- != *i* ]] && return

# Only if git is available
# Only if git and ldapsearch are available
command -v git >/dev/null 2>&1 || return
command -v ldapsearch >/dev/null 2>&1 || return

# Skip if already configured — user-set values always take precedence
[ -n "$(git config --global user.email 2>/dev/null)" ] && [ -n "$(git config --global user.name 2>/dev/null)" ] && return

_GIT_SETUP_USER="${USER:-$(id -un 2>/dev/null)}"
[ -z "$_GIT_SETUP_USER" ] && return
[ "$_GIT_SETUP_USER" = "root" ] && return

# Full name from NSS (SSSD reads the LDAP gecos attribute by default via ldap_user_gecos)
_GIT_SETUP_NAME=$(getent passwd "$_GIT_SETUP_USER" 2>/dev/null | cut -d: -f5)

# Email from LDAP anonymous query
_GIT_SETUP_LDAP_HOST="${LDAP_URI:-ldaps://ldap1:636}"

# Resolve baseDN the same way SSSD does: rootDSE namingContexts autodiscovery.
# Use LDAP_BASE_DN if explicitly set; otherwise query rootDSE.
# - Single namingContexts entry -> use it directly
# - Multiple namingContexts -> use defaultNamingContext
# - Neither resolvable -> abort
if [ -n "${LDAP_BASE_DN:-}" ]; then
_GIT_SETUP_LDAP_BASE="$LDAP_BASE_DN"
else
_GIT_SETUP_ROOTDSE=$(ldapsearch -x -H "$_GIT_SETUP_LDAP_HOST" -b "" -s base namingContexts defaultNamingContext 2>/dev/null)
_GIT_SETUP_NC_COUNT=$(echo "$_GIT_SETUP_ROOTDSE" | grep -c '^namingContexts:')
if [ "$_GIT_SETUP_NC_COUNT" -eq 1 ]; then
_GIT_SETUP_LDAP_BASE=$(echo "$_GIT_SETUP_ROOTDSE" | awk '/^namingContexts:/{print $2; exit}')
elif [ "$_GIT_SETUP_NC_COUNT" -gt 1 ]; then
_GIT_SETUP_LDAP_BASE=$(echo "$_GIT_SETUP_ROOTDSE" | awk '/^defaultNamingContext:/{print $2; exit}')
_git_identity_setup() {
local user uri base name_attr rootdse nc_count name email
local -a bind

user="${USER:-$(id -un 2>/dev/null)}"
[ -z "$user" ] && return
[ "$user" = "root" ] && return

# Same server list sssd uses (ldapsearch -H accepts a comma-separated list).
# LDAP_URI is the legacy pre-SSSD_* variable name.
uri="${SSSD_LDAP_URI:-${LDAP_URI:-ldaps://ldap1:636}}"

# Bind the same way sssd does: with the default bind DN and token when
# configured (directories such as the Authentik LDAP outpost reject
# anonymous searches), otherwise anonymously.
bind=(-x)
if [ -n "${SSSD_LDAP_DEFAULT_BIND_DN:-}" ] && [ -n "${SSSD_DEFAULT_AUTHTOK:-}" ] &&
{ [ -z "${SSSD_DEFAULT_AUTHTOK_TYPE:-}" ] || [ "${SSSD_DEFAULT_AUTHTOK_TYPE}" = "password" ]; }; then
bind+=(-D "$SSSD_LDAP_DEFAULT_BIND_DN" -w "$SSSD_DEFAULT_AUTHTOK")
fi
Comment on lines +37 to 41
unset _GIT_SETUP_ROOTDSE _GIT_SETUP_NC_COUNT
fi
[ -z "${_GIT_SETUP_LDAP_BASE:-}" ] && return

_GIT_SETUP_EMAIL=$(ldapsearch -x \
-H "$_GIT_SETUP_LDAP_HOST" \
-b "$_GIT_SETUP_LDAP_BASE" \
"(uid=${_GIT_SETUP_USER})" mail 2>/dev/null \
| awk '/^mail:/{print $2; exit}')
# Resolve the search base the same way sssd does: explicit configuration
# first, then rootDSE namingContexts autodiscovery.
# - Single namingContexts entry -> use it directly
# - Multiple namingContexts -> use defaultNamingContext
# - Neither resolvable -> abort
base="${SSSD_LDAP_USER_SEARCH_BASE:-${SSSD_LDAP_SEARCH_BASE:-${LDAP_BASE_DN:-}}}"
if [ -z "$base" ]; then
rootdse=$(ldapsearch "${bind[@]}" -H "$uri" -b "" -s base namingContexts defaultNamingContext 2>/dev/null)
nc_count=$(echo "$rootdse" | grep -c '^namingContexts:')
if [ "$nc_count" -eq 1 ]; then
base=$(echo "$rootdse" | awk '/^namingContexts:/{print $2; exit}')
elif [ "$nc_count" -gt 1 ]; then
base=$(echo "$rootdse" | awk '/^defaultNamingContext:/{print $2; exit}')
fi
fi
[ -z "$base" ] && return

# Same login-name attribute sssd maps (ldap_user_name, default uid)
name_attr="${SSSD_LDAP_USER_NAME:-uid}"

# Full name from NSS (sssd maps the LDAP gecos attribute via ldap_user_gecos)
name=$(getent passwd "$user" 2>/dev/null | cut -d: -f5)

if [ -n "$_GIT_SETUP_NAME" ]; then
git config --global user.name "$_GIT_SETUP_NAME"
fi
email=$(ldapsearch "${bind[@]}" \
-H "$uri" \
-b "$base" \
"(${name_attr}=${user})" mail 2>/dev/null \
| awk '/^mail:/{print $2; exit}')

if [ -n "$_GIT_SETUP_EMAIL" ]; then
git config --global user.email "$_GIT_SETUP_EMAIL"
fi
[ -n "$name" ] && [ -z "$(git config --global user.name 2>/dev/null)" ] && git config --global user.name "$name"
[ -n "$email" ] && [ -z "$(git config --global user.email 2>/dev/null)" ] && git config --global user.email "$email"
return 0
}

unset _GIT_SETUP_USER _GIT_SETUP_NAME _GIT_SETUP_EMAIL _GIT_SETUP_LDAP_HOST _GIT_SETUP_LDAP_BASE _GIT_SETUP_ROOTDSE _GIT_SETUP_NC_COUNT
_git_identity_setup
unset -f _git_identity_setup
74 changes: 74 additions & 0 deletions images/base/test-git-identity.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
# Smoke test for images/base/git-identity.sh with stubbed ldapsearch/getent.
set -u

SCRIPT="$(cd "$(dirname "$0")" && pwd)/git-identity.sh"
export SCRIPT
STUBS=$(mktemp -d)
export HOME=$(mktemp -d)

cat >"$STUBS/ldapsearch" <<'EOF'
#!/usr/bin/env bash
# Record args for assertions, then answer rootDSE or mail queries.
echo "$@" >>/tmp/ldapsearch-args.log
if [[ "$*" == *"-s base namingContexts"* ]]; then
echo "namingContexts: dc=example,dc=com"
exit 0
fi
echo "mail: tester@example.com"
EOF

cat >"$STUBS/getent" <<'EOF'
#!/usr/bin/env bash
echo "tester:x:2000:4000:Testy McTestface:/home/tester:/bin/bash"
EOF

chmod +x "$STUBS/ldapsearch" "$STUBS/getent"
export PATH="$STUBS:$PATH"
export USER=tester
rm -f /tmp/ldapsearch-args.log

fail=0
check() { # desc expected actual
if [ "$2" = "$3" ]; then echo "PASS: $1"; else echo "FAIL: $1 (expected '$2', got '$3')"; fail=1; fi
}

# --- Case 1: SSSD_* vars set incl. bind creds; explicit user search base ---
export SSSD_LDAP_URI="ldaps://a:636, ldaps://b:636"
export SSSD_LDAP_USER_SEARCH_BASE="ou=people,dc=example,dc=com"
export SSSD_LDAP_DEFAULT_BIND_DN="cn=proxy,dc=example,dc=com"
export SSSD_DEFAULT_AUTHTOK_TYPE="password"
export SSSD_DEFAULT_AUTHTOK="secret"
export SSSD_LDAP_USER_NAME="sAMAccountName"

bash -ic '. "$SCRIPT"'
check "email set" "tester@example.com" "$(git config --global user.email)"
check "name set" "Testy McTestface" "$(git config --global user.name)"
grep -q -- "-D cn=proxy,dc=example,dc=com -w secret" /tmp/ldapsearch-args.log && echo "PASS: bound with sssd creds" || { echo "FAIL: bind creds missing"; fail=1; }
grep -q -- "-b ou=people,dc=example,dc=com (sAMAccountName=tester) mail" /tmp/ldapsearch-args.log && echo "PASS: base + name attr honored" || { echo "FAIL: base/name attr"; fail=1; }
grep -q -- "-H ldaps://a:636, ldaps://b:636" /tmp/ldapsearch-args.log && echo "PASS: SSSD_LDAP_URI used" || { echo "FAIL: uri"; fail=1; }

# --- Case 2: no base configured -> rootDSE discovery, anonymous bind ---
git config --global --unset user.email; git config --global --unset user.name
unset SSSD_LDAP_USER_SEARCH_BASE SSSD_LDAP_DEFAULT_BIND_DN SSSD_DEFAULT_AUTHTOK SSSD_DEFAULT_AUTHTOK_TYPE SSSD_LDAP_USER_NAME
rm -f /tmp/ldapsearch-args.log

bash -ic '. "$SCRIPT"'
check "email via rootDSE base" "tester@example.com" "$(git config --global user.email)"
grep -q -- "-b dc=example,dc=com (uid=tester) mail" /tmp/ldapsearch-args.log && echo "PASS: rootDSE base + default uid attr" || { echo "FAIL: rootDSE"; fail=1; }
grep -q -- "-D " /tmp/ldapsearch-args.log && { echo "FAIL: unexpected bind"; fail=1; } || echo "PASS: anonymous when no creds"

# --- Case 3: existing config untouched ---
git config --global user.email keep@me.com
git config --global user.name "Keep Me"
rm -f /tmp/ldapsearch-args.log
bash -ic '. "$SCRIPT"'
check "existing email kept" "keep@me.com" "$(git config --global user.email)"
[ -f /tmp/ldapsearch-args.log ] && { echo "FAIL: ldapsearch ran despite existing config"; fail=1; } || echo "PASS: skipped when already configured"

# --- Case 4: non-interactive shell does nothing ---
git config --global --unset user.email; git config --global --unset user.name
bash -c '. "$SCRIPT"'
check "non-interactive no-op" "" "$(git config --global user.email 2>/dev/null)"

exit $fail
3 changes: 3 additions & 0 deletions mie-opensource-landing/docs/admins/ldap-servers.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ In the admin UI: **Settings** → **Default Container Environment Variables**. T
!!! tip
`SSSD_LDAP_DEFAULT_BIND_DN`, `SSSD_DEFAULT_AUTHTOK_TYPE`, and `SSSD_DEFAULT_AUTHTOK` work as a set. Provide all three when your directory requires an authenticated bind to read users and groups; leave all three blank to bind anonymously. The service account only needs read access to the user and group subtrees — user passwords are verified by a separate bind as the authenticating user.

!!! note "Git identity"
On first interactive login, a profile script (`/etc/profile.d/git-identity.sh`) sets the user's global `git config user.name`/`user.email` from the directory: the name from the NSS gecos field (`SSSD_LDAP_USER_GECOS`) and the email from an `ldapsearch` for the user's `mail` attribute. The search uses the same `SSSD_*` variables — URI, search base, login-name attribute, and bind DN/credential — so no extra configuration is needed. Values a user sets manually are never overwritten.

!!! warning
`SSSD_DEFAULT_AUTHTOK` is a secret. The rendered `/etc/sssd/sssd.conf` is written with restrictive permissions (mode `0600`), but the value is also visible in the container's environment. Prefer a dedicated, least-privilege service account and rotate it like any other credential.

Expand Down
Loading