Google Secure LDAP on MacOS devices

Andy_McCaskill
Contributor

I've been trying to figure out a way to utilize this new service across our mac devices that still require LDAP user authentication. Has anyone else successfully got this to work?
Right off the bat I cannot figure a simple solution of injecting the certificates Google requires to a client and authenticate directly. So I started digging into the idea of running open directory and using stunnel. I ran into some road blocks configuring the .conf file and getting it to communicate to google. Is this the way to go? Or is there is better solution I am not able to find online?

1 ACCEPTED SOLUTION
14 REPLIES 14

tcarlson
New Contributor III

Did you find a solution for this. I'm looking into the same scenario

ega
Contributor III

So I have been looking at this and you want to look at the new "Cloud Identity Providers" section. This document has details on pages 3-9. https://hcsonline.com/images/PDFs/Google_SSO.pdf If you ignore the SSO part this should get you the same functionality according to the docs as configuring an LDAP Server. The latest Admin guide has a section https://docs.jamf.com/10.20.0/jamf-pro/administrator-guide/Integrating_with_Cloud_Identity_Providers.html
That says:
"Integrating with Cloud Identity Providers, which is similar to integrating with an LDAP directory service, allows you to do the following:
Look up and populate user information from the secure LDAP service for inventory purposes.
Add Jamf Pro user accounts or groups from the secure LDAP service.
Require users to log in to Self Service or the enrollment portal using their LDAP directory accounts.
Require users to log in during mobile device setup using their LDAP directory accounts.
Base the scope of remote management tasks on users or groups from the secure LDAP service."
So the only question I have remaining is if an Enrollment Customization can be configured that uses the Identity Provider ?
I can't answer that yet as I am still waiting for my Google Admins to approve the testing. If you get there before me please let me know.

gachowski
Valued Contributor II

We do it with Okta, so you should be able to do with Google. That said I think the macOS may not have all the features that you hope it would bring. : )

C

glopez1
New Contributor II

Secure LDAP requires a mobile account and deep configuration of opendirectoryd. Given 2020 and SSO, probably not worth the time investment with solutions like native catalina SSO/Kerb connectors, JAMFConnect, etc that work off normal local accounts and dont have secureToken complexity with the bootstrap token.

swapple
Contributor III

We want to stop binding to local AD in favor of using Google Workspace credentials. How would we configure the native catalina/bigsur SSO/Kerb connectors to use Google Workspace credentials to log into macOS?? Streamlining this into ABM enrollments to Jamf would be huge!!

mortopc4
New Contributor III

So my question for this overall process is in this statement "Require users to log in during mobile device setup using their LDAP directory accounts. Does JAMF mean the mobile device such as iPad do they mean the Mobile account Google Secure LDAP creates to keep the user account on the system in the event of loss of network connection.

Another question, is once the Google Secure LDAP mobile account is created on the local box, can the user sync local/server passwords if the LDAP password is changed?

We deployed this solution last year &it is working properly with Google Secure LDAP. We are still using Mobile accounts. Once the user log in yes we set Jamf config to create mobile account locally on the computer. This account is working without syncing any user data and it is linked to the google Secure LDAP. If the users change their passwords it will be synced to the local Mobile account on the computer. This Solution is perfect as we don't need to pay any other module like Jamf connect but the question now is for how long does Apple will support directory service & when it will be discontinued? So far Monterey is supported but not sure about any changes apple will make in the future, Any ideas?

This is exactly what I'm tying to accomplish. Do you happen to have any information on how you set this  up? 

Following the instructions posted in a link above looks like it would require some advanced scripting. Did you get into that? Anything you could share with me to get a baseline? 

Honestly, using Jamf connect ended up being our route. Also, look into
macOS Ventura as they will be bringing cloud identity to the os natively.

ayoussef
New Contributor

Jamf connect is not a free solution. Secure LDAP is. I can help if you need more info. Just contact me I would be happy to help. (ayoussef@cacegypt.org).

If anyone have another solution for the for macOS Ventura that would be helpful too.

 

Amira

ega
Contributor III

ega
Contributor III

Michal_Rakoczi
New Contributor
  • Create python script ldap.py

 

 

#!/usr/bin/managed_python3
from OpenDirectory import ODNode, ODSession, kODNodeTypeConfigure
from Foundation import NSMutableData, NSData

import os
import sys

# Reading plist
GOOGLELDAPCONFIGFILE = open(sys.argv[1], "r")
CONFIG = GOOGLELDAPCONFIGFILE.read()
GOOGLELDAPCONFIGFILE.close()

# Write the plist
od_session = ODSession.defaultSession()
od_conf_node, err = ODNode.nodeWithSession_type_error_(od_session, kODNodeTypeConfigure, None)
request = NSMutableData.dataWithBytes_length_(b'\x00'*32, 32)
request.appendData_(NSData.dataWithBytes_length_(str.encode(CONFIG), len(CONFIG)))
response, err = od_conf_node.customCall_sendData_error_(99991, request, None)

# Edit the default search path and append the new node to allow for login
os.system("dscl -q localhost -append /Search CSPSearchPath /LDAPv3/ldap.google.com")
os.system("dscl -q localhost -append /Contact CSPSearchPath /LDAPv3/ldap.google.com")
os.system("bash -c 'echo -e \"TLS_IDENTITY\tLDAP Client\" >> /etc/openldap/ldap.conf' ")

 

  • Create ldap.google.com.plist (this name exactly) 
  • Change your domain all of those lines-->  " <string>dc=HERE,dc=YOUR,dc=DOMAIN</string>"
  • Change your uuid it this line-->
    "<string>HERE UUID</string>"

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>description</key>
	<string>ldap.google.com</string>
	<key>mappings</key>
	<dict>
		<key>attributes</key>
		<array>
			<string>objectClass</string>
		</array>
		<key>function</key>
		<string>ldap:translate_recordtype</string>
		<key>recordtypes</key>
		<dict>
			<key>dsRecTypeStandard:Automount</key>
			<dict>
				<key>attributetypes</key>
				<dict>
					<key>dsAttrTypeStandard:AutomountInformation</key>
					<dict>
						<key>native</key>
						<string>automountInformation</string>
					</dict>
					<key>dsAttrTypeStandard:Comment</key>
					<dict>
						<key>native</key>
						<string>description</string>
					</dict>
					<key>dsAttrTypeStandard:CreationTimestamp</key>
					<dict>
						<key>native</key>
						<string>createTimestamp</string>
					</dict>
					<key>dsAttrTypeStandard:ModificationTimestamp</key>
					<dict>
						<key>native</key>
						<string>modifyTimestamp</string>
					</dict>
					<key>dsAttrTypeStandard:RecordName</key>
					<dict>
						<key>native</key>
						<string>automountKey</string>
					</dict>
				</dict>
				<key>info</key>
				<dict>
					<key>Group Object Classes</key>
					<string>OR</string>
					<key>Object Classes</key>
					<array>
						<string>automount</string>
					</array>
					<key>Search Base</key>
					<string>dc=HERE,dc=YOUR,dc=DOMAIN</string>
				</dict>
			</dict>
			<key>dsRecTypeStandard:AutomountMap</key>
			<dict>
				<key>attributetypes</key>
				<dict>
					<key>dsAttrTypeStandard:Comment</key>
					<dict>
						<key>native</key>
						<string>description</string>
					</dict>
					<key>dsAttrTypeStandard:CreationTimestamp</key>
					<dict>
						<key>native</key>
						<string>createTimestamp</string>
					</dict>
					<key>dsAttrTypeStandard:ModificationTimestamp</key>
					<dict>
						<key>native</key>
						<string>modifyTimestamp</string>
					</dict>
					<key>dsAttrTypeStandard:RecordName</key>
					<dict>
						<key>native</key>
						<string>automountMapName</string>
					</dict>
				</dict>
				<key>info</key>
				<dict>
					<key>Group Object Classes</key>
					<string>OR</string>
					<key>Object Classes</key>
					<array>
						<string>automountMap</string>
					</array>
					<key>Search Base</key>
					<string>dc=HERE,dc=YOUR,dc=DOMAIN</string>
				</dict>
			</dict>
			<key>dsRecTypeStandard:CertificateAuthorities</key>
			<dict>
				<key>attributetypes</key>
				<dict>
					<key>dsAttrTypeStandard:AuthorityRevocationList</key>
					<dict>
						<key>native</key>
						<string>authorityRevocationList;binary</string>
					</dict>
					<key>dsAttrTypeStandard:CACertificate</key>
					<dict>
						<key>native</key>
						<string>cACertificate;binary</string>
					</dict>
					<key>dsAttrTypeStandard:CertificateRevocationList</key>
					<dict>
						<key>native</key>
						<string>certificateRevocationList;binary</string>
					</dict>
					<key>dsAttrTypeStandard:CreationTimestamp</key>
					<dict>
						<key>native</key>
						<string>createTimestamp</string>
					</dict>
					<key>dsAttrTypeStandard:CrossCertificatePair</key>
					<dict>
						<key>native</key>
						<string>crossCertificatePair;binary</string>
					</dict>
					<key>dsAttrTypeStandard:ModificationTimestamp</key>
					<dict>
						<key>native</key>
						<string>modifyTimestamp</string>
					</dict>
					<key>dsAttrTypeStandard:RecordName</key>
					<dict>
						<key>native</key>
						<string>cn</string>
					</dict>
				</dict>
				<key>info</key>
				<dict>
					<key>Group Object Classes</key>
					<string>OR</string>
					<key>Object Classes</key>
					<array>
						<string>certificationAuthority</string>
					</array>
					<key>Search Base</key>
					<string>dc=HERE,dc=YOUR,dc=DOMAIN</string>
				</dict>
			</dict>
			<key>dsRecTypeStandard:Groups</key>
			<dict>
				<key>attributetypes</key>
				<dict>
					<key>dsAttrTypeStandard:CreationTimestamp</key>
					<dict>
						<key>native</key>
						<string>createTimestamp</string>
					</dict>
					<key>dsAttrTypeStandard:GroupMembership</key>
					<dict>
						<key>native</key>
						<string>memberUid</string>
					</dict>
					<key>dsAttrTypeStandard:Member</key>
					<dict>
						<key>native</key>
						<string>memberUid</string>
					</dict>
					<key>dsAttrTypeStandard:ModificationTimestamp</key>
					<dict>
						<key>native</key>
						<string>modifyTimestamp</string>
					</dict>
					<key>dsAttrTypeStandard:PrimaryGroupID</key>
					<dict>
						<key>native</key>
						<string>gidNumber</string>
					</dict>
					<key>dsAttrTypeStandard:RecordName</key>
					<dict>
						<key>native</key>
						<string>cn</string>
					</dict>
				</dict>
				<key>info</key>
				<dict>
					<key>Group Object Classes</key>
					<string>OR</string>
					<key>Object Classes</key>
					<array>
						<string>posixGroup</string>
					</array>
					<key>Search Base</key>
					<string>dc=HERE,dc=YOUR,dc=DOMAIN</string>
				</dict>
			</dict>
			<key>dsRecTypeStandard:Mounts</key>
			<dict>
				<key>attributetypes</key>
				<dict>
					<key>dsAttrTypeStandard:CreationTimestamp</key>
					<dict>
						<key>native</key>
						<string>createTimestamp</string>
					</dict>
					<key>dsAttrTypeStandard:ModificationTimestamp</key>
					<dict>
						<key>native</key>
						<string>modifyTimestamp</string>
					</dict>
					<key>dsAttrTypeStandard:RecordName</key>
					<dict>
						<key>native</key>
						<string>cn</string>
					</dict>
					<key>dsAttrTypeStandard:VFSDumpFreq</key>
					<dict>
						<key>native</key>
						<string>mountDumpFrequency</string>
					</dict>
					<key>dsAttrTypeStandard:VFSLinkDir</key>
					<dict>
						<key>native</key>
						<string>mountDirectory</string>
					</dict>
					<key>dsAttrTypeStandard:VFSOpts</key>
					<dict>
						<key>native</key>
						<string>mountOption</string>
					</dict>
					<key>dsAttrTypeStandard:VFSPassNo</key>
					<dict>
						<key>native</key>
						<string>mountPassNo</string>
					</dict>
					<key>dsAttrTypeStandard:VFSType</key>
					<dict>
						<key>native</key>
						<string>mountType</string>
					</dict>
				</dict>
				<key>info</key>
				<dict>
					<key>Group Object Classes</key>
					<string>OR</string>
					<key>Object Classes</key>
					<array>
						<string>mount</string>
					</array>
					<key>Search Base</key>
					<string>dc=HERE,dc=YOUR,dc=DOMAIN</string>
				</dict>
			</dict>
			<key>dsRecTypeStandard:OrganizationalUnit</key>
			<dict>
				<key>attributetypes</key>
				<dict>
					<key>dsAttrTypeStandard:AddressLine1</key>
					<dict>
						<key>native</key>
						<string>street</string>
					</dict>
					<key>dsAttrTypeStandard:City</key>
					<dict>
						<key>native</key>
						<string>l</string>
					</dict>
					<key>dsAttrTypeStandard:Comment</key>
					<dict>
						<key>native</key>
						<string>description</string>
					</dict>
					<key>dsAttrTypeStandard:Country</key>
					<dict>
						<key>native</key>
						<string>c</string>
					</dict>
					<key>dsAttrTypeStandard:FAXNumber</key>
					<dict>
						<key>native</key>
						<string>facsimileTelephoneNumber</string>
					</dict>
					<key>dsAttrTypeStandard:Password</key>
					<dict>
						<key>native</key>
						<string>userPassword</string>
					</dict>
					<key>dsAttrTypeStandard:PhoneNumber</key>
					<dict>
						<key>native</key>
						<string>telephoneNumber</string>
					</dict>
					<key>dsAttrTypeStandard:PostalAddress</key>
					<dict>
						<key>native</key>
						<string>postalAddress</string>
					</dict>
					<key>dsAttrTypeStandard:PostalCode</key>
					<dict>
						<key>native</key>
						<string>postalCode</string>
					</dict>
					<key>dsAttrTypeStandard:RealName</key>
					<dict>
						<key>native</key>
						<string>cn</string>
					</dict>
					<key>dsAttrTypeStandard:RecordName</key>
					<dict>
						<key>native</key>
						<string>ou</string>
					</dict>
					<key>dsAttrTypeStandard:State</key>
					<dict>
						<key>native</key>
						<string>st</string>
					</dict>
					<key>dsAttrTypeStandard:Street</key>
					<dict>
						<key>native</key>
						<string>street</string>
					</dict>
				</dict>
				<key>info</key>
				<dict>
					<key>Group Object Classes</key>
					<string>OR</string>
					<key>Object Classes</key>
					<array>
						<string>organizationalUnit</string>
					</array>
					<key>Search Base</key>
					<string>dc=HERE,dc=YOUR,dc=DOMAIN</string>
				</dict>
			</dict>
			<key>dsRecTypeStandard:People</key>
			<dict>
				<key>attributetypes</key>
				<dict>
					<key>dsAttrTypeStandard:AddressLine1</key>
					<dict>
						<key>native</key>
						<string>street</string>
					</dict>
					<key>dsAttrTypeStandard:Building</key>
					<dict>
						<key>native</key>
						<string>buildingName</string>
					</dict>
					<key>dsAttrTypeStandard:City</key>
					<dict>
						<key>native</key>
						<string>l</string>
					</dict>
					<key>dsAttrTypeStandard:Country</key>
					<dict>
						<key>native</key>
						<string>c</string>
					</dict>
					<key>dsAttrTypeStandard:CreationTimestamp</key>
					<dict>
						<key>native</key>
						<string>createTimestamp</string>
					</dict>
					<key>dsAttrTypeStandard:Department</key>
					<dict>
						<key>native</key>
						<string>departmentNumber</string>
					</dict>
					<key>dsAttrTypeStandard:EMailAddress</key>
					<dict>
						<key>native</key>
						<string>mail</string>
					</dict>
					<key>dsAttrTypeStandard:FAXNumber</key>
					<dict>
						<key>native</key>
						<string>facsimileTelephoneNumber</string>
					</dict>
					<key>dsAttrTypeStandard:FirstName</key>
					<dict>
						<key>native</key>
						<string>givenName</string>
					</dict>
					<key>dsAttrTypeStandard:HomePhoneNumber</key>
					<dict>
						<key>native</key>
						<string>homePhone</string>
					</dict>
					<key>dsAttrTypeStandard:JobTitle</key>
					<dict>
						<key>native</key>
						<string>title</string>
					</dict>
					<key>dsAttrTypeStandard:LastName</key>
					<dict>
						<key>native</key>
						<string>sn</string>
					</dict>
					<key>dsAttrTypeStandard:MobileNumber</key>
					<dict>
						<key>native</key>
						<string>mobile</string>
					</dict>
					<key>dsAttrTypeStandard:ModificationTimestamp</key>
					<dict>
						<key>native</key>
						<string>modifyTimestamp</string>
					</dict>
					<key>dsAttrTypeStandard:OrganizationName</key>
					<dict>
						<key>native</key>
						<string>o</string>
					</dict>
					<key>dsAttrTypeStandard:PagerNumber</key>
					<dict>
						<key>native</key>
						<string>pager</string>
					</dict>
					<key>dsAttrTypeStandard:PhoneNumber</key>
					<dict>
						<key>native</key>
						<string>telephoneNumber</string>
					</dict>
					<key>dsAttrTypeStandard:PostalAddress</key>
					<dict>
						<key>native</key>
						<string>postalAddress</string>
					</dict>
					<key>dsAttrTypeStandard:PostalCode</key>
					<dict>
						<key>native</key>
						<string>postalCode</string>
					</dict>
					<key>dsAttrTypeStandard:RealName</key>
					<dict>
						<key>native</key>
						<string>cn</string>
					</dict>
					<key>dsAttrTypeStandard:RecordName</key>
					<dict>
						<key>native</key>
						<string>cn</string>
					</dict>
					<key>dsAttrTypeStandard:State</key>
					<dict>
						<key>native</key>
						<string>st</string>
					</dict>
					<key>dsAttrTypeStandard:Street</key>
					<dict>
						<key>native</key>
						<string>street</string>
					</dict>
					<key>dsAttrTypeStandard:UserCertificate</key>
					<dict>
						<key>native</key>
						<string>userCertificate;binary</string>
					</dict>
					<key>dsAttrTypeStandard:UserPKCS12Data</key>
					<dict>
						<key>native</key>
						<string>userPKCS12</string>
					</dict>
					<key>dsAttrTypeStandard:UserSMIMECertificate</key>
					<dict>
						<key>native</key>
						<string>userSMIMECertificate</string>
					</dict>
				</dict>
				<key>info</key>
				<dict>
					<key>Group Object Classes</key>
					<string>OR</string>
					<key>Object Classes</key>
					<array>
						<string>inetOrgPerson</string>
					</array>
					<key>Search Base</key>
					<string>dc=HERE,dc=YOUR,dc=DOMAIN</string>
				</dict>
			</dict>
			<key>dsRecTypeStandard:Users</key>
			<dict>
				<key>attributetypes</key>
				<dict>
					<key>dsAttrTypeStandard:Change</key>
					<dict>
						<key>native</key>
						<string>shadowLastChange</string>
					</dict>
					<key>dsAttrTypeStandard:Comment</key>
					<dict>
						<key>native</key>
						<string>description</string>
					</dict>
					<key>dsAttrTypeStandard:CreationTimestamp</key>
					<dict>
						<key>native</key>
						<string>createTimestamp</string>
					</dict>
					<key>dsAttrTypeStandard:Expire</key>
					<dict>
						<key>native</key>
						<string>shadowExpire</string>
					</dict>
					<key>dsAttrTypeStandard:GeneratedUID</key>
					<dict>
						<key>native</key>
						<string>apple-generateduid</string>
					</dict>
					<key>dsAttrTypeStandard:ModificationTimestamp</key>
					<dict>
						<key>native</key>
						<string>modifyTimestamp</string>
					</dict>
					<key>dsAttrTypeStandard:NFSHomeDirectory</key>
					<dict>
						<key>native</key>
						<string>#/Users/$uid$</string>
					</dict>
					<key>dsAttrTypeStandard:Password</key>
					<dict>
						<key>native</key>
						<string>userPassword</string>
					</dict>
					<key>dsAttrTypeStandard:PrimaryGroupID</key>
					<dict>
						<key>native</key>
						<string>gidNumber</string>
					</dict>
					<key>dsAttrTypeStandard:RealName</key>
					<dict>
						<key>native</key>
						<string>cn</string>
					</dict>
					<key>dsAttrTypeStandard:RecordName</key>
					<dict>
						<key>native</key>
						<string>uid</string>
					</dict>
					<key>dsAttrTypeStandard:UniqueID</key>
					<dict>
						<key>native</key>
						<string>uidNumber</string>
					</dict>
					<key>dsAttrTypeStandard:UserShell</key>
					<dict>
						<key>native</key>
						<string>loginShell</string>
					</dict>
				</dict>
				<key>info</key>
				<dict>
					<key>Group Object Classes</key>
					<string>OR</string>
					<key>Object Classes</key>
					<array>
						<string>posixAccount</string>
						<string>inetOrgPerson</string>
						<string>shadowAccount</string>
					</array>
					<key>Search Base</key>
					<string>dc=HERE,dc=YOUR,dc=DOMAIN</string>
				</dict>
			</dict>
		</dict>
	</dict>
	<key>module options</key>
	<dict>
		<key>AppleODClient</key>
		<dict>
			<key>Server Mappings</key>
			<false/>
		</dict>
		<key>ldap</key>
		<dict>
			<key>Template Search Base Suffix</key>
			<string>dc=HERE,dc=YOU,dc=DOMAIN</string>
		</dict>
	</dict>
	<key>node name</key>
	<string>/LDAPv3/ldap.google.com</string>
	<key>options</key>
	<dict>
		<key>connection idle disconnect</key>
		<integer>120</integer>
		<key>destination</key>
		<dict>
			<key>host</key>
			<string>ldap.google.com</string>
			<key>other</key>
			<string>ldaps</string>
			<key>port</key>
			<integer>636</integer>
		</dict>
		<key>man-in-the-middle</key>
		<false/>
		<key>no cleartext authentication</key>
		<false/>
		<key>packet encryption</key>
		<integer>3</integer>
		<key>packet signing</key>
		<integer>1</integer>
	</dict>
	<key>template</key>
	<string>LDAPv3</string>
	<key>trusttype</key>
	<string>anonymous</string>
	<key>uuid</key>
	<string>YOUR UUID</string>
</dict>
</plist>
​

 

I personally created binary from the script (ldap.py) for connivance. You need a lot of python modules to be installed for it to run. After i created .pkg with ldap.google.com.plist, binary and postinstall script to run the binary. Sing it, load it into your prestage and you basically can login with Google LDAP for prestage user creation. Its very simple just run .pkg and you're connected.