CodeCrusaders

a blog with some code

Discover Govc: set and sync vCenter permissions

2023-08-18 code Davy

Table of Contents

No longer than a couple of weeks ago I stumbled on this nifty little tool called govc. Sure, I heard of this before, but I did not realize how powerful this tool can be. Until now! So, I decided to write a blog about my specific use-case, Set and sync vCenter permissions with an openLDAP group.

What is govc

Govc is basicly a wrapper around GO modules for vSphere. To be more specific, it relies on the official VMware govmomi library. The syntax is incredibly suitable for usage in Bash scripting and that makes it easy to use in cloudnative environments. For example, integration with Concourse pipelines.

The syntax is kept clean and simple. Most commands speak to itself and examples of usage are provided. When used in combination with Bash it is easy to search and manipulate results. Because of the Bash compatability it is also incredibly easy to mix in and combine other resources from your environment. Also, for advanced use most search queries can also output in json. Which you can then search with jq and do lots of cool stuff with it. Advanced json and jq usage with govc is not part of this writing.

Use case

Context

For my project I have been working in an environment that is highly automated. There is a vCenter installation present and it has a FreeIPA openLDAP integration. As the environment is growing, more admins need access to the environment and I want to automate this. Unfortunately vCenter won’t accept sso usergroups from FreeIPA for authentication. Well, it kinda does, but then it is not working. Long story. It does accept individual sso users from FreeIPA though. So, how to go forward with this?

The idea

In my line of thinking the vcenter-admin usergroup in FreeIPA must be leading. Meaning, somehow I need a way to leverage the users in that group to vCenter for proper authentication. Even though vCenter won’t accept a FreeIPA usergroup. To mimic group behaviour I came up with the idea to create a ‘admin-vcenter’ role in vCenter and add individual sso users to this role. Originating from the vcenter-admin sso group in FreeIPA. Then give the role permissions on the vCenter level. This should give users in the right FreeIPA sso group the permission to login to vCenter and have admin rights.

The goal (1)

Create a script that reads from a FreeIPA usergroup and sync this information with a vCenter role to set permissions for the users. When the role does not exist, it has to be created automatically. Permissions for users should match the users in an existing FreeIPA group. Mutations should be processed automatically. Eventually the script should be compatible for integration with a Concourse pipeline or at least be suitable to be scheduled as a job.

The goal (2)

Ofcourse I understand that this use-case is pretty specific and caused by behaviour of FreeIPA. FreeIPA is not a vCenter sso integration you will find very often. However, my goal is to show you what govc in combination with bash can do and how it can help to solve problems and complex automation questions.

Creating the script

Usually I like my scripts as short and efficient as possible. As being a newbie on as well govc AND bash. I had a lot to figure out. It was a fun ride though and I am pretty sure there is more to come. Feel free to comment if you feel like the script could use improvements.

Prerequisites

  • govc - latest
  • bash terminal
  • vCenter (or ESXi) - at least 6.x or later
    • active sso integration, preferrably openLDAP
  • FreeIPA
    • or a similar openLDAP
    • might even by an array created by yourself
    • when you diverge from FreeIPA, some of the grepping might fail

Investigating govc

First of all I needed to get comfortable with govc. Basicly this meant trying a lot of commands and finding out what happens. I like the USAGE.md page. This page shows all commands with options and examples. Just try it out. Finally, for me it came down to the following commands.
$ govc role.ls # list of roles in the vCenter
$ govc role.create # create role in the vCenter
$ govc permissions.ls # list of permissions in the vCenter
$ govc permissions.set # set permissions on a role in the vCenter

One thing to be aware of is the $ govc env command. This will show you the environment variables present. If these are not present you have to put them in the script. The vcenter URL accepts the following format:
GOVC_URL="${GOVC_USERNAME}:${GOVC_PASSWORD}@${vc_ip}"

Investigating bash

As I mentioned before, bash scripting is fairly new to me. I have read it and have used it. Written myself, not so much. To me it is important to learn though. That is why I was happy to discover that it has lots of similarities with scripting I do know. Personally, I like short blocks of code. Clear beginning and end. Bash does just that.

Then came the issue of sorting and grepping. That is where things got a little complicated. I can do some grepping, but I am certainly not used to advanced cutting and sorting tools or awk. So, ofcourse I asked ChatGPT. That helped some. Also, some good blogs and forums helped me out.

Basicly what I was trying to do is retrieving an unique accountname from FreeIPA and check it against vCenter. Because of differences in listing formats that was certainly not as easy as it sounds.

In my script you will see the option -z in a lot of statements. This tell the script that if the command does not return any results, do something. Which is very useful if for example I want to check if a user or a role exists.

Thoughts

For those who don’t know. When you do an openLDAP integration in vcenter, all the openLDAP users become visible in vCenter and are available for connecting to roles for example. The real tricky part is to retrieve an exact accountname from FreeIPA and then match it with the user visible in vCenter. It is worth figuring out though. Because now I am able to filter users from a group in FreeIPA and leverage them for usage with govc in vCenter. And vice versa! That is much cooler than it actually sounds.

The script

Open To Show Code

 1#!/bin/bash -e
 2
 3# normally you would set these variables somewhere else
 4export GOVC_INSECURE=1
 5export vc_ip=your_vc_ip
 6export GOVC_PASSWORD=${GOVC_PASSWORD:-"yourpassword"}
 7export GOVC_USERNAME=${GOVC_USERNAME:-"yourusername@my.homelab"}
 8export GOVC_URL="${GOVC_USERNAME}:${GOVC_PASSWORD}@${vc_ip}"
 9
10# some coloring for output. Just because I can
11RED="\e[31m"
12GREEN="\e[32m"
13YELLOW="\e[33m"
14BLUE="\e[34m"
15ENDCOLOR="\e[0m"
16
17# Some variables
18entity=/        # leave like this for entry at vcenter level. 
19domain=         # example: my.homelab
20role_name=""    # example "my-role",  will be created in vcenter
21ldapgroup=""    # example: "my-sso-ldap-group", must exist in your ldap 
22
23# check if role exists, if not, create the role
24if [ -z "$(govc role.ls | grep -w "$role_name")" ] ; then
25    echo -e "${YELLOW} $role_name not found - creating new role ${ENDCOLOR}"
26    govc role.create $role_name $(govc role.ls Admin | grep -v Datacenter.)
27    echo -e "${GREEN}Role $role_name created. Checking for permissions ${ENDCOLOR}"
28else
29    echo -e "${GREEN}Role $role_name present. Checking for permissions ${ENDCOLOR}"
30fi
31
32# extract users from $ldapgroup group that exists in FreeIPA and make an array
33mapfile -t ldapgroup_members < <(ipa group-show $ldapgroup | grep "Member users:"  | cut -d ":" -f 2 | tr -d '[:space:]' | tr ',' '\n')
34# uncomment below lines if you want to read the array
35#for member in "${ldapgroup_members[@]}" ; do
36#    echo "sso-$member"
37#done
38
39# check if retrieved $ldapgroup  users are present in given Role. If not, add to Role ($role_name).
40for user in ${ldapgroup_members[@]} ; do
41    if [ -z "$(govc permissions.ls "$entity" | grep -w "$role_name" | awk -F' ' -v user="$user" ' NR>0 {sub(/.*\\/, "", $3); if ($3==user) print $3}')" ] ; then
42        echo -e "${YELLOW}LDAP groupmember: $user is added to role $role_name ${ENDCOLOR}"
43        govc permissions.set -principal $user@$domain -role $role_name $entity
44    else 
45        echo -e "${GREEN}LDAP groupmember: $user is already present in role $role_name ${ENDCOLOR}"
46    fi
47done
48
49# create variable that reads permissions in vcenter and compares it to users in $ldapgroup (array)
50# if user has a vcenter permission but is not member of the correct ldap group, permission is deleted on the entity
51adminpermissions=$(govc permissions.ls $entity | grep -w $role_name | awk 'NR>0 {sub(/.*\\/, "", $3); print $3}')
52#echo -e "${BLUE}all users connected to: $role_name:\n$adminpermissions ${ENDCOLOR}"
53echo -e "${BLUE}syncing with ldapgroup $ldapgroup ... ${ENDCOLOR}"
54while IFS= read -r permission_users; do
55    if [[ ! " ${ldapgroup_members[@]} " =~ " ${permission_users} " ]] ; then
56    	echo -e "${RED}$permission_users niet aanwezig in juiste ldap groep ${ENDCOLOR}"
57    	govc permissions.remove -principal $permission_users@$domain "$entity"
58    	echo -e "${YELLOW}$permission_users uit alle rollen op $entity verwijderd ${ENDCOLOR}"
59#    else 
60#    echo "$permission_users is  lid van de juiste LDAP groep"
61    fi
62done <<< "$adminpermissions"

Script explained

On top of things already written down, some additional explaining.

line 4 to 8

It is recommended to set environment variables. However, for testing purposes you can also do an export from the script. These lines make sure you have a vcenter connection.

line 11 to 15

Just some coloring for the output.

line 18 to 21

  • Variables needed for the script. Make sure they are filled out. The variable entity makes sure you are targeting the vcenter.
  • It is also possible to target the Datacenter or specified cluster with entity=/Datacanter/clustername.
  • ldapgroup must already exist.

line 24 to 30

Checks if the desired rolename is already present in vCenter. If the rolename cannot be found it will be created. If the rolename already exists the script will continue operations targeting this role.

line 33 to 37

Line 33 reads a group from FreeIPA (ldapgroup_members), selects the unique username without domain notations and puts them in an array. Uncomment line 35-37 to have the array contents printed.

line 40 to 47

Reads the array and checks for each item if the user already has permissions with the role. If the result is empty it assumes the user has no permissions. Permissions for the user will be set on the vCenter level.

line 51 to 53

Find all users tied to the role. This output will be used to check against the ldap sso group.

line 54 to 62

Checks if the users attached to the role are still a member of the sso group in FreeIPA. Since the sso group in FreeIPA should be leading and the source of truth, the script will remove acces from users not present in the FreeIPA sso group.

Output examples

Two examples of output from the script. Due to circumstances it is in Dutch.

new user present in ldapgroup

In this case I put myself in the correct ldapgroup ant then ran the script.

  • First line confirms the Role already exists in vCenter
  • Line two and three confirm users from the ldap group already having permissions
  • Line number four confirms that my user account is added to the role in vCenter
  • Last line in blue is the syncing back to LDAP. There is nothing to sync back for now



user removed from ldapgroup

I removed myself from the ldapgroup and ran the script. Green lines are omitted from this example.

  • First line in blue indicates it is checking the ldapgroup against vcenter permissions
  • Second line tells me that I am not in the admin ldapgroup anymore
  • Third line says that I am removed from all roles on the entity vcenter (/)


Conclusion

This was a really fun ride! I am most certainly triggered to explore and do more with govc. Govc is already really powerfull in its basic form, but becomes even more powerfull when you know how to use json and jq.

For me this was mostly about discovering govc from scratch and discover what it can do. Then using govc in bash and combine it with other tooling and that worked out. Great!

Hopefully I was able to take you on my ride with this blog. If you feel there is room for improvement, please don’t hesitate to contact me.

Thanks for reading