#!/bin/bash # set -x set -e # Text to display in the banner banner_text="This utility will go through all Rancher users and perform an Active Directory lookup using the configured service account to get the user's distinguished name. Next, it will perform lookups inside Rancher for all the user's Tokens, ClusterRoleTemplateBindings, and ProjectRoleTemplateBindings. If any of those objects, including the user object itself are referencing a principalID based on the GUID of that user, those objects will be updated to reference the distinguished name-based principalID (unless the utility is run with -dry-run, in that case the only results are log messages indicating the changes that would be made by a run without that flag). This utility will also detect and correct the case where a single ActiveDirectory GUID is mapped to multiple Rancher users. That condition was likely caused by a race in the original migration to use GUIDs and resulted in a second Rancher user being created. This caused Rancher logins to fail for the duplicated user. The utility remedies that situation by mapping any tokens and bindings to the original user before removing the newer user, which was created in error. It is also important to note that migration of ClusterRoleTemplateBindings and ProjectRoleTemplateBindings require a delete/create operation rather than an update. This will result in new object names for the migrated bindings. A label with the former object name will be included in the migrated bindings. The Rancher Agent image to be used with this utility can be found at rancher/rancher-agent:v2.7.6 It is recommended that you perform a Rancher backup prior to running this utility." CLEAR='\033[0m' RED='\033[0;31m' # cluster resources, including the service account used to run the script cluster_resources_yaml=$(cat << 'EOF' apiVersion: v1 kind: ServiceAccount metadata: name: cattle-cleanup-sa namespace: cattle-system labels: rancher-cleanup: "true" --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: cattle-cleanup-binding labels: rancher-cleanup: "true" roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cattle-cleanup-role subjects: - kind: ServiceAccount name: cattle-cleanup-sa namespace: cattle-system --- apiVersion: batch/v1 kind: Job metadata: name: cattle-cleanup-job namespace: cattle-system labels: rancher-cleanup: "true" spec: backoffLimit: 6 completions: 1 parallelism: 1 selector: template: metadata: creationTimestamp: null spec: containers: - env: - name: AD_GUID_CLEANUP value: "true" #dryrun - name: DRY_RUN #dryrun value: "true" #deletemissing - name: AD_DELETE_MISSING_GUID_USERS #deletemissing value: "true" #debug - name: RANCHER_DEBUG #debug value: "true" image: agent_image imagePullPolicy: Always command: ["agent"] name: cleanup-agent resources: {} terminationMessagePath: /dev/termination-log terminationMessagePolicy: File dnsPolicy: ClusterFirst restartPolicy: OnFailure schedulerName: default-scheduler securityContext: {} serviceAccountName: cattle-cleanup-sa terminationGracePeriodSeconds: 30 --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: cattle-cleanup-role labels: rancher-cleanup: "true" rules: - apiGroups: - '*' resources: - '*' verbs: - '*' - nonResourceURLs: - '*' verbs: - '*' EOF ) # Agent image to use in the yaml file agent_image="$1" show_usage() { if [ -n "$1" ]; then echo -e "${RED}👉 $1${CLEAR}\n"; fi echo "Usage: $0 AGENT_IMAGE [OPTIONS]" echo "" echo "Options:" echo -e "\t-h, --help Display this help message" echo -e "\t-n, --dry-run Display the resources that would be updated without making changes" echo -e "\t-d, --delete-missing Permanently remove user objects whose GUID cannot be found in Active Directory" echo -e "\t-v, --debug Run with extra debug verbosity" } display_banner() { local text="$1" local border_char="=" local text_width=$(($(tput cols))) local border=$(printf "%${text_width}s" | tr " " "$border_char") echo "$border" printf "%-${text_width}s \n" "$text" echo "$border" echo "Dry run: $dry_run" echo "Delete missing: $delete_missing" echo "Agent image: $agent_image" echo "Debug: $debug_mode" if [[ "$dry_run" = true ]] && [[ "$delete_missing" = true ]] then echo "Setting the dry-run option to true overrides the delete-missing option. NO CHANGES WILL BE MADE." fi echo "$border" } OPTS=$(getopt -o hndv -l help,dry-run,delete-missing,debug -- "$@") if [ $? != 0 ]; then show_usage "Invalid option" exit 1 fi eval set -- "$OPTS" dry_run=false delete_missing=false debug_mode=false while true; do case "$1" in -h | --help) show_usage exit 0 ;; -n | --dry-run) dry_run=true shift ;; -d | --delete-missing) delete_missing=true shift ;; -v | --debug) debug_mode=true shift ;; --) shift break ;; *) show_usage "Invalid option" exit 1 ;; esac done shift "$((OPTIND - 1))" # Ensure AGENT_IMAGE is provided if [ $# -lt 1 ]; then show_usage "AGENT_IMAGE is a required argument" exit 1 fi display_banner "${banner_text}" if [ "$dry_run" != true ] then # Check the Rancher version before doing anything. # If it is v2.7.5, make it clear that configuration is not the recommended way to run this utility. rancher_version=$(kubectl get settings server-version --template='{{.value}}') if [ "$rancher_version" = "v2.7.5" ]; then echo -e "${RED}IT IS NOT RECOMMENDED TO RUN THIS UTILITY AGAINST RANCHER VERSION v2.7.5${CLEAR}" echo -e "${RED}IF RANCHER v.2.7.5 RESTARTS AFTER RUNNING THIS UTILITY, IT WILL UNDO THE EFFECTS OF THIS UTILITY.${CLEAR}" echo -e "${RED}IF YOU DO WANT TO RUN THIS UTILITY, IT IS RECOMMENDED THAT YOU MAKE A BACKUP PRIOR TO CONTINUING.${CLEAR}" read -p "Do you want to continue? (y/n): " choice if [[ ! $choice =~ ^[Yy]$ ]]; then echo "Exiting..." exit 0 fi fi fi read -p "Do you want to continue? (y/n): " choice if [[ ! $choice =~ ^[Yy]$ ]]; then echo "Exiting..." exit 0 fi # apply the provided rancher agent yaml=$(sed -e 's=agent_image='"$agent_image"'=' <<< $cluster_resources_yaml) if [ "$dry_run" = true ] then # Uncomment the env var for dry-run mode yaml=$(sed -e 's/#dryrun // ' <<< "$yaml") elif [ "$delete_missing" = true ] then # Uncomment the env var for missing user cleanup yaml=$(sed -e 's/#deletemissing // ' <<< "$yaml") fi if [ "$debug_mode" = true ] then # Uncomment the env var for debug logging yaml=$(sed -e 's/#debug // ' <<< "$yaml") fi echo "$yaml" | kubectl apply -f - # Get the pod ID to tail the logs retry_interval=1 max_retries=10 retry_count=0 pod_id="" while [ $retry_count -lt $max_retries ]; do pod_id=$(kubectl --namespace=cattle-system get pod -l job-name=cattle-cleanup-job -o jsonpath="{.items[0].metadata.name}") if [ -n "$pod_id" ]; then break else sleep $retry_interval ((retry_count++)) fi done # 600 is equal to 5 minutes, because the sleep interval is 0.5 seconds job_start_timeout=600 declare -i count=0 until kubectl --namespace=cattle-system logs $pod_id -f do if [ $count -gt $job_start_timeout ] then echo "Timeout reached, check the job by running kubectl --namespace=cattle-system get jobs" echo "To cleanup manually, you can run:" echo " kubectl --namespace=cattle-system delete serviceaccount,job -l rancher-cleanup=true" echo " kubectl delete clusterrole,clusterrolebinding -l rancher-cleanup=true" exit 1 fi sleep 0.5 count+=1 done # Cleanup after it completes successfully echo "$yaml" | kubectl delete -f -