-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1594 from vmware-tanzu/jtc/add-ldapsearch-hack-sc…
…ript Add ldapsearch hack script
- Loading branch information
Showing
1 changed file
with
232 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
#!/usr/bin/env bash | ||
|
||
# Copyright 2023 the Pinniped contributors. All Rights Reserved. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
set -euo pipefail | ||
|
||
# This script is a rough approximation of what the Pinniped Supervisor Golang code does to search an LDAP provider | ||
# during an end-user's login. This is intended to be helpful for debugging your LDAPIdentityProvider spec settings. | ||
# Because it is implemented in bash, it is not necessarily exactly the same as the actual Supervisor code. | ||
# Note that this does not yet support ActiveDirectoryIdentityProvider, which has some more complex behavior to | ||
# determine default values for some of the spec fields. | ||
|
||
pinniped_path="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" | ||
cd "$pinniped_path" || exit 1 | ||
|
||
source hack/lib/helpers.sh | ||
|
||
# | ||
# Handle argument parsing and help message | ||
# | ||
help=no | ||
resource_type_and_name="" | ||
namespace="" | ||
username="" | ||
|
||
while (("$#")); do | ||
case "$1" in | ||
-h | --help) | ||
help=yes | ||
shift | ||
;; | ||
-r | --resource) | ||
shift | ||
# If there are no more command line arguments, or there is another command line argument but it starts with a dash, then error | ||
if [[ "$#" == "0" || "$1" == -* ]]; then | ||
log_error "-r|--resource requires a resource type and name to be specified as type/name" | ||
exit 1 | ||
fi | ||
resource_type_and_name=$1 | ||
shift | ||
;; | ||
-n | --namespace) | ||
shift | ||
# If there are no more command line arguments, or there is another command line argument but it starts with a dash, then error | ||
if [[ "$#" == "0" || "$1" == -* ]]; then | ||
log_error "-n|--namespace requires a namespace name to be specified" | ||
exit 1 | ||
fi | ||
namespace=$1 | ||
shift | ||
;; | ||
-u | --username) | ||
shift | ||
# If there are no more command line arguments, or there is another command line argument but it starts with a dash, then error | ||
if [[ "$#" == "0" || "$1" == -* ]]; then | ||
log_error "-u|--username requires a username to be specified" | ||
exit 1 | ||
fi | ||
username=$1 | ||
shift | ||
;; | ||
-*) | ||
log_error "Unsupported flag $1" >&2 | ||
if [[ "$1" == *"active-directory"* ]]; then | ||
log_error "Did you mean --get-active-directory-vars?" | ||
fi | ||
exit 1 | ||
;; | ||
*) | ||
log_error "Unsupported positional arg $1" >&2 | ||
exit 1 | ||
;; | ||
esac | ||
done | ||
|
||
if [[ "$help" == "yes" ]]; then | ||
me="$(basename "${BASH_SOURCE[0]}")" | ||
log_note "Usage:" | ||
log_note " $me [flags]" | ||
log_note | ||
log_note "Flags:" | ||
log_note " -h, --help: print this usage" | ||
log_note " -r, --resource: specify the resource type and name (e.g. ldapidentityprovider/my-ldap-idp) - required" | ||
log_note " -n, --namespace: specify the namespace in which the resource exists - required" | ||
log_note " -u, --username: specify a username, as an end-user would type it during a login - required" | ||
exit 1 | ||
fi | ||
|
||
if [[ -z "$namespace" ]]; then | ||
log_error "-n|--namespace is required" | ||
exit 1 | ||
fi | ||
|
||
if [[ -z "$resource_type_and_name" ]]; then | ||
log_error "-r|--resource is required" | ||
exit 1 | ||
fi | ||
|
||
if [[ "$resource_type_and_name" != ldapidentityprovider/* ]]; then | ||
log_error "-r|--resource currently only supports ldapidentityprovider type resources." | ||
log_error "Please specify the value as \"ldapidentityprovider/name-of-resource\"." | ||
exit 1 | ||
fi | ||
|
||
if [[ -z "$username" ]]; then | ||
log_error "-u|--username is required" | ||
exit 1 | ||
fi | ||
|
||
RESOURCE_FILE=$(mktemp) | ||
trap "rm $RESOURCE_FILE" EXIT | ||
|
||
kubectl get "$resource_type_and_name" \ | ||
--namespace "$namespace" \ | ||
--output yaml >"$RESOURCE_FILE" | ||
|
||
# See docs for LDAPIdentityProvider.spec for details about these settings. | ||
# https://github.com/vmware-tanzu/pinniped/blob/main/generated/1.28/README.adoc#k8s-api-go-pinniped-dev-generated-1-28-apis-supervisor-idp-v1alpha1-ldapidentityproviderspec | ||
LDAP_HOST=$(yq '.spec.host' "$RESOURCE_FILE") # required | ||
LDAP_CA_BUNDLE=$(yq '.spec.tls.certificateAuthorityData // ""' "$RESOURCE_FILE") # optional | ||
LDAP_BIND_SECRETNAME=$(yq '.spec.bind.secretName' "$RESOURCE_FILE") # required | ||
LDAP_USER_SEARCH_BASE=$(yq '.spec.userSearch.base' "$RESOURCE_FILE") # required | ||
LDAP_USER_SEARCH_FILTER=$(yq '.spec.userSearch.filter // ""' "$RESOURCE_FILE") # optional unless Attributes.Username is set to "dn" | ||
LDAP_USER_SEARCH_ATTRIBUTES_USERNAME=$(yq '.spec.userSearch.attributes.username' "$RESOURCE_FILE") # required | ||
LDAP_USER_SEARCH_ATTRIBUTES_UID=$(yq '.spec.userSearch.attributes.uid' "$RESOURCE_FILE") # required | ||
LDAP_GROUP_SEARCH_BASE=$(yq '.spec.groupSearch.base // ""' "$RESOURCE_FILE") # optional, disables group search when blank | ||
LDAP_GROUP_SEARCH_FILTER=$(yq '.spec.groupSearch.filter // ""' "$RESOURCE_FILE") # optional, defaults to member={} | ||
LDAP_GROUP_SEARCH_USER_ATTRIBUTE_FOR_FILTER=$(yq '.spec.groupSearch.userAttributeForFilter // ""' "$RESOURCE_FILE") # optional, defaults to using dn | ||
LDAP_GROUP_SEARCH_ATTRIBUTES_GROUPNAME=$(yq '.spec.groupSearch.attributes.groupName // ""' "$RESOURCE_FILE") # optional, defaults to using dn | ||
|
||
# Set defaults for missing optional values. | ||
if [[ -z "$LDAP_USER_SEARCH_FILTER" ]]; then | ||
LDAP_USER_SEARCH_FILTER="${LDAP_USER_SEARCH_ATTRIBUTES_USERNAME}={}" | ||
fi | ||
if [[ -z "$LDAP_GROUP_SEARCH_FILTER" ]]; then | ||
LDAP_GROUP_SEARCH_FILTER="member={}" | ||
fi | ||
if [[ -z "$LDAP_GROUP_SEARCH_USER_ATTRIBUTE_FOR_FILTER" ]]; then | ||
LDAP_GROUP_SEARCH_USER_ATTRIBUTE_FOR_FILTER="dn" | ||
fi | ||
if [[ -z "$LDAP_GROUP_SEARCH_ATTRIBUTES_GROUPNAME" ]]; then | ||
LDAP_GROUP_SEARCH_ATTRIBUTES_GROUPNAME="dn" | ||
fi | ||
|
||
# LDAP filters must be surrounded by parens. Pinniped will automatically add | ||
# the missing parens, if needed, as a convenience, so do that here too. | ||
if [[ "$LDAP_USER_SEARCH_FILTER" != "("* ]]; then | ||
LDAP_USER_SEARCH_FILTER="(${LDAP_USER_SEARCH_FILTER})" | ||
fi | ||
if [[ "$LDAP_GROUP_SEARCH_FILTER" != "("* ]]; then | ||
LDAP_GROUP_SEARCH_FILTER="(${LDAP_GROUP_SEARCH_FILTER})" | ||
fi | ||
|
||
LDAP_BIND_SECRET_FILE=$(mktemp) | ||
trap "rm $LDAP_BIND_SECRET_FILE" EXIT | ||
|
||
kubectl get secret "$LDAP_BIND_SECRETNAME" \ | ||
--namespace "$namespace" \ | ||
--output yaml >"$LDAP_BIND_SECRET_FILE" | ||
|
||
LDAP_BIND_DN=$(yq '.data.username | @base64d' "$LDAP_BIND_SECRET_FILE") # required | ||
LDAP_BIND_PASSWORD=$(yq '.data.password | @base64d' "$LDAP_BIND_SECRET_FILE") # required | ||
|
||
basic_cmd=() | ||
|
||
if [[ -n "${LDAP_CA_BUNDLE}" ]]; then | ||
LDAP_CA_BUNDLE_FILE="/tmp/ldap_tls_cacert.pem" | ||
echo "$LDAP_CA_BUNDLE" | base64 -d >$LDAP_CA_BUNDLE_FILE | ||
|
||
basic_cmd+=("LDAPTLS_CACERT=$LDAP_CA_BUNDLE_FILE") | ||
fi | ||
|
||
basic_cmd+=("ldapsearch" "-x") | ||
basic_cmd+=("-H" "'ldaps://$LDAP_HOST'") | ||
|
||
if [[ -n "${LDAP_BIND_DN}" ]]; then | ||
basic_cmd+=("-D" "'$LDAP_BIND_DN'") | ||
fi | ||
|
||
if [[ -n "${LDAP_BIND_PASSWORD}" ]]; then | ||
basic_cmd+=("-w" "'$LDAP_BIND_PASSWORD'") | ||
fi | ||
|
||
# Construct a command which will print the whole user record, if found. | ||
find_user_cmd=${basic_cmd[*]} | ||
find_user_cmd+=("-b" "'$LDAP_USER_SEARCH_BASE'") | ||
find_user_cmd+=("-z" "1") # limit one result | ||
find_user_cmd+=("-s" "sub") | ||
find_user_cmd+=("'${LDAP_USER_SEARCH_FILTER//\{\}/"$username"}'") | ||
|
||
log_note "# The following commands are provided to aid in debugging." | ||
log_note "# Copy and paste these commands into a bash shell to run them." | ||
|
||
echo | ||
log_note "# Use the following command to search for the user's LDAP record." | ||
log_note "# The value of the \"$LDAP_USER_SEARCH_ATTRIBUTES_USERNAME\" attribute will be their Kubernetes username" | ||
log_note "# (not including any configured transformations on the FederationDomain)," | ||
log_note "# and the value of the \"$LDAP_USER_SEARCH_ATTRIBUTES_UID\" attribute will be their Supervisor UID." | ||
echo "${find_user_cmd[*]}" | ||
|
||
if [[ -z "$LDAP_GROUP_SEARCH_BASE" ]]; then | ||
echo | ||
log_note "# Group search is not enabled because spec.groupSearch.base is empty." | ||
exit | ||
fi | ||
|
||
# Add more to the user search command to get only the value of the configured username attribute. | ||
find_user_cmd+=("$LDAP_GROUP_SEARCH_USER_ATTRIBUTE_FOR_FILTER" "-LLL") | ||
find_user_cmd+=("|" "grep" "-E" "'^${LDAP_GROUP_SEARCH_USER_ATTRIBUTE_FOR_FILTER}: '") | ||
find_user_cmd+=("|" "sed" "'s/^${LDAP_GROUP_SEARCH_USER_ATTRIBUTE_FOR_FILTER}: //'") | ||
|
||
# Construct a command that will print a list of group names to which the user belongs. | ||
find_groups_cmd=${basic_cmd[*]} | ||
find_groups_cmd+=("-b" "'$LDAP_GROUP_SEARCH_BASE'") | ||
find_groups_cmd+=("-s" "sub") | ||
find_groups_cmd+=('${LDAP_GROUP_SEARCH_FILTER//\{\}/"$GROUP_SEARCH_KEY"}') | ||
find_groups_cmd+=("${LDAP_GROUP_SEARCH_ATTRIBUTES_GROUPNAME}") | ||
find_groups_cmd+=("-LLL") | ||
find_groups_cmd+=("|" "grep" "-E" "'^${LDAP_GROUP_SEARCH_ATTRIBUTES_GROUPNAME}: '") | ||
find_groups_cmd+=("|" "sed" "'s/^${LDAP_GROUP_SEARCH_ATTRIBUTES_GROUPNAME}: //'") | ||
|
||
echo | ||
log_note "# Use the following three commands to search for the user's group memberships." | ||
log_note "# The third command should result in their list of group names for Kubernetes" | ||
log_note "# (not including any configured transformations on the FederationDomain)." | ||
echo "LDAP_GROUP_SEARCH_FILTER=\"${LDAP_GROUP_SEARCH_FILTER}\"" | ||
echo | ||
echo "GROUP_SEARCH_KEY=\$( ${find_user_cmd[*]} ) && echo \$GROUP_SEARCH_KEY" | ||
echo | ||
echo "${find_groups_cmd[*]}" | ||
echo |